2

The Twython module uses requests module internally.

I want to wrap/decorate request's requests.post(*k, **kw) method so everything Twython makes a request.post(...) call it will be transparently decorated/wrapped without interfering with the Twython module.

If I edited the requests codebase that'd be easy enough but I'm curious how to solve the general problem of adding a decorator to an already defined function/method.

import requests
def magic_wrapper_doodad(...)
...
...
requests.post = magic_wrapper_doodad(my_function, requests.post) # plz?

import Twython

# thanks to the above magic, requests.post is wrapped just as if it was defined like:
@decorator
def trace(f, *args, **kw):
    print("calling %s with args %s, %s" % (f.__name__, args, kw))
    return f(*args, **kw)

...
... #inside requests.py now:
@trace
def post(self, *args, **kw):
...

how do I write magic_wrapper_doodad() - or some alternative code - so I can decorate the code like this?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
sente
  • 2,327
  • 2
  • 18
  • 24

3 Answers3

5

Decorator @ notation is just syntactic sugar for calling the decorator callable with the decorated function and replacing the decorated function with the result.

In other words, the following two examples are the exact same thing:

@yourdecorator
def a_function():
    pass

def b_function():
    pass
b_function = yourdecorator(b_function)

and the same applies to methods in class definitions.

Thus, you can simply decorate a class method by replacing the class method with the result of the decorator callable:

requests.post = my_function(requests.post)

This is the same as if you had written:

class requests(object):
    @my_function
    def post(self, *args):
        pass

Note that in your case requests is actually a module, not a class, but the same principles apply.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • @jterrace: `s` characters duly inserted. – Martijn Pieters Aug 03 '12 at 21:57
  • requests is a module though, not a class. +1 – jterrace Aug 03 '12 at 21:57
  • @jterrace: yeah, the question wording of the OP indicated *he/she* thought it was a class; I was in the process of adding a module note. – Martijn Pieters Aug 03 '12 at 21:59
  • yeah, this was pretty certain of this but yet... the function didn't appear wrapped. My actual code isn't as straight forward as the twython/requests example but I thought it was functionally equivalent, apparently I've got something fishy going on somewhere which is screwing with logs/output I presume. – sente Aug 03 '12 at 22:05
  • 1
    If you do your wrapping after you already obtained a reference to the *unwrapped* function, the reference is not going to be automagically wrapped for you. Try importing requests *before* you import Twyton, for example. – Martijn Pieters Aug 03 '12 at 22:08
2

Martijn Pieters' answer is the way to go if you need to decorate the requests function. I just thought I'd mention that requests does actually have a mechanism for hooks.

If you can do what you need to do with a hook instead, you should. It's cleaner than monkey patching.

Community
  • 1
  • 1
jterrace
  • 64,866
  • 22
  • 157
  • 202
  • I know of the hooks, there are a few different reasons I want to decorate the `request` calls, one of which was to automatically add hooks to the request. How would this ideally be done without decorating the `request.get`/`request.post` methods or modifying `Twython`'s code? – sente Aug 04 '12 at 02:25
1

What you are looking for might be done with aspect oriented programming.

Check this post from Stackoverflow and please let me know if it helps:

Do you use AOP (Aspect Oriented Programming) in production software?

Community
  • 1
  • 1
Edmon
  • 4,752
  • 4
  • 32
  • 42