Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions Include/internal/pycore_time.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
7 changes: 5 additions & 2 deletions Lib/test/test_clinic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"""
Expand Down Expand Up @@ -3566,6 +3567,7 @@ def test_cli_converters(self):
"char",
"defining_class",
"double",
"duration",
"DWORD",
"fildes",
"float",
Expand All @@ -3587,6 +3589,7 @@ def test_cli_converters(self):
"size_t",
"slice_index",
"str",
"timestamp",
"uint16",
"uint32",
"uint64",
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -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`.
20 changes: 10 additions & 10 deletions Modules/_datetimemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand All @@ -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,
Expand All @@ -3381,7 +3377,11 @@ datetime_date_fromtimestamp_capi(PyObject *cls, PyObject *args)
PyObject *result = NULL;

if (PyArg_UnpackTuple(args, "fromtimestamp", 1, 1, &timestamp)) {
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;
Expand Down
27 changes: 18 additions & 9 deletions Modules/_multiprocessing/clinic/semaphore.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 19 additions & 28 deletions Modules/_multiprocessing/semaphore.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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};
Expand All @@ -325,25 +322,19 @@ _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;
if (gettimeofday(&now, NULL) < 0) {
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;
}
Expand Down Expand Up @@ -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]
Expand Down
22 changes: 5 additions & 17 deletions Modules/_queuemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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);
}

Expand Down Expand Up @@ -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]
Expand Down
3 changes: 2 additions & 1 deletion Modules/_sqlite/clinic/_sqlite3.connect.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading