-
-
Notifications
You must be signed in to change notification settings - Fork 422
Use timedelta for elapsed-time handling to improve Requests compatibility #661
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Better compatibility with |
|
Using a tuple could break backward compatibility since any return value that isn’t a # pure python
import operator as _op
from typing import Optional
from datetime import timedelta
class TimedeltaFloat(timedelta):
"""
A timedelta subclass that behaves like a float in numeric contexts.
"""
def __new__(cls, td: Optional[timedelta] = None):
if isinstance(td, timedelta):
return super().__new__(cls, td.days, td.seconds, td.microseconds)
return super().__new__(cls)
def _f(self):
return self.total_seconds()
# numeric conversions
__float__ = lambda self: self._f()
__int__ = lambda self: int(self._f())
# comparisons
def __eq__(self, other):
if isinstance(other, (int, float)):
return self._f() == other
if isinstance(other, timedelta):
return self._f() == other.total_seconds()
return NotImplemented
def __lt__(self, other):
if isinstance(other, (int, float)):
return self._f() < other
if isinstance(other, timedelta):
return self._f() < other.total_seconds()
return NotImplemented
def __gt__(self, other):
if isinstance(other, (int, float)):
return self._f() > other
if isinstance(other, timedelta):
return self._f() > other.total_seconds()
return NotImplemented
def __le__(self, other):
if isinstance(other, (int, float)):
return self._f() <= other
if isinstance(other, timedelta):
return self._f() <= other.total_seconds()
return NotImplemented
def __ge__(self, other):
if isinstance(other, (int, float)):
return self._f() >= other
if isinstance(other, timedelta):
return self._f() >= other.total_seconds()
return NotImplemented
def __ne__(self, other):
if isinstance(other, (int, float)):
return self._f() != other
if isinstance(other, timedelta):
return self._f() != other.total_seconds()
return NotImplemented
# arithmetic with float returns float; with timedelta returns timedelta
def _arith(self, other, op):
if isinstance(other, (int, float)):
return op(self._f(), other)
if isinstance(other, timedelta):
return TimedeltaFloat(op(timedelta(self.days, self.seconds, self.microseconds), other))
return NotImplemented
__add__ = lambda self, o: self._arith(o, _op.add)
__radd__ = lambda self, o: self._arith(o, _op.add)
__sub__ = lambda self, o: self._arith(o, _op.sub)
__rsub__ = lambda self, o: self._arith(o, lambda x, y: y - x)
__mul__ = lambda self, o: self._arith(o, _op.mul)
__rmul__ = lambda self, o: self._arith(o, _op.mul)
__truediv__ = lambda self, o: self._arith(o, _op.truediv)
__rtruediv__ = lambda self, o: self._arith(o, lambda x, y: y / x)
__neg__ = lambda self: -self._f()
__pos__ = lambda self: +self._f()
__abs__ = lambda self: abs(self._f())
def __str__(self):
return f"{self._f()}"
if __name__ == "__main__":
from datetime import datetime
td = TimedeltaFloat(timedelta(seconds=5, microseconds=500000))
print(td) # 5.5s
print(float(td)) # 5.5
print(td + 2) # 7.5
print(2 + td) # 7.5
print(td * 2) # 11.0
print(td / 2) # 2.75
print(td > 3) # True
print(td > timedelta(seconds=1)) # True
print(td + timedelta(seconds=2)) # TimedeltaFloat(7.5s)
print(datetime.now() + td) # current time + 5.5s
|
|
Oh, sorry. I was thinking that this is a parameter in requesting. Well, if it's an attribute of the response, I guess this minor breaking change is acceptable. Please try to fix the unit tests then. |
|
I have slightly modified the unit tests and they are now passing. |
This change updates the
Response.elapsedattribute to use adatetime.timedeltainstead of a float. The standardrequestslibrary exposes.elapsedas atimedelta, so this change improves compatibility.For example, in
requests:After this change,
curl_cffi.requestswill match this behavior.Changes:
timedeltawhere needed.Response.elapsedas atimedeltaobject.TOTAL_TIMEfrom seconds totimedelta(seconds=...)when parsing responses.