Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python monkey patching

I need to monkeypatch requests' Response class (version 1.0.4, current as of this question), to add additional methods.

I have this code:

import requests

class Response(requests.models.Response):
    def hmm(self):
        return 'ok'

requests.models.Response = Response

r = requests.get('http://bbc.co.uk')

print r

It fails when the original Response calls super() - https://github.com/kennethreitz/requests/blob/master/requests/models.py#L391

I think this is because it gets confused, as I have replaced the class, I feel like I'm doing something silly, any ideas? Thanks in advance.

like image 518
user964375 Avatar asked Jan 04 '13 14:01

user964375


3 Answers

I don’t think you can monkey-patch that type like that. When importing requests, all these modules are initialized. And as the whole library uses from xy import Request over and over, it will have an exact reference to the actual type. And only after that, you replace the Response type within the models module, so only subsequent imports are affected.

Unless you go through all the modules and manually replace their Response reference with your new type, they will still use the original one, making your patch useless.

Instead, you should keep the original type but expand it directly, as Martijn suggested.

like image 90
poke Avatar answered Oct 19 '22 10:10

poke


You'd be better off just adding your function directly to the class:

def hmm(self):
    return 'ok'
requests.models.Response.hmm = hmm

This works just fine:

>>> import requests
>>> def hmm(self):
...     return 'ok'
... 
>>> requests.models.Response.hmm = hmm
>>> r = requests.get('http://bbc.co.uk')
>>> print r
<Response [200]>
>>> r.hmm()
'ok'
>>> requests.__version__
'1.0.4'
like image 31
Martijn Pieters Avatar answered Oct 19 '22 12:10

Martijn Pieters


Just a quick fix using setattr, but it's a bit ugly (but semantically equivalent to @Martijn answer):

def hmm(self):
    return 'OK - %s' % self.status_code

setattr(requests.models.Response, 'hmm', hmm)

r = requests.get('http://bbc.co.uk')
print r.hmm()
# prints 
# OK - 200
like image 32
miku Avatar answered Oct 19 '22 11:10

miku