]> granicus.if.org Git - python/commitdiff
Issue #13964: signal.sigtimedwait() timeout is now a float instead of a tuple
authorVictor Stinner <victor.stinner@gmail.com>
Fri, 2 Mar 2012 21:54:03 +0000 (22:54 +0100)
committerVictor Stinner <victor.stinner@gmail.com>
Fri, 2 Mar 2012 21:54:03 +0000 (22:54 +0100)
Add a private API to convert an int or float to a C timespec structure.

Doc/library/signal.rst
Include/pytime.h
Lib/test/test_signal.py
Lib/test/test_time.py
Modules/_testcapimodule.c
Modules/signalmodule.c
Python/pytime.c

index 4fc3fd6405c624cf06a8700df180df371d14386b..04afd9e8a4ed79e7af193da8c6fbc11ee8113e86 100644 (file)
@@ -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).
index d707bdb9a87c7c31cec598fab828f86a8c3ef217..2ea64c9bc4a1e09487477bf8663a9681d39a8f3e 100644 (file)
@@ -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);
 
index fdeb4c2261f9746bbea886e0fac28c72eec8061b..6be259bdb93ee49d001b9e86fa9c16900ff284fd 100644 (file)
@@ -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()')
index a89c511c691cd4ceef30cd228eb8068ddcf3bbb1..26492c1c6c59f2a8a9b584f471a374430cf20e9c 100644 (file)
@@ -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()
index 23a4d5ac9b76c055a7e53d49993b47e43945a2de..9294df3e63b7143b1bfed005d5e6a345e9379578 100644 (file)
@@ -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 */
 };
 
index e46f8cf1dcf895716b07e6bcdada605ae0dfc886..2eb7f297ebe39fcb2d12798cbc7c50307ae23c1a 100644 (file)
@@ -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) {
index bec1c713e602cf636bf8ff2f74b9cea0fa106ecb..d23ce75b43f44224475620eee545697079879b0b 100644 (file)
@@ -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()
 {