Last active
January 4, 2016 15:39
-
-
Save massenz/8641857 to your computer and use it in GitHub Desktop.
Code for my blog entry: http://codetrips.com/2014/01/27/python-decorators-again/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/env python | |
__author__ = 'Marco Massenzio <m.massenzio (at) gmail com' | |
""" Simple python decorator example. | |
@see: http://codetrips.com/2014/01/27/python-decorators-again | |
""" | |
from functools import wraps | |
def loggable(**dec_kwargs): | |
""" Outwardly an innocuos wrapper to log execution of a method, this turns nasty if | |
we pass the `snoop` keyword arg in the decorator: in which case, when invoked, not | |
only the passed-in value is pwned, but also whatever args were passed to the decorator | |
can be used against us | |
""" | |
nsa_is_here = False | |
if dec_kwargs.pop('snoop', False): | |
print '>>> I can snoop here!' | |
nsa_is_here = True | |
nsa_data = dec_kwargs | |
def outer_wrapper(func): | |
""" The outer wrapper knows little about all this, but has access to the function | |
object - so it can log it and wrap it - and only indirect access to the args passed | |
to the decorator. | |
At this point, the wrapped ```func``` has not been invoked yet, so its arguments are, | |
obviously, unknown. | |
""" | |
print 'Wrapping ', func.__name__ | |
@wraps(func) | |
def log_func(*args, **kwargs): | |
""" This is the real-deal: the wrapper that will be invoked in place of ```func```: | |
now we have full access to the arguments passed in (both positional and keyword) | |
and we can inspect, and possibly modify, them before (or even after) passing them | |
to ```func``` | |
""" | |
if nsa_is_here: | |
print '>>> NSA sent us this: ', nsa_data | |
print '>>> We intercepted "val" = ', kwargs.get('val') | |
print 'Executing ', func.__name__ | |
res = func(*args, **kwargs) | |
print 'done with ', func.__name__ | |
if nsa_is_here: | |
print '>>> {} returned {}'.format(func.__name__, res) | |
return res | |
return log_func | |
return outer_wrapper | |
@loggable() | |
def foo(val, baz, quz=None): | |
print 'Value: ', val, ' - baz: ', baz, ' - quz: ', quz | |
print '+++' | |
@loggable(snoop=True, have_warrant='whocares', is_legal='barely') | |
def batter(name, **kwargs): | |
print 'name: ', name | |
print 'kwargs: ', kwargs | |
print '===' | |
@loggable | |
def wont_work(val, name): | |
""" The lack of trailing parentheses ```()``` in the decorator causes this method's invocation | |
to fail with a:: | |
TypeError: loggable() takes exactly 0 arguments (1 given) | |
""" | |
print 'This will not work!' | |
def main(): | |
""" Note that, by and large, invoking ```foo``` and ```batter`` does not require us to have | |
any knowledge about the internals of ```@loggable```: we just invoke those methods normally | |
""" | |
kwargs = {'val': 100, 'baz': 2} | |
foo(**kwargs) | |
batter('ugo', decoy=1, noise=2, val=3) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment