diff --git a/Include/internal/pycore_time.h b/Include/internal/pycore_time.h index b671225ca6ea44..33e5e3aa5ce33c 100644 --- a/Include/internal/pycore_time.h +++ b/Include/internal/pycore_time.h @@ -102,6 +102,25 @@ PyAPI_FUNC(time_t) _PyLong_AsTime_t(PyObject *obj); // Convert a number of seconds, int or float, to time_t. // Export for '_datetime' shared extension. +// Argument Clinic converters for durations, see the "duration" converter. +// Export for shared extensions (Argument Clinic code). +PyAPI_FUNC(int) _PyTime_Duration_Seconds_Converter(PyObject *, void *); +PyAPI_FUNC(int) _PyTime_DurationOrNone_Seconds_Converter(PyObject *, void *); +PyAPI_FUNC(int) _PyTime_Duration_SecondsCeil_Converter(PyObject *, void *); +PyAPI_FUNC(int) _PyTime_DurationOrNone_SecondsCeil_Converter(PyObject *, void *); +PyAPI_FUNC(int) _PyTime_Duration_Milliseconds_Converter(PyObject *, void *); +PyAPI_FUNC(int) _PyTime_DurationOrNone_Milliseconds_Converter(PyObject *, void *); + +#ifndef MS_WINDOWS +PyAPI_FUNC(int) _PyTime_Duration_Timeval_Converter(PyObject *, void *); +PyAPI_FUNC(int) _PyTime_Duration_TimevalCeil_Converter(PyObject *, void *); +#endif + +// Argument Clinic converters for timestamps, see the "timestamp" converter. +// Export for shared extensions (Argument Clinic code). +PyAPI_FUNC(int) _PyTime_Timestamp_Time_t_Converter(PyObject *, void *); +PyAPI_FUNC(int) _PyTime_Timestamp_Converter(PyObject *, void *); + PyAPI_FUNC(int) _PyTime_ObjectToTime_t( PyObject *obj, time_t *sec, diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index d07447d66571e5..c3d3fa0c67e3ac 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -3150,10 +3150,11 @@ def test_var_keyword_with_pos_or_kw_and_kw_only(self): err = "Function 'bar' has an invalid parameter declaration (**kwargs?): '**kwds: dict'" self.expect_failure(block, err) - def test_allow_negative_accepted_by_py_ssize_t_converter_only(self): + def test_allow_negative_accepted_by_few_converters_only(self): errmsg = re.escape("converter_init() got an unexpected keyword argument 'allow_negative'") + supported = {"Py_ssize_t", "duration"} unsupported_converters = [converter_name for converter_name in converters.keys() - if converter_name != "Py_ssize_t"] + if converter_name not in supported] for converter in unsupported_converters: with self.subTest(converter=converter): block = f""" @@ -3566,6 +3567,7 @@ def test_cli_converters(self): "char", "defining_class", "double", + "duration", "DWORD", "fildes", "float", @@ -3587,6 +3589,7 @@ def test_cli_converters(self): "size_t", "slice_index", "str", + "timestamp", "uint16", "uint32", "uint64", diff --git a/Misc/NEWS.d/next/Library/2026-08-23-14-30-00.gh-issue-156263.Kf3vQm.rst b/Misc/NEWS.d/next/Library/2026-08-23-14-30-00.gh-issue-156263.Kf3vQm.rst new file mode 100644 index 00000000000000..3ae1c4964e7e7d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-08-23-14-30-00.gh-issue-156263.Kf3vQm.rst @@ -0,0 +1,7 @@ +The timeout arguments in the :mod:`select`, :mod:`signal`, :mod:`faulthandler`, +:mod:`queue`, :mod:`_thread`, :mod:`multiprocessing`, :mod:`socket` and +:mod:`sqlite3` modules are now converted by Argument Clinic. As a result, +they are validated even if blocking is false, +:meth:`socket.socket.settimeout` and :func:`socket.setdefaulttimeout` raise +:exc:`ValueError` with a different message for a negative timeout, and +:func:`sqlite3.connect` raises :exc:`OverflowError` for a too large timeout. diff --git a/Misc/NEWS.d/next/Tools-Demos/2026-08-23-14-45-00.gh-issue-156263.Bt7hLp.rst b/Misc/NEWS.d/next/Tools-Demos/2026-08-23-14-45-00.gh-issue-156263.Bt7hLp.rst new file mode 100644 index 00000000000000..eb23e84c942772 --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2026-08-23-14-45-00.gh-issue-156263.Bt7hLp.rst @@ -0,0 +1,4 @@ +Argument Clinic: add the ``duration`` converter, which converts a number of +seconds or milliseconds to :c:type:`PyTime_t` or ``struct timeval``, and the +``timestamp`` converter, which converts a number of seconds since the epoch +to :c:type:`time_t` or :c:type:`PyTime_t`. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index cd02b298b406e6..305cb49cc95fe2 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -3286,13 +3286,9 @@ datetime_date_impl(PyTypeObject *type, int year, int month, int day) } static PyObject * -date_fromtimestamp(PyTypeObject *cls, PyObject *obj) +date_fromtimet(PyTypeObject *cls, time_t t) { struct tm tm; - time_t t; - - if (_PyTime_ObjectToTime_t(obj, &t, _PyTime_ROUND_FLOOR) == -1) - return NULL; if (_PyTime_localtime(t, &tm) != 0) return NULL; @@ -3354,7 +3350,7 @@ datetime_date_today_impl(PyTypeObject *type) @classmethod datetime.date.fromtimestamp - timestamp: object + timestamp: timestamp / Create a date from a POSIX timestamp. @@ -3364,10 +3360,10 @@ interpreted as local time. [clinic start generated code]*/ static PyObject * -datetime_date_fromtimestamp_impl(PyTypeObject *type, PyObject *timestamp) -/*[clinic end generated code: output=59def4e32c028fb6 input=15720eef43b169a1]*/ +datetime_date_fromtimestamp_impl(PyTypeObject *type, time_t timestamp) +/*[clinic end generated code: output=a4240b6ce153c150 input=74a7bdf0575c89a8]*/ { - return date_fromtimestamp(type, timestamp); + return date_fromtimet(type, timestamp); } /* bpo-36025: This is a wrapper for API compatibility with the public C API, @@ -3381,7 +3377,11 @@ datetime_date_fromtimestamp_capi(PyObject *cls, PyObject *args) PyObject *result = NULL; if (PyArg_UnpackTuple(args, "fromtimestamp", 1, 1, ×tamp)) { - result = date_fromtimestamp((PyTypeObject *)cls, timestamp); + time_t t; + if (_PyTime_ObjectToTime_t(timestamp, &t, _PyTime_ROUND_FLOOR) == -1) { + return NULL; + } + result = date_fromtimet((PyTypeObject *)cls, t); } return result; diff --git a/Modules/_multiprocessing/clinic/semaphore.c.h b/Modules/_multiprocessing/clinic/semaphore.c.h index 6b1c0092ce4816..f6103055373276 100644 --- a/Modules/_multiprocessing/clinic/semaphore.c.h +++ b/Modules/_multiprocessing/clinic/semaphore.c.h @@ -8,6 +8,7 @@ preserve #endif #include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() +#include "pycore_time.h" // _PyTime_FromSecondsObject() #if defined(HAVE_MP_SEMAPHORE) && defined(MS_WINDOWS) @@ -22,7 +23,7 @@ PyDoc_STRVAR(_multiprocessing_SemLock_acquire__doc__, static PyObject * _multiprocessing_SemLock_acquire_impl(SemLockObject *self, int blocking, - PyObject *timeout_obj); + PyTime_t timeout); static PyObject * _multiprocessing_SemLock_acquire(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -58,7 +59,7 @@ _multiprocessing_SemLock_acquire(PyObject *self, PyObject *const *args, Py_ssize PyObject *argsbuf[2]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int blocking = 1; - PyObject *timeout_obj = Py_None; + PyTime_t timeout = PyTime_MIN; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); @@ -77,10 +78,14 @@ _multiprocessing_SemLock_acquire(PyObject *self, PyObject *const *args, Py_ssize goto skip_optional_pos; } } - timeout_obj = args[1]; + if (args[1] != Py_None) { + if (_PyTime_FromSecondsObject(&timeout, args[1], _PyTime_ROUND_TIMEOUT) < 0) { + goto exit; + } + } skip_optional_pos: Py_BEGIN_CRITICAL_SECTION(self); - return_value = _multiprocessing_SemLock_acquire_impl((SemLockObject *)self, blocking, timeout_obj); + return_value = _multiprocessing_SemLock_acquire_impl((SemLockObject *)self, blocking, timeout); Py_END_CRITICAL_SECTION(); exit: @@ -130,7 +135,7 @@ PyDoc_STRVAR(_multiprocessing_SemLock_acquire__doc__, static PyObject * _multiprocessing_SemLock_acquire_impl(SemLockObject *self, int blocking, - PyObject *timeout_obj); + PyTime_t timeout); static PyObject * _multiprocessing_SemLock_acquire(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -166,7 +171,7 @@ _multiprocessing_SemLock_acquire(PyObject *self, PyObject *const *args, Py_ssize PyObject *argsbuf[2]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int blocking = 1; - PyObject *timeout_obj = Py_None; + PyTime_t timeout = PyTime_MIN; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); @@ -185,10 +190,14 @@ _multiprocessing_SemLock_acquire(PyObject *self, PyObject *const *args, Py_ssize goto skip_optional_pos; } } - timeout_obj = args[1]; + if (args[1] != Py_None) { + if (_PyTime_FromSecondsObject(&timeout, args[1], _PyTime_ROUND_TIMEOUT) < 0) { + goto exit; + } + } skip_optional_pos: Py_BEGIN_CRITICAL_SECTION(self); - return_value = _multiprocessing_SemLock_acquire_impl((SemLockObject *)self, blocking, timeout_obj); + return_value = _multiprocessing_SemLock_acquire_impl((SemLockObject *)self, blocking, timeout); Py_END_CRITICAL_SECTION(); exit: @@ -582,4 +591,4 @@ _multiprocessing_SemLock___exit__(PyObject *self, PyObject *const *args, Py_ssiz #ifndef _MULTIPROCESSING_SEMLOCK___EXIT___METHODDEF #define _MULTIPROCESSING_SEMLOCK___EXIT___METHODDEF #endif /* !defined(_MULTIPROCESSING_SEMLOCK___EXIT___METHODDEF) */ -/*[clinic end generated code: output=d1e349d4ee3d4bbf input=a9049054013a1b77]*/ +/*[clinic end generated code: output=137c7fdfb58f161c input=a9049054013a1b77]*/ diff --git a/Modules/_multiprocessing/semaphore.c b/Modules/_multiprocessing/semaphore.c index 85cc0ac70a6563..9634121bb4b16a 100644 --- a/Modules/_multiprocessing/semaphore.c +++ b/Modules/_multiprocessing/semaphore.c @@ -89,38 +89,35 @@ _GetSemaphoreValue(HANDLE handle, int *value) _multiprocessing.SemLock.acquire block as blocking: bool = True - timeout as timeout_obj: object = None + timeout: duration(accept={float, NoneType}, c_default='PyTime_MIN') = None Acquire the semaphore/lock. [clinic start generated code]*/ static PyObject * _multiprocessing_SemLock_acquire_impl(SemLockObject *self, int blocking, - PyObject *timeout_obj) -/*[clinic end generated code: output=f9998f0b6b0b0872 input=079ca779975f3ad6]*/ + PyTime_t timeout) +/*[clinic end generated code: output=38d34f4b0b2f918e input=b76d85c9695dc3a4]*/ { - double timeout; DWORD res, full_msecs, nhandles; HANDLE handles[2], sigint_event; /* calculate timeout */ if (!blocking) { full_msecs = 0; - } else if (timeout_obj == Py_None) { + } else if (timeout == PyTime_MIN) { /* timeout=None: wait forever */ full_msecs = INFINITE; } else { - timeout = PyFloat_AsDouble(timeout_obj); - if (PyErr_Occurred()) - return NULL; - timeout *= 1000.0; /* convert to millisecs */ - if (timeout < 0.0) { - timeout = 0.0; - } else if (timeout >= 0.5 * INFINITE) { /* 25 days */ + if (timeout < 0) { + timeout = 0; + } + PyTime_t msecs = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_TIMEOUT); + if (msecs >= INFINITE / 2) { /* 25 days */ PyErr_SetString(PyExc_OverflowError, "timeout is too large"); return NULL; } - full_msecs = (DWORD)(timeout + 0.5); + full_msecs = (DWORD)msecs; } /* check whether we already own the lock */ @@ -307,15 +304,15 @@ sem_timedwait_save(sem_t *sem, struct timespec *deadline, PyThreadState *_save) _multiprocessing.SemLock.acquire block as blocking: bool = True - timeout as timeout_obj: object = None + timeout: duration(accept={float, NoneType}, c_default='PyTime_MIN') = None Acquire the semaphore/lock. [clinic start generated code]*/ static PyObject * _multiprocessing_SemLock_acquire_impl(SemLockObject *self, int blocking, - PyObject *timeout_obj) -/*[clinic end generated code: output=f9998f0b6b0b0872 input=079ca779975f3ad6]*/ + PyTime_t timeout) +/*[clinic end generated code: output=38d34f4b0b2f918e input=b76d85c9695dc3a4]*/ { int res, err = 0; struct timespec deadline = {0}; @@ -325,14 +322,10 @@ _multiprocessing_SemLock_acquire_impl(SemLockObject *self, int blocking, Py_RETURN_TRUE; } - int use_deadline = (timeout_obj != Py_None); + int use_deadline = (timeout != PyTime_MIN); /* timeout=None: wait forever */ if (use_deadline) { - double timeout = PyFloat_AsDouble(timeout_obj); - if (PyErr_Occurred()) { - return NULL; - } - if (timeout < 0.0) { - timeout = 0.0; + if (timeout < 0) { + timeout = 0; } struct timeval now; @@ -340,10 +333,8 @@ _multiprocessing_SemLock_acquire_impl(SemLockObject *self, int blocking, PyErr_SetFromErrno(PyExc_OSError); return NULL; } - long sec = (long) timeout; - long nsec = (long) (1e9 * (timeout - sec) + 0.5); - deadline.tv_sec = now.tv_sec + sec; - deadline.tv_nsec = now.tv_usec * 1000 + nsec; + deadline.tv_sec = now.tv_sec + (time_t)(timeout / 1000000000); + deadline.tv_nsec = now.tv_usec * 1000 + (long)(timeout % 1000000000); deadline.tv_sec += (deadline.tv_nsec / 1000000000); deadline.tv_nsec %= 1000000000; } @@ -697,7 +688,7 @@ static PyObject * _multiprocessing_SemLock___enter___impl(SemLockObject *self) /*[clinic end generated code: output=beeb2f07c858511f input=d35c9860992ee790]*/ { - return _multiprocessing_SemLock_acquire_impl(self, 1, Py_None); + return _multiprocessing_SemLock_acquire_impl(self, 1, PyTime_MIN); } /*[clinic input] diff --git a/Modules/_queuemodule.c b/Modules/_queuemodule.c index af54e42a6af584..34edc072ef4e4d 100644 --- a/Modules/_queuemodule.c +++ b/Modules/_queuemodule.c @@ -357,7 +357,7 @@ _queue.SimpleQueue.get cls: defining_class / block: bool = True - timeout as timeout_obj: object = None + timeout: duration(round='ceiling', accept={float, NoneType}, allow_negative=False) = None Remove and return an item from the queue. @@ -374,25 +374,13 @@ in that case). static PyObject * _queue_SimpleQueue_get_impl(simplequeueobject *self, PyTypeObject *cls, - int block, PyObject *timeout_obj) -/*[clinic end generated code: output=5c2cca914cd1e55b input=afa0889bbc6b4761]*/ + int block, PyTime_t timeout) +/*[clinic end generated code: output=08940a9800530258 input=4274501b8112609f]*/ { PyTime_t endtime = 0; - // XXX Use PyThread_ParseTimeoutArg(). - - if (block != 0 && !Py_IsNone(timeout_obj)) { + if (block != 0 && timeout >= 0) { /* With timeout */ - PyTime_t timeout; - if (_PyTime_FromSecondsObject(&timeout, - timeout_obj, _PyTime_ROUND_CEILING) < 0) { - return NULL; - } - if (timeout < 0) { - PyErr_SetString(PyExc_ValueError, - "'timeout' must be a non-negative number"); - return NULL; - } endtime = _PyDeadline_Init(timeout); } @@ -467,7 +455,7 @@ _queue_SimpleQueue_get_nowait_impl(simplequeueobject *self, PyTypeObject *cls) /*[clinic end generated code: output=620c58e2750f8b8a input=d48be63633fefae9]*/ { - return _queue_SimpleQueue_get_impl(self, cls, 0, Py_None); + return _queue_SimpleQueue_get_impl(self, cls, 0, -1); } /*[clinic input] diff --git a/Modules/_sqlite/clinic/_sqlite3.connect.c.h b/Modules/_sqlite/clinic/_sqlite3.connect.c.h index e9d560666c1491..8fd304aeb625ac 100644 --- a/Modules/_sqlite/clinic/_sqlite3.connect.c.h +++ b/Modules/_sqlite/clinic/_sqlite3.connect.c.h @@ -7,6 +7,7 @@ preserve # include "pycore_runtime.h" // _Py_ID() #endif #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() +#include "pycore_time.h" // _PyTime_FromSecondsObject() PyDoc_STRVAR(pysqlite_connect__doc__, "connect($module, /, database, *, timeout=5.0, detect_types=0,\n" @@ -22,4 +23,4 @@ PyDoc_STRVAR(pysqlite_connect__doc__, #define PYSQLITE_CONNECT_METHODDEF \ {"connect", _PyCFunction_CAST(pysqlite_connect), METH_FASTCALL|METH_KEYWORDS, pysqlite_connect__doc__}, -/*[clinic end generated code: output=3d83139ba65e0bb5 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=d8ca9f8d7afe7301 input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/clinic/connection.c.h b/Modules/_sqlite/clinic/connection.c.h index 2feea0ec97bbca..0513d9445996fe 100644 --- a/Modules/_sqlite/clinic/connection.c.h +++ b/Modules/_sqlite/clinic/connection.c.h @@ -7,10 +7,11 @@ preserve # include "pycore_runtime.h" // _Py_ID() #endif #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() +#include "pycore_time.h" // _PyTime_FromSecondsObject() static int pysqlite_connection_init_impl(pysqlite_Connection *self, PyObject *database, - double timeout, int detect_types, + PyTime_t timeout, int detect_types, const char *isolation_level, int check_same_thread, PyObject *factory, int cache_size, int uri, @@ -52,7 +53,7 @@ pysqlite_connection_init(PyObject *self, PyObject *args, PyObject *kwargs) Py_ssize_t nargs = PyTuple_GET_SIZE(args); Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1; PyObject *database; - double timeout = 5.0; + PyTime_t timeout = _PyTime_FromSeconds(5); int detect_types = 0; const char *isolation_level = ""; int check_same_thread = 1; @@ -71,15 +72,8 @@ pysqlite_connection_init(PyObject *self, PyObject *args, PyObject *kwargs) goto skip_optional_kwonly; } if (fastargs[1]) { - if (PyFloat_CheckExact(fastargs[1])) { - timeout = PyFloat_AS_DOUBLE(fastargs[1]); - } - else - { - timeout = PyFloat_AsDouble(fastargs[1]); - if (timeout == -1.0 && PyErr_Occurred()) { - goto exit; - } + if (_PyTime_FromSecondsObject(&timeout, fastargs[1], _PyTime_ROUND_TIMEOUT) < 0) { + goto exit; } if (!--noptargs) { goto skip_optional_kwonly; @@ -1725,4 +1719,4 @@ getconfig(PyObject *self, PyObject *arg) #ifndef DESERIALIZE_METHODDEF #define DESERIALIZE_METHODDEF #endif /* !defined(DESERIALIZE_METHODDEF) */ -/*[clinic end generated code: output=11ccc746e9223121 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=55fa2dffee650a3e input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index ede996b0598ee7..6569368cd8ea42 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -220,7 +220,7 @@ _sqlite3.Connection.__init__ as pysqlite_connection_init database: object * - timeout: double = 5.0 + timeout: duration(c_default='_PyTime_FromSeconds(5)') = 5.0 detect_types: int = 0 isolation_level: IsolationLevel = "" check_same_thread: bool = True @@ -232,12 +232,12 @@ _sqlite3.Connection.__init__ as pysqlite_connection_init static int pysqlite_connection_init_impl(pysqlite_Connection *self, PyObject *database, - double timeout, int detect_types, + PyTime_t timeout, int detect_types, const char *isolation_level, int check_same_thread, PyObject *factory, int cache_size, int uri, enum autocommit_mode autocommit) -/*[clinic end generated code: output=cba057313ea7712f input=5ca4883d8747a49b]*/ +/*[clinic end generated code: output=603e0ce0ba280cc7 input=c5059221250a1d12]*/ { if (PySys_Audit("sqlite3.connect", "O", database) < 0) { return -1; @@ -261,12 +261,14 @@ pysqlite_connection_init_impl(pysqlite_Connection *self, PyObject *database, // Create and configure SQLite database object. sqlite3 *db; int rc; + PyTime_t msecs = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_TIMEOUT); + int busy_timeout = (int)Py_MIN(msecs, (PyTime_t)INT_MAX); Py_BEGIN_ALLOW_THREADS rc = sqlite3_open_v2(PyBytes_AS_STRING(bytes), &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | (uri ? SQLITE_OPEN_URI : 0), NULL); if (rc == SQLITE_OK) { - (void)sqlite3_busy_timeout(db, (int)(timeout*1000)); + (void)sqlite3_busy_timeout(db, busy_timeout); } Py_END_ALLOW_THREADS diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 199e4ac3db723b..841a78a2642f43 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -83,14 +83,11 @@ static PF_SET_THREAD_DESCRIPTION pSetThreadDescription = NULL; /*[clinic input] module _thread +class _thread._ThreadHandle "PyThreadHandleObject *" "clinic_state()->thread_handle_type" class _thread.lock "lockobject *" "clinic_state()->lock_type" class _thread.RLock "rlockobject *" "clinic_state()->rlock_type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=c5a0f8c492a0c263]*/ - -#define clinic_state() get_thread_state_by_cls(type) -#include "clinic/_threadmodule.c.h" -#undef clinic_state +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=64b6ce6e1cea56df]*/ // _ThreadHandle type @@ -631,6 +628,10 @@ typedef struct { #define PyThreadHandleObject_CAST(op) ((PyThreadHandleObject *)(op)) +#define clinic_state() get_thread_state_by_cls(type) +#include "clinic/_threadmodule.c.h" +#undef clinic_state + static PyThreadHandleObject * PyThreadHandleObject_new(PyTypeObject *type) { @@ -684,25 +685,20 @@ PyThreadHandleObject_get_ident(PyObject *op, void *Py_UNUSED(closure)) return PyLong_FromUnsignedLongLong(ThreadHandle_ident(self->handle)); } -static PyObject * -PyThreadHandleObject_join(PyObject *op, PyObject *args) -{ - PyThreadHandleObject *self = PyThreadHandleObject_CAST(op); +/*[clinic input] +_thread._ThreadHandle.join - PyObject *timeout_obj = NULL; - if (!PyArg_ParseTuple(args, "|O:join", &timeout_obj)) { - return NULL; - } + timeout: duration(accept={float, NoneType}) = None + / - PyTime_t timeout_ns = -1; - if (timeout_obj != NULL && timeout_obj != Py_None) { - if (_PyTime_FromSecondsObject(&timeout_ns, timeout_obj, - _PyTime_ROUND_TIMEOUT) < 0) { - return NULL; - } - } +Wait until the thread finishes or the timeout expires. +[clinic start generated code]*/ - if (ThreadHandle_join(self->handle, timeout_ns) < 0) { +static PyObject * +_thread__ThreadHandle_join_impl(PyThreadHandleObject *self, PyTime_t timeout) +/*[clinic end generated code: output=7bfa7df9549cded2 input=08ea86bbe8d01f1f]*/ +{ + if (ThreadHandle_join(self->handle, timeout) < 0) { return NULL; } Py_RETURN_NONE; @@ -739,7 +735,7 @@ static PyGetSetDef ThreadHandle_getsetlist[] = { }; static PyMethodDef ThreadHandle_methods[] = { - {"join", PyThreadHandleObject_join, METH_VARARGS, NULL}, + _THREAD__THREADHANDLE_JOIN_METHODDEF {"_set_done", PyThreadHandleObject_set_done, METH_NOARGS, NULL}, {"is_done", PyThreadHandleObject_is_done, METH_NOARGS, NULL}, {0, 0} @@ -777,17 +773,11 @@ lock_dealloc(PyObject *self) static int -lock_acquire_parse_timeout(PyObject *timeout_obj, int blocking, PyTime_t *timeout) +lock_acquire_check_timeout(int blocking, PyTime_t *timeout) { // XXX Use PyThread_ParseTimeoutArg(). const PyTime_t unset_timeout = _PyTime_FromSeconds(-1); - *timeout = unset_timeout; - - if (timeout_obj - && _PyTime_FromSecondsObject(timeout, - timeout_obj, _PyTime_ROUND_TIMEOUT) < 0) - return -1; if (!blocking && *timeout != unset_timeout ) { PyErr_SetString(PyExc_ValueError, @@ -816,7 +806,7 @@ lock_acquire_parse_timeout(PyObject *timeout_obj, int blocking, PyTime_t *timeou /*[clinic input] _thread.lock.acquire blocking: bool = True - timeout as timeoutobj: object(py_default="-1") = NULL + timeout: duration(c_default='_PyTime_FromSeconds(-1)') = -1 Lock the lock. @@ -829,13 +819,10 @@ The blocking operation is interruptible. [clinic start generated code]*/ static PyObject * -_thread_lock_acquire_impl(lockobject *self, int blocking, - PyObject *timeoutobj) -/*[clinic end generated code: output=569d6b25d508bf6f input=73e75b3d2ec32677]*/ +_thread_lock_acquire_impl(lockobject *self, int blocking, PyTime_t timeout) +/*[clinic end generated code: output=6dd3b3f1d7dc3fa7 input=9f0d15a879c16859]*/ { - PyTime_t timeout; - - if (lock_acquire_parse_timeout(timeoutobj, blocking, &timeout) < 0) { + if (lock_acquire_check_timeout(blocking, &timeout) < 0) { return NULL; } @@ -861,10 +848,10 @@ An obsolete synonym of acquire(). static PyObject * _thread_lock_acquire_lock_impl(lockobject *self, int blocking, - PyObject *timeoutobj) -/*[clinic end generated code: output=ea6c87ea13b56694 input=5e65bd56327ebe85]*/ + PyTime_t timeout) +/*[clinic end generated code: output=cfb5c193e6c84191 input=5e65bd56327ebe85]*/ { - return _thread_lock_acquire_impl(self, blocking, timeoutobj); + return _thread_lock_acquire_impl(self, blocking, timeout); } /*[clinic input] @@ -913,7 +900,7 @@ static PyObject * _thread_lock___enter___impl(lockobject *self) /*[clinic end generated code: output=f27725de751ae064 input=8f982991608d38e7]*/ { - return _thread_lock_acquire_impl(self, 1, NULL); + return _thread_lock_acquire_impl(self, 1, _PyTime_FromSeconds(-1)); } /*[clinic input] @@ -1070,7 +1057,7 @@ rlock_dealloc(PyObject *self) /*[clinic input] _thread.RLock.acquire blocking: bool = True - timeout as timeoutobj: object(py_default="-1") = NULL + timeout: duration(c_default='_PyTime_FromSeconds(-1)') = -1 Lock the lock. @@ -1089,13 +1076,10 @@ the lock is taken and its internal counter initialized to 1. [clinic start generated code]*/ static PyObject * -_thread_RLock_acquire_impl(rlockobject *self, int blocking, - PyObject *timeoutobj) -/*[clinic end generated code: output=73df5af6f67c1513 input=d55a0f5014522a8d]*/ +_thread_RLock_acquire_impl(rlockobject *self, int blocking, PyTime_t timeout) +/*[clinic end generated code: output=d9ed7a3a364ccf23 input=b2742a15f03a7248]*/ { - PyTime_t timeout; - - if (lock_acquire_parse_timeout(timeoutobj, blocking, &timeout) < 0) { + if (lock_acquire_check_timeout(blocking, &timeout) < 0) { return NULL; } @@ -1123,7 +1107,7 @@ static PyObject * _thread_RLock___enter___impl(rlockobject *self) /*[clinic end generated code: output=63135898476bf89f input=33be37f459dca390]*/ { - return _thread_RLock_acquire_impl(self, 1, NULL); + return _thread_RLock_acquire_impl(self, 1, _PyTime_FromSeconds(-1)); } /*[clinic input] diff --git a/Modules/clinic/_datetimemodule.c.h b/Modules/clinic/_datetimemodule.c.h index fac41e7aefc7f4..a26ddedb35c3da 100644 --- a/Modules/clinic/_datetimemodule.c.h +++ b/Modules/clinic/_datetimemodule.c.h @@ -7,6 +7,7 @@ preserve # include "pycore_runtime.h" // _Py_ID() #endif #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() +#include "pycore_time.h" // _PyTime_ObjectToTime_t() PyDoc_STRVAR(delta_new__doc__, "timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0,\n" @@ -221,15 +222,20 @@ PyDoc_STRVAR(datetime_date_fromtimestamp__doc__, {"fromtimestamp", (PyCFunction)datetime_date_fromtimestamp, METH_O|METH_CLASS, datetime_date_fromtimestamp__doc__}, static PyObject * -datetime_date_fromtimestamp_impl(PyTypeObject *type, PyObject *timestamp); +datetime_date_fromtimestamp_impl(PyTypeObject *type, time_t timestamp); static PyObject * -datetime_date_fromtimestamp(PyObject *type, PyObject *timestamp) +datetime_date_fromtimestamp(PyObject *type, PyObject *arg) { PyObject *return_value = NULL; + time_t timestamp; + if (_PyTime_ObjectToTime_t(arg, ×tamp, _PyTime_ROUND_FLOOR) < 0) { + goto exit; + } return_value = datetime_date_fromtimestamp_impl((PyTypeObject *)type, timestamp); +exit: return return_value; } @@ -2091,4 +2097,4 @@ datetime_datetime___reduce__(PyObject *self, PyObject *Py_UNUSED(ignored)) { return datetime_datetime___reduce___impl((PyDateTime_DateTime *)self); } -/*[clinic end generated code: output=8f63509398651723 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=336fd62531faf9cb input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_queuemodule.c.h b/Modules/clinic/_queuemodule.c.h index b67dd23f260c9e..6f062138233cbe 100644 --- a/Modules/clinic/_queuemodule.c.h +++ b/Modules/clinic/_queuemodule.c.h @@ -8,6 +8,7 @@ preserve #endif #include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_NoKeywords() +#include "pycore_time.h" // _PyTime_FromSecondsObject() PyDoc_STRVAR(simplequeue_new__doc__, "SimpleQueue()\n" @@ -203,7 +204,7 @@ PyDoc_STRVAR(_queue_SimpleQueue_get__doc__, static PyObject * _queue_SimpleQueue_get_impl(simplequeueobject *self, PyTypeObject *cls, - int block, PyObject *timeout_obj); + int block, PyTime_t timeout); static PyObject * _queue_SimpleQueue_get(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -239,7 +240,7 @@ _queue_SimpleQueue_get(PyObject *self, PyTypeObject *cls, PyObject *const *args, PyObject *argsbuf[2]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int block = 1; - PyObject *timeout_obj = Py_None; + PyTime_t timeout = -1; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); @@ -258,10 +259,19 @@ _queue_SimpleQueue_get(PyObject *self, PyTypeObject *cls, PyObject *const *args, goto skip_optional_pos; } } - timeout_obj = args[1]; + if (args[1] != Py_None) { + if (_PyTime_FromSecondsObject(&timeout, args[1], _PyTime_ROUND_CEILING) < 0) { + goto exit; + } + if (timeout < 0) { + PyErr_SetString(PyExc_ValueError, + "timeout must be non-negative"); + goto exit; + } + } skip_optional_pos: Py_BEGIN_CRITICAL_SECTION(self); - return_value = _queue_SimpleQueue_get_impl((simplequeueobject *)self, cls, block, timeout_obj); + return_value = _queue_SimpleQueue_get_impl((simplequeueobject *)self, cls, block, timeout); Py_END_CRITICAL_SECTION(); exit: @@ -390,4 +400,4 @@ _queue_SimpleQueue___sizeof__(PyObject *self, PyObject *Py_UNUSED(ignored)) exit: return return_value; } -/*[clinic end generated code: output=8219fe2f2ed5f068 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=55d25e55fec8dd55 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_threadmodule.c.h b/Modules/clinic/_threadmodule.c.h index 926bea8e1e419a..f9679271a3de97 100644 --- a/Modules/clinic/_threadmodule.c.h +++ b/Modules/clinic/_threadmodule.c.h @@ -6,7 +6,44 @@ preserve # include "pycore_gc.h" // PyGC_Head # include "pycore_runtime.h" // _Py_ID() #endif -#include "pycore_modsupport.h" // _PyArg_UnpackKeywords() +#include "pycore_modsupport.h" // _PyArg_CheckPositional() +#include "pycore_time.h" // _PyTime_FromSecondsObject() + +PyDoc_STRVAR(_thread__ThreadHandle_join__doc__, +"join($self, timeout=None, /)\n" +"--\n" +"\n" +"Wait until the thread finishes or the timeout expires."); + +#define _THREAD__THREADHANDLE_JOIN_METHODDEF \ + {"join", _PyCFunction_CAST(_thread__ThreadHandle_join), METH_FASTCALL, _thread__ThreadHandle_join__doc__}, + +static PyObject * +_thread__ThreadHandle_join_impl(PyThreadHandleObject *self, PyTime_t timeout); + +static PyObject * +_thread__ThreadHandle_join(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyTime_t timeout = -1; + + if (!_PyArg_CheckPositional("join", nargs, 0, 1)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + if (args[0] != Py_None) { + if (_PyTime_FromSecondsObject(&timeout, args[0], _PyTime_ROUND_TIMEOUT) < 0) { + goto exit; + } + } +skip_optional: + return_value = _thread__ThreadHandle_join_impl((PyThreadHandleObject *)self, timeout); + +exit: + return return_value; +} PyDoc_STRVAR(_thread_lock_acquire__doc__, "acquire($self, /, blocking=True, timeout=-1)\n" @@ -25,8 +62,7 @@ PyDoc_STRVAR(_thread_lock_acquire__doc__, {"acquire", _PyCFunction_CAST(_thread_lock_acquire), METH_FASTCALL|METH_KEYWORDS, _thread_lock_acquire__doc__}, static PyObject * -_thread_lock_acquire_impl(lockobject *self, int blocking, - PyObject *timeoutobj); +_thread_lock_acquire_impl(lockobject *self, int blocking, PyTime_t timeout); static PyObject * _thread_lock_acquire(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -62,7 +98,7 @@ _thread_lock_acquire(PyObject *self, PyObject *const *args, Py_ssize_t nargs, Py PyObject *argsbuf[2]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int blocking = 1; - PyObject *timeoutobj = NULL; + PyTime_t timeout = _PyTime_FromSeconds(-1); args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); @@ -81,9 +117,11 @@ _thread_lock_acquire(PyObject *self, PyObject *const *args, Py_ssize_t nargs, Py goto skip_optional_pos; } } - timeoutobj = args[1]; + if (_PyTime_FromSecondsObject(&timeout, args[1], _PyTime_ROUND_TIMEOUT) < 0) { + goto exit; + } skip_optional_pos: - return_value = _thread_lock_acquire_impl((lockobject *)self, blocking, timeoutobj); + return_value = _thread_lock_acquire_impl((lockobject *)self, blocking, timeout); exit: return return_value; @@ -100,7 +138,7 @@ PyDoc_STRVAR(_thread_lock_acquire_lock__doc__, static PyObject * _thread_lock_acquire_lock_impl(lockobject *self, int blocking, - PyObject *timeoutobj); + PyTime_t timeout); static PyObject * _thread_lock_acquire_lock(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -136,7 +174,7 @@ _thread_lock_acquire_lock(PyObject *self, PyObject *const *args, Py_ssize_t narg PyObject *argsbuf[2]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int blocking = 1; - PyObject *timeoutobj = NULL; + PyTime_t timeout = _PyTime_FromSeconds(-1); args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); @@ -155,9 +193,11 @@ _thread_lock_acquire_lock(PyObject *self, PyObject *const *args, Py_ssize_t narg goto skip_optional_pos; } } - timeoutobj = args[1]; + if (_PyTime_FromSecondsObject(&timeout, args[1], _PyTime_ROUND_TIMEOUT) < 0) { + goto exit; + } skip_optional_pos: - return_value = _thread_lock_acquire_lock_impl((lockobject *)self, blocking, timeoutobj); + return_value = _thread_lock_acquire_lock_impl((lockobject *)self, blocking, timeout); exit: return return_value; @@ -357,8 +397,7 @@ PyDoc_STRVAR(_thread_RLock_acquire__doc__, {"acquire", _PyCFunction_CAST(_thread_RLock_acquire), METH_FASTCALL|METH_KEYWORDS, _thread_RLock_acquire__doc__}, static PyObject * -_thread_RLock_acquire_impl(rlockobject *self, int blocking, - PyObject *timeoutobj); +_thread_RLock_acquire_impl(rlockobject *self, int blocking, PyTime_t timeout); static PyObject * _thread_RLock_acquire(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -394,7 +433,7 @@ _thread_RLock_acquire(PyObject *self, PyObject *const *args, Py_ssize_t nargs, P PyObject *argsbuf[2]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int blocking = 1; - PyObject *timeoutobj = NULL; + PyTime_t timeout = _PyTime_FromSeconds(-1); args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); @@ -413,9 +452,11 @@ _thread_RLock_acquire(PyObject *self, PyObject *const *args, Py_ssize_t nargs, P goto skip_optional_pos; } } - timeoutobj = args[1]; + if (_PyTime_FromSecondsObject(&timeout, args[1], _PyTime_ROUND_TIMEOUT) < 0) { + goto exit; + } skip_optional_pos: - return_value = _thread_RLock_acquire_impl((rlockobject *)self, blocking, timeoutobj); + return_value = _thread_RLock_acquire_impl((rlockobject *)self, blocking, timeout); exit: return return_value; @@ -740,4 +781,4 @@ _thread_set_name(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb #ifndef _THREAD_SET_NAME_METHODDEF #define _THREAD_SET_NAME_METHODDEF #endif /* !defined(_THREAD_SET_NAME_METHODDEF) */ -/*[clinic end generated code: output=0f1707cbafc0e8f2 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=e0d24c7cec77c83c input=a9049054013a1b77]*/ diff --git a/Modules/clinic/faulthandler.c.h b/Modules/clinic/faulthandler.c.h index 07a9dd7dc561e1..84c2c3aae43ff8 100644 --- a/Modules/clinic/faulthandler.c.h +++ b/Modules/clinic/faulthandler.c.h @@ -9,6 +9,7 @@ preserve #include "pycore_abstract.h" // _PyNumber_Index() #include "pycore_long.h" // _PyLong_UnsignedInt_Converter() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() +#include "pycore_time.h" // _PyTime_FromSecondsObject() PyDoc_STRVAR(faulthandler_dump_traceback_py__doc__, "dump_traceback($module, /, file=sys.stderr, all_threads=True, *,\n" @@ -342,9 +343,8 @@ PyDoc_STRVAR(faulthandler_dump_traceback_later__doc__, {"dump_traceback_later", _PyCFunction_CAST(faulthandler_dump_traceback_later), METH_FASTCALL|METH_KEYWORDS, faulthandler_dump_traceback_later__doc__}, static PyObject * -faulthandler_dump_traceback_later_impl(PyObject *module, - PyObject *timeout_obj, int repeat, - PyObject *file, int exit, +faulthandler_dump_traceback_later_impl(PyObject *module, PyTime_t timeout, + int repeat, PyObject *file, int exit, Py_ssize_t max_threads); static PyObject * @@ -380,7 +380,7 @@ faulthandler_dump_traceback_later(PyObject *module, PyObject *const *args, Py_ss #undef KWTUPLE PyObject *argsbuf[5]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; - PyObject *timeout_obj; + PyTime_t timeout = -1; int repeat = 0; PyObject *file = NULL; int exit = 0; @@ -391,7 +391,9 @@ faulthandler_dump_traceback_later(PyObject *module, PyObject *const *args, Py_ss if (!args) { goto exit; } - timeout_obj = args[0]; + if (_PyTime_FromSecondsObject(&timeout, args[0], _PyTime_ROUND_TIMEOUT) < 0) { + goto exit; + } if (!noptargs) { goto skip_optional_pos; } @@ -436,7 +438,7 @@ faulthandler_dump_traceback_later(PyObject *module, PyObject *const *args, Py_ss max_threads = ival; } skip_optional_kwonly: - return_value = faulthandler_dump_traceback_later_impl(module, timeout_obj, repeat, file, exit, max_threads); + return_value = faulthandler_dump_traceback_later_impl(module, timeout, repeat, file, exit, max_threads); exit: return return_value; @@ -782,4 +784,4 @@ faulthandler__raise_exception(PyObject *module, PyObject *const *args, Py_ssize_ #ifndef FAULTHANDLER__RAISE_EXCEPTION_METHODDEF #define FAULTHANDLER__RAISE_EXCEPTION_METHODDEF #endif /* !defined(FAULTHANDLER__RAISE_EXCEPTION_METHODDEF) */ -/*[clinic end generated code: output=14815a5f8afe813f input=a9049054013a1b77]*/ +/*[clinic end generated code: output=1e27da7f1be13de1 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/selectmodule.c.h b/Modules/clinic/selectmodule.c.h index c1c8ad40e724f5..bf3d98b9479bb9 100644 --- a/Modules/clinic/selectmodule.c.h +++ b/Modules/clinic/selectmodule.c.h @@ -9,6 +9,7 @@ preserve #include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_long.h" // _PyLong_UnsignedShort_Converter() #include "pycore_modsupport.h" // _PyArg_CheckPositional() +#include "pycore_time.h" // _PyTime_FromSecondsObject() PyDoc_STRVAR(select_select__doc__, "select($module, rlist, wlist, xlist, timeout=None, /)\n" @@ -43,7 +44,7 @@ PyDoc_STRVAR(select_select__doc__, static PyObject * select_select_impl(PyObject *module, PyObject *rlist, PyObject *wlist, - PyObject *xlist, PyObject *timeout_obj); + PyObject *xlist, PyTime_t timeout); static PyObject * select_select(PyObject *module, PyObject *const *args, Py_ssize_t nargs) @@ -52,7 +53,7 @@ select_select(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyObject *rlist; PyObject *wlist; PyObject *xlist; - PyObject *timeout_obj = Py_None; + PyTime_t timeout = -1; if (!_PyArg_CheckPositional("select", nargs, 3, 4)) { goto exit; @@ -63,9 +64,18 @@ select_select(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (nargs < 4) { goto skip_optional; } - timeout_obj = args[3]; + if (args[3] != Py_None) { + if (_PyTime_FromSecondsObject(&timeout, args[3], _PyTime_ROUND_TIMEOUT) < 0) { + goto exit; + } + if (timeout < 0) { + PyErr_SetString(PyExc_ValueError, + "timeout must be non-negative"); + goto exit; + } + } skip_optional: - return_value = select_select_impl(module, rlist, wlist, xlist, timeout_obj); + return_value = select_select_impl(module, rlist, wlist, xlist, timeout); exit: return return_value; @@ -222,13 +232,13 @@ PyDoc_STRVAR(select_poll_poll__doc__, {"poll", _PyCFunction_CAST(select_poll_poll), METH_FASTCALL, select_poll_poll__doc__}, static PyObject * -select_poll_poll_impl(pollObject *self, PyObject *timeout_obj); +select_poll_poll_impl(pollObject *self, PyTime_t timeout); static PyObject * select_poll_poll(PyObject *self, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *timeout_obj = Py_None; + PyTime_t timeout = -1; if (!_PyArg_CheckPositional("poll", nargs, 0, 1)) { goto exit; @@ -236,10 +246,14 @@ select_poll_poll(PyObject *self, PyObject *const *args, Py_ssize_t nargs) if (nargs < 1) { goto skip_optional; } - timeout_obj = args[0]; + if (args[0] != Py_None) { + if (_PyTime_FromMillisecondsObject(&timeout, args[0], _PyTime_ROUND_TIMEOUT) < 0) { + goto exit; + } + } skip_optional: Py_BEGIN_CRITICAL_SECTION(self); - return_value = select_poll_poll_impl((pollObject *)self, timeout_obj); + return_value = select_poll_poll_impl((pollObject *)self, timeout); Py_END_CRITICAL_SECTION(); exit: @@ -407,13 +421,13 @@ PyDoc_STRVAR(select_devpoll_poll__doc__, {"poll", _PyCFunction_CAST(select_devpoll_poll), METH_FASTCALL, select_devpoll_poll__doc__}, static PyObject * -select_devpoll_poll_impl(devpollObject *self, PyObject *timeout_obj); +select_devpoll_poll_impl(devpollObject *self, PyTime_t timeout); static PyObject * select_devpoll_poll(PyObject *self, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *timeout_obj = Py_None; + PyTime_t timeout = -1; if (!_PyArg_CheckPositional("poll", nargs, 0, 1)) { goto exit; @@ -421,10 +435,14 @@ select_devpoll_poll(PyObject *self, PyObject *const *args, Py_ssize_t nargs) if (nargs < 1) { goto skip_optional; } - timeout_obj = args[0]; + if (args[0] != Py_None) { + if (_PyTime_FromMillisecondsObject(&timeout, args[0], _PyTime_ROUND_TIMEOUT) < 0) { + goto exit; + } + } skip_optional: Py_BEGIN_CRITICAL_SECTION(self); - return_value = select_devpoll_poll_impl((devpollObject *)self, timeout_obj); + return_value = select_devpoll_poll_impl((devpollObject *)self, timeout); Py_END_CRITICAL_SECTION(); exit: @@ -986,8 +1004,7 @@ PyDoc_STRVAR(select_epoll_poll__doc__, {"poll", _PyCFunction_CAST(select_epoll_poll), METH_FASTCALL|METH_KEYWORDS, select_epoll_poll__doc__}, static PyObject * -select_epoll_poll_impl(pyEpoll_Object *self, PyObject *timeout_obj, - int maxevents); +select_epoll_poll_impl(pyEpoll_Object *self, PyTime_t timeout, int maxevents); static PyObject * select_epoll_poll(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -1022,7 +1039,7 @@ select_epoll_poll(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObj #undef KWTUPLE PyObject *argsbuf[2]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - PyObject *timeout_obj = Py_None; + PyTime_t timeout = -1; int maxevents = -1; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, @@ -1034,7 +1051,11 @@ select_epoll_poll(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObj goto skip_optional_pos; } if (args[0]) { - timeout_obj = args[0]; + if (args[0] != Py_None) { + if (_PyTime_FromSecondsObject(&timeout, args[0], _PyTime_ROUND_TIMEOUT) < 0) { + goto exit; + } + } if (!--noptargs) { goto skip_optional_pos; } @@ -1044,7 +1065,7 @@ select_epoll_poll(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObj goto exit; } skip_optional_pos: - return_value = select_epoll_poll_impl((pyEpoll_Object *)self, timeout_obj, maxevents); + return_value = select_epoll_poll_impl((pyEpoll_Object *)self, timeout, maxevents); exit: return return_value; @@ -1270,7 +1291,7 @@ PyDoc_STRVAR(select_kqueue_control__doc__, static PyObject * select_kqueue_control_impl(kqueue_queue_Object *self, PyObject *changelist, - int maxevents, PyObject *otimeout); + int maxevents, PyTime_t timeout); static PyObject * select_kqueue_control(PyObject *self, PyObject *const *args, Py_ssize_t nargs) @@ -1278,7 +1299,7 @@ select_kqueue_control(PyObject *self, PyObject *const *args, Py_ssize_t nargs) PyObject *return_value = NULL; PyObject *changelist; int maxevents; - PyObject *otimeout = Py_None; + PyTime_t timeout = -1; if (!_PyArg_CheckPositional("control", nargs, 2, 3)) { goto exit; @@ -1291,9 +1312,18 @@ select_kqueue_control(PyObject *self, PyObject *const *args, Py_ssize_t nargs) if (nargs < 3) { goto skip_optional; } - otimeout = args[2]; + if (args[2] != Py_None) { + if (_PyTime_FromSecondsObject(&timeout, args[2], _PyTime_ROUND_TIMEOUT) < 0) { + goto exit; + } + if (timeout < 0) { + PyErr_SetString(PyExc_ValueError, + "timeout must be non-negative"); + goto exit; + } + } skip_optional: - return_value = select_kqueue_control_impl((kqueue_queue_Object *)self, changelist, maxevents, otimeout); + return_value = select_kqueue_control_impl((kqueue_queue_Object *)self, changelist, maxevents, timeout); exit: return return_value; @@ -1400,4 +1430,4 @@ select_kqueue_control(PyObject *self, PyObject *const *args, Py_ssize_t nargs) #ifndef SELECT_KQUEUE_CONTROL_METHODDEF #define SELECT_KQUEUE_CONTROL_METHODDEF #endif /* !defined(SELECT_KQUEUE_CONTROL_METHODDEF) */ -/*[clinic end generated code: output=a1ac666294fd14bd input=a9049054013a1b77]*/ +/*[clinic end generated code: output=fcd6eea7096b311e input=a9049054013a1b77]*/ diff --git a/Modules/clinic/signalmodule.c.h b/Modules/clinic/signalmodule.c.h index 6125f253ac0380..8f2f0b8c78346a 100644 --- a/Modules/clinic/signalmodule.c.h +++ b/Modules/clinic/signalmodule.c.h @@ -7,6 +7,7 @@ preserve # include "pycore_runtime.h" // _Py_ID() #endif #include "pycore_modsupport.h" // _PyArg_CheckPositional() +#include "pycore_time.h" // _PyTime_Duration_TimevalCeil_Converter() PyDoc_STRVAR(signal_default_int_handler__doc__, "default_int_handler($module, signalnum, frame, /)\n" @@ -372,16 +373,16 @@ PyDoc_STRVAR(signal_setitimer__doc__, {"setitimer", _PyCFunction_CAST(signal_setitimer), METH_FASTCALL, signal_setitimer__doc__}, static PyObject * -signal_setitimer_impl(PyObject *module, int which, PyObject *seconds, - PyObject *interval); +signal_setitimer_impl(PyObject *module, int which, struct timeval seconds, + struct timeval interval); static PyObject * signal_setitimer(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; int which; - PyObject *seconds; - PyObject *interval = NULL; + struct timeval seconds = {0, 0}; + struct timeval interval = {0, 0}; if (!_PyArg_CheckPositional("setitimer", nargs, 2, 3)) { goto exit; @@ -390,11 +391,15 @@ signal_setitimer(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (which == -1 && PyErr_Occurred()) { goto exit; } - seconds = args[1]; + if (!_PyTime_Duration_TimevalCeil_Converter(args[1], &seconds)) { + goto exit; + } if (nargs < 3) { goto skip_optional; } - interval = args[2]; + if (!_PyTime_Duration_TimevalCeil_Converter(args[2], &interval)) { + goto exit; + } skip_optional: return_value = signal_setitimer_impl(module, which, seconds, interval); @@ -607,15 +612,14 @@ PyDoc_STRVAR(signal_sigtimedwait__doc__, {"sigtimedwait", _PyCFunction_CAST(signal_sigtimedwait), METH_FASTCALL, signal_sigtimedwait__doc__}, static PyObject * -signal_sigtimedwait_impl(PyObject *module, sigset_t sigset, - PyObject *timeout_obj); +signal_sigtimedwait_impl(PyObject *module, sigset_t sigset, PyTime_t timeout); static PyObject * signal_sigtimedwait(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; sigset_t sigset; - PyObject *timeout_obj; + PyTime_t timeout = -1; if (!_PyArg_CheckPositional("sigtimedwait", nargs, 2, 2)) { goto exit; @@ -623,8 +627,15 @@ signal_sigtimedwait(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (!_Py_Sigset_Converter(args[0], &sigset)) { goto exit; } - timeout_obj = args[1]; - return_value = signal_sigtimedwait_impl(module, sigset, timeout_obj); + if (_PyTime_FromSecondsObject(&timeout, args[1], _PyTime_ROUND_CEILING) < 0) { + goto exit; + } + if (timeout < 0) { + PyErr_SetString(PyExc_ValueError, + "timeout must be non-negative"); + goto exit; + } + return_value = signal_sigtimedwait_impl(module, sigset, timeout); exit: return return_value; @@ -807,4 +818,4 @@ signal_pidfd_send_signal(PyObject *module, PyObject *const *args, Py_ssize_t nar #ifndef SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF #define SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF #endif /* !defined(SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF) */ -/*[clinic end generated code: output=2a04ec31f49b1c93 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=ca257fd2153935b7 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/socketmodule.c.h b/Modules/clinic/socketmodule.c.h index f89b91b9b99753..3523040b393497 100644 --- a/Modules/clinic/socketmodule.c.h +++ b/Modules/clinic/socketmodule.c.h @@ -9,6 +9,7 @@ preserve #include "pycore_abstract.h" // _PyNumber_Index() #include "pycore_long.h" // _PyLong_UInt16_Converter() #include "pycore_modsupport.h" // _PyArg_CheckPositional() +#include "pycore_time.h" // _PyTime_FromSecondsObject() #if (defined(HAVE_ACCEPT) || defined(HAVE_ACCEPT4)) @@ -100,15 +101,27 @@ PyDoc_STRVAR(_socket_socket_settimeout__doc__, {"settimeout", (PyCFunction)_socket_socket_settimeout, METH_O, _socket_socket_settimeout__doc__}, static PyObject * -_socket_socket_settimeout_impl(PySocketSockObject *s, PyObject *arg); +_socket_socket_settimeout_impl(PySocketSockObject *s, PyTime_t timeout); static PyObject * _socket_socket_settimeout(PyObject *s, PyObject *arg) { PyObject *return_value = NULL; + PyTime_t timeout = -1; - return_value = _socket_socket_settimeout_impl((PySocketSockObject *)s, arg); + if (arg != Py_None) { + if (_PyTime_FromSecondsObject(&timeout, arg, _PyTime_ROUND_TIMEOUT) < 0) { + goto exit; + } + if (timeout < 0) { + PyErr_SetString(PyExc_ValueError, + "timeout must be non-negative"); + goto exit; + } + } + return_value = _socket_socket_settimeout_impl((PySocketSockObject *)s, timeout); +exit: return return_value; } @@ -2159,6 +2172,31 @@ PyDoc_STRVAR(_socket_setdefaulttimeout__doc__, #define _SOCKET_SETDEFAULTTIMEOUT_METHODDEF \ {"setdefaulttimeout", (PyCFunction)_socket_setdefaulttimeout, METH_O, _socket_setdefaulttimeout__doc__}, +static PyObject * +_socket_setdefaulttimeout_impl(PyObject *module, PyTime_t timeout); + +static PyObject * +_socket_setdefaulttimeout(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyTime_t timeout = -1; + + if (arg != Py_None) { + if (_PyTime_FromSecondsObject(&timeout, arg, _PyTime_ROUND_TIMEOUT) < 0) { + goto exit; + } + if (timeout < 0) { + PyErr_SetString(PyExc_ValueError, + "timeout must be non-negative"); + goto exit; + } + } + return_value = _socket_setdefaulttimeout_impl(module, timeout); + +exit: + return return_value; +} + #if (defined(HAVE_IF_NAMEINDEX) || defined(MS_WINDOWS)) PyDoc_STRVAR(_socket_if_nameindex__doc__, @@ -2478,4 +2516,4 @@ _socket_CMSG_SPACE(PyObject *module, PyObject *arg) #ifndef _SOCKET_CMSG_SPACE_METHODDEF #define _SOCKET_CMSG_SPACE_METHODDEF #endif /* !defined(_SOCKET_CMSG_SPACE_METHODDEF) */ -/*[clinic end generated code: output=acc30d6fdeb54e90 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=260f1d042fa906ca input=a9049054013a1b77]*/ diff --git a/Modules/faulthandler.c b/Modules/faulthandler.c index 21734d068270c5..116c3c1afe167d 100644 --- a/Modules/faulthandler.c +++ b/Modules/faulthandler.c @@ -783,7 +783,7 @@ format_timeout(PyTime_t us) /*[clinic input] faulthandler.dump_traceback_later - timeout as timeout_obj: object + timeout: duration repeat: bool = False file: object(py_default="sys.stderr") = NULL exit: bool = False @@ -798,22 +798,17 @@ max_threads caps the number of threads dumped. [clinic start generated code]*/ static PyObject * -faulthandler_dump_traceback_later_impl(PyObject *module, - PyObject *timeout_obj, int repeat, - PyObject *file, int exit, +faulthandler_dump_traceback_later_impl(PyObject *module, PyTime_t timeout, + int repeat, PyObject *file, int exit, Py_ssize_t max_threads) -/*[clinic end generated code: output=543a0f3807113394 input=32aaf7437d0928db]*/ +/*[clinic end generated code: output=9f28abab0cb9a89d input=80e588c432b70d7e]*/ { - PyTime_t timeout, timeout_us; + PyTime_t timeout_us; int fd; PyThreadState *tstate; char *header; size_t header_len; - if (_PyTime_FromSecondsObject(&timeout, timeout_obj, - _PyTime_ROUND_TIMEOUT) < 0) { - return NULL; - } timeout_us = _PyTime_AsMicroseconds(timeout, _PyTime_ROUND_TIMEOUT); if (timeout_us <= 0) { PyErr_SetString(PyExc_ValueError, "timeout must be greater than 0"); diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index b9fb7762e3dacd..d9b914be3c516b 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -247,7 +247,7 @@ select.select rlist: object wlist: object xlist: object - timeout as timeout_obj: object = None + timeout: duration(accept={float, NoneType}, allow_negative=False) = None / Wait until one or more file descriptors are ready for some kind of I/O. @@ -277,8 +277,8 @@ descriptors can be used. static PyObject * select_select_impl(PyObject *module, PyObject *rlist, PyObject *wlist, - PyObject *xlist, PyObject *timeout_obj) -/*[clinic end generated code: output=2b3cfa824f7ae4cf input=cc93e9bb9ffacbaf]*/ + PyObject *xlist, PyTime_t timeout) +/*[clinic end generated code: output=a745f606d2d8944b input=b8323e681af8584e]*/ { #ifdef SELECT_USES_HEAP pylist *rfd2obj, *wfd2obj, *efd2obj; @@ -298,27 +298,13 @@ select_select_impl(PyObject *module, PyObject *rlist, PyObject *wlist, struct timeval tv, *tvp; int imax, omax, emax, max; int n; - PyTime_t timeout, deadline = 0; + PyTime_t deadline = 0; - if (timeout_obj == Py_None) + if (timeout < 0) tvp = (struct timeval *)NULL; else { - if (_PyTime_FromSecondsObject(&timeout, timeout_obj, - _PyTime_ROUND_TIMEOUT) < 0) { - if (PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_Format(PyExc_TypeError, - "timeout must be a real number or None, not %T", - timeout_obj); - } - return NULL; - } - if (_PyTime_AsTimeval(timeout, &tv, _PyTime_ROUND_TIMEOUT) == -1) return NULL; - if (tv.tv_sec < 0) { - PyErr_SetString(PyExc_ValueError, "timeout must be non-negative"); - return NULL; - } tvp = &tv; } @@ -609,7 +595,7 @@ select_poll_unregister_impl(pollObject *self, int fd) @critical_section select.poll.poll - timeout as timeout_obj: object = None + timeout: duration(unit='ms', accept={float, NoneType}) = None The maximum time to wait in milliseconds, or else None (or a negative value) to wait indefinitely. / @@ -621,43 +607,28 @@ to report, as a list of (fd, event) 2-tuples. [clinic start generated code]*/ static PyObject * -select_poll_poll_impl(pollObject *self, PyObject *timeout_obj) -/*[clinic end generated code: output=876e837d193ed7e4 input=e0a9c0aa283de8c8]*/ +select_poll_poll_impl(pollObject *self, PyTime_t timeout) +/*[clinic end generated code: output=3277f509c8d65943 input=55893cf923910f95]*/ { PyObject *result_list = NULL; int poll_result, i, j; PyObject *value = NULL, *num = NULL; - PyTime_t timeout = -1, deadline = 0; + PyTime_t deadline = 0; int async_err = 0; - if (timeout_obj != Py_None) { - if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj, - _PyTime_ROUND_TIMEOUT) < 0) { - if (PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_Format(PyExc_TypeError, - "timeout must be a real number or None, not %T", - timeout_obj); - } - return NULL; - } - } - #ifdef HAVE_PPOLL struct timespec ts, *ts_p = NULL; - if (timeout_obj != Py_None) { + if (timeout >= 0) { if (_PyTime_AsTimespec(timeout, &ts) < 0) { return NULL; } - - if (timeout >= 0) { - ts_p = &ts; - } + ts_p = &ts; } #else PyTime_t ms = -1; - if (timeout_obj != Py_None) { + if (timeout >= 0) { ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_TIMEOUT); if (ms < INT_MIN || ms > INT_MAX) { PyErr_SetString(PyExc_OverflowError, "timeout is too large"); @@ -974,7 +945,7 @@ select_devpoll_unregister_impl(devpollObject *self, int fd) /*[clinic input] @critical_section select.devpoll.poll - timeout as timeout_obj: object = None + timeout: duration(unit='ms', accept={float, NoneType}) = None The maximum time to wait in milliseconds, or else None (or a negative value) to wait indefinitely. / @@ -986,34 +957,22 @@ to report, as a list of (fd, event) 2-tuples. [clinic start generated code]*/ static PyObject * -select_devpoll_poll_impl(devpollObject *self, PyObject *timeout_obj) -/*[clinic end generated code: output=2654e5457cca0b3c input=9e1672658d728539]*/ +select_devpoll_poll_impl(devpollObject *self, PyTime_t timeout) +/*[clinic end generated code: output=1482d1e3a61f509b input=a106ed5295252e80]*/ { struct dvpoll dvp; PyObject *result_list = NULL; int poll_result, i; PyObject *value, *num1, *num2; - PyTime_t timeout, ms, deadline = 0; + PyTime_t ms, deadline = 0; if (self->fd_devpoll < 0) return devpoll_err_closed(); - /* Check values for timeout */ - if (timeout_obj == Py_None) { - timeout = -1; + if (timeout < 0) { ms = -1; } else { - if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj, - _PyTime_ROUND_TIMEOUT) < 0) { - if (PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_Format(PyExc_TypeError, - "timeout must be a real number or None, not %T", - timeout_obj); - } - return NULL; - } - ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_TIMEOUT); if (ms < -1 || ms > INT_MAX) { PyErr_SetString(PyExc_OverflowError, "timeout is too large"); @@ -1597,7 +1556,7 @@ select_epoll_unregister_impl(pyEpoll_Object *self, int fd) /*[clinic input] select.epoll.poll - timeout as timeout_obj: object = None + timeout: duration(accept={float, NoneType}) = None the maximum time to wait in seconds (with fractions); a timeout of None or -1 makes poll wait indefinitely maxevents: int = -1 @@ -1610,31 +1569,20 @@ report, as a list of (fd, events) 2-tuples. [clinic start generated code]*/ static PyObject * -select_epoll_poll_impl(pyEpoll_Object *self, PyObject *timeout_obj, - int maxevents) -/*[clinic end generated code: output=e02d121a20246c6c input=911ddc16978a9159]*/ +select_epoll_poll_impl(pyEpoll_Object *self, PyTime_t timeout, int maxevents) +/*[clinic end generated code: output=bf26920ae5d044aa input=494d1006b9770384]*/ { int nfds, i; PyObject *elist = NULL, *etuple = NULL; struct epoll_event *evs = NULL; - PyTime_t timeout = -1, ms = -1, deadline = 0; + PyTime_t ms = -1, deadline = 0; if (self->epfd < 0) return pyepoll_err_closed(); - if (timeout_obj != Py_None) { + if (timeout >= 0) { /* epoll_wait() has a resolution of 1 millisecond, round towards infinity to wait at least timeout seconds. */ - if (_PyTime_FromSecondsObject(&timeout, timeout_obj, - _PyTime_ROUND_TIMEOUT) < 0) { - if (PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_Format(PyExc_TypeError, - "timeout must be a real number or None, not %T", - timeout_obj); - } - return NULL; - } - ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING); if (ms < INT_MIN || ms > INT_MAX) { PyErr_SetString(PyExc_OverflowError, "timeout is too large"); @@ -2324,7 +2272,7 @@ select.kqueue.control to the kernel's watch list or None. maxevents: int The maximum number of events that the kernel will return. - timeout as otimeout: object = None + timeout: duration(accept={float, NoneType}, allow_negative=False) = None The maximum time to wait in seconds, or else None to wait forever. This accepts non-integers for smaller timeouts, too. / @@ -2334,8 +2282,8 @@ Calls the kernel kevent function. static PyObject * select_kqueue_control_impl(kqueue_queue_Object *self, PyObject *changelist, - int maxevents, PyObject *otimeout) -/*[clinic end generated code: output=81324ff5130db7ae input=be969d2bc6f84205]*/ + int maxevents, PyTime_t timeout) +/*[clinic end generated code: output=93c3b6c262b640db input=e3d6d3a498f277ab]*/ { int gotevents = 0; int nchanges = 0; @@ -2346,7 +2294,7 @@ select_kqueue_control_impl(kqueue_queue_Object *self, PyObject *changelist, struct kevent *chl = NULL; struct timespec timeoutspec; struct timespec *ptimeoutspec; - PyTime_t timeout, deadline = 0; + PyTime_t deadline = 0; _selectstate *state = _selectstate_by_type(Py_TYPE(self)); if (self->kqfd < 0) @@ -2359,28 +2307,12 @@ select_kqueue_control_impl(kqueue_queue_Object *self, PyObject *changelist, return NULL; } - if (otimeout == Py_None) { + if (timeout < 0) { ptimeoutspec = NULL; } else { - if (_PyTime_FromSecondsObject(&timeout, - otimeout, _PyTime_ROUND_TIMEOUT) < 0) { - if (PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_Format(PyExc_TypeError, - "timeout must be a real number or None, not %T", - otimeout); - } - return NULL; - } - if (_PyTime_AsTimespec(timeout, &timeoutspec) == -1) return NULL; - - if (timeoutspec.tv_sec < 0) { - PyErr_SetString(PyExc_ValueError, - "timeout must be positive or None"); - return NULL; - } ptimeoutspec = &timeoutspec; } diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index bc5aef55648e07..86b3cb365ce8b6 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -168,25 +168,6 @@ compare_handler(PyObject *func, PyObject *dfl_ign_handler) return PyObject_RichCompareBool(func, dfl_ign_handler, Py_EQ) == 1; } -#ifdef HAVE_SETITIMER -/* auxiliary function for setitimer */ -static int -timeval_from_double(PyObject *obj, struct timeval *tv) -{ - if (obj == NULL) { - tv->tv_sec = 0; - tv->tv_usec = 0; - return 0; - } - - PyTime_t t; - if (_PyTime_FromSecondsObject(&t, obj, _PyTime_ROUND_CEILING) < 0) { - return -1; - } - return _PyTime_AsTimeval(t, tv, _PyTime_ROUND_CEILING); -} -#endif - #if defined(HAVE_SETITIMER) || defined(HAVE_GETITIMER) /* auxiliary functions for get/setitimer */ Py_LOCAL_INLINE(double) @@ -846,8 +827,8 @@ PySignal_SetWakeupFd(int fd) signal.setitimer which: int - seconds: object - interval: object(c_default="NULL") = 0.0 + seconds: duration(type='struct timeval', round='ceiling') + interval: duration(type='struct timeval', round='ceiling') = 0.0 / Sets given itimer (one of ITIMER_REAL, ITIMER_VIRTUAL or ITIMER_PROF). @@ -859,19 +840,15 @@ Returns old values as a tuple: (delay, interval). [clinic start generated code]*/ static PyObject * -signal_setitimer_impl(PyObject *module, int which, PyObject *seconds, - PyObject *interval) -/*[clinic end generated code: output=65f9dcbddc35527b input=bd9f0d2ed8614193]*/ +signal_setitimer_impl(PyObject *module, int which, struct timeval seconds, + struct timeval interval) +/*[clinic end generated code: output=8506576c0c927686 input=1dd76b9d1b0979bd]*/ { _signal_module_state *modstate = get_signal_state(module); struct itimerval new; - if (timeval_from_double(seconds, &new.it_value) < 0) { - return NULL; - } - if (timeval_from_double(interval, &new.it_interval) < 0) { - return NULL; - } + new.it_value = seconds; + new.it_interval = interval; /* Let OS check "which" value */ struct itimerval old; @@ -1198,7 +1175,7 @@ signal_sigwaitinfo_impl(PyObject *module, sigset_t sigset) signal.sigtimedwait sigset: sigset_t - timeout as timeout_obj: object + timeout: duration(round='ceiling', allow_negative=False) / Like sigwaitinfo(), but with a timeout. @@ -1207,20 +1184,9 @@ The timeout is specified in seconds, rounded up to nanoseconds. [clinic start generated code]*/ static PyObject * -signal_sigtimedwait_impl(PyObject *module, sigset_t sigset, - PyObject *timeout_obj) -/*[clinic end generated code: output=59c8971e8ae18a64 input=f89af57d645e48e0]*/ +signal_sigtimedwait_impl(PyObject *module, sigset_t sigset, PyTime_t timeout) +/*[clinic end generated code: output=35388ab3f20ecef0 input=afd79136ca25e3f0]*/ { - PyTime_t timeout; - if (_PyTime_FromSecondsObject(&timeout, - timeout_obj, _PyTime_ROUND_CEILING) < 0) - return NULL; - - if (timeout < 0) { - PyErr_SetString(PyExc_ValueError, "timeout must be non-negative"); - return NULL; - } - PyTime_t deadline = _PyDeadline_Init(timeout); siginfo_t si; diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 70d3738b176cda..1b66b23fc82cae 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -3230,7 +3230,7 @@ _socket_socket_getblocking_impl(PySocketSockObject *s) static int -socket_parse_timeout(PyTime_t *timeout, PyObject *timeout_obj) +socket_check_timeout(PyTime_t timeout) { #ifdef MS_WINDOWS struct timeval tv; @@ -3240,25 +3240,15 @@ socket_parse_timeout(PyTime_t *timeout, PyObject *timeout_obj) #endif int overflow = 0; - if (timeout_obj == Py_None) { - *timeout = _PyTime_FromSeconds(-1); + if (timeout < 0) { /* no timeout */ return 0; } - if (_PyTime_FromSecondsObject(timeout, - timeout_obj, _PyTime_ROUND_TIMEOUT) < 0) - return -1; - - if (*timeout < 0) { - PyErr_SetString(PyExc_ValueError, "Timeout value out of range"); - return -1; - } - #ifdef MS_WINDOWS - overflow |= (_PyTime_AsTimeval(*timeout, &tv, _PyTime_ROUND_TIMEOUT) < 0); + overflow |= (_PyTime_AsTimeval(timeout, &tv, _PyTime_ROUND_TIMEOUT) < 0); #endif #ifndef HAVE_POLL - ms = _PyTime_AsMilliseconds(*timeout, _PyTime_ROUND_TIMEOUT); + ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_TIMEOUT); overflow |= (ms > INT_MAX); #endif if (overflow) { @@ -3279,7 +3269,7 @@ socket_parse_timeout(PyTime_t *timeout, PyObject *timeout_obj) /*[clinic input] _socket.socket.settimeout self as s: self(type="PySocketSockObject *") - timeout as arg: object + timeout: duration(accept={float, NoneType}, allow_negative=False) / Set a timeout on socket operations. @@ -3291,12 +3281,10 @@ setblocking(0). [clinic start generated code]*/ static PyObject * -_socket_socket_settimeout_impl(PySocketSockObject *s, PyObject *arg) -/*[clinic end generated code: output=5e57e2e1cba1c234 input=08bc8324b9fdb3f9]*/ +_socket_socket_settimeout_impl(PySocketSockObject *s, PyTime_t timeout) +/*[clinic end generated code: output=c95af193c52a5e4d input=e8caf62b665d6009]*/ { - PyTime_t timeout; - - if (socket_parse_timeout(&timeout, arg) < 0) + if (socket_check_timeout(timeout) < 0) return NULL; s->sock_timeout = timeout; @@ -7287,7 +7275,7 @@ _socket_getdefaulttimeout_impl(PyObject *module) /*[clinic input] _socket.setdefaulttimeout - timeout as arg: object + timeout: duration(accept={float, NoneType}, allow_negative=False) / Set the default timeout in seconds for new socket objects. @@ -7297,12 +7285,10 @@ When the socket module is first imported, the default is None. [clinic start generated code]*/ static PyObject * -_socket_setdefaulttimeout(PyObject *module, PyObject *arg) -/*[clinic end generated code: output=b5d59296163d66bf input=929785d885173684]*/ +_socket_setdefaulttimeout_impl(PyObject *module, PyTime_t timeout) +/*[clinic end generated code: output=c731770cfca966af input=ee28b7e8f7d0c5f8]*/ { - PyTime_t timeout; - - if (socket_parse_timeout(&timeout, arg) < 0) + if (socket_check_timeout(timeout) < 0) return NULL; socket_state *state = get_module_state(module); diff --git a/Python/pytime.c b/Python/pytime.c index 53c82736137a16..7f73928e0f9025 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -480,6 +480,75 @@ pytime_object_to_denominator(PyObject *obj, time_t *sec, long *numerator, } +/* Converters for the "duration" Argument Clinic converter. The "OrNone" + variants convert None to -1, which usually means "no timeout". */ + +#define DURATION_CONVERTER(NAME, FROMOBJECT, ROUND, ALLOW_NONE) \ +int \ +NAME(PyObject *obj, void *ptr) \ +{ \ + if (ALLOW_NONE && obj == Py_None) { \ + *(PyTime_t *)ptr = -1; \ + return 1; \ + } \ + return FROMOBJECT((PyTime_t *)ptr, obj, ROUND) < 0 ? 0 : 1; \ +} + +DURATION_CONVERTER(_PyTime_Duration_Seconds_Converter, + _PyTime_FromSecondsObject, _PyTime_ROUND_TIMEOUT, 0) +DURATION_CONVERTER(_PyTime_DurationOrNone_Seconds_Converter, + _PyTime_FromSecondsObject, _PyTime_ROUND_TIMEOUT, 1) +DURATION_CONVERTER(_PyTime_Duration_SecondsCeil_Converter, + _PyTime_FromSecondsObject, _PyTime_ROUND_CEILING, 0) +DURATION_CONVERTER(_PyTime_DurationOrNone_SecondsCeil_Converter, + _PyTime_FromSecondsObject, _PyTime_ROUND_CEILING, 1) +DURATION_CONVERTER(_PyTime_Duration_Milliseconds_Converter, + _PyTime_FromMillisecondsObject, _PyTime_ROUND_TIMEOUT, 0) +DURATION_CONVERTER(_PyTime_DurationOrNone_Milliseconds_Converter, + _PyTime_FromMillisecondsObject, _PyTime_ROUND_TIMEOUT, 1) + +#undef DURATION_CONVERTER + + +#ifndef MS_WINDOWS +/* Converters producing a struct timeval for the "duration" Argument Clinic + converter with out='timeval'. */ + +#define TIMEVAL_CONVERTER(NAME, ROUND) \ +int \ +NAME(PyObject *obj, void *ptr) \ +{ \ + PyTime_t t; \ + if (_PyTime_FromSecondsObject(&t, obj, ROUND) < 0) { \ + return 0; \ + } \ + return _PyTime_AsTimeval(t, (struct timeval *)ptr, ROUND) < 0 ? 0 : 1; \ +} + +TIMEVAL_CONVERTER(_PyTime_Duration_Timeval_Converter, _PyTime_ROUND_TIMEOUT) +TIMEVAL_CONVERTER(_PyTime_Duration_TimevalCeil_Converter, _PyTime_ROUND_CEILING) + +#undef TIMEVAL_CONVERTER +#endif + + +/* Converters for the "timestamp" Argument Clinic converter. */ + +int +_PyTime_Timestamp_Time_t_Converter(PyObject *obj, void *ptr) +{ + return _PyTime_ObjectToTime_t(obj, (time_t *)ptr, + _PyTime_ROUND_FLOOR) < 0 ? 0 : 1; +} + +int +_PyTime_Timestamp_Converter(PyObject *obj, void *ptr) +{ + return _PyTime_FromSecondsObject((PyTime_t *)ptr, obj, + _PyTime_ROUND_FLOOR) < 0 ? 0 : 1; +} + + int _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round) { diff --git a/Tools/clinic/libclinic/converters.py b/Tools/clinic/libclinic/converters.py index c2ac6fd22d5bdc..23a5744ea76b2c 100644 --- a/Tools/clinic/libclinic/converters.py +++ b/Tools/clinic/libclinic/converters.py @@ -4,7 +4,8 @@ from types import NoneType from typing import Any -from libclinic import fail, NullType, unspecified, NULL, c_bytes_repr, c_unichar_repr +from libclinic import (fail, NullType, unspecified, NULL, c_bytes_repr, + c_unichar_repr, indent_all_lines) from libclinic.function import ( Function, Parameter, CALLABLE, STATIC_METHOD, CLASS_METHOD, METHOD_INIT, METHOD_NEW, @@ -601,6 +602,147 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st argname=argname) +class duration_converter(CConverter): + """Convert a number of seconds or milliseconds to a time interval. + + type is the C type used by the implementation: 'PyTime_t' + (nanoseconds) or 'struct timeval'. + unit is the unit of the argument: 's' (seconds) or 'ms' (milliseconds). + round is the rounding mode: 'timeout', 'ceiling', 'floor' or 'up'. + If NoneType is accepted, None keeps the default value, which usually + means "no timeout". + """ + type = 'PyTime_t' + default_type = (int, float, NoneType) + c_ignored_default = '0' + # The default value for None, unless overridden with c_default. + c_init_default = '-1' + + _UNITS = {'s': '_PyTime_FromSecondsObject', + 'ms': '_PyTime_FromMillisecondsObject'} + + # (type, unit, round, None is accepted) -> the "O&" converter function + _CONVERTERS = { + ('PyTime_t', 's', 'timeout', False): '_PyTime_Duration_Seconds_Converter', + ('PyTime_t', 's', 'timeout', True): '_PyTime_DurationOrNone_Seconds_Converter', + ('PyTime_t', 's', 'ceiling', False): '_PyTime_Duration_SecondsCeil_Converter', + ('PyTime_t', 's', 'ceiling', True): '_PyTime_DurationOrNone_SecondsCeil_Converter', + ('PyTime_t', 'ms', 'timeout', False): '_PyTime_Duration_Milliseconds_Converter', + ('PyTime_t', 'ms', 'timeout', True): '_PyTime_DurationOrNone_Milliseconds_Converter', + ('struct timeval', 's', 'timeout', False): '_PyTime_Duration_Timeval_Converter', + ('struct timeval', 's', 'ceiling', False): '_PyTime_Duration_TimevalCeil_Converter', + } + + _ROUNDING = {'timeout': '_PyTime_ROUND_TIMEOUT', + 'ceiling': '_PyTime_ROUND_CEILING', + 'floor': '_PyTime_ROUND_FLOOR', + 'up': '_PyTime_ROUND_UP'} + + def converter_init(self, *, type: str = 'PyTime_t', unit: str = 's', + round: str = 'timeout', accept: TypeSet = {float}, + allow_negative: bool = True) -> None: + if type not in ('PyTime_t', 'struct timeval'): + fail(f"duration_converter: illegal 'type' argument {type!r}") + if unit not in self._UNITS: + fail(f"duration_converter: illegal 'unit' argument {unit!r}") + if round not in self._ROUNDING: + fail(f"duration_converter: illegal 'round' argument {round!r}") + if accept not in ({float}, {float, NoneType}): + fail(f"duration_converter: illegal 'accept' argument {accept!r}") + if type == 'struct timeval': + if unit != 's': + fail("duration_converter: type='struct timeval' " + "requires unit='s'") + self.type = type + self.c_init_default = '{0, 0}' + self.c_ignored_default = '{0, 0}' + if not self.c_default: + # A struct cannot be initialized with a number, so the only + # supported default is zero. + self.c_default = '{0, 0}' + self.unit = unit + self.rounding = round + self.accept_none = accept == {float, NoneType} + self.allow_negative = allow_negative + name = self._CONVERTERS.get((type, unit, round, self.accept_none)) + if name is None: + fail(f"duration_converter: unsupported combination of " + f"unit={unit!r} and round={round!r}") + self.converter = name + + def use_converter(self) -> None: + self.add_include('pycore_time.h', f'{self.converter}()') + + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: + if limited_capi: + return None + if self.type == 'struct timeval': + self.add_include('pycore_time.h', f'{self.converter}()') + convert = ("if (!{converter}({argname}, &{paramname})) {{{{\n" + " goto exit;\n" + "}}}}\n") + else: + self.add_include('pycore_time.h', f'{self._UNITS[self.unit]}()') + convert = ("if ({fromobject}(&{paramname}, {argname}, {rounding}) < 0) {{{{\n" + " goto exit;\n" + "}}}}\n") + if not self.allow_negative and self.type != 'struct timeval': + convert += ('if ({paramname} < 0) {{{{\n' + ' PyErr_SetString(PyExc_ValueError,\n' + ' "{name} must be non-negative");\n' + ' goto exit;\n' + '}}}}\n') + if self.accept_none: + # None does not change the value, it keeps the default. + code = ('if ({argname} != Py_None) {{{{\n' + + indent_all_lines(convert, ' ') + + '}}}}\n') + else: + code = convert + return self.format_code(code, + argname=argname, + name=self.name, + converter=self.converter, + fromobject=self._UNITS[self.unit], + rounding=self._ROUNDING[self.rounding]) + + +class timestamp_converter(CConverter): + """Convert a number of seconds since the epoch. + + type is the C type used by the implementation: 'time_t' (whole seconds) + or 'PyTime_t' (nanoseconds). A fractional value is rounded down, + as in time.gmtime() and datetime.date.fromtimestamp(). + """ + type = 'time_t' + default_type = (int, float) + + # type -> the "O&" converter function and the inline conversion + _TYPES = { + 'time_t': ('_PyTime_Timestamp_Time_t_Converter', + '_PyTime_ObjectToTime_t({argname}, &{paramname}, {rounding})'), + 'PyTime_t': ('_PyTime_Timestamp_Converter', + '_PyTime_FromSecondsObject(&{paramname}, {argname}, {rounding})'), + } + + def converter_init(self, *, type: str = 'time_t') -> None: + if type not in self._TYPES: + fail(f"timestamp_converter: illegal 'type' argument {type!r}") + self.type = type + self.converter, self._call = self._TYPES[type] + + def use_converter(self) -> None: + self.add_include('pycore_time.h', f'{self.converter}()') + + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: + if limited_capi: + return None + self.add_include('pycore_time.h', self._call.split('(', 1)[0] + '()') + return self.format_code( + 'if (' + self._call + ' < 0) {{{{\n' + ' goto exit;\n' + '}}}}\n', + argname=argname, rounding='_PyTime_ROUND_FLOOR') class pid_t_converter(CConverter): type = 'pid_t' format_unit = '" _Py_PARSE_PID "'