Interface to the pselect() system call

This module defines a class PSelecter which can be used to call the system call pselect() and which can also be used in a with statement to block given signals until PSelecter.pselect() is called.

AUTHORS:

Waiting for subprocesses

One possible use is to wait with a timeout until any child process exits, as opposed to os.wait() which doesn’t have a timeout or multiprocessing.Process.join() which waits for one specific process.

Since SIGCHLD is ignored by default, we first need to install a signal handler for SIGCHLD. It doesn’t matter what it does, as long as the signal isn’t ignored:

sage: import signal
sage: def dummy_handler(sig, frame):
....:     pass
....:
sage: _ = signal.signal(signal.SIGCHLD, dummy_handler)

We wait for a child created using the subprocess module:

sage: from sage.ext.pselect import PSelecter
sage: from subprocess import *
sage: with PSelecter([signal.SIGCHLD]) as sel:
....:     p = Popen(["sleep", "1"])
....:     _ = sel.sleep()
....:
sage: p.poll()  # p should be finished
0

Now using the multiprocessing module:

sage: from sage.ext.pselect import PSelecter
sage: from multiprocessing import *
sage: import time
sage: with PSelecter([signal.SIGCHLD]) as sel:
....:     p = Process(target=time.sleep, args=(1,))
....:     p.start()
....:     _ = sel.sleep()
....:     p.is_alive()  # p should be finished
....:
False
class sage.ext.pselect.PSelecter

Bases: object

This class gives an interface to the pselect system call.

It can be used in a with statement to block given signals such that they can only occur during the pselect() or sleep() calls.

As an example, we block the SIGHUP and SIGALRM signals and then raise a SIGALRM signal. The interrupt will only be seen during the sleep() call:

sage: from sage.ext.pselect import PSelecter
sage: import signal, time
sage: with PSelecter([signal.SIGHUP, signal.SIGALRM]) as sel:
....:     os.kill(os.getpid(), signal.SIGALRM)
....:     time.sleep(0.5)  # Simply sleep, no interrupt detected
....:     try:
....:         _ = sel.sleep(1)  # Interrupt seen here
....:     except AlarmInterrupt:
....:         print("Interrupt OK")
....:
Interrupt OK

Warning

If SIGCHLD is blocked inside the with block, then you should not use Popen().wait() or Process().join() because those might block, even if the process has actually exited. Use non-blocking alternatives such as Popen.poll() or multiprocessing.active_children() instead.

pselect(rlist=, []wlist=, []xlist=, []timeout=None)

Wait until one of the given files is ready, or a signal has been received, or until timeout seconds have past.

INPUT:

  • rlist – (default: []) a list of files to wait for reading.
  • wlist – (default: []) a list of files to wait for writing.
  • xlist – (default: []) a list of files to wait for exceptions.
  • timeout – (default: None) a timeout in seconds, where None stands for no timeout.

OUTPUT: A 4-tuple (rready, wready, xready, tmout) where the first three are lists of file descriptors which are ready, that is a subset of (rlist, wlist, xlist). The fourth is a boolean which is True if and only if the command timed out. If pselect was interrupted by a signal, the output is ([], [], [], False).

See also

Use the sleep() method instead if you don’t care about file descriptors.

EXAMPLES:

The file /dev/null should always be available for reading and writing:

sage: from sage.ext.pselect import PSelecter
sage: f = open(os.devnull, "r+")
sage: sel = PSelecter()
sage: sel.pselect(rlist=[f])
([<open file '/dev/null', mode 'r+' at ...>], [], [], False)
sage: sel.pselect(wlist=[f])
([], [<open file '/dev/null', mode 'r+' at ...>], [], False)

A list of various files, all of them should be ready for reading. Also create a pipe, which should be ready for writing, but not reading (since nothing has been written):

sage: f = open(os.devnull, "r")
sage: g = open(os.path.join(SAGE_LOCAL, 'bin', 'python'), "r")
sage: (pr, pw) = os.pipe()
sage: r, w, x, t = PSelecter().pselect([f,g,pr,pw], [pw], [pr,pw])
sage: len(r), len(w), len(x), t
(2, 1, 0, False)

Checking for exceptions on the pipe should simply time out:

sage: sel.pselect(xlist=[pr,pw], timeout=0.2)
([], [], [], True)

TESTS:

It is legal (but silly) to list the same file multiple times:

sage: r, w, x, t = PSelecter().pselect([f,g,f,f,g])
sage: len(r)
5

Invalid input:

sage: PSelecter().pselect([None])
Traceback (most recent call last):
...
TypeError: an integer is required

Open a file and close it, but save the (invalid) file descriptor:

sage: f = open(os.devnull, "r")
sage: n = f.fileno()
sage: f.close()
sage: PSelecter().pselect([n])
Traceback (most recent call last):
...
IOError: ...
sleep(timeout=None)

Wait until a signal has been received, or until timeout seconds have past.

This is implemented as a special case of pselect() with empty lists of file descriptors.

INPUT:

  • timeout – (default: None) a timeout in seconds, where None stands for no timeout.

OUTPUT: A boolean which is True if the call timed out, False if it was interrupted.

EXAMPLES:

A simple wait with timeout:

sage: from sage.ext.pselect import PSelecter
sage: sel = PSelecter()
sage: sel.sleep(timeout=0.1)
True

0 or negative time-outs are allowed, sleep should then return immediately:

sage: sel.sleep(timeout=0)
True
sage: sel.sleep(timeout=-123.45)
True
sage.ext.pselect.get_fileno(f)

Return the file descriptor of f.

INPUT:

  • f – an object with a .fileno method or an integer, which is a file descriptor.

OUTPUT: A C long representing the file descriptor.

EXAMPLES:

sage: from sage.ext.pselect import get_fileno
sage: get_fileno(open(os.devnull))  # random
5
sage: get_fileno(42)
42
sage: get_fileno(None)
Traceback (most recent call last):
...
TypeError: an integer is required
sage: get_fileno(-1)
Traceback (most recent call last):
...
ValueError: Invalid file descriptor
sage: get_fileno(2^30)
Traceback (most recent call last):
...
ValueError: Invalid file descriptor

Table Of Contents

Previous topic

MISSING TITLE

This Page