3

I want to use python-twitter, but extend the Status class to add a few new methods and attributes. What's the pythonic way to do this?

At the moment, I have functions which add attributes and new functionality to a Status, e.g.

process_status(status):
    status.datetime = ...
    status.phrase = ...

prettyprint_status(status):
    # do something...

Naturally, I'd just like to add the extra methods to the Status constructor. I found a stackoverflow question discussing this which proposed making a new module, ext-twitter, which contains new implementations of each class, like follows:

# ext_twitter.py
import twitter

class Api(twitter.Api):
    pass

class Status(twitter.Status):
    def __init__(self, *args):
        twitter.Status.__init__(self, *args)
        self.args = args
        self.time = parseTime(self.created_at)
        self.phrase = ...

    def prettyprint(self):
        # something

However, this doesn't work since the Status classes are generated by the Twitter API object, and here ext-twitter.Api() calls python-twitter.Api() which has no reference to my extended Status class.

Any way to add my functionality to the python-twitter module without forking it and making my own version?

Community
  • 1
  • 1
dandelion
  • 1,742
  • 1
  • 15
  • 18

2 Answers2

2

Try this:

# patch_twitter.py
import twitter

TwitterStatus = twitter.Status

class Status(TwitterStatus):
     def __init__(self, *args, **kwargs):
        TwitterStatus.__init__(self, *args, **kwargs)
        self.args = args
        self.time = parseTime(self.created_at)
        self.phrase = ...

    def prettyprint(self):
        # something

twitter.Status = Status

# use api

This should work, at least for some values of "work". If there's anything else that captures a reference to the class object originally stored in twitter.Status, then those references won't be updated. That doesn't happen too often, but it could be a source of subtle bugs.

You'll need to import this immediately after importing twitter.py and before doing anything else.

Marcin
  • 48,559
  • 18
  • 128
  • 201
  • Thanks, this worked once I changed the constructor to take **kwargs as well as *args, and the Status(TwitterStatus) constructor needs to call TwitterStatus rather than twitter.Status to avoid a circular self-reference. edited your answer to reflect this – dandelion May 12 '13 at 19:54
  • Ah, good catch. Note that the call to the superclass constructor is better performed using `super`. Google "Python super considered super" for a good guide. If you're using Py3k, it's even better. – Marcin May 12 '13 at 20:16
1

Instead of using inheritance, you can use composition to add functionality to the Status object:

# ext_twitter.py
import twitter

class Api(twitter.Api):
    def method_that_returns_status(self, *args, **kwargs):
        status = super(Api, self).methot_that_returns_status(*args, **kwargs)

        # wrap the original status with your custom status
        return Status(status)


class Status(object):        
    def __init__(self, status):
        self._internal = status

    def __getattr__(self, attr):
        return getattr(self._internal, attr)

    def __setattr__(self, attr, value):
        if attr == '_internal':
            super(Status, self).__setattr__(attr, value)
        else:
            setattr(self._internal, attr, value)

    def prettyprint(self):
        # something
asermax
  • 3,053
  • 2
  • 23
  • 28
  • I tried to implement [a simple example of this](https://gist.github.com/danmane/5564726). When I call the SpottedDog composition constructor, it results in an infinite recursion – dandelion May 12 '13 at 20:06
  • Ow sorry, I wrote that from the top of my head and forgot to add the exception for the `_internal` attribute on the `__setattr__` method. The only problem with this approach is that your decorator class can't have attributes of his own, unless you determine a 'white list' for attributes that your decorator class will have and modify the `__setattr__` method to respect it. – asermax May 13 '13 at 00:01