1

I currently have the following code which uses a python library:

f = Foo(original_method, parameters)

I would like to augment original_method, and have a decorator add a few lines of code. Let's call the new decorated method decorated_method. Finally I would like to have something like this:

f = Foo(decorated_method(original_method), parameters)

My questions are: is this possible? how would the decorator look like? I must say that I can't extend original_method, since it is part of an external library.

Edit: original_method is not executed, is only passed to Foo as a parameter. decorated_method function should do some logging and gather some statistics of the number of calls.

Later edit: the code in examples below works fine. I had a few additional problems because original_method had a few attributes, so this is the final code:

def decorated_method(method):

    def _reporter(*args, **kwargs):
        addmetric('apicall', method.__name__)
        return method(*args, **kwargs)

    _reporter.original_method_attribute = method.original_method_attribute
    return _reporter
Alex
  • 681
  • 2
  • 9
  • 18
  • 1
    Both of the answers posted so far answer your question, but your edit says you haven't understood them. Try what was suggested and see what happens. The decorator replaces your original_method with a new function that wraps the original. When the new function is called it can gather statistics and then calls the original. `original_method` is not called by the decorator, only when the code inside `Foo` calls it. – Duncan Jul 11 '12 at 11:48

2 Answers2

2

You don't mention what you want decorated_method to do, but this is certainly possible:

def decorated_method(f):
    def _wrapped(*args, **kwargs):
        print "About to call f!"
        ret = f(*args, **kwargs)
        print "Just got finished with f, ret = %r" % (ret,)
        return ret
    return _wrapped

This is just standard decorator structure: A decorator is a function which accepts a function and returns a function.

Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
  • I don't really understand how this works. Shouldn't Foo receive a parameter of the same type as original_method? – Alex Jul 11 '12 at 11:08
  • @Alex: sure, and it does. It gets a callable function. When a function is declared accepting `(*args, **kwargs)`, it can accept any arguments at all, and then pass them along to another function the same way. This is how `_wrapped` can be called arbitrarily and than call `f` in the same way. – Ned Batchelder Jul 11 '12 at 11:11
  • The thing is that decorated_method should not call the original method, it should just augment it with some logging and statistics. It should just return original_method as it is, because it is just a parameter for Foo. Is this possible? – Alex Jul 11 '12 at 11:23
  • Have you tried this code? I thought you wanted new statements that run every time `original_method` is invoked, right? In that case, you need to return a new function to `Foo`. – Ned Batchelder Jul 11 '12 at 11:26
  • Thanks for the answers! It doesn't work now, because original_method has a few attributes which are not set in decorator, but at least I understood, so thanks again. Any idea on how can I set original_method's attributes to the decorator? – Alex Jul 11 '12 at 12:46
0

Absolutely:

def decorated_method(fn):
    def inner_method(*args, **kwargs):
        print("before calling")
        result = fn(*args, **kwargs)
        print("after calling")
        return result
    return inner_method

Once you've got this working, you should look at signature-preserving decorators.

Community
  • 1
  • 1
ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • The trick is that I don't actually call original_method, I only send it as a parameter to Foo. All I want to do in the decorated_method is to gather some statistics. – Alex Jul 11 '12 at 11:15