1

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)?

WoJ
  • 27,165
  • 48
  • 180
  • 345
  • `arrow.get` isn't a method on `Arrow`, it's [a function in `api`](https://github.com/crsmithdev/arrow/blob/master/arrow/api.py#L17) that exposes the factory's API. You can create a new factory for your own class by calling `ArrowFactory(MyArrow)`, on which you can then call `.get`. This specific problem is related to `arrow`, which is relatively complex internally; in general just subclassing the module's classes is sufficient. – jonrsharpe Nov 19 '16 at 14:33

1 Answers1

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

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • Thank you. Damn, how is that that I never see these things in the documentation - the example given is almost identical to what I wanted to achieve (and yes, I was quite scared when looking at the sources) – WoJ Nov 19 '16 at 15:06