But this blocks indefinitely. Ok lets try with GLib.IOFlags.NONBLOCK and see what we get, nothing. For comparison, os.read(FD, 8) in the same callback works fine.
How is GLib.IOChannel.read_chars() supposed to work under python?
I appreciate the comment but it isn’t an answer to the question. I can think of at least 3 other ways to monitor /watching /dev/rfkill. The question is how GLib.IOChannel.read_chars() is supposed to work. Am I doing it wrong, is it broken in python (or in general)?
The IOChannel watch is working perfectly, it is the reading that is causing problems.
GIOStatus
g_io_channel_read_chars (GIOChannel *channel,
/* ↑ the IOChannel instance, like `self` */
gchar *buf,
/* ↑ a byte† array to put data in
(that is, _returned_) */
gsize count,
/* ↑ how big is buf, really buf+count are
two parts of one “object” */
gsize *bytes_read,
/* ↑ somewhere to put (again _return_)
the number of bytes, ≤ count,
actually read */
GError **error);
/* ↑ We might error (raise an exception),
this is where error information is put
(returned) */
/* † - Yes, simplifying I know */
Now understandably PyGObject hides GError completely, you’ve got exceptions for that
Since python allows returning multiple values bytes_read is quite simply handled: Just add it to the return tuple along with the status
The tricky part is buf+count, PyGObject has noticed byte array + length is basically just bytes - In theory a perfectly reasonable thing to do. Of course what’s failed here is that now you can’t set the buffer size, presumably PyGObject has picked one for you. So your not randomly hung, it’s just GIOChannel is trying to read some unreasonable number of bytes which /dev/rfkill won’t provide.
Congrats, you found a bug!
Though I’m guessing you probably want read_to_end here anyway
I just tried read_to_end, read_line and pretty much all the new api provides. All block indefinitely and when I set GLib.IOFlags.NONBLOCK always get 0 bytes back.
I went back to using os.read for now because unfortunately reading from a IOChannel under python is too broken.
Apparently reading from an IOChannel in pygobject can only be done with the read() method. It works because it is a static binding around read_chars(), using read_chars() directly is broken (presumably all of the reading api is). However read() has it’s own set of problems .
For future reference, just use os.read() and ignore any of the read methods. Working example.
from gi.repository import GLib
import os
def io_event(channel, condition):
fd = channel.unix_get_fd()
data = os.read(fd, 8)
print(f"Just use os.read() folks {data}")
return True
c = GLib.IOChannel.new_file("/dev/rfkill", "r")
wid = GLib.io_add_watch(c, GLib.IO_IN, io_event)
loop = GLib.MainLoop()
loop.run()