Issue #13964: signal.sigtimedwait() timeout is now a float instead of a tuple
Add a private API to convert an int or float to a C timespec structure.
This commit is contained in:
parent
1c13f84f55
commit
643cd68ea4
|
@ -369,12 +369,11 @@ The :mod:`signal` module defines the following functions:
|
|||
.. versionadded:: 3.3
|
||||
|
||||
|
||||
.. function:: sigtimedwait(sigset, (timeout_sec, timeout_nsec))
|
||||
.. function:: sigtimedwait(sigset, timeout)
|
||||
|
||||
Like :func:`sigtimedwait`, but takes a tuple of ``(seconds, nanoseconds)``
|
||||
as an additional argument specifying a timeout. If both *timeout_sec* and
|
||||
*timeout_nsec* are specified as :const:`0`, a poll is performed. Returns
|
||||
:const:`None` if a timeout occurs.
|
||||
Like :func:`sigwaitinfo`, but takes an additional *timeout* argument
|
||||
specifying a timeout. If *timeout* is specified as :const:`0`, a poll is
|
||||
performed. Returns :const:`None` if a timeout occurs.
|
||||
|
||||
Availability: Unix (see the man page :manpage:`sigtimedwait(2)` for further
|
||||
information).
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#define Py_PYTIME_H
|
||||
|
||||
#include "pyconfig.h" /* include for defines */
|
||||
#include "object.h"
|
||||
|
||||
/**************************************************************************
|
||||
Symbols and macros to supply platform-independent interfaces to time related
|
||||
|
@ -37,6 +38,16 @@ do { \
|
|||
((tv_end.tv_sec - tv_start.tv_sec) + \
|
||||
(tv_end.tv_usec - tv_start.tv_usec) * 0.000001)
|
||||
|
||||
#ifndef Py_LIMITED_API
|
||||
/* Convert a number of seconds, int or float, to a timespec structure.
|
||||
nsec is always in the range [0; 999999999]. For example, -1.2 is converted
|
||||
to (-2, 800000000). */
|
||||
PyAPI_FUNC(int) _PyTime_ObjectToTimespec(
|
||||
PyObject *obj,
|
||||
time_t *sec,
|
||||
long *nsec);
|
||||
#endif
|
||||
|
||||
/* Dummy to force linking. */
|
||||
PyAPI_FUNC(void) _PyTime_Init(void);
|
||||
|
||||
|
|
|
@ -662,7 +662,7 @@ class PendingSignalsTests(unittest.TestCase):
|
|||
self.wait_helper(signal.SIGALRM, '''
|
||||
def test(signum):
|
||||
signal.alarm(1)
|
||||
info = signal.sigtimedwait([signum], (10, 1000))
|
||||
info = signal.sigtimedwait([signum], 10.1000)
|
||||
if info.si_signo != signum:
|
||||
raise Exception('info.si_signo != %s' % signum)
|
||||
''')
|
||||
|
@ -675,7 +675,7 @@ class PendingSignalsTests(unittest.TestCase):
|
|||
def test(signum):
|
||||
import os
|
||||
os.kill(os.getpid(), signum)
|
||||
info = signal.sigtimedwait([signum], (0, 0))
|
||||
info = signal.sigtimedwait([signum], 0)
|
||||
if info.si_signo != signum:
|
||||
raise Exception('info.si_signo != %s' % signum)
|
||||
''')
|
||||
|
@ -685,7 +685,7 @@ class PendingSignalsTests(unittest.TestCase):
|
|||
def test_sigtimedwait_timeout(self):
|
||||
self.wait_helper(signal.SIGALRM, '''
|
||||
def test(signum):
|
||||
received = signal.sigtimedwait([signum], (1, 0))
|
||||
received = signal.sigtimedwait([signum], 1.0)
|
||||
if received is not None:
|
||||
raise Exception("received=%r" % (received,))
|
||||
''')
|
||||
|
@ -694,9 +694,7 @@ class PendingSignalsTests(unittest.TestCase):
|
|||
'need signal.sigtimedwait()')
|
||||
def test_sigtimedwait_negative_timeout(self):
|
||||
signum = signal.SIGALRM
|
||||
self.assertRaises(ValueError, signal.sigtimedwait, [signum], (-1, -1))
|
||||
self.assertRaises(ValueError, signal.sigtimedwait, [signum], (0, -1))
|
||||
self.assertRaises(ValueError, signal.sigtimedwait, [signum], (-1, 0))
|
||||
self.assertRaises(ValueError, signal.sigtimedwait, [signum], -1.0)
|
||||
|
||||
@unittest.skipUnless(hasattr(signal, 'sigwaitinfo'),
|
||||
'need signal.sigwaitinfo()')
|
||||
|
|
|
@ -497,12 +497,31 @@ class TestStrftime4dyear(_TestStrftimeYear, _Test4dYear):
|
|||
pass
|
||||
|
||||
|
||||
class TestPytime(unittest.TestCase):
|
||||
def test_timespec(self):
|
||||
from _testcapi import pytime_object_to_timespec
|
||||
for obj, timespec in (
|
||||
(0, (0, 0)),
|
||||
(-1, (-1, 0)),
|
||||
(-1.0, (-1, 0)),
|
||||
(-1e-9, (-1, 999999999)),
|
||||
(-1.2, (-2, 800000000)),
|
||||
(1.123456789, (1, 123456789)),
|
||||
):
|
||||
self.assertEqual(pytime_object_to_timespec(obj), timespec)
|
||||
|
||||
for invalid in (-(2 ** 100), -(2.0 ** 100.0), 2 ** 100, 2.0 ** 100.0):
|
||||
self.assertRaises(OverflowError, pytime_object_to_timespec, invalid)
|
||||
|
||||
|
||||
|
||||
def test_main():
|
||||
support.run_unittest(
|
||||
TimeTestCase,
|
||||
TestLocale,
|
||||
TestAsctime4dyear,
|
||||
TestStrftime4dyear)
|
||||
TestStrftime4dyear,
|
||||
TestPytime)
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_main()
|
||||
|
|
|
@ -2323,6 +2323,24 @@ run_in_subinterp(PyObject *self, PyObject *args)
|
|||
return PyLong_FromLong(r);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
test_pytime_object_to_timespec(PyObject *self, PyObject *args)
|
||||
{
|
||||
PyObject *obj;
|
||||
time_t sec;
|
||||
long nsec;
|
||||
if (!PyArg_ParseTuple(args, "O:pytime_object_to_timespec", &obj))
|
||||
return NULL;
|
||||
if (_PyTime_ObjectToTimespec(obj, &sec, &nsec) == -1)
|
||||
return NULL;
|
||||
#if defined(HAVE_LONG_LONG) && SIZEOF_TIME_T == SIZEOF_LONG_LONG
|
||||
return Py_BuildValue("Ll", (PY_LONG_LONG)sec, nsec);
|
||||
#else
|
||||
assert(sizeof(time_t) <= sizeof(long));
|
||||
return Py_BuildValue("ll", (long)sec, nsec);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static PyMethodDef TestMethods[] = {
|
||||
{"raise_exception", raise_exception, METH_VARARGS},
|
||||
|
@ -2412,6 +2430,7 @@ static PyMethodDef TestMethods[] = {
|
|||
METH_NOARGS},
|
||||
{"crash_no_current_thread", (PyCFunction)crash_no_current_thread, METH_NOARGS},
|
||||
{"run_in_subinterp", run_in_subinterp, METH_VARARGS},
|
||||
{"pytime_object_to_timespec", test_pytime_object_to_timespec, METH_VARARGS},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
|
|
|
@ -783,16 +783,11 @@ signal_sigtimedwait(PyObject *self, PyObject *args)
|
|||
siginfo_t si;
|
||||
int err;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "OO:sigtimedwait", &signals, &timeout))
|
||||
if (!PyArg_ParseTuple(args, "OO:sigtimedwait",
|
||||
&signals, &timeout))
|
||||
return NULL;
|
||||
|
||||
if (!PyTuple_Check(timeout) || PyTuple_Size(timeout) != 2) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"sigtimedwait() arg 2 must be a tuple "
|
||||
"(timeout_sec, timeout_nsec)");
|
||||
return NULL;
|
||||
} else if (!PyArg_ParseTuple(timeout, "ll:sigtimedwait",
|
||||
&(buf.tv_sec), &(buf.tv_nsec)))
|
||||
if (_PyTime_ObjectToTimespec(timeout, &buf.tv_sec, &buf.tv_nsec) == -1)
|
||||
return NULL;
|
||||
|
||||
if (buf.tv_sec < 0 || buf.tv_nsec < 0) {
|
||||
|
|
|
@ -70,6 +70,51 @@ _PyTime_gettimeofday(_PyTime_timeval *tp)
|
|||
#endif /* MS_WINDOWS */
|
||||
}
|
||||
|
||||
int
|
||||
_PyTime_ObjectToTimespec(PyObject *obj, time_t *sec, long *nsec)
|
||||
{
|
||||
if (PyFloat_Check(obj)) {
|
||||
double d, intpart, floatpart, err;
|
||||
|
||||
d = PyFloat_AsDouble(obj);
|
||||
floatpart = modf(d, &intpart);
|
||||
if (floatpart < 0) {
|
||||
floatpart = 1.0 + floatpart;
|
||||
intpart -= 1.0;
|
||||
}
|
||||
|
||||
*sec = (time_t)intpart;
|
||||
err = intpart - (double)*sec;
|
||||
if (err <= -1.0 || err >= 1.0)
|
||||
goto overflow;
|
||||
|
||||
floatpart *= 1e9;
|
||||
*nsec = (long)floatpart;
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
#if defined(HAVE_LONG_LONG) && SIZEOF_TIME_T == SIZEOF_LONG_LONG
|
||||
*sec = PyLong_AsLongLong(obj);
|
||||
#else
|
||||
assert(sizeof(time_t) <= sizeof(long));
|
||||
*sec = PyLong_AsLong(obj);
|
||||
#endif
|
||||
if (*sec == -1 && PyErr_Occurred()) {
|
||||
if (PyErr_ExceptionMatches(PyExc_OverflowError))
|
||||
goto overflow;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
*nsec = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
overflow:
|
||||
PyErr_SetString(PyExc_OverflowError,
|
||||
"timestamp out of range for platform time_t");
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
_PyTime_Init()
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue