You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
time.sleep() should support objects with __float__ #79888
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee=Noneclosed_at=Nonecreated_at=<Date2019-01-10.15:58:39.782>labels= ['interpreter-core', '3.10']
title='time.sleep() should support objects with __float__'updated_at=<Date2020-05-23.00:14:27.187>user='https://github.com/jdemeyer'
The problem comes from the private C function _PyTime_FromObject() of Python/pytime.c. This function must use the proper conversion to minimize the precision loss. Lib/test/test_time.py contains a lot of tests on conversions from different types and ensure that values are rounded correctly. See also my PEP-564 "Add new time functions with nanosecond resolution".
The correct code works for float and int (and maybe decimal.Decimal, I don't recall!), but it seems like it doesn't support types with __float__(). You have to explicitly cast such objects using float(value).
PyNumber_Float() can be used to convert arbitrary object to a float, but I'm not sure in which order the conversion should be tried to avoid/reduce precision loss during the conversion.
Example:
>>> x=2**53+1; x -int(float(x))
1
If we convert 'x' (int) to float, we introduce an error of 1.
Deriving __int__ from __float__ wouldn't be the right answer, as that can easily lead to unwanted OverflowError exceptions and other issues.
However, Jeroen's suggested order of checking __index__ then __float__ then __int__ in _PyTime_FromObject makes sense to me, as that addresses Victor's desire to use the most precise conversion available.
If we want to support other numerical types with loss in double rounding, the most reliable way is to represent them as fractions (x.as_integer_ratio() or (x.numerator, x.denominator)) and use precise integer arithmetic.
the most reliable way is to represent them as fractions (x.as_integer_ratio() or (x.numerator, x.denominator))
I don't think that we can rely on non-dunder names like that. They are not reserved names, so classes can give them any semantics that they like. This is not just hypothetical: SageMath for example uses numerator() and denominator() methods, not properties.
If you really want to go through with this, probably a special method like __as_integer_ratio__ should be defined.
Anyway, I personally consider the double rounding for time.sleep() a non-issue. We are not trying to write a precise math library here, nobody will complain about sleeping a femtosecond too long.
If we want to support other numerical types with loss in double rounding, the most reliable way is to represent them as fractions (x.as_integer_ratio() or (x.numerator, x.denominator))
If this issue is covering all the _PyTime_FromObject() uses, the change to PyLong_AsLongLong() in Python 3.10 (#80229) increased the severity. Many time functions that accept floats by design raise TypeError when passed (e.g.) a decimal.Decimal (they previously just printed a DeprecationWarning).
>>> class Half(object):
... def __float__(self):
... return 0.5
... import time
... time.sleep(Half())
...
Traceback (most recent call last):
File "<python-input-5>", line 5, in <module>
time.sleep(Half())
~~~~~~~~~~^^^^^^^^
TypeError: 'Half' object cannot be interpreted as an integer or float
Activity
jdemeyer commentedon Jan 10, 2019
This used to work correctly in Python 2:
With Python 3.6, one gets instead
AVINASHMISHRA commentedon Jan 10, 2019
hey i am a total newbie to open source contribution.
can you help me understand this issue and can i help solve this issue?
remilapeyre commentedon Jan 10, 2019
time.sleep() is probably not the only function to have such a bug.
Maybe __int__() should default to:
when __float__ is defined and not __int__.
Nick Coghlan suggested something similar for __int__ and __index__.
remilapeyre commentedon Jan 10, 2019
See bpo-33039 for the proposed change to __int__.
vstinner commentedon Jan 10, 2019
The problem comes from the private C function _PyTime_FromObject() of Python/pytime.c. This function must use the proper conversion to minimize the precision loss. Lib/test/test_time.py contains a lot of tests on conversions from different types and ensure that values are rounded correctly. See also my PEP-564 "Add new time functions with nanosecond resolution".
The correct code works for float and int (and maybe decimal.Decimal, I don't recall!), but it seems like it doesn't support types with __float__(). You have to explicitly cast such objects using float(value).
PyNumber_Float() can be used to convert arbitrary object to a float, but I'm not sure in which order the conversion should be tried to avoid/reduce precision loss during the conversion.
Example:
If we convert 'x' (int) to float, we introduce an error of 1.
vstinner commentedon Jan 10, 2019
PR 11507 is not directly related to this issue, see bpo-26669. But I wrote this PR when trying to fix this issue :-)
jdemeyer commentedon Jan 10, 2019
I would suggest the order
ncoghlan commentedon Jan 13, 2019
Deriving __int__ from __float__ wouldn't be the right answer, as that can easily lead to unwanted OverflowError exceptions and other issues.
However, Jeroen's suggested order of checking __index__ then __float__ then __int__ in _PyTime_FromObject makes sense to me, as that addresses Victor's desire to use the most precise conversion available.
serhiy-storchaka commentedon Jan 13, 2019
This can cause a loss of precision for Decimal.
If we want to support other numerical types with loss in double rounding, the most reliable way is to represent them as fractions (x.as_integer_ratio() or (x.numerator, x.denominator)) and use precise integer arithmetic.
jdemeyer commentedon Jan 13, 2019
I don't think that we can rely on non-dunder names like that. They are not reserved names, so classes can give them any semantics that they like. This is not just hypothetical: SageMath for example uses numerator() and denominator() methods, not properties.
If you really want to go through with this, probably a special method like __as_integer_ratio__ should be defined.
Anyway, I personally consider the double rounding for time.sleep() a non-issue. We are not trying to write a precise math library here, nobody will complain about sleeping a femtosecond too long.
jdemeyer commentedon Jan 13, 2019
Not for Decimal! In fact sleep(Decimal("0.99")) is interpreted as sleep(0) because __int__ is used to convert.
jdemeyer commentedon Jan 18, 2019
My proposal vastly improves the situation for Decimal. I will write a PR for this and I hope that it won't be rejected just because it's not perfect.
vstinner commentedon Jan 18, 2019
Oh oh. I didn't know that. It should be fixed.
14 remaining items
jdemeyer commentedon Mar 29, 2019
Is anybody willing to review PR 11636?
jdemeyer commentedon Aug 4, 2019
See https://discuss.python.org/t/pep-3141-ratio-instead-of-numerator-denominator/2037/24?u=jdemeyer for a proposal to define __ratio__ for this.
vstinner commentedon Aug 13, 2019
See also bpo-20861.
ald-extrahop commentedon Aug 3, 2023
If this issue is covering all the _PyTime_FromObject() uses, the change to PyLong_AsLongLong() in Python 3.10 (#80229) increased the severity. Many time functions that accept floats by design raise TypeError when passed (e.g.) a decimal.Decimal (they previously just printed a DeprecationWarning).
StanFromIreland commentedon Aug 12, 2025
The error message now contradicts the behavior:
StanFromIreland commentedon Dec 20, 2025
This was fixed by Serhiy in 1a2e00c: