A few missing files from the twisted patch
This commit is contained in:
101
vendor/Twisted-10.0.0/twisted/internet/_sigchld.c
vendored
Normal file
101
vendor/Twisted-10.0.0/twisted/internet/_sigchld.c
vendored
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2010 Twisted Matrix Laboratories.
|
||||||
|
* See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "Python.h"
|
||||||
|
|
||||||
|
static int sigchld_pipe_fd = -1;
|
||||||
|
|
||||||
|
static void got_signal(int sig) {
|
||||||
|
int saved_errno = errno;
|
||||||
|
int ignored_result;
|
||||||
|
|
||||||
|
/* write() errors are unhandled. If the buffer is full, we don't
|
||||||
|
* care. What about other errors? */
|
||||||
|
ignored_result = write(sigchld_pipe_fd, "x", 1);
|
||||||
|
|
||||||
|
errno = saved_errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(install_sigchld_handler_doc, "\
|
||||||
|
install_sigchld_handler(fd)\n\
|
||||||
|
\n\
|
||||||
|
Installs a SIGCHLD handler which will write a byte to the given fd\n\
|
||||||
|
whenever a SIGCHLD occurs. This is done in C code because the python\n\
|
||||||
|
signal handling system is not reliable, and additionally cannot\n\
|
||||||
|
specify SA_RESTART.\n\
|
||||||
|
\n\
|
||||||
|
Please ensure fd is in non-blocking mode.\n\
|
||||||
|
");
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
install_sigchld_handler(PyObject *self, PyObject *args) {
|
||||||
|
int fd, old_fd;
|
||||||
|
struct sigaction sa;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "i:install_sigchld_handler", &fd)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
old_fd = sigchld_pipe_fd;
|
||||||
|
sigchld_pipe_fd = fd;
|
||||||
|
|
||||||
|
if (fd == -1) {
|
||||||
|
sa.sa_handler = SIG_DFL;
|
||||||
|
} else {
|
||||||
|
sa.sa_handler = got_signal;
|
||||||
|
sa.sa_flags = SA_RESTART;
|
||||||
|
/* mask all signals so I don't worry about EINTR from the write. */
|
||||||
|
sigfillset(&sa.sa_mask);
|
||||||
|
}
|
||||||
|
if (sigaction(SIGCHLD, &sa, 0) != 0) {
|
||||||
|
sigchld_pipe_fd = old_fd;
|
||||||
|
return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
}
|
||||||
|
return PyLong_FromLong(old_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(is_default_handler_doc, "\
|
||||||
|
Return 1 if the SIGCHLD handler is SIG_DFL, 0 otherwise.\n\
|
||||||
|
");
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
is_default_handler(PyObject *self, PyObject *args) {
|
||||||
|
/*
|
||||||
|
* This implementation is necessary since the install_sigchld_handler
|
||||||
|
* function above bypasses the Python signal handler installation API, so
|
||||||
|
* CPython doesn't notice that the handler has changed and signal.getsignal
|
||||||
|
* won't return an accurate result.
|
||||||
|
*/
|
||||||
|
struct sigaction sa;
|
||||||
|
|
||||||
|
if (sigaction(SIGCHLD, NULL, &sa) != 0) {
|
||||||
|
return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
}
|
||||||
|
|
||||||
|
return PyLong_FromLong(sa.sa_handler == SIG_DFL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyMethodDef sigchld_methods[] = {
|
||||||
|
{"installHandler", install_sigchld_handler, METH_VARARGS,
|
||||||
|
install_sigchld_handler_doc},
|
||||||
|
{"isDefaultHandler", is_default_handler, METH_NOARGS,
|
||||||
|
is_default_handler_doc},
|
||||||
|
/* sentinel */
|
||||||
|
{NULL, NULL, 0, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static const char _sigchld_doc[] = "\n\
|
||||||
|
This module contains an API for receiving SIGCHLD via a file descriptor.\n\
|
||||||
|
";
|
||||||
|
|
||||||
|
PyMODINIT_FUNC
|
||||||
|
init_sigchld(void) {
|
||||||
|
/* Create the module and add the functions */
|
||||||
|
Py_InitModule3(
|
||||||
|
"twisted.internet._sigchld", sigchld_methods, _sigchld_doc);
|
||||||
|
}
|
184
vendor/Twisted-10.0.0/twisted/internet/_signals.py
vendored
Normal file
184
vendor/Twisted-10.0.0/twisted/internet/_signals.py
vendored
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
# -*- test-case-name: twisted.test.test_process,twisted.internet.test.test_process -*-
|
||||||
|
# Copyright (c) 2010 Twisted Matrix Laboratories.
|
||||||
|
# See LICENSE for details.
|
||||||
|
|
||||||
|
"""
|
||||||
|
This module provides a uniform interface to the several mechanisms which are
|
||||||
|
possibly available for dealing with signals.
|
||||||
|
|
||||||
|
This module is used to integrate child process termination into a
|
||||||
|
reactor event loop. This is a challenging feature to provide because
|
||||||
|
most platforms indicate process termination via SIGCHLD and do not
|
||||||
|
provide a way to wait for that signal and arbitrary I/O events at the
|
||||||
|
same time. The naive implementation involves installing a Python
|
||||||
|
SIGCHLD handler; unfortunately this leads to other syscalls being
|
||||||
|
interrupted (whenever SIGCHLD is received) and failing with EINTR
|
||||||
|
(which almost no one is prepared to handle). This interruption can be
|
||||||
|
disabled via siginterrupt(2) (or one of the equivalent mechanisms);
|
||||||
|
however, if the SIGCHLD is delivered by the platform to a non-main
|
||||||
|
thread (not a common occurrence, but difficult to prove impossible),
|
||||||
|
the main thread (waiting on select() or another event notification
|
||||||
|
API) may not wake up leading to an arbitrary delay before the child
|
||||||
|
termination is noticed.
|
||||||
|
|
||||||
|
The basic solution to all these issues involves enabling SA_RESTART
|
||||||
|
(ie, disabling system call interruption) and registering a C signal
|
||||||
|
handler which writes a byte to a pipe. The other end of the pipe is
|
||||||
|
registered with the event loop, allowing it to wake up shortly after
|
||||||
|
SIGCHLD is received. See L{twisted.internet.posixbase._SIGCHLDWaker}
|
||||||
|
for the implementation of the event loop side of this solution. The
|
||||||
|
use of a pipe this way is known as the U{self-pipe
|
||||||
|
trick<http://cr.yp.to/docs/selfpipe.html>}.
|
||||||
|
|
||||||
|
The actual solution implemented in this module depends on the version
|
||||||
|
of Python. From version 2.6, C{signal.siginterrupt} and
|
||||||
|
C{signal.set_wakeup_fd} allow the necessary C signal handler which
|
||||||
|
writes to the pipe to be registered with C{SA_RESTART}. Prior to 2.6,
|
||||||
|
the L{twisted.internet._sigchld} extension module provides similar
|
||||||
|
functionality.
|
||||||
|
|
||||||
|
If neither of these is available, a Python signal handler is used
|
||||||
|
instead. This is essentially the naive solution mentioned above and
|
||||||
|
has the problems described there.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
try:
|
||||||
|
from signal import set_wakeup_fd, siginterrupt
|
||||||
|
except ImportError:
|
||||||
|
set_wakeup_fd = siginterrupt = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
import signal
|
||||||
|
except ImportError:
|
||||||
|
signal = None
|
||||||
|
|
||||||
|
from twisted.python.log import msg
|
||||||
|
|
||||||
|
try:
|
||||||
|
from twisted.internet._sigchld import installHandler as _extInstallHandler, \
|
||||||
|
isDefaultHandler as _extIsDefaultHandler
|
||||||
|
except ImportError:
|
||||||
|
_extInstallHandler = _extIsDefaultHandler = None
|
||||||
|
|
||||||
|
|
||||||
|
class _Handler(object):
|
||||||
|
"""
|
||||||
|
L{_Handler} is a signal handler which writes a byte to a file descriptor
|
||||||
|
whenever it is invoked.
|
||||||
|
|
||||||
|
@ivar fd: The file descriptor to which to write. If this is C{None},
|
||||||
|
nothing will be written.
|
||||||
|
"""
|
||||||
|
def __init__(self, fd):
|
||||||
|
self.fd = fd
|
||||||
|
|
||||||
|
|
||||||
|
def __call__(self, *args):
|
||||||
|
"""
|
||||||
|
L{_Handler.__call__} is the signal handler. It will write a byte to
|
||||||
|
the wrapped file descriptor, if there is one.
|
||||||
|
"""
|
||||||
|
if self.fd is not None:
|
||||||
|
try:
|
||||||
|
os.write(self.fd, '\0')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _installHandlerUsingSignal(fd):
|
||||||
|
"""
|
||||||
|
Install a signal handler which will write a byte to C{fd} when
|
||||||
|
I{SIGCHLD} is received.
|
||||||
|
|
||||||
|
This is implemented by creating an instance of L{_Handler} with C{fd}
|
||||||
|
and installing it as the signal handler.
|
||||||
|
|
||||||
|
@param fd: The file descriptor to which to write when I{SIGCHLD} is
|
||||||
|
received.
|
||||||
|
@type fd: C{int}
|
||||||
|
"""
|
||||||
|
if fd == -1:
|
||||||
|
previous = signal.signal(signal.SIGCHLD, signal.SIG_DFL)
|
||||||
|
else:
|
||||||
|
previous = signal.signal(signal.SIGCHLD, _Handler(fd))
|
||||||
|
if isinstance(previous, _Handler):
|
||||||
|
return previous.fd
|
||||||
|
return -1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _installHandlerUsingSetWakeup(fd):
|
||||||
|
"""
|
||||||
|
Install a signal handler which will write a byte to C{fd} when
|
||||||
|
I{SIGCHLD} is received.
|
||||||
|
|
||||||
|
This is implemented by installing an instance of L{_Handler} wrapped
|
||||||
|
around C{None}, setting the I{SIGCHLD} handler as not allowed to
|
||||||
|
interrupt system calls, and using L{signal.set_wakeup_fd} to do the
|
||||||
|
actual writing.
|
||||||
|
|
||||||
|
@param fd: The file descriptor to which to write when I{SIGCHLD} is
|
||||||
|
received.
|
||||||
|
@type fd: C{int}
|
||||||
|
"""
|
||||||
|
if fd == -1:
|
||||||
|
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
|
||||||
|
else:
|
||||||
|
signal.signal(signal.SIGCHLD, _Handler(None))
|
||||||
|
siginterrupt(signal.SIGCHLD, False)
|
||||||
|
return set_wakeup_fd(fd)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _isDefaultHandler():
|
||||||
|
"""
|
||||||
|
Determine whether the I{SIGCHLD} handler is the default or not.
|
||||||
|
"""
|
||||||
|
return signal.getsignal(signal.SIGCHLD) == signal.SIG_DFL
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _cannotInstallHandler(fd):
|
||||||
|
"""
|
||||||
|
Fail to install a signal handler for I{SIGCHLD}.
|
||||||
|
|
||||||
|
This implementation is used when the supporting code for the other
|
||||||
|
implementations is unavailable (on Python versions 2.5 and older where
|
||||||
|
neither the L{twisted.internet._sigchld} extension nor the standard
|
||||||
|
L{signal} module is available).
|
||||||
|
|
||||||
|
@param fd: Ignored; only for compatibility with the other
|
||||||
|
implementations of this interface.
|
||||||
|
|
||||||
|
@raise RuntimeError: Always raised to indicate no I{SIGCHLD} handler can
|
||||||
|
be installed.
|
||||||
|
"""
|
||||||
|
raise RuntimeError("Cannot install a SIGCHLD handler")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _cannotDetermineDefault():
|
||||||
|
raise RuntimeError("No usable signal API available")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if set_wakeup_fd is not None:
|
||||||
|
msg('using set_wakeup_fd')
|
||||||
|
installHandler = _installHandlerUsingSetWakeup
|
||||||
|
isDefaultHandler = _isDefaultHandler
|
||||||
|
elif _extInstallHandler is not None:
|
||||||
|
msg('using _sigchld')
|
||||||
|
installHandler = _extInstallHandler
|
||||||
|
isDefaultHandler = _extIsDefaultHandler
|
||||||
|
elif signal is not None:
|
||||||
|
msg('using signal module')
|
||||||
|
installHandler = _installHandlerUsingSignal
|
||||||
|
isDefaultHandler = _isDefaultHandler
|
||||||
|
else:
|
||||||
|
msg('nothing unavailable')
|
||||||
|
installHandler = _cannotInstallHandler
|
||||||
|
isDefaultHandler = _cannotDetermineDefault
|
||||||
|
|
194
vendor/Twisted-10.0.0/twisted/internet/test/test_sigchld.py
vendored
Normal file
194
vendor/Twisted-10.0.0/twisted/internet/test/test_sigchld.py
vendored
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
# Copyright (c) 2010 Twisted Matrix Laboratories.
|
||||||
|
# See LICENSE for details.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Tests for L{twisted.internet._sigchld}, an alternate, superior SIGCHLD
|
||||||
|
monitoring API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os, signal, errno
|
||||||
|
|
||||||
|
from twisted.python.log import msg
|
||||||
|
from twisted.trial.unittest import TestCase
|
||||||
|
from twisted.internet.fdesc import setNonBlocking
|
||||||
|
from twisted.internet._signals import installHandler, isDefaultHandler
|
||||||
|
from twisted.internet._signals import _extInstallHandler, _extIsDefaultHandler
|
||||||
|
from twisted.internet._signals import _installHandlerUsingSetWakeup, \
|
||||||
|
_installHandlerUsingSignal, _isDefaultHandler
|
||||||
|
|
||||||
|
|
||||||
|
class SIGCHLDTestsMixin:
|
||||||
|
"""
|
||||||
|
Mixin for L{TestCase} subclasses which defines several tests for
|
||||||
|
I{installHandler} and I{isDefaultHandler}. Subclasses are expected to
|
||||||
|
define C{self.installHandler} and C{self.isDefaultHandler} to invoke the
|
||||||
|
implementation to be tested.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if getattr(signal, 'SIGCHLD', None) is None:
|
||||||
|
skip = "Platform does not have SIGCHLD"
|
||||||
|
|
||||||
|
def installHandler(self, fd):
|
||||||
|
"""
|
||||||
|
Override in a subclass to install a SIGCHLD handler which writes a byte
|
||||||
|
to the given file descriptor. Return the previously registered file
|
||||||
|
descriptor.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
def isDefaultHandler(self):
|
||||||
|
"""
|
||||||
|
Override in a subclass to determine if the current SIGCHLD handler is
|
||||||
|
SIG_DFL or not. Return True if it is SIG_DFL, False otherwise.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
def pipe(self):
|
||||||
|
"""
|
||||||
|
Create a non-blocking pipe which will be closed after the currently
|
||||||
|
running test.
|
||||||
|
"""
|
||||||
|
read, write = os.pipe()
|
||||||
|
self.addCleanup(os.close, read)
|
||||||
|
self.addCleanup(os.close, write)
|
||||||
|
setNonBlocking(read)
|
||||||
|
setNonBlocking(write)
|
||||||
|
return read, write
|
||||||
|
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
Save the current SIGCHLD handler as reported by L{signal.signal} and
|
||||||
|
the current file descriptor registered with L{installHandler}.
|
||||||
|
"""
|
||||||
|
handler = signal.getsignal(signal.SIGCHLD)
|
||||||
|
if handler != signal.SIG_DFL:
|
||||||
|
self.signalModuleHandler = handler
|
||||||
|
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
|
||||||
|
else:
|
||||||
|
self.signalModuleHandler = None
|
||||||
|
|
||||||
|
self.oldFD = self.installHandler(-1)
|
||||||
|
|
||||||
|
if self.signalModuleHandler is not None and self.oldFD != -1:
|
||||||
|
msg("SIGCHLD setup issue: %r %r" % (self.signalModuleHandler, self.oldFD))
|
||||||
|
raise RuntimeError("You used some signal APIs wrong! Try again.")
|
||||||
|
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
"""
|
||||||
|
Restore whatever signal handler was present when setUp ran.
|
||||||
|
"""
|
||||||
|
# If tests set up any kind of handlers, clear them out.
|
||||||
|
self.installHandler(-1)
|
||||||
|
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
|
||||||
|
|
||||||
|
# Now restore whatever the setup was before the test ran.
|
||||||
|
if self.signalModuleHandler is not None:
|
||||||
|
signal.signal(signal.SIGCHLD, self.signalModuleHandler)
|
||||||
|
elif self.oldFD != -1:
|
||||||
|
self.installHandler(self.oldFD)
|
||||||
|
|
||||||
|
|
||||||
|
def test_isDefaultHandler(self):
|
||||||
|
"""
|
||||||
|
L{isDefaultHandler} returns true if the SIGCHLD handler is SIG_DFL,
|
||||||
|
false otherwise.
|
||||||
|
"""
|
||||||
|
self.assertTrue(self.isDefaultHandler())
|
||||||
|
signal.signal(signal.SIGCHLD, signal.SIG_IGN)
|
||||||
|
self.assertFalse(self.isDefaultHandler())
|
||||||
|
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
|
||||||
|
self.assertTrue(self.isDefaultHandler())
|
||||||
|
signal.signal(signal.SIGCHLD, lambda *args: None)
|
||||||
|
self.assertFalse(self.isDefaultHandler())
|
||||||
|
|
||||||
|
|
||||||
|
def test_returnOldFD(self):
|
||||||
|
"""
|
||||||
|
L{installHandler} returns the previously registered file descriptor.
|
||||||
|
"""
|
||||||
|
read, write = self.pipe()
|
||||||
|
oldFD = self.installHandler(write)
|
||||||
|
self.assertEqual(self.installHandler(oldFD), write)
|
||||||
|
|
||||||
|
|
||||||
|
def test_uninstallHandler(self):
|
||||||
|
"""
|
||||||
|
C{installHandler(-1)} removes the SIGCHLD handler completely.
|
||||||
|
"""
|
||||||
|
read, write = self.pipe()
|
||||||
|
self.assertTrue(self.isDefaultHandler())
|
||||||
|
self.installHandler(write)
|
||||||
|
self.assertFalse(self.isDefaultHandler())
|
||||||
|
self.installHandler(-1)
|
||||||
|
self.assertTrue(self.isDefaultHandler())
|
||||||
|
|
||||||
|
|
||||||
|
def test_installHandler(self):
|
||||||
|
"""
|
||||||
|
The file descriptor passed to L{installHandler} has a byte written to
|
||||||
|
it when SIGCHLD is delivered to the process.
|
||||||
|
"""
|
||||||
|
read, write = self.pipe()
|
||||||
|
self.installHandler(write)
|
||||||
|
|
||||||
|
exc = self.assertRaises(OSError, os.read, read, 1)
|
||||||
|
self.assertEqual(exc.errno, errno.EAGAIN)
|
||||||
|
|
||||||
|
os.kill(os.getpid(), signal.SIGCHLD)
|
||||||
|
|
||||||
|
self.assertEqual(len(os.read(read, 5)), 1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class DefaultSIGCHLDTests(SIGCHLDTestsMixin, TestCase):
|
||||||
|
"""
|
||||||
|
Tests for whatever implementation is selected for the L{installHandler}
|
||||||
|
and L{isDefaultHandler} APIs.
|
||||||
|
"""
|
||||||
|
installHandler = staticmethod(installHandler)
|
||||||
|
isDefaultHandler = staticmethod(isDefaultHandler)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ExtensionSIGCHLDTests(SIGCHLDTestsMixin, TestCase):
|
||||||
|
"""
|
||||||
|
Tests for the L{twisted.internet._sigchld} implementation of the
|
||||||
|
L{installHandler} and L{isDefaultHandler} APIs.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
import twisted.internet._sigchld
|
||||||
|
except ImportError:
|
||||||
|
skip = "twisted.internet._sigchld is not available"
|
||||||
|
|
||||||
|
installHandler = _extInstallHandler
|
||||||
|
isDefaultHandler = _extIsDefaultHandler
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SetWakeupSIGCHLDTests(SIGCHLDTestsMixin, TestCase):
|
||||||
|
"""
|
||||||
|
Tests for the L{signal.set_wakeup_fd} implementation of the
|
||||||
|
L{installHandler} and L{isDefaultHandler} APIs.
|
||||||
|
"""
|
||||||
|
# Check both of these. On Ubuntu 9.10 (to take an example completely at
|
||||||
|
# random), Python 2.5 has set_wakeup_fd but not siginterrupt.
|
||||||
|
if (getattr(signal, 'set_wakeup_fd', None) is None
|
||||||
|
or getattr(signal, 'siginterrupt', None) is None):
|
||||||
|
skip = "signal.set_wakeup_fd is not available"
|
||||||
|
|
||||||
|
installHandler = staticmethod(_installHandlerUsingSetWakeup)
|
||||||
|
isDefaultHandler = staticmethod(_isDefaultHandler)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class PlainSignalModuleSIGCHLDTests(SIGCHLDTestsMixin, TestCase):
|
||||||
|
"""
|
||||||
|
Tests for the L{signal.signal} implementation of the L{installHandler}
|
||||||
|
and L{isDefaultHandler} APIs.
|
||||||
|
"""
|
||||||
|
installHandler = staticmethod(_installHandlerUsingSignal)
|
||||||
|
isDefaultHandler = staticmethod(_isDefaultHandler)
|
4
vendor/Twisted-10.0.0/twisted/topfiles/733.bugfix
vendored
Normal file
4
vendor/Twisted-10.0.0/twisted/topfiles/733.bugfix
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
On POSIX platforms, reactors now support child processes in a way
|
||||||
|
which doesn't cause other syscalls to sometimes fail with EINTR (if
|
||||||
|
running on Python 2.6 or if Twisted's extension modules have been
|
||||||
|
built).
|
Reference in New Issue
Block a user