Skip to content

Instantly share code, notes, and snippets.

@kajuberdut
Last active December 18, 2021 00:54
Show Gist options
  • Save kajuberdut/8a9c9998fa3847f1757064b6e072385b to your computer and use it in GitHub Desktop.
Save kajuberdut/8a9c9998fa3847f1757064b6e072385b to your computer and use it in GitHub Desktop.
A method of wrapping datetime to preserve chainable calls
"""
This example uses datetime, but the method applies generally to classes with chainable methods
(i.e. classes with methods that return an instance of the same class.)
Subclassing such classes quickly becomes annoying, because the methods return an instance of the original class, not the subclass.
With this method, you can still call the methods of the wrapped datetime, but you get back an instance of WrappedDateTime.
Also preserves comparisons with datetime.datetime.
"""
import datetime as _datetime
import typing as t
from functools import partial
def dt_wrapper(callable, *args, **kwargs):
result = callable(*args, **kwargs)
if isinstance(result, _datetime.datetime):
return WrappedDateTime(datetime=result)
else:
return result
class WrappedDateTime:
def __init__(self, datetime: _datetime.datetime = None):
if datetime is None:
datetime = _datetime.datetime.now()
self.wrapped_datetime = datetime
def __getattr__(self, __name: str) -> t.Any:
# Importantly, this is not __getattribute__
if hasattr(self.wrapped_datetime, __name):
return partial(dt_wrapper, getattr(self.wrapped_datetime, __name))
else:
raise AttributeError(
"Not able to provide that attribute from datetime or WrappedDateTime"
)
@property
def timestamp(self):
return _datetime.datetime.timestamp(self.wrapped_datetime)
def __repr__(self):
return self.wrapped_datetime.__repr__()
def __str__(self):
return self.wrapped_datetime.__str__()
def __eq__(self, other):
return self.wrapped_datetime.__eq__(other)
def __ne__(self, other):
return self.wrapped_datetime.__ne__(other)
def __lt__(self, other):
return self.wrapped_datetime.__lt__(other)
def __le__(self, other):
return self.wrapped_datetime.__le__(other)
def __gt__(self, other):
return self.wrapped_datetime.__gt__(other)
def __ge__(self, other):
return self.wrapped_datetime.__ge__(other)
if __name__ == "__main__":
import datetime
t = WrappedDateTime().utcnow().astimezone(datetime.timezone.utc)
print(type(t))
# <class '__main__.WrappedDateTime'>
later = datetime.datetime.fromtimestamp(t.timestamp - 100).astimezone(datetime.timezone.utc)
print(t > later)
# True
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment