WoJ WoJ - 21 days ago 8
Python Question

How to extend arrow? (and similar classes in modules)

I am trying to extend

and fail to understand how I can replicate the functionality of the base classes. This is probably due to a lack of clear understanding on how to extend classes in modules (emphasized by my
arrow
case - this is to say that the question is probably more general than limited to
arrow
only).

arrow
basic usage:

>>> import arrow
>>> arrow.now()
<Arrow [2016-11-19T15:13:23.897484+01:00]>
>>> arrow.get("2016-11-20")
<Arrow [2016-11-20T00:00:00+00:00]>


I would like to add a
when
method which will return 'today', 'tomorrow' or 'later'. I first tried this:

import arrow

class MyArrow(arrow.Arrow):
def __init__(self, *args):
arrow.Arrow.__init__(self, *args)

def when(self):
now = arrow.now()
end_today = now.ceil('day')
end_tomorrow = now.replace(days=+1).ceil('day')
start_tomorrow = now.replace(days=+1).floor('day')
if self < end_today:
return 'today'
elif self < end_tomorrow:
return 'tomorrow'
else:
return 'later'

if __name__ == "__main__":
tom = MyArrow.now().replace(days=+1)
print(tom.when())
someday = MyArrow.get("2016-11-19")


The result is

tomorrow
Traceback (most recent call last):
File "D:/Dropbox/dev/domotique/testing/myarrow.py", line 23, in <module>
someday = MyArrow.get("2016-11-19")
AttributeError: type object 'MyArrow' has no attribute 'get'


So the first part worked, but
get()
failed. I had a look at the sources and
get
is in
ArrowFactory
. If I extend
ArrowFactory
instead of
Arrow
I will be able to use
get
but not
now()
anymore.

This is the point I am at loss: the "basic usage" above shows that I can call
arrow.whatever_is_available
no matter if it is defined in the class
Arrow
or
ArrowFactory
.

How does this work?

How can I add my
when
method to keep the rest of
arrow
as it is (and all its methods)?

Answer
  • Extensible for your own Arrow-derived types

is one of the highlighted features in Arrow's documentation, which actually demonstrates exactly how to create and use a custom Arrow subclass:

Factories

Use factories to harness Arrow’s module API for a custom Arrow-derived type. First, derive your type:

>>> class CustomArrow(arrow.Arrow):
...
...     def days_till_xmas(self):
...
...         xmas = arrow.Arrow(self.year, 12, 25)
...
...         if self > xmas:
...             xmas = xmas.replace(years=1)
...
...         return (xmas - self).days

Then get and use a factory for it:

>>> factory = arrow.Factory(CustomArrow)
>>> custom = factory.utcnow()
>>> custom
>>> <CustomArrow [2013-05-27T23:35:35.533160+00:00]>

>>> custom.days_till_xmas()
>>> 211

You can then call the .get, .now and .utcnow methods on the factory and get your custom subclass, with its .when method.

This is specific to dealing with Arrow and its module-level API; with simpler modules, you could just subclass their classes directly.