gh-67795: Accept any real numbers as timestamp and timeout (GH-139224)

Functions that take timestamp or timeout arguments now accept any
real numbers (such as Decimal and Fraction), not only integers or floats,
although this does not improve precision.
This commit is contained in:
Serhiy Storchaka 2025-09-23 21:31:42 +03:00 committed by GitHub
parent 6ec058a1f7
commit 1a2e00c97a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 342 additions and 97 deletions

View file

@ -368,8 +368,20 @@ pytime_object_to_denominator(PyObject *obj, time_t *sec, long *numerator,
{
assert(denominator >= 1);
if (PyFloat_Check(obj)) {
if (PyIndex_Check(obj)) {
*sec = _PyLong_AsTime_t(obj);
*numerator = 0;
if (*sec == (time_t)-1 && PyErr_Occurred()) {
return -1;
}
return 0;
}
else {
double d = PyFloat_AsDouble(obj);
if (d == -1 && PyErr_Occurred()) {
*numerator = 0;
return -1;
}
if (isnan(d)) {
*numerator = 0;
PyErr_SetString(PyExc_ValueError, "Invalid value NaN (not a number)");
@ -378,30 +390,28 @@ pytime_object_to_denominator(PyObject *obj, time_t *sec, long *numerator,
return pytime_double_to_denominator(d, sec, numerator,
denominator, round);
}
else {
*sec = _PyLong_AsTime_t(obj);
*numerator = 0;
if (*sec == (time_t)-1 && PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_Format(PyExc_TypeError,
"argument must be int or float, not %T", obj);
}
return -1;
}
return 0;
}
}
int
_PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round)
{
if (PyFloat_Check(obj)) {
if (PyIndex_Check(obj)) {
*sec = _PyLong_AsTime_t(obj);
if (*sec == (time_t)-1 && PyErr_Occurred()) {
return -1;
}
return 0;
}
else {
double intpart;
/* volatile avoids optimization changing how numbers are rounded */
volatile double d;
d = PyFloat_AsDouble(obj);
if (d == -1 && PyErr_Occurred()) {
return -1;
}
if (isnan(d)) {
PyErr_SetString(PyExc_ValueError, "Invalid value NaN (not a number)");
return -1;
@ -418,13 +428,6 @@ _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round)
*sec = (time_t)intpart;
return 0;
}
else {
*sec = _PyLong_AsTime_t(obj);
if (*sec == (time_t)-1 && PyErr_Occurred()) {
return -1;
}
return 0;
}
}
@ -586,39 +589,38 @@ static int
pytime_from_object(PyTime_t *tp, PyObject *obj, _PyTime_round_t round,
long unit_to_ns)
{
if (PyFloat_Check(obj)) {
if (PyIndex_Check(obj)) {
long long sec = PyLong_AsLongLong(obj);
if (sec == -1 && PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
pytime_overflow();
}
return -1;
}
static_assert(sizeof(long long) <= sizeof(PyTime_t),
"PyTime_t is smaller than long long");
PyTime_t ns = (PyTime_t)sec;
if (pytime_mul(&ns, unit_to_ns) < 0) {
pytime_overflow();
return -1;
}
*tp = ns;
return 0;
}
else {
double d;
d = PyFloat_AsDouble(obj);
if (d == -1 && PyErr_Occurred()) {
return -1;
}
if (isnan(d)) {
PyErr_SetString(PyExc_ValueError, "Invalid value NaN (not a number)");
return -1;
}
return pytime_from_double(tp, d, round, unit_to_ns);
}
long long sec = PyLong_AsLongLong(obj);
if (sec == -1 && PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
pytime_overflow();
}
else if (PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_Format(PyExc_TypeError,
"'%T' object cannot be interpreted as an integer or float",
obj);
}
return -1;
}
static_assert(sizeof(long long) <= sizeof(PyTime_t),
"PyTime_t is smaller than long long");
PyTime_t ns = (PyTime_t)sec;
if (pytime_mul(&ns, unit_to_ns) < 0) {
pytime_overflow();
return -1;
}
*tp = ns;
return 0;
}