From 01086a8af21ca092438fad1b216743ee684cd393 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 10 Aug 2026 22:04:34 +0300 Subject: [PATCH 1/5] gh-155496: Use Argument Clinic for more functions of the time module --- Modules/clinic/timemodule.c.h | 682 +++++++++++++++++++++++++++++++++- Modules/timemodule.c | 512 +++++++++++++------------ 2 files changed, 964 insertions(+), 230 deletions(-) diff --git a/Modules/clinic/timemodule.c.h b/Modules/clinic/timemodule.c.h index bbc0748f9a9c0d..d4c54fa697546f 100644 --- a/Modules/clinic/timemodule.c.h +++ b/Modules/clinic/timemodule.c.h @@ -2,6 +2,57 @@ preserve [clinic start generated code]*/ +#include "pycore_modsupport.h" // _PyArg_CheckPositional() + +PyDoc_STRVAR(time_time__doc__, +"time($module, /)\n" +"--\n" +"\n" +"Return the current time in seconds since the Epoch.\n" +"\n" +"Fractions of a second may be present if the system clock provides\n" +"them."); + +#define TIME_TIME_METHODDEF \ + {"time", (PyCFunction)time_time, METH_NOARGS, time_time__doc__}, + +static double +time_time_impl(PyObject *module); + +static PyObject * +time_time(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + double _return_value; + + _return_value = time_time_impl(module); + if ((_return_value == -1.0) && PyErr_Occurred()) { + goto exit; + } + return_value = PyFloat_FromDouble(_return_value); + +exit: + return return_value; +} + +PyDoc_STRVAR(time_time_ns__doc__, +"time_ns($module, /)\n" +"--\n" +"\n" +"Return the current time in nanoseconds since the Epoch."); + +#define TIME_TIME_NS_METHODDEF \ + {"time_ns", (PyCFunction)time_time_ns, METH_NOARGS, time_time_ns__doc__}, + +static PyObject * +time_time_ns_impl(PyObject *module); + +static PyObject * +time_time_ns(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return time_time_ns_impl(module); +} + #if defined(HAVE_CLOCK_GETTIME) PyDoc_STRVAR(time_clock_gettime__doc__, @@ -64,6 +115,603 @@ time_clock_gettime_ns(PyObject *module, PyObject *arg) #endif /* defined(HAVE_CLOCK_GETTIME) */ +#if defined(HAVE_CLOCK_SETTIME) + +PyDoc_STRVAR(time_clock_settime__doc__, +"clock_settime($module, clk_id, time, /)\n" +"--\n" +"\n" +"Set the time of the specified clock clk_id."); + +#define TIME_CLOCK_SETTIME_METHODDEF \ + {"clock_settime", _PyCFunction_CAST(time_clock_settime), METH_FASTCALL, time_clock_settime__doc__}, + +static PyObject * +time_clock_settime_impl(PyObject *module, int clk_id, PyObject *obj); + +static PyObject * +time_clock_settime(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + int clk_id; + PyObject *obj; + + if (!_PyArg_CheckPositional("clock_settime", nargs, 2, 2)) { + goto exit; + } + clk_id = PyLong_AsInt(args[0]); + if (clk_id == -1 && PyErr_Occurred()) { + goto exit; + } + obj = args[1]; + return_value = time_clock_settime_impl(module, clk_id, obj); + +exit: + return return_value; +} + +#endif /* defined(HAVE_CLOCK_SETTIME) */ + +#if defined(HAVE_CLOCK_SETTIME) + +PyDoc_STRVAR(time_clock_settime_ns__doc__, +"clock_settime_ns($module, clk_id, time, /)\n" +"--\n" +"\n" +"Set the time of the specified clock clk_id with nanoseconds."); + +#define TIME_CLOCK_SETTIME_NS_METHODDEF \ + {"clock_settime_ns", _PyCFunction_CAST(time_clock_settime_ns), METH_FASTCALL, time_clock_settime_ns__doc__}, + +static PyObject * +time_clock_settime_ns_impl(PyObject *module, int clk_id, PyObject *obj); + +static PyObject * +time_clock_settime_ns(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + int clk_id; + PyObject *obj; + + if (!_PyArg_CheckPositional("clock_settime_ns", nargs, 2, 2)) { + goto exit; + } + clk_id = PyLong_AsInt(args[0]); + if (clk_id == -1 && PyErr_Occurred()) { + goto exit; + } + obj = args[1]; + return_value = time_clock_settime_ns_impl(module, clk_id, obj); + +exit: + return return_value; +} + +#endif /* defined(HAVE_CLOCK_SETTIME) */ + +#if defined(HAVE_CLOCK_GETRES) + +PyDoc_STRVAR(time_clock_getres__doc__, +"clock_getres($module, clk_id, /)\n" +"--\n" +"\n" +"Return the resolution (precision) of the specified clock clk_id."); + +#define TIME_CLOCK_GETRES_METHODDEF \ + {"clock_getres", (PyCFunction)time_clock_getres, METH_O, time_clock_getres__doc__}, + +static double +time_clock_getres_impl(PyObject *module, int clk_id); + +static PyObject * +time_clock_getres(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + int clk_id; + double _return_value; + + clk_id = PyLong_AsInt(arg); + if (clk_id == -1 && PyErr_Occurred()) { + goto exit; + } + _return_value = time_clock_getres_impl(module, clk_id); + if ((_return_value == -1.0) && PyErr_Occurred()) { + goto exit; + } + return_value = PyFloat_FromDouble(_return_value); + +exit: + return return_value; +} + +#endif /* defined(HAVE_CLOCK_GETRES) */ + +#if defined(HAVE_PTHREAD_GETCPUCLOCKID) + +PyDoc_STRVAR(time_pthread_getcpuclockid__doc__, +"pthread_getcpuclockid($module, thread_id, /)\n" +"--\n" +"\n" +"Return the clk_id of a thread\'s CPU time clock."); + +#define TIME_PTHREAD_GETCPUCLOCKID_METHODDEF \ + {"pthread_getcpuclockid", (PyCFunction)time_pthread_getcpuclockid, METH_O, time_pthread_getcpuclockid__doc__}, + +static PyObject * +time_pthread_getcpuclockid_impl(PyObject *module, unsigned long thread_id); + +static PyObject * +time_pthread_getcpuclockid(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + unsigned long thread_id; + + if (!PyIndex_Check(arg)) { + _PyArg_BadArgument("pthread_getcpuclockid", "argument", "int", arg); + goto exit; + } + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(arg, &thread_id, sizeof(unsigned long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } + return_value = time_pthread_getcpuclockid_impl(module, thread_id); + +exit: + return return_value; +} + +#endif /* defined(HAVE_PTHREAD_GETCPUCLOCKID) */ + +PyDoc_STRVAR(time_sleep__doc__, +"sleep($module, seconds, /)\n" +"--\n" +"\n" +"Delay execution for a given number of seconds.\n" +"\n" +"The argument may be a floating-point number for subsecond precision."); + +#define TIME_SLEEP_METHODDEF \ + {"sleep", (PyCFunction)time_sleep, METH_O, time_sleep__doc__}, + +PyDoc_STRVAR(time_gmtime__doc__, +"gmtime($module, seconds=None, /)\n" +"--\n" +"\n" +"Convert seconds since the Epoch to a time tuple expressing UTC.\n" +"\n" +"That is, Greenwich Mean Time. When \'seconds\' is not passed in, convert\n" +"the current time instead.\n" +"\n" +"If the platform supports the tm_gmtoff and tm_zone, they are available\n" +"as attributes only."); + +#define TIME_GMTIME_METHODDEF \ + {"gmtime", _PyCFunction_CAST(time_gmtime), METH_FASTCALL, time_gmtime__doc__}, + +static PyObject * +time_gmtime_impl(PyObject *module, PyObject *ot); + +static PyObject * +time_gmtime(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *ot = Py_None; + + if (!_PyArg_CheckPositional("gmtime", nargs, 0, 1)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + ot = args[0]; +skip_optional: + return_value = time_gmtime_impl(module, ot); + +exit: + return return_value; +} + +PyDoc_STRVAR(time_localtime__doc__, +"localtime($module, seconds=None, /)\n" +"--\n" +"\n" +"Convert seconds since the Epoch to a time tuple expressing local time.\n" +"\n" +"When \'seconds\' is not passed in, convert the current time instead."); + +#define TIME_LOCALTIME_METHODDEF \ + {"localtime", _PyCFunction_CAST(time_localtime), METH_FASTCALL, time_localtime__doc__}, + +static PyObject * +time_localtime_impl(PyObject *module, PyObject *ot); + +static PyObject * +time_localtime(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *ot = Py_None; + + if (!_PyArg_CheckPositional("localtime", nargs, 0, 1)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + ot = args[0]; +skip_optional: + return_value = time_localtime_impl(module, ot); + +exit: + return return_value; +} + +PyDoc_STRVAR(time_asctime__doc__, +"asctime($module, time_tuple=None, /)\n" +"--\n" +"\n" +"Convert a time tuple to a string, e.g. \'Sat Jun 06 16:26:11 1998\'.\n" +"\n" +"When the time tuple is not present, current time as returned by\n" +"localtime() is used."); + +#define TIME_ASCTIME_METHODDEF \ + {"asctime", _PyCFunction_CAST(time_asctime), METH_FASTCALL, time_asctime__doc__}, + +static PyObject * +time_asctime_impl(PyObject *module, PyObject *tup); + +static PyObject * +time_asctime(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *tup = Py_None; + + if (!_PyArg_CheckPositional("asctime", nargs, 0, 1)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + tup = args[0]; +skip_optional: + return_value = time_asctime_impl(module, tup); + +exit: + return return_value; +} + +PyDoc_STRVAR(time_ctime__doc__, +"ctime($module, seconds=None, /)\n" +"--\n" +"\n" +"Convert a time in seconds since the Epoch to a string in local time.\n" +"\n" +"This is equivalent to asctime(localtime(seconds)). When the time tuple\n" +"is not present, current time as returned by localtime() is used."); + +#define TIME_CTIME_METHODDEF \ + {"ctime", _PyCFunction_CAST(time_ctime), METH_FASTCALL, time_ctime__doc__}, + +static PyObject * +time_ctime_impl(PyObject *module, PyObject *ot); + +static PyObject * +time_ctime(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *ot = Py_None; + + if (!_PyArg_CheckPositional("ctime", nargs, 0, 1)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + ot = args[0]; +skip_optional: + return_value = time_ctime_impl(module, ot); + +exit: + return return_value; +} + +#if defined(HAVE_MKTIME) + +PyDoc_STRVAR(time_mktime__doc__, +"mktime($module, time_tuple, /)\n" +"--\n" +"\n" +"Convert a time tuple in local time to seconds since the Epoch.\n" +"\n" +"Note that mktime(gmtime(0)) will not generally return zero for most\n" +"time zones; instead the returned value will either be equal to that of\n" +"the timezone or altzone attributes on the time module."); + +#define TIME_MKTIME_METHODDEF \ + {"mktime", (PyCFunction)time_mktime, METH_O, time_mktime__doc__}, + +#endif /* defined(HAVE_MKTIME) */ + +#if defined(HAVE_WORKING_TZSET) + +PyDoc_STRVAR(time_tzset__doc__, +"tzset($module, /)\n" +"--\n" +"\n" +"Initialize, or reinitialize, the local timezone.\n" +"\n" +"The value is stored in os.environ[\'TZ\']. The TZ environment variable\n" +"should be specified in standard Unix timezone format as documented in\n" +"the tzset man page (eg. \'US/Eastern\', \'Europe/Amsterdam\'). Unknown\n" +"timezones will silently fall back to UTC. If the TZ environment\n" +"variable is not set, the local timezone is set to the systems best\n" +"guess of wallclock time. Changing the TZ environment variable without\n" +"calling tzset *may* change the local timezone used by methods such as\n" +"localtime, but this behaviour should not be relied on."); + +#define TIME_TZSET_METHODDEF \ + {"tzset", (PyCFunction)time_tzset, METH_NOARGS, time_tzset__doc__}, + +static PyObject * +time_tzset_impl(PyObject *module); + +static PyObject * +time_tzset(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return time_tzset_impl(module); +} + +#endif /* defined(HAVE_WORKING_TZSET) */ + +PyDoc_STRVAR(time_monotonic__doc__, +"monotonic($module, /)\n" +"--\n" +"\n" +"Monotonic clock, cannot go backward."); + +#define TIME_MONOTONIC_METHODDEF \ + {"monotonic", (PyCFunction)time_monotonic, METH_NOARGS, time_monotonic__doc__}, + +static double +time_monotonic_impl(PyObject *module); + +static PyObject * +time_monotonic(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + double _return_value; + + _return_value = time_monotonic_impl(module); + if ((_return_value == -1.0) && PyErr_Occurred()) { + goto exit; + } + return_value = PyFloat_FromDouble(_return_value); + +exit: + return return_value; +} + +PyDoc_STRVAR(time_monotonic_ns__doc__, +"monotonic_ns($module, /)\n" +"--\n" +"\n" +"Monotonic clock, cannot go backward, as nanoseconds."); + +#define TIME_MONOTONIC_NS_METHODDEF \ + {"monotonic_ns", (PyCFunction)time_monotonic_ns, METH_NOARGS, time_monotonic_ns__doc__}, + +static PyObject * +time_monotonic_ns_impl(PyObject *module); + +static PyObject * +time_monotonic_ns(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return time_monotonic_ns_impl(module); +} + +PyDoc_STRVAR(time_perf_counter__doc__, +"perf_counter($module, /)\n" +"--\n" +"\n" +"Performance counter for benchmarking."); + +#define TIME_PERF_COUNTER_METHODDEF \ + {"perf_counter", (PyCFunction)time_perf_counter, METH_NOARGS, time_perf_counter__doc__}, + +static double +time_perf_counter_impl(PyObject *module); + +static PyObject * +time_perf_counter(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + double _return_value; + + _return_value = time_perf_counter_impl(module); + if ((_return_value == -1.0) && PyErr_Occurred()) { + goto exit; + } + return_value = PyFloat_FromDouble(_return_value); + +exit: + return return_value; +} + +PyDoc_STRVAR(time_perf_counter_ns__doc__, +"perf_counter_ns($module, /)\n" +"--\n" +"\n" +"Performance counter for benchmarking as nanoseconds."); + +#define TIME_PERF_COUNTER_NS_METHODDEF \ + {"perf_counter_ns", (PyCFunction)time_perf_counter_ns, METH_NOARGS, time_perf_counter_ns__doc__}, + +static PyObject * +time_perf_counter_ns_impl(PyObject *module); + +static PyObject * +time_perf_counter_ns(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return time_perf_counter_ns_impl(module); +} + +PyDoc_STRVAR(time_process_time__doc__, +"process_time($module, /)\n" +"--\n" +"\n" +"Process time for profiling.\n" +"\n" +"That is the sum of the kernel and user-space CPU time."); + +#define TIME_PROCESS_TIME_METHODDEF \ + {"process_time", (PyCFunction)time_process_time, METH_NOARGS, time_process_time__doc__}, + +static double +time_process_time_impl(PyObject *module); + +static PyObject * +time_process_time(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + double _return_value; + + _return_value = time_process_time_impl(module); + if ((_return_value == -1.0) && PyErr_Occurred()) { + goto exit; + } + return_value = PyFloat_FromDouble(_return_value); + +exit: + return return_value; +} + +PyDoc_STRVAR(time_process_time_ns__doc__, +"process_time_ns($module, /)\n" +"--\n" +"\n" +"Process time for profiling as nanoseconds.\n" +"\n" +"That is the sum of the kernel and user-space CPU time."); + +#define TIME_PROCESS_TIME_NS_METHODDEF \ + {"process_time_ns", (PyCFunction)time_process_time_ns, METH_NOARGS, time_process_time_ns__doc__}, + +static PyObject * +time_process_time_ns_impl(PyObject *module); + +static PyObject * +time_process_time_ns(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return time_process_time_ns_impl(module); +} + +#if defined(HAVE_THREAD_TIME) + +PyDoc_STRVAR(time_thread_time__doc__, +"thread_time($module, /)\n" +"--\n" +"\n" +"Thread time for profiling.\n" +"\n" +"That is the sum of the kernel and user-space CPU time."); + +#define TIME_THREAD_TIME_METHODDEF \ + {"thread_time", (PyCFunction)time_thread_time, METH_NOARGS, time_thread_time__doc__}, + +static double +time_thread_time_impl(PyObject *module); + +static PyObject * +time_thread_time(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + double _return_value; + + _return_value = time_thread_time_impl(module); + if ((_return_value == -1.0) && PyErr_Occurred()) { + goto exit; + } + return_value = PyFloat_FromDouble(_return_value); + +exit: + return return_value; +} + +#endif /* defined(HAVE_THREAD_TIME) */ + +#if defined(HAVE_THREAD_TIME) + +PyDoc_STRVAR(time_thread_time_ns__doc__, +"thread_time_ns($module, /)\n" +"--\n" +"\n" +"Thread time for profiling as nanoseconds.\n" +"\n" +"That is the sum of the kernel and user-space CPU time."); + +#define TIME_THREAD_TIME_NS_METHODDEF \ + {"thread_time_ns", (PyCFunction)time_thread_time_ns, METH_NOARGS, time_thread_time_ns__doc__}, + +static PyObject * +time_thread_time_ns_impl(PyObject *module); + +static PyObject * +time_thread_time_ns(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return time_thread_time_ns_impl(module); +} + +#endif /* defined(HAVE_THREAD_TIME) */ + +PyDoc_STRVAR(time_get_clock_info__doc__, +"get_clock_info($module, name, /)\n" +"--\n" +"\n" +"Get information of the specified clock."); + +#define TIME_GET_CLOCK_INFO_METHODDEF \ + {"get_clock_info", (PyCFunction)time_get_clock_info, METH_O, time_get_clock_info__doc__}, + +static PyObject * +time_get_clock_info_impl(PyObject *module, const char *name); + +static PyObject * +time_get_clock_info(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + const char *name; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("get_clock_info", "argument", "str", arg); + goto exit; + } + Py_ssize_t name_length; + name = PyUnicode_AsUTF8AndSize(arg, &name_length); + if (name == NULL) { + goto exit; + } + if (strlen(name) != (size_t)name_length) { + PyErr_SetString(PyExc_ValueError, "embedded null character"); + goto exit; + } + return_value = time_get_clock_info_impl(module, name); + +exit: + return return_value; +} + #ifndef TIME_CLOCK_GETTIME_METHODDEF #define TIME_CLOCK_GETTIME_METHODDEF #endif /* !defined(TIME_CLOCK_GETTIME_METHODDEF) */ @@ -71,4 +719,36 @@ time_clock_gettime_ns(PyObject *module, PyObject *arg) #ifndef TIME_CLOCK_GETTIME_NS_METHODDEF #define TIME_CLOCK_GETTIME_NS_METHODDEF #endif /* !defined(TIME_CLOCK_GETTIME_NS_METHODDEF) */ -/*[clinic end generated code: output=b589a2132aa9df47 input=a9049054013a1b77]*/ + +#ifndef TIME_CLOCK_SETTIME_METHODDEF + #define TIME_CLOCK_SETTIME_METHODDEF +#endif /* !defined(TIME_CLOCK_SETTIME_METHODDEF) */ + +#ifndef TIME_CLOCK_SETTIME_NS_METHODDEF + #define TIME_CLOCK_SETTIME_NS_METHODDEF +#endif /* !defined(TIME_CLOCK_SETTIME_NS_METHODDEF) */ + +#ifndef TIME_CLOCK_GETRES_METHODDEF + #define TIME_CLOCK_GETRES_METHODDEF +#endif /* !defined(TIME_CLOCK_GETRES_METHODDEF) */ + +#ifndef TIME_PTHREAD_GETCPUCLOCKID_METHODDEF + #define TIME_PTHREAD_GETCPUCLOCKID_METHODDEF +#endif /* !defined(TIME_PTHREAD_GETCPUCLOCKID_METHODDEF) */ + +#ifndef TIME_MKTIME_METHODDEF + #define TIME_MKTIME_METHODDEF +#endif /* !defined(TIME_MKTIME_METHODDEF) */ + +#ifndef TIME_TZSET_METHODDEF + #define TIME_TZSET_METHODDEF +#endif /* !defined(TIME_TZSET_METHODDEF) */ + +#ifndef TIME_THREAD_TIME_METHODDEF + #define TIME_THREAD_TIME_METHODDEF +#endif /* !defined(TIME_THREAD_TIME_METHODDEF) */ + +#ifndef TIME_THREAD_TIME_NS_METHODDEF + #define TIME_THREAD_TIME_NS_METHODDEF +#endif /* !defined(TIME_THREAD_TIME_NS_METHODDEF) */ +/*[clinic end generated code: output=e1a38728f085edbd input=a9049054013a1b77]*/ diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 70d7e1b3713687..5fa9b325e3e51a 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -96,33 +96,36 @@ get_time_state(PyObject *module) } -static PyObject* -_PyFloat_FromPyTime(PyTime_t t) -{ - double d = PyTime_AsSecondsDouble(t); - return PyFloat_FromDouble(d); -} +/*[clinic input] +time.time -> double +Return the current time in seconds since the Epoch. -static PyObject * -time_time(PyObject *self, PyObject *unused) +Fractions of a second may be present if the system clock provides +them. +[clinic start generated code]*/ + +static double +time_time_impl(PyObject *module) +/*[clinic end generated code: output=4adfc457b48923ca input=eea2e80c63bbcaad]*/ { PyTime_t t; if (PyTime_Time(&t) < 0) { - return NULL; + return -1.0; } - return _PyFloat_FromPyTime(t); + return PyTime_AsSecondsDouble(t); } -PyDoc_STRVAR(time_doc, -"time() -> floating-point number\n\ -\n\ -Return the current time in seconds since the Epoch.\n\ -Fractions of a second may be present if the system clock provides them."); +/*[clinic input] +time.time_ns + +Return the current time in nanoseconds since the Epoch. +[clinic start generated code]*/ static PyObject * -time_time_ns(PyObject *self, PyObject *unused) +time_time_ns_impl(PyObject *module) +/*[clinic end generated code: output=f5f1924ebdcf1cf3 input=3acccd9786731be4]*/ { PyTime_t t; if (PyTime_Time(&t) < 0) { @@ -131,11 +134,6 @@ time_time_ns(PyObject *self, PyObject *unused) return PyLong_FromInt64(t); } -PyDoc_STRVAR(time_ns_doc, -"time_ns() -> int\n\ -\n\ -Return the current time in nanoseconds since the Epoch."); - #ifdef HAVE_CLOCK #ifndef CLOCKS_PER_SEC @@ -266,18 +264,24 @@ time_clock_gettime_ns_impl(PyObject *module, clockid_t clk_id) #endif /* HAVE_CLOCK_GETTIME */ #ifdef HAVE_CLOCK_SETTIME +/*[clinic input] +time.clock_settime + + clk_id: int + time as obj: object + / + +Set the time of the specified clock clk_id. +[clinic start generated code]*/ + static PyObject * -time_clock_settime(PyObject *self, PyObject *args) +time_clock_settime_impl(PyObject *module, int clk_id, PyObject *obj) +/*[clinic end generated code: output=ff2fc2e129f5fdea input=0e71daa237ff6edc]*/ { - int clk_id; - PyObject *obj; PyTime_t t; struct timespec tp; int ret; - if (!PyArg_ParseTuple(args, "iO:clock_settime", &clk_id, &obj)) - return NULL; - if (_PyTime_FromSecondsObject(&t, obj, _PyTime_ROUND_FLOOR) < 0) return NULL; @@ -292,24 +296,24 @@ time_clock_settime(PyObject *self, PyObject *args) Py_RETURN_NONE; } -PyDoc_STRVAR(clock_settime_doc, -"clock_settime(clk_id, time)\n\ -\n\ -Set the time of the specified clock clk_id."); +/*[clinic input] +time.clock_settime_ns + + clk_id: int + time as obj: object + / + +Set the time of the specified clock clk_id with nanoseconds. +[clinic start generated code]*/ static PyObject * -time_clock_settime_ns(PyObject *self, PyObject *args) +time_clock_settime_ns_impl(PyObject *module, int clk_id, PyObject *obj) +/*[clinic end generated code: output=5d40ca0217bfe058 input=f2333aae32a7f441]*/ { - int clk_id; - PyObject *obj; PyTime_t t; struct timespec ts; int ret; - if (!PyArg_ParseTuple(args, "iO:clock_settime", &clk_id, &obj)) { - return NULL; - } - if (PyLong_AsInt64(obj, &t) < 0) { return NULL; } @@ -325,37 +329,34 @@ time_clock_settime_ns(PyObject *self, PyObject *args) Py_RETURN_NONE; } -PyDoc_STRVAR(clock_settime_ns_doc, -"clock_settime_ns(clk_id, time)\n\ -\n\ -Set the time of the specified clock clk_id with nanoseconds."); #endif /* HAVE_CLOCK_SETTIME */ #ifdef HAVE_CLOCK_GETRES -static PyObject * -time_clock_getres(PyObject *self, PyObject *args) +/*[clinic input] +time.clock_getres -> double + + clk_id: int + / + +Return the resolution (precision) of the specified clock clk_id. +[clinic start generated code]*/ + +static double +time_clock_getres_impl(PyObject *module, int clk_id) +/*[clinic end generated code: output=94c4fb7df9f0c2f2 input=d1409a8b5b3be180]*/ { int ret; - int clk_id; struct timespec tp; - if (!PyArg_ParseTuple(args, "i:clock_getres", &clk_id)) - return NULL; - ret = clock_getres((clockid_t)clk_id, &tp); if (ret != 0) { PyErr_SetFromErrno(PyExc_OSError); - return NULL; + return -1.0; } - return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9); + return tp.tv_sec + tp.tv_nsec * 1e-9; } -PyDoc_STRVAR(clock_getres_doc, -"clock_getres(clk_id) -> floating-point number\n\ -\n\ -Return the resolution (precision) of the specified clock clk_id."); - #ifdef __APPLE__ #pragma clang diagnostic pop #endif @@ -363,15 +364,21 @@ Return the resolution (precision) of the specified clock clk_id."); #endif /* HAVE_CLOCK_GETRES */ #ifdef HAVE_PTHREAD_GETCPUCLOCKID +/*[clinic input] +time.pthread_getcpuclockid + + thread_id: unsigned_long(bitwise=True) + / + +Return the clk_id of a thread's CPU time clock. +[clinic start generated code]*/ + static PyObject * -time_pthread_getcpuclockid(PyObject *self, PyObject *args) +time_pthread_getcpuclockid_impl(PyObject *module, unsigned long thread_id) +/*[clinic end generated code: output=4fc7d4cb73d2e894 input=eb8af7bbcf189270]*/ { - unsigned long thread_id; int err; clockid_t clk_id; - if (!PyArg_ParseTuple(args, "k:pthread_getcpuclockid", &thread_id)) { - return NULL; - } err = pthread_getcpuclockid((pthread_t)thread_id, &clk_id); if (err) { errno = err; @@ -384,14 +391,22 @@ time_pthread_getcpuclockid(PyObject *self, PyObject *args) return PyLong_FromLong(clk_id); } -PyDoc_STRVAR(pthread_getcpuclockid_doc, -"pthread_getcpuclockid(thread_id) -> int\n\ -\n\ -Return the clk_id of a thread's CPU time clock."); #endif /* HAVE_PTHREAD_GETCPUCLOCKID */ +/*[clinic input] +time.sleep + + seconds as timeout_obj: object + / + +Delay execution for a given number of seconds. + +The argument may be a floating-point number for subsecond precision. +[clinic start generated code]*/ + static PyObject * -time_sleep(PyObject *self, PyObject *timeout_obj) +time_sleep(PyObject *module, PyObject *timeout_obj) +/*[clinic end generated code: output=f46e88f5f5756f65 input=3928b1704a2faa12]*/ { if (PySys_Audit("time.sleep", "O", timeout_obj) < 0) { return NULL; @@ -411,12 +426,6 @@ time_sleep(PyObject *self, PyObject *timeout_obj) Py_RETURN_NONE; } -PyDoc_STRVAR(sleep_doc, -"sleep(seconds)\n\ -\n\ -Delay execution for a given number of seconds. The argument may be\n\ -a floating-point number for subsecond precision."); - static PyStructSequence_Field struct_time_type_fields[] = { {"tm_year", "year, for example, 1993"}, {"tm_mon", "month of year, range [1, 12]"}, @@ -505,14 +514,11 @@ tmtotuple(time_module_state *state, struct tm *p Returns non-zero on success (parallels PyArg_ParseTuple). */ static int -parse_time_t_args(PyObject *args, const char *format, time_t *pwhen) +parse_time_t_arg(PyObject *ot, time_t *pwhen) { - PyObject *ot = NULL; time_t whent; - if (!PyArg_ParseTuple(args, format, &ot)) - return 0; - if (ot == NULL || ot == Py_None) { + if (ot == Py_None) { whent = time(NULL); } else { @@ -523,13 +529,29 @@ parse_time_t_args(PyObject *args, const char *format, time_t *pwhen) return 1; } +/*[clinic input] +time.gmtime + + seconds as ot: object = None + / + +Convert seconds since the Epoch to a time tuple expressing UTC. + +That is, Greenwich Mean Time. When 'seconds' is not passed in, convert +the current time instead. + +If the platform supports the tm_gmtoff and tm_zone, they are available +as attributes only. +[clinic start generated code]*/ + static PyObject * -time_gmtime(PyObject *module, PyObject *args) +time_gmtime_impl(PyObject *module, PyObject *ot) +/*[clinic end generated code: output=375372dd236a6ed6 input=784de5b57649d4d7]*/ { time_t when; struct tm buf; - if (!parse_time_t_args(args, "|O:gmtime", &when)) + if (!parse_time_t_arg(ot, &when)) return NULL; errno = 0; @@ -557,23 +579,25 @@ timegm(struct tm *p) } #endif -PyDoc_STRVAR(gmtime_doc, -"gmtime([seconds]) -> (tm_year, tm_mon, tm_mday, tm_hour, tm_min,\n\ - tm_sec, tm_wday, tm_yday, tm_isdst)\n\ -\n\ -Convert seconds since the Epoch to a time tuple expressing UTC (a.k.a.\n\ -GMT). When 'seconds' is not passed in, convert the current time instead.\n\ -\n\ -If the platform supports the tm_gmtoff and tm_zone, they are available as\n\ -attributes only."); +/*[clinic input] +time.localtime + + seconds as ot: object = None + / + +Convert seconds since the Epoch to a time tuple expressing local time. + +When 'seconds' is not passed in, convert the current time instead. +[clinic start generated code]*/ static PyObject * -time_localtime(PyObject *module, PyObject *args) +time_localtime_impl(PyObject *module, PyObject *ot) +/*[clinic end generated code: output=d3c1c6818abd34a1 input=43b4e8bf4914300e]*/ { time_t when; struct tm buf; - if (!parse_time_t_args(args, "|O:localtime", &when)) + if (!parse_time_t_arg(ot, &when)) return NULL; if (_PyTime_localtime(when, &buf) != 0) return NULL; @@ -597,13 +621,6 @@ time_localtime(PyObject *module, PyObject *args) static const char *utc_string = NULL; #endif -PyDoc_STRVAR(localtime_doc, -"localtime([seconds]) -> (tm_year,tm_mon,tm_mday,tm_hour,tm_min,\n\ - tm_sec,tm_wday,tm_yday,tm_isdst)\n\ -\n\ -Convert seconds since the Epoch to a time tuple expressing local time.\n\ -When 'seconds' is not passed in, convert the current time instead."); - /* Convert 9-item tuple to tm structure. Return 1 on success, set * an exception and return 0 on error. */ @@ -1031,17 +1048,26 @@ _asctime(struct tm *timeptr) 1900 + timeptr->tm_year); } +/*[clinic input] +time.asctime + + time_tuple as tup: object = None + / + +Convert a time tuple to a string, e.g. 'Sat Jun 06 16:26:11 1998'. + +When the time tuple is not present, current time as returned by +localtime() is used. +[clinic start generated code]*/ + static PyObject * -time_asctime(PyObject *module, PyObject *args) +time_asctime_impl(PyObject *module, PyObject *tup) +/*[clinic end generated code: output=a1bc45f84a00fb55 input=38a2a45e233a2a95]*/ { - PyObject *tup = NULL; struct tm buf; - if (!PyArg_UnpackTuple(args, "asctime", 0, 1, &tup)) - return NULL; - time_module_state *state = get_time_state(module); - if (tup == NULL) { + if (tup == Py_None) { time_t tt = time(NULL); if (_PyTime_localtime(tt, &buf) != 0) return NULL; @@ -1055,35 +1081,48 @@ time_asctime(PyObject *module, PyObject *args) return _asctime(&buf); } -PyDoc_STRVAR(asctime_doc, -"asctime([tuple]) -> string\n\ -\n\ -Convert a time tuple to a string, e.g. 'Sat Jun 06 16:26:11 1998'.\n\ -When the time tuple is not present, current time as returned by localtime()\n\ -is used."); +/*[clinic input] +time.ctime + + seconds as ot: object = None + / + +Convert a time in seconds since the Epoch to a string in local time. + +This is equivalent to asctime(localtime(seconds)). When the time tuple +is not present, current time as returned by localtime() is used. +[clinic start generated code]*/ static PyObject * -time_ctime(PyObject *self, PyObject *args) +time_ctime_impl(PyObject *module, PyObject *ot) +/*[clinic end generated code: output=c3a028f5c6931cbc input=7a4cf111320a727b]*/ { time_t tt; struct tm buf; - if (!parse_time_t_args(args, "|O:ctime", &tt)) + if (!parse_time_t_arg(ot, &tt)) return NULL; if (_PyTime_localtime(tt, &buf) != 0) return NULL; return _asctime(&buf); } -PyDoc_STRVAR(ctime_doc, -"ctime(seconds) -> string\n\ -\n\ -Convert a time in seconds since the Epoch to a string in local time.\n\ -This is equivalent to asctime(localtime(seconds)). When the time tuple is\n\ -not present, current time as returned by localtime() is used."); - #ifdef HAVE_MKTIME +/*[clinic input] +time.mktime + + time_tuple as tm_tuple: object + / + +Convert a time tuple in local time to seconds since the Epoch. + +Note that mktime(gmtime(0)) will not generally return zero for most +time zones; instead the returned value will either be equal to that of +the timezone or altzone attributes on the time module. +[clinic start generated code]*/ + static PyObject * time_mktime(PyObject *module, PyObject *tm_tuple) +/*[clinic end generated code: output=1b2a224cd309deb7 input=70b93e1c2e57e14e]*/ { struct tm tm; time_t tt; @@ -1152,20 +1191,29 @@ time_mktime(PyObject *module, PyObject *tm_tuple) return PyFloat_FromDouble((double)tt); } -PyDoc_STRVAR(mktime_doc, -"mktime(tuple) -> floating-point number\n\ -\n\ -Convert a time tuple in local time to seconds since the Epoch.\n\ -Note that mktime(gmtime(0)) will not generally return zero for most\n\ -time zones; instead the returned value will either be equal to that\n\ -of the timezone or altzone attributes on the time module."); #endif /* HAVE_MKTIME */ #ifdef HAVE_WORKING_TZSET static int init_timezone(PyObject *module); +/*[clinic input] +time.tzset + +Initialize, or reinitialize, the local timezone. + +The value is stored in os.environ['TZ']. The TZ environment variable +should be specified in standard Unix timezone format as documented in +the tzset man page (eg. 'US/Eastern', 'Europe/Amsterdam'). Unknown +timezones will silently fall back to UTC. If the TZ environment +variable is not set, the local timezone is set to the systems best +guess of wallclock time. Changing the TZ environment variable without +calling tzset *may* change the local timezone used by methods such as +localtime, but this behaviour should not be relied on. +[clinic start generated code]*/ + static PyObject * -time_tzset(PyObject *self, PyObject *unused) +time_tzset_impl(PyObject *module) +/*[clinic end generated code: output=d1564ac4d48d320b input=ae8cbbf5583146d8]*/ { PyObject* m; @@ -1190,38 +1238,35 @@ time_tzset(PyObject *self, PyObject *unused) Py_RETURN_NONE; } -PyDoc_STRVAR(tzset_doc, -"tzset()\n\ -\n\ -Initialize, or reinitialize, the local timezone to the value stored in\n\ -os.environ['TZ']. The TZ environment variable should be specified in\n\ -standard Unix timezone format as documented in the tzset man page\n\ -(eg. 'US/Eastern', 'Europe/Amsterdam'). Unknown timezones will silently\n\ -fall back to UTC. If the TZ environment variable is not set, the local\n\ -timezone is set to the systems best guess of wallclock time.\n\ -Changing the TZ environment variable without calling tzset *may* change\n\ -the local timezone used by methods such as localtime, but this behaviour\n\ -should not be relied on."); #endif /* HAVE_WORKING_TZSET */ -static PyObject * -time_monotonic(PyObject *self, PyObject *unused) +/*[clinic input] +time.monotonic -> double + +Monotonic clock, cannot go backward. +[clinic start generated code]*/ + +static double +time_monotonic_impl(PyObject *module) +/*[clinic end generated code: output=ab51899a17e16542 input=059cb42b2bad6df0]*/ { PyTime_t t; if (PyTime_Monotonic(&t) < 0) { - return NULL; + return -1.0; } - return _PyFloat_FromPyTime(t); + return PyTime_AsSecondsDouble(t); } -PyDoc_STRVAR(monotonic_doc, -"monotonic() -> float\n\ -\n\ -Monotonic clock, cannot go backward."); +/*[clinic input] +time.monotonic_ns + +Monotonic clock, cannot go backward, as nanoseconds. +[clinic start generated code]*/ static PyObject * -time_monotonic_ns(PyObject *self, PyObject *unused) +time_monotonic_ns_impl(PyObject *module) +/*[clinic end generated code: output=57a9261f91740349 input=14032d6b1601a300]*/ { PyTime_t t; if (PyTime_Monotonic(&t) < 0) { @@ -1230,30 +1275,32 @@ time_monotonic_ns(PyObject *self, PyObject *unused) return PyLong_FromInt64(t); } -PyDoc_STRVAR(monotonic_ns_doc, -"monotonic_ns() -> int\n\ -\n\ -Monotonic clock, cannot go backward, as nanoseconds."); +/*[clinic input] +time.perf_counter -> double +Performance counter for benchmarking. +[clinic start generated code]*/ -static PyObject * -time_perf_counter(PyObject *self, PyObject *unused) +static double +time_perf_counter_impl(PyObject *module) +/*[clinic end generated code: output=6eee280ca7b73cb1 input=f786239c5015893b]*/ { PyTime_t t; if (PyTime_PerfCounter(&t) < 0) { - return NULL; + return -1.0; } - return _PyFloat_FromPyTime(t); + return PyTime_AsSecondsDouble(t); } -PyDoc_STRVAR(perf_counter_doc, -"perf_counter() -> float\n\ -\n\ -Performance counter for benchmarking."); +/*[clinic input] +time.perf_counter_ns +Performance counter for benchmarking as nanoseconds. +[clinic start generated code]*/ static PyObject * -time_perf_counter_ns(PyObject *self, PyObject *unused) +time_perf_counter_ns_impl(PyObject *module) +/*[clinic end generated code: output=e11a728338108d42 input=178bf260d7d48a02]*/ { PyTime_t t; if (PyTime_PerfCounter(&t) < 0) { @@ -1262,12 +1309,6 @@ time_perf_counter_ns(PyObject *self, PyObject *unused) return PyLong_FromInt64(t); } -PyDoc_STRVAR(perf_counter_ns_doc, -"perf_counter_ns() -> int\n\ -\n\ -Performance counter for benchmarking as nanoseconds."); - - // gh-115714: Don't use times() on WASI. #if defined(HAVE_TIMES) && !defined(__wasi__) static int @@ -1424,24 +1465,37 @@ py_process_time(time_module_state *state, PyTime_t *tp, #endif } -static PyObject * -time_process_time(PyObject *module, PyObject *unused) +/*[clinic input] +time.process_time -> double + +Process time for profiling. + +That is the sum of the kernel and user-space CPU time. +[clinic start generated code]*/ + +static double +time_process_time_impl(PyObject *module) +/*[clinic end generated code: output=13b593cb16d415a8 input=6d539ae686c37c06]*/ { time_module_state *state = get_time_state(module); PyTime_t t; if (py_process_time(state, &t, NULL) < 0) { - return NULL; + return -1.0; } - return _PyFloat_FromPyTime(t); + return PyTime_AsSecondsDouble(t); } -PyDoc_STRVAR(process_time_doc, -"process_time() -> float\n\ -\n\ -Process time for profiling: sum of the kernel and user-space CPU time."); +/*[clinic input] +time.process_time_ns + +Process time for profiling as nanoseconds. + +That is the sum of the kernel and user-space CPU time. +[clinic start generated code]*/ static PyObject * -time_process_time_ns(PyObject *module, PyObject *unused) +time_process_time_ns_impl(PyObject *module) +/*[clinic end generated code: output=857f0c20105c4d1a input=6d998fd7a213c0cd]*/ { time_module_state *state = get_time_state(module); PyTime_t t; @@ -1451,13 +1505,6 @@ time_process_time_ns(PyObject *module, PyObject *unused) return PyLong_FromInt64(t); } -PyDoc_STRVAR(process_time_ns_doc, -"process_time() -> int\n\ -\n\ -Process time for profiling as nanoseconds:\n\ -sum of the kernel and user-space CPU time."); - - #if defined(MS_WINDOWS) #define HAVE_THREAD_TIME static int @@ -1599,23 +1646,36 @@ _PyTime_GetThreadTimeWithInfo(PyTime_t *tp, _Py_clock_info_t *info) #pragma clang diagnostic ignored "-Wunguarded-availability" #endif -static PyObject * -time_thread_time(PyObject *self, PyObject *unused) +/*[clinic input] +time.thread_time -> double + +Thread time for profiling. + +That is the sum of the kernel and user-space CPU time. +[clinic start generated code]*/ + +static double +time_thread_time_impl(PyObject *module) +/*[clinic end generated code: output=33f6639edb42e8f5 input=cdf4621822b8ed60]*/ { PyTime_t t; if (_PyTime_GetThreadTimeWithInfo(&t, NULL) < 0) { - return NULL; + return -1.0; } - return _PyFloat_FromPyTime(t); + return PyTime_AsSecondsDouble(t); } -PyDoc_STRVAR(thread_time_doc, -"thread_time() -> float\n\ -\n\ -Thread time for profiling: sum of the kernel and user-space CPU time."); +/*[clinic input] +time.thread_time_ns + +Thread time for profiling as nanoseconds. + +That is the sum of the kernel and user-space CPU time. +[clinic start generated code]*/ static PyObject * -time_thread_time_ns(PyObject *self, PyObject *unused) +time_thread_time_ns_impl(PyObject *module) +/*[clinic end generated code: output=2a1ac9cc3e1d4c37 input=a79b2b91f308277d]*/ { PyTime_t t; if (_PyTime_GetThreadTimeWithInfo(&t, NULL) < 0) { @@ -1624,12 +1684,6 @@ time_thread_time_ns(PyObject *self, PyObject *unused) return PyLong_FromInt64(t); } -PyDoc_STRVAR(thread_time_ns_doc, -"thread_time() -> int\n\ -\n\ -Thread time for profiling as nanoseconds:\n\ -sum of the kernel and user-space CPU time."); - #ifdef __APPLE__ #pragma clang diagnostic pop #endif @@ -1637,18 +1691,23 @@ sum of the kernel and user-space CPU time."); #endif +/*[clinic input] +time.get_clock_info + + name: str + / + +Get information of the specified clock. +[clinic start generated code]*/ + static PyObject * -time_get_clock_info(PyObject *module, PyObject *args) +time_get_clock_info_impl(PyObject *module, const char *name) +/*[clinic end generated code: output=a77a07bdf3554bd6 input=6812ae049f1b1031]*/ { - char *name; _Py_clock_info_t info; PyObject *obj = NULL, *dict, *ns; PyTime_t t; - if (!PyArg_ParseTuple(args, "s:get_clock_info", &name)) { - return NULL; - } - #ifdef Py_DEBUG info.implementation = NULL; info.monotonic = -1; @@ -1760,11 +1819,6 @@ time_get_clock_info(PyObject *module, PyObject *args) return NULL; } -PyDoc_STRVAR(get_clock_info_doc, -"get_clock_info(name: str) -> dict\n\ -\n\ -Get information of the specified clock."); - #ifndef HAVE_DECL_TZNAME static void get_zone(char *zone, int n, struct tm *p) @@ -1913,48 +1967,48 @@ init_timezone(PyObject *m) #include "clinic/timemodule.c.h" static PyMethodDef time_methods[] = { - {"time", time_time, METH_NOARGS, time_doc}, - {"time_ns", time_time_ns, METH_NOARGS, time_ns_doc}, + TIME_TIME_METHODDEF + TIME_TIME_NS_METHODDEF #ifdef HAVE_CLOCK_GETTIME TIME_CLOCK_GETTIME_METHODDEF TIME_CLOCK_GETTIME_NS_METHODDEF #endif #ifdef HAVE_CLOCK_SETTIME - {"clock_settime", time_clock_settime, METH_VARARGS, clock_settime_doc}, - {"clock_settime_ns",time_clock_settime_ns, METH_VARARGS, clock_settime_ns_doc}, + TIME_CLOCK_SETTIME_METHODDEF + TIME_CLOCK_SETTIME_NS_METHODDEF #endif #ifdef HAVE_CLOCK_GETRES - {"clock_getres", time_clock_getres, METH_VARARGS, clock_getres_doc}, + TIME_CLOCK_GETRES_METHODDEF #endif #ifdef HAVE_PTHREAD_GETCPUCLOCKID - {"pthread_getcpuclockid", time_pthread_getcpuclockid, METH_VARARGS, pthread_getcpuclockid_doc}, + TIME_PTHREAD_GETCPUCLOCKID_METHODDEF #endif - {"sleep", time_sleep, METH_O, sleep_doc}, - {"gmtime", time_gmtime, METH_VARARGS, gmtime_doc}, - {"localtime", time_localtime, METH_VARARGS, localtime_doc}, - {"asctime", time_asctime, METH_VARARGS, asctime_doc}, - {"ctime", time_ctime, METH_VARARGS, ctime_doc}, + TIME_SLEEP_METHODDEF + TIME_GMTIME_METHODDEF + TIME_LOCALTIME_METHODDEF + TIME_ASCTIME_METHODDEF + TIME_CTIME_METHODDEF #ifdef HAVE_MKTIME - {"mktime", time_mktime, METH_O, mktime_doc}, + TIME_MKTIME_METHODDEF #endif #ifdef HAVE_STRFTIME {"strftime", time_strftime, METH_VARARGS, strftime_doc}, #endif {"strptime", time_strptime, METH_VARARGS, strptime_doc}, #ifdef HAVE_WORKING_TZSET - {"tzset", time_tzset, METH_NOARGS, tzset_doc}, + TIME_TZSET_METHODDEF #endif - {"monotonic", time_monotonic, METH_NOARGS, monotonic_doc}, - {"monotonic_ns", time_monotonic_ns, METH_NOARGS, monotonic_ns_doc}, - {"process_time", time_process_time, METH_NOARGS, process_time_doc}, - {"process_time_ns", time_process_time_ns, METH_NOARGS, process_time_ns_doc}, + TIME_MONOTONIC_METHODDEF + TIME_MONOTONIC_NS_METHODDEF + TIME_PROCESS_TIME_METHODDEF + TIME_PROCESS_TIME_NS_METHODDEF #ifdef HAVE_THREAD_TIME - {"thread_time", time_thread_time, METH_NOARGS, thread_time_doc}, - {"thread_time_ns", time_thread_time_ns, METH_NOARGS, thread_time_ns_doc}, + TIME_THREAD_TIME_METHODDEF + TIME_THREAD_TIME_NS_METHODDEF #endif - {"perf_counter", time_perf_counter, METH_NOARGS, perf_counter_doc}, - {"perf_counter_ns", time_perf_counter_ns, METH_NOARGS, perf_counter_ns_doc}, - {"get_clock_info", time_get_clock_info, METH_VARARGS, get_clock_info_doc}, + TIME_PERF_COUNTER_METHODDEF + TIME_PERF_COUNTER_NS_METHODDEF + TIME_GET_CLOCK_INFO_METHODDEF {NULL, NULL} /* sentinel */ }; From ed893d6a3e20f1dab106b6ca0185361a0c6d09c5 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 13 Aug 2026 21:05:32 +0300 Subject: [PATCH 2/5] Update test_inspect for the new signatures --- Lib/test/test_inspect/test_inspect.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 844811692df2d6..9235425fc7bf04 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -6359,14 +6359,7 @@ def test_thread_module_has_signatures(self): self._test_module_has_signatures(_thread, no_signature) def test_time_module_has_signatures(self): - no_signature = { - 'asctime', 'ctime', 'get_clock_info', 'gmtime', 'localtime', - 'strftime', 'strptime' - } - no_signature |= {name for name in - ['clock_getres', 'clock_settime', 'clock_settime_ns', - 'pthread_getcpuclockid'] - if hasattr(time, name)} + no_signature = {'strftime', 'strptime'} self._test_module_has_signatures(time, no_signature) def test_tokenize_module_has_signatures(self): From 41083727dee00738c698888b1841154225ea535b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 13 Aug 2026 21:43:24 +0300 Subject: [PATCH 3/5] Fix test_autospec_on_bound_builtin_function It needs a builtin function without a signature, but time.ctime() now has one. --- Lib/test/test_unittest/testmock/testhelpers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_unittest/testmock/testhelpers.py b/Lib/test/test_unittest/testmock/testhelpers.py index 0e82c723ec3eaa..aff64650284a53 100644 --- a/Lib/test/test_unittest/testmock/testhelpers.py +++ b/Lib/test/test_unittest/testmock/testhelpers.py @@ -1,5 +1,4 @@ import inspect -import time import types import unittest @@ -929,8 +928,9 @@ def check_data_descriptor(mock_attr): def test_autospec_on_bound_builtin_function(self): - meth = types.MethodType(time.ctime, time.time()) - self.assertIsInstance(meth(), str) + # max() is variadic, therefore it has no signature. + meth = types.MethodType(max, [4, 5, 6]) + self.assertEqual(meth(), 6) mocked = create_autospec(meth) # no signature, so no spec to check against From ca3e77105aa9b1da1f988a87a1ec42d8d55edfeb Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 13 Aug 2026 22:03:58 +0300 Subject: [PATCH 4/5] Use a function which is defined without a signature Any builtin function can get a signature, therefore the test uses the function which exists for testing the lack of it. --- Lib/test/test_unittest/testmock/testhelpers.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_unittest/testmock/testhelpers.py b/Lib/test/test_unittest/testmock/testhelpers.py index aff64650284a53..476e9105063bf9 100644 --- a/Lib/test/test_unittest/testmock/testhelpers.py +++ b/Lib/test/test_unittest/testmock/testhelpers.py @@ -2,6 +2,8 @@ import types import unittest +from test.support import import_helper + from unittest.mock import ( call, _Call, create_autospec, MagicMock, Mock, ANY, _CallList, patch, PropertyMock, _callable @@ -928,9 +930,10 @@ def check_data_descriptor(mock_attr): def test_autospec_on_bound_builtin_function(self): - # max() is variadic, therefore it has no signature. - meth = types.MethodType(max, [4, 5, 6]) - self.assertEqual(meth(), 6) + _testcapi = import_helper.import_module('_testcapi') + # This function is defined without a signature. + meth = types.MethodType(_testcapi.docstring_no_signature, object()) + self.assertIsNone(meth()) mocked = create_autospec(meth) # no signature, so no spec to check against From b10998e6319336ec183a60ed6bf0d716e45f4176 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 14 Aug 2026 13:55:19 +0300 Subject: [PATCH 5/5] Address review comments Restore the default of time.asctime() as NULL, so that passing None raises a TypeError as before, and fix the docstring of time.ctime() and the comment for parse_time_t_arg(). --- Lib/test/test_inspect/test_inspect.py | 4 +++- Modules/clinic/timemodule.c.h | 10 +++++----- Modules/timemodule.c | 16 ++++++++-------- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 9235425fc7bf04..a0421c12f65700 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -6360,7 +6360,9 @@ def test_thread_module_has_signatures(self): def test_time_module_has_signatures(self): no_signature = {'strftime', 'strptime'} - self._test_module_has_signatures(time, no_signature) + unsupported_signature = {'asctime'} + self._test_module_has_signatures(time, no_signature, + unsupported_signature) def test_tokenize_module_has_signatures(self): import tokenize diff --git a/Modules/clinic/timemodule.c.h b/Modules/clinic/timemodule.c.h index d4c54fa697546f..da0c2982f4e0af 100644 --- a/Modules/clinic/timemodule.c.h +++ b/Modules/clinic/timemodule.c.h @@ -358,7 +358,7 @@ time_localtime(PyObject *module, PyObject *const *args, Py_ssize_t nargs) } PyDoc_STRVAR(time_asctime__doc__, -"asctime($module, time_tuple=None, /)\n" +"asctime($module, time_tuple=, /)\n" "--\n" "\n" "Convert a time tuple to a string, e.g. \'Sat Jun 06 16:26:11 1998\'.\n" @@ -376,7 +376,7 @@ static PyObject * time_asctime(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *tup = Py_None; + PyObject *tup = NULL; if (!_PyArg_CheckPositional("asctime", nargs, 0, 1)) { goto exit; @@ -398,8 +398,8 @@ PyDoc_STRVAR(time_ctime__doc__, "\n" "Convert a time in seconds since the Epoch to a string in local time.\n" "\n" -"This is equivalent to asctime(localtime(seconds)). When the time tuple\n" -"is not present, current time as returned by localtime() is used."); +"This is equivalent to asctime(localtime(seconds)). When \'seconds\' is\n" +"not passed in, convert the current time instead."); #define TIME_CTIME_METHODDEF \ {"ctime", _PyCFunction_CAST(time_ctime), METH_FASTCALL, time_ctime__doc__}, @@ -751,4 +751,4 @@ time_get_clock_info(PyObject *module, PyObject *arg) #ifndef TIME_THREAD_TIME_NS_METHODDEF #define TIME_THREAD_TIME_NS_METHODDEF #endif /* !defined(TIME_THREAD_TIME_NS_METHODDEF) */ -/*[clinic end generated code: output=e1a38728f085edbd input=a9049054013a1b77]*/ +/*[clinic end generated code: output=75f2a96aa9a21a15 input=a9049054013a1b77]*/ diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 5fa9b325e3e51a..47e75e76c1719b 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -509,8 +509,8 @@ tmtotuple(time_module_state *state, struct tm *p return v; } -/* Parse arg tuple that can contain an optional float-or-None value; - format needs to be "|O:name". +/* Convert a number of seconds since the Epoch, or None which means the + current time, to time_t. Returns non-zero on success (parallels PyArg_ParseTuple). */ static int @@ -1051,7 +1051,7 @@ _asctime(struct tm *timeptr) /*[clinic input] time.asctime - time_tuple as tup: object = None + time_tuple as tup: object = NULL / Convert a time tuple to a string, e.g. 'Sat Jun 06 16:26:11 1998'. @@ -1062,12 +1062,12 @@ localtime() is used. static PyObject * time_asctime_impl(PyObject *module, PyObject *tup) -/*[clinic end generated code: output=a1bc45f84a00fb55 input=38a2a45e233a2a95]*/ +/*[clinic end generated code: output=a1bc45f84a00fb55 input=083c132f3cb23f1e]*/ { struct tm buf; time_module_state *state = get_time_state(module); - if (tup == Py_None) { + if (tup == NULL) { time_t tt = time(NULL); if (_PyTime_localtime(tt, &buf) != 0) return NULL; @@ -1089,13 +1089,13 @@ time.ctime Convert a time in seconds since the Epoch to a string in local time. -This is equivalent to asctime(localtime(seconds)). When the time tuple -is not present, current time as returned by localtime() is used. +This is equivalent to asctime(localtime(seconds)). When 'seconds' is +not passed in, convert the current time instead. [clinic start generated code]*/ static PyObject * time_ctime_impl(PyObject *module, PyObject *ot) -/*[clinic end generated code: output=c3a028f5c6931cbc input=7a4cf111320a727b]*/ +/*[clinic end generated code: output=c3a028f5c6931cbc input=ee744f25ce87d1ae]*/ { time_t tt; struct tm buf;