diff --git a/Doc/library/signal.rst b/Doc/library/signal.rst --- a/Doc/library/signal.rst +++ b/Doc/library/signal.rst @@ -369,12 +369,11 @@ The :mod:`signal` module defines the fol .. 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). diff --git a/Include/pytime.h b/Include/pytime.h --- a/Include/pytime.h +++ b/Include/pytime.h @@ -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); diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py --- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -662,7 +662,7 @@ class PendingSignalsTests(unittest.TestC 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.TestC 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.TestC 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.TestC '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()') diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -497,12 +497,31 @@ class TestStrftime4dyear(_TestStrftimeYe 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) + + self.assertRaises(OverflowError, pytime_object_to_timespec, 2 ** 100) + self.assertRaises(OverflowError, pytime_object_to_timespec, 2.0 ** 100) + + + def test_main(): support.run_unittest( TimeTestCase, TestLocale, TestAsctime4dyear, - TestStrftime4dyear) + TestStrftime4dyear, + TestPytime) if __name__ == "__main__": test_main() diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2412,6 +2412,24 @@ run_in_subinterp(PyObject *self, PyObjec 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}, @@ -2502,6 +2520,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 */ }; diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -783,16 +783,11 @@ signal_sigtimedwait(PyObject *self, PyOb 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) { diff --git a/Python/pytime.c b/Python/pytime.c --- a/Python/pytime.c +++ b/Python/pytime.c @@ -70,6 +70,45 @@ _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) { + PyErr_SetString(PyExc_OverflowError, + "timestamp out of range for platform time_t"); + return -1; + } + + floatpart *= 1e9; + *nsec = (long)floatpart; + return 0; + } + else { +#if defined(HAVE_LONG_LONG) && SIZEOF_TIME_T == SIZEOF_LONG_LONG + *sec = PyLong_AsUnsignedLongLong(obj); +#else + assert(sizeof(time_t) <= sizeof(long)); + *sec = PyLong_AsLong(obj); +#endif + if (*sec == -1 && PyErr_Occurred()) + return -1; + *nsec = 0; + return 0; + } +} + void _PyTime_Init() {