pythonclasspython-3.xpython-modulearrow-python

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


I am trying to extend arrow 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)?


Solution

    • 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.