Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3c1b379

Browse files
committed
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.
1 parent 23f628d commit 3c1b379

10 files changed

Lines changed: 179 additions & 62 deletions

File tree

Include/pytime.h

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,19 @@ do { \
5353
(tv_end.tv_usec - tv_start.tv_usec) * 0.000001)
5454

5555
#ifndef Py_LIMITED_API
56+
57+
typedef enum {
58+
/* Round towards zero. */
59+
_PyTime_ROUND_DOWN=0,
60+
/* Round away from zero. */
61+
_PyTime_ROUND_UP
62+
} _PyTime_round_t;
63+
5664
/* Convert a number of seconds, int or float, to time_t. */
5765
PyAPI_FUNC(int) _PyTime_ObjectToTime_t(
5866
PyObject *obj,
59-
time_t *sec);
67+
time_t *sec,
68+
_PyTime_round_t);
6069

6170
/* Convert a time_t to a PyLong. */
6271
PyAPI_FUNC(PyObject *) _PyLong_FromTime_t(
@@ -72,15 +81,17 @@ PyAPI_FUNC(time_t) _PyLong_AsTime_t(
7281
PyAPI_FUNC(int) _PyTime_ObjectToTimeval(
7382
PyObject *obj,
7483
time_t *sec,
75-
long *usec);
84+
long *usec,
85+
_PyTime_round_t);
7686

7787
/* Convert a number of seconds, int or float, to a timespec structure.
7888
nsec is in the range [0; 999999999] and rounded towards zero.
7989
For example, -1.2 is converted to (-2, 800000000). */
8090
PyAPI_FUNC(int) _PyTime_ObjectToTimespec(
8191
PyObject *obj,
8292
time_t *sec,
83-
long *nsec);
93+
long *nsec,
94+
_PyTime_round_t);
8495
#endif
8596

8697
/* Dummy to force linking. */

Lib/test/test_time.py

Lines changed: 95 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
SIZEOF_INT = sysconfig.get_config_var('SIZEOF_INT') or 4
1515
TIME_MAXYEAR = (1 << 8 * SIZEOF_INT - 1) - 1
1616
TIME_MINYEAR = -TIME_MAXYEAR - 1
17+
_PyTime_ROUND_DOWN = 0
18+
_PyTime_ROUND_UP = 1
1719

1820

1921
class TimeTestCase(unittest.TestCase):
@@ -585,58 +587,116 @@ def setUp(self):
585587
@support.cpython_only
586588
def test_time_t(self):
587589
from _testcapi import pytime_object_to_time_t
588-
for obj, time_t in (
589-
(0, 0),
590-
(-1, -1),
591-
(-1.0, -1),
592-
(-1.9, -1),
593-
(1.0, 1),
594-
(1.9, 1),
590+
for obj, time_t, rnd in (
591+
# Round towards zero
592+
(0, 0, _PyTime_ROUND_DOWN),
593+
(-1, -1, _PyTime_ROUND_DOWN),
594+
(-1.0, -1, _PyTime_ROUND_DOWN),
595+
(-1.9, -1, _PyTime_ROUND_DOWN),
596+
(1.0, 1, _PyTime_ROUND_DOWN),
597+
(1.9, 1, _PyTime_ROUND_DOWN),
598+
# Round away from zero
599+
(0, 0, _PyTime_ROUND_UP),
600+
(-1, -1, _PyTime_ROUND_UP),
601+
(-1.0, -1, _PyTime_ROUND_UP),
602+
(-1.9, -2, _PyTime_ROUND_UP),
603+
(1.0, 1, _PyTime_ROUND_UP),
604+
(1.9, 2, _PyTime_ROUND_UP),
595605
):
596-
self.assertEqual(pytime_object_to_time_t(obj), time_t)
606+
self.assertEqual(pytime_object_to_time_t(obj, rnd), time_t)
597607

608+
rnd = _PyTime_ROUND_DOWN
598609
for invalid in self.invalid_values:
599-
self.assertRaises(OverflowError, pytime_object_to_time_t, invalid)
610+
self.assertRaises(OverflowError,
611+
pytime_object_to_time_t, invalid, rnd)
600612

601613
@support.cpython_only
602614
def test_timeval(self):
603615
from _testcapi import pytime_object_to_timeval
604-
for obj, timeval in (
605-
(0, (0, 0)),
606-
(-1, (-1, 0)),
607-
(-1.0, (-1, 0)),
608-
(1e-6, (0, 1)),
609-
(-1e-6, (-1, 999999)),
610-
(-1.2, (-2, 800000)),
611-
(1.1234560, (1, 123456)),
612-
(1.1234569, (1, 123456)),
613-
(-1.1234560, (-2, 876544)),
614-
(-1.1234561, (-2, 876543)),
616+
for obj, timeval, rnd in (
617+
# Round towards zero
618+
(0, (0, 0), _PyTime_ROUND_DOWN),
619+
(-1, (-1, 0), _PyTime_ROUND_DOWN),
620+
(-1.0, (-1, 0), _PyTime_ROUND_DOWN),
621+
(1e-6, (0, 1), _PyTime_ROUND_DOWN),
622+
(1e-7, (0, 0), _PyTime_ROUND_DOWN),
623+
(-1e-6, (-1, 999999), _PyTime_ROUND_DOWN),
624+
(-1e-7, (-1, 999999), _PyTime_ROUND_DOWN),
625+
(-1.2, (-2, 800000), _PyTime_ROUND_DOWN),
626+
(0.9999999, (0, 999999), _PyTime_ROUND_DOWN),
627+
(0.0000041, (0, 4), _PyTime_ROUND_DOWN),
628+
(1.1234560, (1, 123456), _PyTime_ROUND_DOWN),
629+
(1.1234569, (1, 123456), _PyTime_ROUND_DOWN),
630+
(-0.0000040, (-1, 999996), _PyTime_ROUND_DOWN),
631+
(-0.0000041, (-1, 999995), _PyTime_ROUND_DOWN),
632+
(-1.1234560, (-2, 876544), _PyTime_ROUND_DOWN),
633+
(-1.1234561, (-2, 876543), _PyTime_ROUND_DOWN),
634+
# Round away from zero
635+
(0, (0, 0), _PyTime_ROUND_UP),
636+
(-1, (-1, 0), _PyTime_ROUND_UP),
637+
(-1.0, (-1, 0), _PyTime_ROUND_UP),
638+
(1e-6, (0, 1), _PyTime_ROUND_UP),
639+
(1e-7, (0, 1), _PyTime_ROUND_UP),
640+
(-1e-6, (-1, 999999), _PyTime_ROUND_UP),
641+
(-1e-7, (-1, 999999), _PyTime_ROUND_UP),
642+
(-1.2, (-2, 800000), _PyTime_ROUND_UP),
643+
(0.9999999, (1, 0), _PyTime_ROUND_UP),
644+
(0.0000041, (0, 5), _PyTime_ROUND_UP),
645+
(1.1234560, (1, 123457), _PyTime_ROUND_UP),
646+
(1.1234569, (1, 123457), _PyTime_ROUND_UP),
647+
(-0.0000040, (-1, 999996), _PyTime_ROUND_UP),
648+
(-0.0000041, (-1, 999995), _PyTime_ROUND_UP),
649+
(-1.1234560, (-2, 876544), _PyTime_ROUND_UP),
650+
(-1.1234561, (-2, 876543), _PyTime_ROUND_UP),
615651
):
616-
self.assertEqual(pytime_object_to_timeval(obj), timeval)
652+
with self.subTest(obj=obj, round=rnd, timeval=timeval):
653+
self.assertEqual(pytime_object_to_timeval(obj, rnd), timeval)
617654

655+
rnd = _PyTime_ROUND_DOWN
618656
for invalid in self.invalid_values:
619-
self.assertRaises(OverflowError, pytime_object_to_timeval, invalid)
657+
self.assertRaises(OverflowError,
658+
pytime_object_to_timeval, invalid, rnd)
620659

621660
@support.cpython_only
622661
def test_timespec(self):
623662
from _testcapi import pytime_object_to_timespec
624-
for obj, timespec in (
625-
(0, (0, 0)),
626-
(-1, (-1, 0)),
627-
(-1.0, (-1, 0)),
628-
(1e-9, (0, 1)),
629-
(-1e-9, (-1, 999999999)),
630-
(-1.2, (-2, 800000000)),
631-
(1.1234567890, (1, 123456789)),
632-
(1.1234567899, (1, 123456789)),
633-
(-1.1234567890, (-2, 876543211)),
634-
(-1.1234567891, (-2, 876543210)),
663+
for obj, timespec, rnd in (
664+
# Round towards zero
665+
(0, (0, 0), _PyTime_ROUND_DOWN),
666+
(-1, (-1, 0), _PyTime_ROUND_DOWN),
667+
(-1.0, (-1, 0), _PyTime_ROUND_DOWN),
668+
(1e-9, (0, 1), _PyTime_ROUND_DOWN),
669+
(1e-10, (0, 0), _PyTime_ROUND_DOWN),
670+
(-1e-9, (-1, 999999999), _PyTime_ROUND_DOWN),
671+
(-1e-10, (-1, 999999999), _PyTime_ROUND_DOWN),
672+
(-1.2, (-2, 800000000), _PyTime_ROUND_DOWN),
673+
(0.9999999999, (0, 999999999), _PyTime_ROUND_DOWN),
674+
(1.1234567890, (1, 123456789), _PyTime_ROUND_DOWN),
675+
(1.1234567899, (1, 123456789), _PyTime_ROUND_DOWN),
676+
(-1.1234567890, (-2, 876543211), _PyTime_ROUND_DOWN),
677+
(-1.1234567891, (-2, 876543210), _PyTime_ROUND_DOWN),
678+
# Round away from zero
679+
(0, (0, 0), _PyTime_ROUND_UP),
680+
(-1, (-1, 0), _PyTime_ROUND_UP),
681+
(-1.0, (-1, 0), _PyTime_ROUND_UP),
682+
(1e-9, (0, 1), _PyTime_ROUND_UP),
683+
(1e-10, (0, 1), _PyTime_ROUND_UP),
684+
(-1e-9, (-1, 999999999), _PyTime_ROUND_UP),
685+
(-1e-10, (-1, 999999999), _PyTime_ROUND_UP),
686+
(-1.2, (-2, 800000000), _PyTime_ROUND_UP),
687+
(0.9999999999, (1, 0), _PyTime_ROUND_UP),
688+
(1.1234567890, (1, 123456790), _PyTime_ROUND_UP),
689+
(1.1234567899, (1, 123456790), _PyTime_ROUND_UP),
690+
(-1.1234567890, (-2, 876543211), _PyTime_ROUND_UP),
691+
(-1.1234567891, (-2, 876543210), _PyTime_ROUND_UP),
635692
):
636-
self.assertEqual(pytime_object_to_timespec(obj), timespec)
693+
with self.subTest(obj=obj, round=rnd, timespec=timespec):
694+
self.assertEqual(pytime_object_to_timespec(obj, rnd), timespec)
637695

696+
rnd = _PyTime_ROUND_DOWN
638697
for invalid in self.invalid_values:
639-
self.assertRaises(OverflowError, pytime_object_to_timespec, invalid)
698+
self.assertRaises(OverflowError,
699+
pytime_object_to_timespec, invalid, rnd)
640700

641701
@unittest.skipUnless(time._STRUCT_TM_ITEMS == 11, "needs tm_zone support")
642702
def test_localtime_timezone(self):

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ Core and Builtins
2525
Library
2626
-------
2727

28+
- Issue #20320: select.select() and select.kqueue.control() now round the
29+
timeout aways from zero, instead of rounding towards zero.
30+
2831
- Issue #20616: Add a format() method to tracemalloc.Traceback.
2932

3033
- Issue #19744: the ensurepip installation step now just prints a warning to

Modules/_datetimemodule.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2459,7 +2459,7 @@ date_local_from_object(PyObject *cls, PyObject *obj)
24592459
struct tm *tm;
24602460
time_t t;
24612461

2462-
if (_PyTime_ObjectToTime_t(obj, &t) == -1)
2462+
if (_PyTime_ObjectToTime_t(obj, &t, _PyTime_ROUND_DOWN) == -1)
24632463
return NULL;
24642464

24652465
tm = localtime(&t);
@@ -4127,7 +4127,7 @@ datetime_from_timestamp(PyObject *cls, TM_FUNC f, PyObject *timestamp,
41274127
time_t timet;
41284128
long us;
41294129

4130-
if (_PyTime_ObjectToTimeval(timestamp, &timet, &us) == -1)
4130+
if (_PyTime_ObjectToTimeval(timestamp, &timet, &us, _PyTime_ROUND_DOWN) == -1)
41314131
return NULL;
41324132
return datetime_from_timet_and_us(cls, f, timet, (int)us, tzinfo);
41334133
}

Modules/_testcapimodule.c

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2516,14 +2516,27 @@ run_in_subinterp(PyObject *self, PyObject *args)
25162516
return PyLong_FromLong(r);
25172517
}
25182518

2519+
static int
2520+
check_time_rounding(int round)
2521+
{
2522+
if (round != _PyTime_ROUND_DOWN && round != _PyTime_ROUND_UP) {
2523+
PyErr_SetString(PyExc_ValueError, "invalid rounding");
2524+
return -1;
2525+
}
2526+
return 0;
2527+
}
2528+
25192529
static PyObject *
25202530
test_pytime_object_to_time_t(PyObject *self, PyObject *args)
25212531
{
25222532
PyObject *obj;
25232533
time_t sec;
2524-
if (!PyArg_ParseTuple(args, "O:pytime_object_to_time_t", &obj))
2534+
int round;
2535+
if (!PyArg_ParseTuple(args, "Oi:pytime_object_to_time_t", &obj, &round))
2536+
return NULL;
2537+
if (check_time_rounding(round) < 0)
25252538
return NULL;
2526-
if (_PyTime_ObjectToTime_t(obj, &sec) == -1)
2539+
if (_PyTime_ObjectToTime_t(obj, &sec, round) == -1)
25272540
return NULL;
25282541
return _PyLong_FromTime_t(sec);
25292542
}
@@ -2534,9 +2547,12 @@ test_pytime_object_to_timeval(PyObject *self, PyObject *args)
25342547
PyObject *obj;
25352548
time_t sec;
25362549
long usec;
2537-
if (!PyArg_ParseTuple(args, "O:pytime_object_to_timeval", &obj))
2550+
int round;
2551+
if (!PyArg_ParseTuple(args, "Oi:pytime_object_to_timeval", &obj, &round))
25382552
return NULL;
2539-
if (_PyTime_ObjectToTimeval(obj, &sec, &usec) == -1)
2553+
if (check_time_rounding(round) < 0)
2554+
return NULL;
2555+
if (_PyTime_ObjectToTimeval(obj, &sec, &usec, round) == -1)
25402556
return NULL;
25412557
return Py_BuildValue("Nl", _PyLong_FromTime_t(sec), usec);
25422558
}
@@ -2547,9 +2563,12 @@ test_pytime_object_to_timespec(PyObject *self, PyObject *args)
25472563
PyObject *obj;
25482564
time_t sec;
25492565
long nsec;
2550-
if (!PyArg_ParseTuple(args, "O:pytime_object_to_timespec", &obj))
2566+
int round;
2567+
if (!PyArg_ParseTuple(args, "Oi:pytime_object_to_timespec", &obj, &round))
2568+
return NULL;
2569+
if (check_time_rounding(round) < 0)
25512570
return NULL;
2552-
if (_PyTime_ObjectToTimespec(obj, &sec, &nsec) == -1)
2571+
if (_PyTime_ObjectToTimespec(obj, &sec, &nsec, round) == -1)
25532572
return NULL;
25542573
return Py_BuildValue("Nl", _PyLong_FromTime_t(sec), nsec);
25552574
}

Modules/posixmodule.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4901,9 +4901,9 @@ posix_utime(PyObject *self, PyObject *args, PyObject *kwargs)
49014901
}
49024902
utime.now = 0;
49034903
if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 0),
4904-
&a_sec, &a_nsec) == -1 ||
4904+
&a_sec, &a_nsec, _PyTime_ROUND_DOWN) == -1 ||
49054905
_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 1),
4906-
&m_sec, &m_nsec) == -1) {
4906+
&m_sec, &m_nsec, _PyTime_ROUND_DOWN) == -1) {
49074907
goto exit;
49084908
}
49094909
utime.atime_s = a_sec;

Modules/selectmodule.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,8 @@ select_select(PyObject *self, PyObject *args)
214214
else {
215215
#ifdef MS_WINDOWS
216216
time_t sec;
217-
if (_PyTime_ObjectToTimeval(tout, &sec, &tv.tv_usec) == -1)
217+
if (_PyTime_ObjectToTimeval(tout, &sec, &tv.tv_usec,
218+
_PyTime_ROUND_UP) == -1)
218219
return NULL;
219220
assert(sizeof(tv.tv_sec) == sizeof(long));
220221
#if SIZEOF_TIME_T > SIZEOF_LONG
@@ -229,7 +230,8 @@ select_select(PyObject *self, PyObject *args)
229230
/* 64-bit OS X has struct timeval.tv_usec as an int (and thus still 4
230231
bytes as required), but no longer defined by a long. */
231232
long tv_usec;
232-
if (_PyTime_ObjectToTimeval(tout, &tv.tv_sec, &tv_usec) == -1)
233+
if (_PyTime_ObjectToTimeval(tout, &tv.tv_sec, &tv_usec,
234+
_PyTime_ROUND_UP) == -1)
233235
return NULL;
234236
tv.tv_usec = tv_usec;
235237
#endif
@@ -2037,8 +2039,8 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args)
20372039
ptimeoutspec = NULL;
20382040
}
20392041
else if (PyNumber_Check(otimeout)) {
2040-
if (_PyTime_ObjectToTimespec(otimeout,
2041-
&timeout.tv_sec, &timeout.tv_nsec) == -1)
2042+
if (_PyTime_ObjectToTimespec(otimeout, &timeout.tv_sec,
2043+
&timeout.tv_nsec, _PyTime_ROUND_UP) == -1)
20422044
return NULL;
20432045

20442046
if (timeout.tv_sec < 0) {

Modules/signalmodule.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -799,7 +799,8 @@ signal_sigtimedwait(PyObject *self, PyObject *args)
799799
&signals, &timeout))
800800
return NULL;
801801

802-
if (_PyTime_ObjectToTimespec(timeout, &tv_sec, &tv_nsec) == -1)
802+
if (_PyTime_ObjectToTimespec(timeout, &tv_sec, &tv_nsec,
803+
_PyTime_ROUND_DOWN) == -1)
803804
return NULL;
804805
buf.tv_sec = tv_sec;
805806
buf.tv_nsec = tv_nsec;

Modules/timemodule.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ time_clock_settime(PyObject *self, PyObject *args)
193193
if (!PyArg_ParseTuple(args, "iO:clock_settime", &clk_id, &obj))
194194
return NULL;
195195

196-
if (_PyTime_ObjectToTimespec(obj, &tv_sec, &tv_nsec) == -1)
196+
if (_PyTime_ObjectToTimespec(obj, &tv_sec, &tv_nsec, _PyTime_ROUND_DOWN) == -1)
197197
return NULL;
198198
tp.tv_sec = tv_sec;
199199
tp.tv_nsec = tv_nsec;
@@ -341,7 +341,7 @@ parse_time_t_args(PyObject *args, char *format, time_t *pwhen)
341341
whent = time(NULL);
342342
}
343343
else {
344-
if (_PyTime_ObjectToTime_t(ot, &whent) == -1)
344+
if (_PyTime_ObjectToTime_t(ot, &whent, _PyTime_ROUND_DOWN) == -1)
345345
return 0;
346346
}
347347
*pwhen = whent;

0 commit comments

Comments
 (0)