[Python-checkins] cpython: Issue #20320: select.select() and select.kqueue.control() now round the timeout

victor.stinner python-checkins at python.org
Mon Feb 17 00:08:28 CET 2014


http://hg.python.org/cpython/rev/180e4b678003
changeset: 89230:180e4b678003
user: Victor Stinner <victor.stinner at gmail.com>
date: Mon Feb 17 00:02:43 2014 +0100
summary:
 Issue #20320: select.select() and select.kqueue.control() now round the timeout
aways from zero, instead of rounding towards zero.
It should make test_asyncio more reliable, especially test_timeout_rounding() test.
files:
 Include/pytime.h | 17 ++-
 Lib/test/test_time.py | 130 +++++++++++++++++++-------
 Misc/NEWS | 3 +
 Modules/_datetimemodule.c | 4 +-
 Modules/_testcapimodule.c | 31 +++++-
 Modules/posixmodule.c | 4 +-
 Modules/selectmodule.c | 10 +-
 Modules/signalmodule.c | 3 +-
 Modules/timemodule.c | 4 +-
 Python/pytime.c | 35 +++++-
 10 files changed, 179 insertions(+), 62 deletions(-)
diff --git a/Include/pytime.h b/Include/pytime.h
--- a/Include/pytime.h
+++ b/Include/pytime.h
@@ -53,10 +53,19 @@
 (tv_end.tv_usec - tv_start.tv_usec) * 0.000001)
 
 #ifndef Py_LIMITED_API
+
+typedef enum {
+ /* Round towards zero. */
+ _PyTime_ROUND_DOWN=0,
+ /* Round away from zero. */
+ _PyTime_ROUND_UP
+} _PyTime_round_t;
+
 /* Convert a number of seconds, int or float, to time_t. */
 PyAPI_FUNC(int) _PyTime_ObjectToTime_t(
 PyObject *obj,
- time_t *sec);
+ time_t *sec,
+ _PyTime_round_t);
 
 /* Convert a time_t to a PyLong. */
 PyAPI_FUNC(PyObject *) _PyLong_FromTime_t(
@@ -72,7 +81,8 @@
 PyAPI_FUNC(int) _PyTime_ObjectToTimeval(
 PyObject *obj,
 time_t *sec,
- long *usec);
+ long *usec,
+ _PyTime_round_t);
 
 /* Convert a number of seconds, int or float, to a timespec structure.
 nsec is in the range [0; 999999999] and rounded towards zero.
@@ -80,7 +90,8 @@
 PyAPI_FUNC(int) _PyTime_ObjectToTimespec(
 PyObject *obj,
 time_t *sec,
- long *nsec);
+ long *nsec,
+ _PyTime_round_t);
 #endif
 
 /* Dummy to force linking. */
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
@@ -14,6 +14,8 @@
 SIZEOF_INT = sysconfig.get_config_var('SIZEOF_INT') or 4
 TIME_MAXYEAR = (1 << 8 * SIZEOF_INT - 1) - 1
 TIME_MINYEAR = -TIME_MAXYEAR - 1
+_PyTime_ROUND_DOWN = 0
+_PyTime_ROUND_UP = 1
 
 
 class TimeTestCase(unittest.TestCase):
@@ -585,58 +587,116 @@
 @support.cpython_only
 def test_time_t(self):
 from _testcapi import pytime_object_to_time_t
- for obj, time_t in (
- (0, 0),
- (-1, -1),
- (-1.0, -1),
- (-1.9, -1),
- (1.0, 1),
- (1.9, 1),
+ for obj, time_t, rnd in (
+ # Round towards zero
+ (0, 0, _PyTime_ROUND_DOWN),
+ (-1, -1, _PyTime_ROUND_DOWN),
+ (-1.0, -1, _PyTime_ROUND_DOWN),
+ (-1.9, -1, _PyTime_ROUND_DOWN),
+ (1.0, 1, _PyTime_ROUND_DOWN),
+ (1.9, 1, _PyTime_ROUND_DOWN),
+ # Round away from zero
+ (0, 0, _PyTime_ROUND_UP),
+ (-1, -1, _PyTime_ROUND_UP),
+ (-1.0, -1, _PyTime_ROUND_UP),
+ (-1.9, -2, _PyTime_ROUND_UP),
+ (1.0, 1, _PyTime_ROUND_UP),
+ (1.9, 2, _PyTime_ROUND_UP),
 ):
- self.assertEqual(pytime_object_to_time_t(obj), time_t)
+ self.assertEqual(pytime_object_to_time_t(obj, rnd), time_t)
 
+ rnd = _PyTime_ROUND_DOWN
 for invalid in self.invalid_values:
- self.assertRaises(OverflowError, pytime_object_to_time_t, invalid)
+ self.assertRaises(OverflowError,
+ pytime_object_to_time_t, invalid, rnd)
 
 @support.cpython_only
 def test_timeval(self):
 from _testcapi import pytime_object_to_timeval
- for obj, timeval in (
- (0, (0, 0)),
- (-1, (-1, 0)),
- (-1.0, (-1, 0)),
- (1e-6, (0, 1)),
- (-1e-6, (-1, 999999)),
- (-1.2, (-2, 800000)),
- (1.1234560, (1, 123456)),
- (1.1234569, (1, 123456)),
- (-1.1234560, (-2, 876544)),
- (-1.1234561, (-2, 876543)),
+ for obj, timeval, rnd in (
+ # Round towards zero
+ (0, (0, 0), _PyTime_ROUND_DOWN),
+ (-1, (-1, 0), _PyTime_ROUND_DOWN),
+ (-1.0, (-1, 0), _PyTime_ROUND_DOWN),
+ (1e-6, (0, 1), _PyTime_ROUND_DOWN),
+ (1e-7, (0, 0), _PyTime_ROUND_DOWN),
+ (-1e-6, (-1, 999999), _PyTime_ROUND_DOWN),
+ (-1e-7, (-1, 999999), _PyTime_ROUND_DOWN),
+ (-1.2, (-2, 800000), _PyTime_ROUND_DOWN),
+ (0.9999999, (0, 999999), _PyTime_ROUND_DOWN),
+ (0.0000041, (0, 4), _PyTime_ROUND_DOWN),
+ (1.1234560, (1, 123456), _PyTime_ROUND_DOWN),
+ (1.1234569, (1, 123456), _PyTime_ROUND_DOWN),
+ (-0.0000040, (-1, 999996), _PyTime_ROUND_DOWN),
+ (-0.0000041, (-1, 999995), _PyTime_ROUND_DOWN),
+ (-1.1234560, (-2, 876544), _PyTime_ROUND_DOWN),
+ (-1.1234561, (-2, 876543), _PyTime_ROUND_DOWN),
+ # Round away from zero
+ (0, (0, 0), _PyTime_ROUND_UP),
+ (-1, (-1, 0), _PyTime_ROUND_UP),
+ (-1.0, (-1, 0), _PyTime_ROUND_UP),
+ (1e-6, (0, 1), _PyTime_ROUND_UP),
+ (1e-7, (0, 1), _PyTime_ROUND_UP),
+ (-1e-6, (-1, 999999), _PyTime_ROUND_UP),
+ (-1e-7, (-1, 999999), _PyTime_ROUND_UP),
+ (-1.2, (-2, 800000), _PyTime_ROUND_UP),
+ (0.9999999, (1, 0), _PyTime_ROUND_UP),
+ (0.0000041, (0, 5), _PyTime_ROUND_UP),
+ (1.1234560, (1, 123457), _PyTime_ROUND_UP),
+ (1.1234569, (1, 123457), _PyTime_ROUND_UP),
+ (-0.0000040, (-1, 999996), _PyTime_ROUND_UP),
+ (-0.0000041, (-1, 999995), _PyTime_ROUND_UP),
+ (-1.1234560, (-2, 876544), _PyTime_ROUND_UP),
+ (-1.1234561, (-2, 876543), _PyTime_ROUND_UP),
 ):
- self.assertEqual(pytime_object_to_timeval(obj), timeval)
+ with self.subTest(obj=obj, round=rnd, timeval=timeval):
+ self.assertEqual(pytime_object_to_timeval(obj, rnd), timeval)
 
+ rnd = _PyTime_ROUND_DOWN
 for invalid in self.invalid_values:
- self.assertRaises(OverflowError, pytime_object_to_timeval, invalid)
+ self.assertRaises(OverflowError,
+ pytime_object_to_timeval, invalid, rnd)
 
 @support.cpython_only
 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, (0, 1)),
- (-1e-9, (-1, 999999999)),
- (-1.2, (-2, 800000000)),
- (1.1234567890, (1, 123456789)),
- (1.1234567899, (1, 123456789)),
- (-1.1234567890, (-2, 876543211)),
- (-1.1234567891, (-2, 876543210)),
+ for obj, timespec, rnd in (
+ # Round towards zero
+ (0, (0, 0), _PyTime_ROUND_DOWN),
+ (-1, (-1, 0), _PyTime_ROUND_DOWN),
+ (-1.0, (-1, 0), _PyTime_ROUND_DOWN),
+ (1e-9, (0, 1), _PyTime_ROUND_DOWN),
+ (1e-10, (0, 0), _PyTime_ROUND_DOWN),
+ (-1e-9, (-1, 999999999), _PyTime_ROUND_DOWN),
+ (-1e-10, (-1, 999999999), _PyTime_ROUND_DOWN),
+ (-1.2, (-2, 800000000), _PyTime_ROUND_DOWN),
+ (0.9999999999, (0, 999999999), _PyTime_ROUND_DOWN),
+ (1.1234567890, (1, 123456789), _PyTime_ROUND_DOWN),
+ (1.1234567899, (1, 123456789), _PyTime_ROUND_DOWN),
+ (-1.1234567890, (-2, 876543211), _PyTime_ROUND_DOWN),
+ (-1.1234567891, (-2, 876543210), _PyTime_ROUND_DOWN),
+ # Round away from zero
+ (0, (0, 0), _PyTime_ROUND_UP),
+ (-1, (-1, 0), _PyTime_ROUND_UP),
+ (-1.0, (-1, 0), _PyTime_ROUND_UP),
+ (1e-9, (0, 1), _PyTime_ROUND_UP),
+ (1e-10, (0, 1), _PyTime_ROUND_UP),
+ (-1e-9, (-1, 999999999), _PyTime_ROUND_UP),
+ (-1e-10, (-1, 999999999), _PyTime_ROUND_UP),
+ (-1.2, (-2, 800000000), _PyTime_ROUND_UP),
+ (0.9999999999, (1, 0), _PyTime_ROUND_UP),
+ (1.1234567890, (1, 123456790), _PyTime_ROUND_UP),
+ (1.1234567899, (1, 123456790), _PyTime_ROUND_UP),
+ (-1.1234567890, (-2, 876543211), _PyTime_ROUND_UP),
+ (-1.1234567891, (-2, 876543210), _PyTime_ROUND_UP),
 ):
- self.assertEqual(pytime_object_to_timespec(obj), timespec)
+ with self.subTest(obj=obj, round=rnd, timespec=timespec):
+ self.assertEqual(pytime_object_to_timespec(obj, rnd), timespec)
 
+ rnd = _PyTime_ROUND_DOWN
 for invalid in self.invalid_values:
- self.assertRaises(OverflowError, pytime_object_to_timespec, invalid)
+ self.assertRaises(OverflowError,
+ pytime_object_to_timespec, invalid, rnd)
 
 @unittest.skipUnless(time._STRUCT_TM_ITEMS == 11, "needs tm_zone support")
 def test_localtime_timezone(self):
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -25,6 +25,9 @@
 Library
 -------
 
+- Issue #20320: select.select() and select.kqueue.control() now round the
+ timeout aways from zero, instead of rounding towards zero.
+
 - Issue #20616: Add a format() method to tracemalloc.Traceback.
 
 - Issue #19744: the ensurepip installation step now just prints a warning to
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -2459,7 +2459,7 @@
 struct tm *tm;
 time_t t;
 
- if (_PyTime_ObjectToTime_t(obj, &t) == -1)
+ if (_PyTime_ObjectToTime_t(obj, &t, _PyTime_ROUND_DOWN) == -1)
 return NULL;
 
 tm = localtime(&t);
@@ -4127,7 +4127,7 @@
 time_t timet;
 long us;
 
- if (_PyTime_ObjectToTimeval(timestamp, &timet, &us) == -1)
+ if (_PyTime_ObjectToTimeval(timestamp, &timet, &us, _PyTime_ROUND_DOWN) == -1)
 return NULL;
 return datetime_from_timet_and_us(cls, f, timet, (int)us, tzinfo);
 }
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -2516,14 +2516,27 @@
 return PyLong_FromLong(r);
 }
 
+static int
+check_time_rounding(int round)
+{
+ if (round != _PyTime_ROUND_DOWN && round != _PyTime_ROUND_UP) {
+ PyErr_SetString(PyExc_ValueError, "invalid rounding");
+ return -1;
+ }
+ return 0;
+}
+
 static PyObject *
 test_pytime_object_to_time_t(PyObject *self, PyObject *args)
 {
 PyObject *obj;
 time_t sec;
- if (!PyArg_ParseTuple(args, "O:pytime_object_to_time_t", &obj))
+ int round;
+ if (!PyArg_ParseTuple(args, "Oi:pytime_object_to_time_t", &obj, &round))
 return NULL;
- if (_PyTime_ObjectToTime_t(obj, &sec) == -1)
+ if (check_time_rounding(round) < 0)
+ return NULL;
+ if (_PyTime_ObjectToTime_t(obj, &sec, round) == -1)
 return NULL;
 return _PyLong_FromTime_t(sec);
 }
@@ -2534,9 +2547,12 @@
 PyObject *obj;
 time_t sec;
 long usec;
- if (!PyArg_ParseTuple(args, "O:pytime_object_to_timeval", &obj))
+ int round;
+ if (!PyArg_ParseTuple(args, "Oi:pytime_object_to_timeval", &obj, &round))
 return NULL;
- if (_PyTime_ObjectToTimeval(obj, &sec, &usec) == -1)
+ if (check_time_rounding(round) < 0)
+ return NULL;
+ if (_PyTime_ObjectToTimeval(obj, &sec, &usec, round) == -1)
 return NULL;
 return Py_BuildValue("Nl", _PyLong_FromTime_t(sec), usec);
 }
@@ -2547,9 +2563,12 @@
 PyObject *obj;
 time_t sec;
 long nsec;
- if (!PyArg_ParseTuple(args, "O:pytime_object_to_timespec", &obj))
+ int round;
+ if (!PyArg_ParseTuple(args, "Oi:pytime_object_to_timespec", &obj, &round))
 return NULL;
- if (_PyTime_ObjectToTimespec(obj, &sec, &nsec) == -1)
+ if (check_time_rounding(round) < 0)
+ return NULL;
+ if (_PyTime_ObjectToTimespec(obj, &sec, &nsec, round) == -1)
 return NULL;
 return Py_BuildValue("Nl", _PyLong_FromTime_t(sec), nsec);
 }
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -4901,9 +4901,9 @@
 }
 utime.now = 0;
 if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 0),
- &a_sec, &a_nsec) == -1 ||
+ &a_sec, &a_nsec, _PyTime_ROUND_DOWN) == -1 ||
 _PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 1),
- &m_sec, &m_nsec) == -1) {
+ &m_sec, &m_nsec, _PyTime_ROUND_DOWN) == -1) {
 goto exit;
 }
 utime.atime_s = a_sec;
diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c
--- a/Modules/selectmodule.c
+++ b/Modules/selectmodule.c
@@ -214,7 +214,8 @@
 else {
 #ifdef MS_WINDOWS
 time_t sec;
- if (_PyTime_ObjectToTimeval(tout, &sec, &tv.tv_usec) == -1)
+ if (_PyTime_ObjectToTimeval(tout, &sec, &tv.tv_usec,
+ _PyTime_ROUND_UP) == -1)
 return NULL;
 assert(sizeof(tv.tv_sec) == sizeof(long));
 #if SIZEOF_TIME_T > SIZEOF_LONG
@@ -229,7 +230,8 @@
 /* 64-bit OS X has struct timeval.tv_usec as an int (and thus still 4
 bytes as required), but no longer defined by a long. */
 long tv_usec;
- if (_PyTime_ObjectToTimeval(tout, &tv.tv_sec, &tv_usec) == -1)
+ if (_PyTime_ObjectToTimeval(tout, &tv.tv_sec, &tv_usec,
+ _PyTime_ROUND_UP) == -1)
 return NULL;
 tv.tv_usec = tv_usec;
 #endif
@@ -2037,8 +2039,8 @@
 ptimeoutspec = NULL;
 }
 else if (PyNumber_Check(otimeout)) {
- if (_PyTime_ObjectToTimespec(otimeout,
- &timeout.tv_sec, &timeout.tv_nsec) == -1)
+ if (_PyTime_ObjectToTimespec(otimeout, &timeout.tv_sec,
+ &timeout.tv_nsec, _PyTime_ROUND_UP) == -1)
 return NULL;
 
 if (timeout.tv_sec < 0) {
diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c
--- a/Modules/signalmodule.c
+++ b/Modules/signalmodule.c
@@ -799,7 +799,8 @@
 &signals, &timeout))
 return NULL;
 
- if (_PyTime_ObjectToTimespec(timeout, &tv_sec, &tv_nsec) == -1)
+ if (_PyTime_ObjectToTimespec(timeout, &tv_sec, &tv_nsec,
+ _PyTime_ROUND_DOWN) == -1)
 return NULL;
 buf.tv_sec = tv_sec;
 buf.tv_nsec = tv_nsec;
diff --git a/Modules/timemodule.c b/Modules/timemodule.c
--- a/Modules/timemodule.c
+++ b/Modules/timemodule.c
@@ -193,7 +193,7 @@
 if (!PyArg_ParseTuple(args, "iO:clock_settime", &clk_id, &obj))
 return NULL;
 
- if (_PyTime_ObjectToTimespec(obj, &tv_sec, &tv_nsec) == -1)
+ if (_PyTime_ObjectToTimespec(obj, &tv_sec, &tv_nsec, _PyTime_ROUND_DOWN) == -1)
 return NULL;
 tp.tv_sec = tv_sec;
 tp.tv_nsec = tv_nsec;
@@ -341,7 +341,7 @@
 whent = time(NULL);
 }
 else {
- if (_PyTime_ObjectToTime_t(ot, &whent) == -1)
+ if (_PyTime_ObjectToTime_t(ot, &whent, _PyTime_ROUND_DOWN) == -1)
 return 0;
 }
 *pwhen = whent;
diff --git a/Python/pytime.c b/Python/pytime.c
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -152,7 +152,7 @@
 
 static int
 _PyTime_ObjectToDenominator(PyObject *obj, time_t *sec, long *numerator,
- double denominator)
+ double denominator, _PyTime_round_t round)
 {
 assert(denominator <= LONG_MAX);
 if (PyFloat_Check(obj)) {
@@ -167,6 +167,20 @@
 intpart -= 1.0;
 }
 
+ floatpart *= denominator;
+ if (round == _PyTime_ROUND_UP) {
+ if (intpart >= 0) {
+ floatpart = ceil(floatpart);
+ if (floatpart >= denominator) {
+ floatpart = 0.0;
+ intpart += 1.0;
+ }
+ }
+ else {
+ floatpart = floor(floatpart);
+ }
+ }
+
 *sec = (time_t)intpart;
 err = intpart - (double)*sec;
 if (err <= -1.0 || err >= 1.0) {
@@ -174,7 +188,6 @@
 return -1;
 }
 
- floatpart *= denominator;
 *numerator = (long)floatpart;
 return 0;
 }
@@ -188,12 +201,18 @@
 }
 
 int
-_PyTime_ObjectToTime_t(PyObject *obj, time_t *sec)
+_PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round)
 {
 if (PyFloat_Check(obj)) {
 double d, intpart, err;
 
 d = PyFloat_AsDouble(obj);
+ if (round == _PyTime_ROUND_UP) {
+ if (d >= 0)
+ d = ceil(d);
+ else
+ d = floor(d);
+ }
 (void)modf(d, &intpart);
 
 *sec = (time_t)intpart;
@@ -213,15 +232,17 @@
 }
 
 int
-_PyTime_ObjectToTimespec(PyObject *obj, time_t *sec, long *nsec)
+_PyTime_ObjectToTimespec(PyObject *obj, time_t *sec, long *nsec,
+ _PyTime_round_t round)
 {
- return _PyTime_ObjectToDenominator(obj, sec, nsec, 1e9);
+ return _PyTime_ObjectToDenominator(obj, sec, nsec, 1e9, round);
 }
 
 int
-_PyTime_ObjectToTimeval(PyObject *obj, time_t *sec, long *usec)
+_PyTime_ObjectToTimeval(PyObject *obj, time_t *sec, long *usec,
+ _PyTime_round_t round)
 {
- return _PyTime_ObjectToDenominator(obj, sec, usec, 1e6);
+ return _PyTime_ObjectToDenominator(obj, sec, usec, 1e6, round);
 }
 
 void
-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /