Movatterモバイル変換


[0]ホーム

URL:


This wiki is in the process of being archived due to lack of usage and the resources necessary to serve it — predominately to bots, crawlers, and LLM companies. Edits are discouraged.
Pages are preserved as they were at the time of archival. For current information, please visitpython.org.
If a change to this archive is absolutely needed, requests can be made via theinfrastructure@python.org mailing list.

This page is meant to be a central repository of decorator code pieces, whether useful or not <wink>. It is NOT a page to discuss decorator syntax!

Feel free to add your suggestions. Please make sure example code conforms withPEP 8.

Contents

  1. Creating Well-Behaved Decorators / "Decorator decorator"
  2. Property Definition
  3. Memoize
  4. Alternate memoize as nested functions
  5. Alternate memoize as dict subclass
  6. Alternate memoize that stores cache between executions
  7. Cached Properties
  8. Retry
  9. Pseudo-currying
  10. Creating decorator with optional arguments
  11. Controllable DIY debug
  12. Easy adding methods to a class instance
  13. Counting function calls
  14. Alternate Counting function calls
  15. Generating Deprecation Warnings
  16. Smart deprecation warnings (with valid filenames, line numbers, etc.)
  17. Ignoring Deprecation Warnings
  18. Enable/Disable Decorators
  19. Easy Dump of Function Arguments
  20. Pre-/Post-Conditions
  21. Profiling/Coverage Analysis
  22. Line Tracing Individual Functions
  23. Synchronization
  24. Type Enforcement (accepts/returns)
  25. CGI method wrapper
  26. State Machine Implementaion
  27. C++/Java-keyword-like function decorators
  28. Different Decorator Forms
  29. Unimplemented function replacement
  30. Redirects stdout printing to python standard logging.
  31. Access control
  32. Events rising and handling
  33. Singleton
  34. Asynchronous Call
  35. Class method decorator using instance
  36. Another Retrying Decorator
  37. Logging decorator with specified logger (or default)
  38. Lazy Thunkify
  39. Aggregative decorators for generator functions
  40. Function Timeout
  41. Collect Data Difference Caused by Decorated Function

Creating Well-Behaved Decorators / "Decorator decorator"

Note: This is only one recipe. Others include inheritance from a standard decorator (link?), thefunctools @wraps decorator, and a factory function such asMichele Simionato's decorator module which even preserves signature information.

   1defsimple_decorator(decorator):   2'''This decorator can be used to turn simple functions   3    into well-behaved decorators, so long as the decorators   4    are fairly simple. If a decorator expects a function and   5    returns a function (no descriptors), and if it doesn't   6    modify function attributes or docstring, then it is   7    eligible to use this. Simply apply @simple_decorator to   8    your decorator and it will automatically preserve the   9    docstring and function attributes of functions to which  10    it is applied.'''  11defnew_decorator(f):  12g =decorator(f)  13g.__name__ =f.__name__  14g.__doc__ =f.__doc__  15g.__dict__.update(f.__dict__)  16returng  17# Now a few lines needed to make simple_decorator itself  18# be a well-behaved decorator.  19new_decorator.__name__ =decorator.__name__  20new_decorator.__doc__ =decorator.__doc__  21new_decorator.__dict__.update(decorator.__dict__)  22returnnew_decorator  23  24#  25# Sample Use:  26#  27@simple_decorator  28defmy_simple_logging_decorator(func):  29defyou_will_never_see_this_name(*args, **kwargs):  30print'calling{}'.format(func.__name__)  31returnfunc(*args, **kwargs)  32returnyou_will_never_see_this_name  33  34@my_simple_logging_decorator  35defdouble(x):  36'Doubles a number.'  37return2 *x  38  39assertdouble.__name__ =='double'  40assertdouble.__doc__ =='Doubles a number.'  41printdouble(155)

Property Definition

These decorators provide a readable way to define properties:

   1importsys   2   3defpropget(func):   4locals =sys._getframe(1).f_locals   5name =func.__name__   6prop =locals.get(name)   7ifnotisinstance(prop,property):   8prop =property(func,doc=func.__doc__)   9else:  10doc =prop.__doc__orfunc.__doc__  11prop =property(func,prop.fset,prop.fdel,doc)  12returnprop  13  14defpropset(func):  15locals =sys._getframe(1).f_locals  16name =func.__name__  17prop =locals.get(name)  18ifnotisinstance(prop,property):  19prop =property(None,func,doc=func.__doc__)  20else:  21doc =prop.__doc__orfunc.__doc__  22prop =property(prop.fget,func,prop.fdel,doc)  23returnprop  24  25defpropdel(func):  26locals =sys._getframe(1).f_locals  27name =func.__name__  28prop =locals.get(name)  29ifnotisinstance(prop,property):  30prop =property(None,None,func,doc=func.__doc__)  31else:  32prop =property(prop.fget,prop.fset,func,prop.__doc__)  33returnprop  34  35# These can be used like this:  36  37classExample(object):  38  39@propget  40defmyattr(self):  41returnself._half *2  42  43@propset  44defmyattr(self,value):  45self._half =value /2  46  47@propdel  48defmyattr(self):  49delself._half

Here's a way that doesn't require any new decorators:

   1classExample(object):   2@apply# doesn't exist in Python 3   3defmyattr():   4doc ='''This is the doc string.'''   5   6deffget(self):   7returnself._half *2   8   9deffset(self,value):  10self._half =value /2  11  12deffdel(self):  13delself._half  14  15returnproperty(**locals())  16#myattr = myattr()  # works in Python 2 and 3

Yet another property decorator:

   1try:   2# Python 2   3import__builtin__asbuiltins   4exceptImportError:   5# Python 3   6importbuiltins   7   8defproperty(function):   9keys ='fget','fset','fdel'  10func_locals = {'doc':function.__doc__}  11defprobe_func(frame,event,arg):  12ifevent =='return':  13locals =frame.f_locals  14func_locals.update(dict((k,locals.get(k))forkinkeys))  15sys.settrace(None)  16returnprobe_func  17sys.settrace(probe_func)  18function()  19returnbuiltins.property(**func_locals)  20  21#====== Example =======================================================  22  23frommathimportradians,degrees,pi  24  25classAngle(object):  26def__init__(self,rad):  27self._rad =rad  28  29@property  30defrad():  31'''The angle in radians'''  32deffget(self):  33returnself._rad  34deffset(self,angle):  35ifisinstance(angle,Angle):  36angle =angle.rad  37self._rad =float(angle)  38  39@property  40defdeg():  41'''The angle in degrees'''  42deffget(self):  43returndegrees(self._rad)  44deffset(self,angle):  45ifisinstance(angle,Angle):  46angle =angle.deg  47self._rad =radians(angle)

Memoize

Here's a memoizing class.

   1importcollections   2importfunctools   3   4classmemoized(object):   5'''Decorator. Caches a function's return value each time it is called.   6   If called later with the same arguments, the cached value is returned   7   (not reevaluated).   8   '''   9def__init__(self,func):  10self.func =func  11self.cache = {}  12def__call__(self, *args):  13ifnotisinstance(args,collections.Hashable):  14# uncacheable. a list, for instance.  15# better to not cache than blow up.  16returnself.func(*args)  17ifargsinself.cache:  18returnself.cache[args]  19else:  20value =self.func(*args)  21self.cache[args] =value  22returnvalue  23def__repr__(self):  24'''Return the function's docstring.'''  25returnself.func.__doc__  26def__get__(self,obj,objtype):  27'''Support instance methods.'''  28returnfunctools.partial(self.__call__,obj)  29  30@memoized  31deffibonacci(n):  32"Return the nth fibonacci number."  33ifnin (0,1):  34returnn  35returnfibonacci(n-1) +fibonacci(n-2)  36  37printfibonacci(12)

Alternate memoize as nested functions

Here's a memoizing function that works on functions, methods, or classes, and exposes the cache publicly.

   1# note that this decorator ignores **kwargs   2defmemoize(obj):   3cache =obj.cache = {}   4   5@functools.wraps(obj)   6defmemoizer(*args, **kwargs):   7ifargsnotincache:   8cache[args] =obj(*args, **kwargs)   9returncache[args]  10returnmemoizer

Here's a modified version that also respects kwargs.

   1defmemoize(obj):   2cache =obj.cache = {}   3   4@functools.wraps(obj)   5defmemoizer(*args, **kwargs):   6key =str(args) +str(kwargs)   7ifkeynotincache:   8cache[key] =obj(*args, **kwargs)   9returncache[key]  10returnmemoizer

Alternate memoize as dict subclass

This is an idea that interests me, but it only seems to work on functions:

   1classmemoize(dict):   2def__init__(self,func):   3self.func =func   4   5def__call__(self, *args):   6returnself[args]   7   8def__missing__(self,key):   9result =self[key] =self.func(*key)  10returnresult  11  12#  13# Sample use  14#  15  16>>>@memoize  17...deffoo(a,b):  18...returna *b  19>>>foo(2,4)  208  21>>>foo  22{(2,4):8}  23>>>foo('hi',3)  24'hihihi'  25>>>foo  26{(2,4):8, ('hi',3):'hihihi'}

Alternate memoize that stores cache between executions

Additional information and documentation for this decorator is available onGithub.

   1importpickle   2importcollections   3importfunctools   4importinspect   5importos.path   6importre   7importunicodedata   8   9classMemorize(object):  10'''  11    A function decorated with @Memorize caches its return  12    value every time it is called. If the function is called  13    later with the same arguments, the cached value is  14    returned (the function is not reevaluated). The cache is  15    stored as a .cache file in the current directory for reuse  16    in future executions. If the Python file containing the  17    decorated function has been updated since the last run,  18    the current cache is deleted and a new cache is created  19    (in case the behavior of the function has changed).  20    '''  21def__init__(self,func):  22self.func =func  23self.set_parent_file()# Sets self.parent_filepath and self.parent_filename  24self.__name__ =self.func.__name__  25self.set_cache_filename()  26ifself.cache_exists():  27self.read_cache()# Sets self.timestamp and self.cache  28ifnotself.is_safe_cache():  29self.cache = {}  30else:  31self.cache = {}  32  33def__call__(self, *args):  34ifnotisinstance(args,collections.Hashable):  35returnself.func(*args)  36ifargsinself.cache:  37returnself.cache[args]  38else:  39value =self.func(*args)  40self.cache[args] =value  41self.save_cache()  42returnvalue  43  44defset_parent_file(self):  45"""  46        Sets self.parent_file to the absolute path of the  47        file containing the memoized function.  48        """  49rel_parent_file =inspect.stack()[-1].filename  50self.parent_filepath =os.path.abspath(rel_parent_file)  51self.parent_filename =_filename_from_path(rel_parent_file)  52  53defset_cache_filename(self):  54"""  55        Sets self.cache_filename to an os-compliant  56        version of "file_function.cache"  57        """  58filename =_slugify(self.parent_filename.replace('.py',''))  59funcname =_slugify(self.__name__)  60self.cache_filename =filename+'_'+funcname+'.cache'  61  62defget_last_update(self):  63"""  64        Returns the time that the parent file was last  65        updated.  66        """  67last_update =os.path.getmtime(self.parent_filepath)  68returnlast_update  69  70defis_safe_cache(self):  71"""  72        Returns True if the file containing the memoized  73        function has not been updated since the cache was  74        last saved.  75        """  76ifself.get_last_update() >self.timestamp:  77returnFalse  78returnTrue  79  80defread_cache(self):  81"""  82        Read a pickled dictionary into self.timestamp and  83        self.cache. See self.save_cache.  84        """  85withopen(self.cache_filename,'rb')asf:  86data =pickle.loads(f.read())  87self.timestamp =data['timestamp']  88self.cache =data['cache']  89  90defsave_cache(self):  91"""  92        Pickle the file's timestamp and the function's cache  93        in a dictionary object.  94        """  95withopen(self.cache_filename,'wb+')asf:  96out =dict()  97out['timestamp'] =self.get_last_update()  98out['cache'] =self.cache  99f.write(pickle.dumps(out)) 100 101defcache_exists(self): 102''' 103        Returns True if a matching cache exists in the current directory. 104        ''' 105ifos.path.isfile(self.cache_filename): 106returnTrue 107returnFalse 108 109def__repr__(self): 110""" Return the function's docstring. """ 111returnself.func.__doc__ 112 113def__get__(self,obj,objtype): 114""" Support instance methods. """ 115returnfunctools.partial(self.__call__,obj) 116 117def_slugify(value): 118""" 119    Normalizes string, converts to lowercase, removes 120    non-alpha characters, and converts spaces to 121    hyphens. From 122    http://stackoverflow.com/questions/295135/turn-a-string-into-a-valid-filename-in-python 123    """ 124value =unicodedata.normalize('NFKD',value).encode('ascii','ignore') 125value =re.sub(r'[^\w\s-]','',value.decode('utf-8','ignore')) 126value =value.strip().lower() 127value =re.sub(r'[-\s]+','-',value) 128returnvalue 129 130def_filename_from_path(filepath): 131returnfilepath.split('/')[-1]

Cached Properties

   1#   2# © 2011 Christopher Arndt, MIT License   3#   4   5importtime   6   7classcached_property(object):   8'''Decorator for read-only properties evaluated only once within TTL period.   9  10    It can be used to create a cached property like this::  11  12        import random  13  14        # the class containing the property must be a new-style class  15        class MyClass(object):  16            # create property whose value is cached for ten minutes  17            @cached_property(ttl=600)  18            def randint(self):  19                # will only be evaluated every 10 min. at maximum.  20                return random.randint(0, 100)  21  22    The value is cached  in the '_cache' attribute of the object instance that  23    has the property getter method wrapped by this decorator. The '_cache'  24    attribute value is a dictionary which has a key for every property of the  25    object which is wrapped by this decorator. Each entry in the cache is  26    created only when the property is accessed for the first time and is a  27    two-element tuple with the last computed property value and the last time  28    it was updated in seconds since the epoch.  29  30    The default time-to-live (TTL) is 300 seconds (5 minutes). Set the TTL to  31    zero for the cached value to never expire.  32  33    To expire a cached property value manually just do::  34  35        del instance._cache[<property name>]  36  37    '''  38def__init__(self,ttl=300):  39self.ttl =ttl  40  41def__call__(self,fget,doc=None):  42self.fget =fget  43self.__doc__ =docorfget.__doc__  44self.__name__ =fget.__name__  45self.__module__ =fget.__module__  46returnself  47  48def__get__(self,inst,owner):  49now =time.time()  50try:  51value,last_update =inst._cache[self.__name__]  52ifself.ttl >0andnow -last_update >self.ttl:  53raiseAttributeError  54except (KeyError,AttributeError):  55value =self.fget(inst)  56try:  57cache =inst._cache  58exceptAttributeError:  59cache =inst._cache = {}  60cache[self.__name__] = (value,now)  61returnvalue

Retry

Call a function which returns True/False to indicate success or failure. On failure, wait, and try the function again. On repeated failures, wait longer between each successive attempt. If the decorator runs out of attempts, then it gives up and returns False, but you could just as easily raise some exception.

   1importtime   2importmath   3   4# Retry decorator with exponential backoff   5defretry(tries,delay=3,backoff=2):   6'''Retries a function or method until it returns True.   7   8  delay sets the initial delay in seconds, and backoff sets the factor by which   9  the delay should lengthen after each failure. backoff must be greater than 1,  10  or else it isn't really a backoff. tries must be at least 0, and delay  11  greater than 0.'''  12  13ifbackoff <=1:  14raiseValueError("backoff must be greater than 1")  15  16tries =math.floor(tries)  17iftries <0:  18raiseValueError("tries must be 0 or greater")  19  20ifdelay <=0:  21raiseValueError("delay must be greater than 0")  22  23defdeco_retry(f):  24deff_retry(*args, **kwargs):  25mtries,mdelay =tries,delay# make mutable  26  27rv =f(*args, **kwargs)# first attempt  28whilemtries >0:  29ifrvisTrue:# Done on success  30returnTrue  31  32mtries -=1# consume an attempt  33time.sleep(mdelay)# wait...  34mdelay *=backoff# make future wait longer  35  36rv =f(*args, **kwargs)# Try again  37  38returnFalse# Ran out of tries :-(  39  40returnf_retry# true decorator -> decorated function  41returndeco_retry# @retry(arg[, ...]) -> true decorator

Pseudo-currying

(FYI you can use functools.partial() to emulate currying (which works even for keyword arguments))

   1classcurried(object):   2'''   3  Decorator that returns a function that keeps returning functions   4  until all arguments are supplied; then the original function is   5  evaluated.   6  '''   7   8def__init__(self,func, *a):   9self.func =func  10self.args =a  11  12def__call__(self, *a):  13args =self.args +a  14iflen(args) <self.func.func_code.co_argcount:  15returncurried(self.func, *args)  16else:  17returnself.func(*args)  18  19  20@curried  21defadd(a,b):  22returna +b  23  24add1 =add(1)  25  26printadd1(2)

Creating decorator with optional arguments

   1importfunctools,inspect   2   3defdecorator(func):   4''' Allow to use decorator either with arguments or not. '''   5   6defisFuncArg(*args, **kw):   7returnlen(args) ==1andlen(kw) ==0and (   8inspect.isfunction(args[0])orisinstance(args[0],type))   9  10ifisinstance(func,type):  11defclass_wrapper(*args, **kw):  12ifisFuncArg(*args, **kw):  13returnfunc()(*args, **kw)# create class before usage  14returnfunc(*args, **kw)  15class_wrapper.__name__ =func.__name__  16class_wrapper.__module__ =func.__module__  17returnclass_wrapper  18  19@functools.wraps(func)  20deffunc_wrapper(*args, **kw):  21ifisFuncArg(*args, **kw):  22returnfunc(*args, **kw)  23  24deffunctor(userFunc):  25returnfunc(userFunc, *args, **kw)  26  27returnfunctor  28  29returnfunc_wrapper

Example:

   1@decorator   2defapply(func, *args, **kw):   3returnfunc(*args, **kw)   4   5@decorator   6classapply:   7def__init__(self, *args, **kw):   8self.args =args   9self.kw   =kw  10  11def__call__(self,func):  12returnfunc(*self.args, **self.kw)  13  14#  15# Usage in both cases:  16#  17@apply  18deftest():  19return'test'  20  21asserttest =='test'  22  23@apply(2,3)  24deftest(a,b):  25returna +b  26  27asserttestis5

Note: There is only one drawback: wrapper checks its arguments for single function or class. To avoid wrong behavior you can use keyword arguments instead of positional, e.g.:

   1@decorator   2defmy_property(getter, *,setter=None,deleter=None,doc=None):   3returnproperty(getter,setter,deleter,doc)

Controllable DIY debug

(Other hooks could be similarly added. Docstrings and exceptions are left out for simplicity of demonstration.)

   1importsys   2   3WHAT_TO_DEBUG =set(['io','core'])# change to what you need   4   5classdebug:   6'''Decorator which helps to control what aspects of a program to debug   7    on per-function basis. Aspects are provided as list of arguments.   8    It DOESN'T slowdown functions which aren't supposed to be debugged.   9    '''  10def__init__(self,aspects=None):  11self.aspects =set(aspects)  12  13def__call__(self,f):  14ifself.aspects &WHAT_TO_DEBUG:  15defnewf(*args, **kwds):  16print >>sys.stderr,f.func_name,args,kwds  17f_result =f(*args, **kwds)  18print >>sys.stderr,f.func_name,"returned",f_result  19returnf_result  20newf.__doc__ =f.__doc__  21returnnewf  22else:  23returnf  24  25@debug(['io'])  26defprn(x):  27printx  28  29@debug(['core'])  30defmult(x,y):  31returnx *y  32  33prn(mult(2,2))

Easy adding methods to a class instance

Credits to John Roth.

   1classFoo:   2def__init__(self):   3self.x =42   4   5foo =Foo()   6   7defaddto(instance):   8defdecorator(f):   9importtypes  10f =types.MethodType(f,instance,instance.__class__)  11setattr(instance,f.func_name,f)  12returnf  13returndecorator  14  15@addto(foo)  16defprint_x(self):  17printself.x  18  19# foo.print_x() would print "42"

Counting function calls

   1classcountcalls(object):   2"Decorator that keeps track of the number of times a function is called."   3   4__instances = {}   5   6def__init__(self,f):   7self.__f =f   8self.__numcalls =0   9countcalls.__instances[f] =self  10  11def__call__(self, *args, **kwargs):  12self.__numcalls +=1  13returnself.__f(*args, **kwargs)  14  15@staticmethod  16defcount(f):  17"Return the number of times the function f was called."  18returncountcalls.__instances[f].__numcalls  19  20@staticmethod  21defcounts():  22"Return a dict of{function: # of calls} for all registered functions."  23returndict([(f,countcalls.count(f))forfincountcalls.__instances])

Alternate Counting function calls

   1classcountcalls(object):   2"Decorator that keeps track of the number of times a function is called."   3   4__instances = {}   5   6def__init__(self,f):   7self.__f =f   8self.__numcalls =0   9countcalls.__instances[f] =self  10  11def__call__(self, *args, **kwargs):  12self.__numcalls +=1  13returnself.__f(*args, **kwargs)  14  15defcount(self):  16"Return the number of times the function f was called."  17returncountcalls.__instances[self.__f].__numcalls  18  19@staticmethod  20defcounts():  21"Return a dict of{function: # of calls} for all registered functions."  22returndict([(f.__name__,countcalls.__instances[f].__numcalls)forfincountcalls.__instances])  23  24#example  25  26@countcalls  27deff():  28print'f called'  29  30@countcalls  31defg():  32print'g called'  33  34f()  35f()  36f()  37printf.count()# prints 3  38printcountcalls.counts()# same as f.counts() or g.counts()  39g()  40printg.count()# prints 1

Generating Deprecation Warnings

   1importwarnings   2   3defdeprecated(func):   4'''This is a decorator which can be used to mark functions   5    as deprecated. It will result in a warning being emitted   6    when the function is used.'''   7defnew_func(*args, **kwargs):   8warnings.warn("Call to deprecated function{}.".format(func.__name__),   9category=DeprecationWarning)  10returnfunc(*args, **kwargs)  11new_func.__name__ =func.__name__  12new_func.__doc__ =func.__doc__  13new_func.__dict__.update(func.__dict__)  14returnnew_func  15  16# === Examples of use ===  17  18@deprecated  19defsome_old_function(x,y):  20returnx +y  21  22classSomeClass:  23@deprecated  24defsome_old_method(self,x,y):  25returnx +y

Smart deprecation warnings (with valid filenames, line numbers, etc.)

   1importwarnings   2importfunctools   3   4   5defdeprecated(func):   6'''This is a decorator which can be used to mark functions   7    as deprecated. It will result in a warning being emitted   8    when the function is used.'''   9  10@functools.wraps(func)  11defnew_func(*args, **kwargs):  12warnings.warn_explicit(  13"Call to deprecated function{}.".format(func.__name__),  14category=DeprecationWarning,  15filename=func.func_code.co_filename,  16lineno=func.func_code.co_firstlineno +1  17        )  18returnfunc(*args, **kwargs)  19returnnew_func  20  21  22## Usage examples ##  23@deprecated  24defmy_func():  25pass  26  27@other_decorators_must_be_upper  28@deprecated  29defmy_func():  30pass

Ignoring Deprecation Warnings

   1importwarnings   2   3defignore_deprecation_warnings(func):   4'''This is a decorator which can be used to ignore deprecation warnings   5    occurring in a function.'''   6defnew_func(*args, **kwargs):   7withwarnings.catch_warnings():   8warnings.filterwarnings("ignore",category=DeprecationWarning)   9returnfunc(*args, **kwargs)  10new_func.__name__ =func.__name__  11new_func.__doc__ =func.__doc__  12new_func.__dict__.update(func.__dict__)  13returnnew_func  14  15# === Examples of use ===  16  17@ignore_deprecation_warnings  18defsome_function_raising_deprecation_warning():  19warnings.warn("This is a deprecationg warning.",  20category=DeprecationWarning)  21  22classSomeClass:  23@ignore_deprecation_warnings  24defsome_method_raising_deprecation_warning():  25warnings.warn("This is a deprecationg warning.",  26category=DeprecationWarning)

Enable/Disable Decorators

   1defunchanged(func):   2"This decorator doesn't add any behavior"   3returnfunc   4   5defdisabled(func):   6"This decorator disables the provided function, and does nothing"   7defempty_func(*args,**kargs):   8pass   9returnempty_func  10  11# define this as equivalent to unchanged, for nice symmetry with disabled  12enabled =unchanged  13  14#  15# Sample use  16#  17  18GLOBAL_ENABLE_FLAG =True  19  20state =enabledifGLOBAL_ENABLE_FLAGelsedisabled  21@state  22defspecial_function_foo():  23print"function was enabled"

Easy Dump of Function Arguments

   1defdump_args(func):   2"This decorator dumps out the arguments passed to a function before calling it"   3argnames =func.func_code.co_varnames[:func.func_code.co_argcount]   4fname =func.func_name   5   6defecho_func(*args,**kwargs):   7printfname,":",','.join(   8'%s=%r' %entry   9forentryinzip(argnames,args) +kwargs.items())  10returnfunc(*args, **kwargs)  11  12returnecho_func  13  14@dump_args  15deff1(a,b,c):  16printa +b +c  17  18f1(1,2,3)

Pre-/Post-Conditions

   1'''   2Provide pre-/postconditions as function decorators.   3   4Example usage:   5   6  >>> def in_ge20(inval):   7  ...    assert inval >= 20, 'Input value < 20'   8  ...   9  >>> def out_lt30(retval, inval):  10  ...    assert retval < 30, 'Return value >= 30'  11  ...  12  >>> @precondition(in_ge20)  13  ... @postcondition(out_lt30)  14  ... def inc(value):  15  ...   return value + 1  16  ...  17  >>> inc(5)  18  Traceback (most recent call last):  19    ...  20  AssertionError: Input value < 20  21  >>> inc(29)  22  Traceback (most recent call last):  23    ...  24  AssertionError: Return value >= 30  25  >>> inc(20)  26  21  27  28You can define as many pre-/postconditions for a function as you  29like. It is also possible to specify both types of conditions at once:  30  31  >>> @conditions(in_ge20, out_lt30)  32  ... def add1(value):  33  ...   return value + 1  34  ...  35  >>> add1(5)  36  Traceback (most recent call last):  37    ...  38  AssertionError: Input value < 20  39  40An interesting feature is the ability to prevent the creation of  41pre-/postconditions at function definition time. This makes it  42possible to use conditions for debugging and then switch them off for  43distribution.  44  45  >>> debug = False  46  >>> @precondition(in_ge20, debug)  47  ... def dec(value):  48  ...   return value - 1  49  ...  50  >>> dec(5)  51  4  52'''  53  54__all__ = ['precondition','postcondition','conditions']  55  56DEFAULT_ON =True  57  58defprecondition(precondition,use_conditions=DEFAULT_ON):  59returnconditions(precondition,None,use_conditions)  60  61defpostcondition(postcondition,use_conditions=DEFAULT_ON):  62returnconditions(None,postcondition,use_conditions)  63  64classconditions(object):  65__slots__ = ('__precondition','__postcondition')  66  67def__init__(self,pre,post,use_conditions=DEFAULT_ON):  68ifnotuse_conditions:  69pre,post =None,None  70  71self.__precondition  =pre  72self.__postcondition =post  73  74def__call__(self,function):  75# combine recursive wrappers (@precondition + @postcondition == @conditions)  76pres  =set((self.__precondition,))  77posts =set((self.__postcondition,))  78  79# unwrap function, collect distinct pre-/post conditions  80whiletype(function)isFunctionWrapper:  81pres.add(function._pre)  82posts.add(function._post)  83function =function._func  84  85# filter out None conditions and build pairs of pre- and postconditions  86conditions =map(None,filter(None,pres),filter(None,posts))  87  88# add a wrapper for each pair (note that 'conditions' may be empty)  89forpre,postinconditions:  90function =FunctionWrapper(pre,post,function)  91  92returnfunction  93  94classFunctionWrapper(object):  95def__init__(self,precondition,postcondition,function):  96self._pre  =precondition  97self._post =postcondition  98self._func =function  99 100def__call__(self, *args, **kwargs): 101precondition  =self._pre 102postcondition =self._post 103 104ifprecondition: 105precondition(*args, **kwargs) 106result =self._func(*args, **kwargs) 107ifpostcondition: 108postcondition(result, *args, **kwargs) 109returnresult 110 111def__test(): 112importdoctest 113doctest.testmod() 114 115if__name__ =="__main__": 116__test()

Profiling/Coverage Analysis

The code and examples are a bit longish, so I'll include a link instead:http://mg.pov.lt/blog/profiling.html

Line Tracing Individual Functions

I cobbled this together from the trace module. It allows you to decorate individual functions so their lines are traced. I think it works out to be a slightly smaller hammer than running the trace module and trying to pare back what it traces using exclusions.

   1importsys   2importos   3importlinecache   4   5deftrace(f):   6defglobaltrace(frame,why,arg):   7ifwhy =="call":   8returnlocaltrace   9returnNone  10  11deflocaltrace(frame,why,arg):  12ifwhy =="line":  13# record the file name and line number of every trace  14filename =frame.f_code.co_filename  15lineno =frame.f_lineno  16  17bname =os.path.basename(filename)  18print"{}({}):{}".format(bname,  19lineno,  20linecache.getline(filename,lineno)),  21returnlocaltrace  22  23def_f(*args, **kwds):  24sys.settrace(globaltrace)  25result =f(*args, **kwds)  26sys.settrace(None)  27returnresult  28  29return_f

Synchronization

Synchronize two (or more) functions on a given lock.

   1defsynchronized(lock):   2'''Synchronization decorator.'''   3   4defwrap(f):   5defnew_function(*args, **kw):   6lock.acquire()   7try:   8returnf(*args, **kw)   9finally:  10lock.release()  11returnnew_function  12returnwrap  13  14# Example usage:  15  16fromthreadingimportLock  17my_lock =Lock()  18  19@synchronized(my_lock)  20defcritical1(*args):  21# Interesting stuff goes here.  22pass  23  24@synchronized(my_lock)  25defcritical2(*args):  26# Other interesting stuff goes here.  27pass

Type Enforcement (accepts/returns)

Provides various degrees of type enforcement for function parameters and return values.

   1'''   2One of three degrees of enforcement may be specified by passing   3the 'debug' keyword argument to the decorator:   4    0 -- NONE:   No type-checking. Decorators disabled.   5 #!python   6-- MEDIUM: Print warning message to stderr. (Default)   7    2 -- STRONG: Raise TypeError with message.   8If 'debug' is not passed to the decorator, the default level is used.   9  10Example usage:  11    >>> NONE, MEDIUM, STRONG = 0, 1, 2  12    >>>  13    >>> @accepts(int, int, int)  14    ... @returns(float)  15    ... def average(x, y, z):  16    ...     return (x + y + z) / 2  17    ...  18    >>> average(5.5, 10, 15.0)  19    TypeWarning:  'average' method accepts (int, int, int), but was given  20    (float, int, float)  21    15.25  22    >>> average(5, 10, 15)  23    TypeWarning:  'average' method returns (float), but result is (int)  24    15  25  26Needed to cast params as floats in function def (or simply divide by 2.0).  27  28    >>> TYPE_CHECK = STRONG  29    >>> @accepts(int, debug=TYPE_CHECK)  30    ... @returns(int, debug=TYPE_CHECK)  31    ... def fib(n):  32    ...     if n in (0, 1): return n  33    ...     return fib(n-1) + fib(n-2)  34    ...  35    >>> fib(5.3)  36    Traceback (most recent call last):  37      ...  38    TypeError: 'fib' method accepts (int), but was given (float)  39  40'''  41importsys  42  43defaccepts(*types, **kw):  44'''Function decorator. Checks decorated function's arguments are  45    of the expected types.  46  47    Parameters:  48    types -- The expected types of the inputs to the decorated function.  49             Must specify type for each parameter.  50    kw    -- Optional specification of 'debug' level (this is the only valid  51             keyword argument, no other should be given).  52             debug = ( 0 | 1 | 2 )  53  54    '''  55ifnotkw:  56# default level: MEDIUM  57debug =1  58else:  59debug =kw['debug']  60try:  61defdecorator(f):  62defnewf(*args):  63ifdebugis0:  64returnf(*args)  65assertlen(args) ==len(types)  66argtypes =tuple(map(type,args))  67ifargtypes !=types:  68msg =info(f.__name__,types,argtypes,0)  69ifdebugis1:  70print >>sys.stderr,'TypeWarning:',msg  71elifdebugis2:  72raiseTypeError,msg  73returnf(*args)  74newf.__name__ =f.__name__  75returnnewf  76returndecorator  77exceptKeyError,key:  78raiseKeyError,key +"is not a valid keyword argument"  79exceptTypeError,msg:  80raiseTypeError,msg  81  82  83defreturns(ret_type, **kw):  84'''Function decorator. Checks decorated function's return value  85    is of the expected type.  86  87    Parameters:  88    ret_type -- The expected type of the decorated function's return value.  89                Must specify type for each parameter.  90    kw       -- Optional specification of 'debug' level (this is the only valid  91                keyword argument, no other should be given).  92                debug=(0 | 1 | 2)  93    '''  94try:  95ifnotkw:  96# default level: MEDIUM  97debug =1  98else:  99debug =kw['debug'] 100defdecorator(f): 101defnewf(*args): 102result =f(*args) 103ifdebugis0: 104returnresult 105res_type =type(result) 106ifres_type !=ret_type: 107msg =info(f.__name__, (ret_type,), (res_type,),1) 108ifdebugis1: 109print >>sys.stderr,'TypeWarning:',msg 110elifdebugis2: 111raiseTypeError,msg 112returnresult 113newf.__name__ =f.__name__ 114returnnewf 115returndecorator 116exceptKeyError,key: 117raiseKeyError,key +"is not a valid keyword argument" 118exceptTypeError,msg: 119raiseTypeError,msg 120 121definfo(fname,expected,actual,flag): 122'''Convenience function returns nicely formatted error/warning msg.''' 123format =lambdatypes:','.join([str(t).split("'")[1]fortintypes]) 124expected,actual =format(expected),format(actual) 125msg ="'{}' method".format(fname )\ 126          + ("accepts","returns")[flag] +" ({}), but".format(expected)\ 127          + ("was given","result is")[flag] +" ({})".format(actual) 128returnmsg

CGI method wrapper

Handles HTML boilerplate at top and bottom of pages returned from CGI methods. Works with the cgi module. Now your request handlers can just output the interesting HTML, and let the decorator deal with all the top and bottom clutter.

(Note: the exception handler eats all exceptions, which in CGI is no big loss, since the program runs in its separate subprocess. At least here, the exception contents will be written to the output page.)

   1classCGImethod(object):   2def__init__(self,title):   3self.title =title   4   5def__call__(self,fn):   6defwrapped_fn(*args):   7print"Content-Type: text/html\n\n"   8print"<HTML>"   9print"<HEAD><TITLE>{}</TITLE></HEAD>".format(self.title)  10print"<BODY>"  11try:  12fn(*args)  13exceptException,e:  14print  15printe  16print  17print"</BODY></HTML>"  18  19returnwrapped_fn  20  21@CGImethod("Hello with Decorator")  22defsay_hello():  23print'<h1>Hello from CGI-Land</h1>'

State Machine Implementaion

A much improved version of decorators for implementing state machines, too long to show here, is atState Machine via Decorators

This example uses Decorators to facilitate the implementation of a state machine in Python. Decorators are used to specify which methods are the event handlers for the class. In this example, actions are associated with the transitions, but it is possible with a little consideration to associate actions with states instead.

The example defines a class,MyMachine that is a state machine. Multiple instances of the class may be instantiated with each maintaining its own state. A class also may have multiple states. Here I've used gstate and tstate.

The code in the imported statedefn file gets a bit hairy, but you may not need to delve into it for your application.

   1# State Machine example Program   2   3fromstatedefnimport *   4   5classMyMachine(object):   6   7# Create Statedefn object for each state you need to keep track of.   8# the name passed to the constructor becomes a StateVar member of the current class.   9# i.e. if my_obj is a MyMachine object, my_obj.gstate maintains the current gstate  10gstate =StateTable("gstate")  11tstate =StateTable("turtle")  12  13def__init__(self,name):  14# must call init method of class's StateTable object. to initialize state variable  15self.gstate.initialize(self)  16self.tstate.initialize(self)  17self.mname =name  18self.a_count =0  19self.b_count =0  20self.c_count =0  21  22# Decorate the Event Handler virtual functions -note gstate parameter  23@event_handler(gstate)  24defevent_a(self):pass  25  26@event_handler(gstate)  27defevent_b(self):pass  28  29@event_handler(gstate)  30defevent_c(self,val):pass  31  32@event_handler(tstate)  33deftoggle(self):pass  34  35  36# define methods to handle events.  37def_event_a_hdlr1(self):  38print"State 1, event A"  39self.a_count +=1  40def_event_b_hdlr1(self):  41print"State 1, event B"  42self.b_count +=1  43def_event_c_hdlr1(self,val):  44print"State 1, event C"  45self.c_count +=3*val  46  47def_event_a_hdlr2(self):  48print"State 2, event A"  49self.a_count +=10  50# here we brute force the tstate to on, leave & enter functions called if state changes.  51# turtle is object's state variable for tstate, comes from constructor argument  52self.turtle.set_state(self,self._t_on)  53def_event_b_hdlr2(self):  54print"State 2, event B"  55self.b_count +=10  56def_event_c_hdlr2(self,val):  57print"State 2, event C"  58self.c_count +=2*val  59  60def_event_a_hdlr3(self):  61self.a_count +=100  62print"State 3, event A"  63def_event_b_hdlr3(self):  64print"State 3, event B"  65self.b_count +=100  66# we decide here we want to go to state 2, overrrides spec in state table below.  67# transition to next_state is made after the method exits.  68self.gstate.next_state =self._state2  69def_event_c_hdlr3(self,val):  70print"State 3, event C"  71self.c_count +=5*val  72  73# Associate the handlers with a state. The first argument is a list of methods.  74# One method for each event_handler decorated function of gstate. Order of methods  75# in the list correspond to order in which the Event Handlers were declared.  76# Second arg is the name of the state.  Third argument is to be come a list of the  77# next states.  78# The first state created becomes the initial state.  79_state1 =gstate.state("One",  (_event_a_hdlr1,_event_b_hdlr1,_event_c_hdlr1),  80                                      ("Two","Three",None))  81_state2 =gstate.state("Two",  (_event_a_hdlr2,_event_b_hdlr2,_event_c_hdlr2),  82                                     ("Three",None,"One"))  83_state3 =gstate.state("Three",(_event_a_hdlr3,_event_b_hdlr3,_event_c_hdlr3),  84                                 (None,"One","Two"))  85  86  87# Declare a function that will be called when entering a new gstate.  88# Can also declare a leave function using @on_leave_function(gstate)  89@on_enter_function(gstate)  90def_enter_gstate(self):  91print"entering state",self.gstate.name() ,"of",self.mname  92@on_leave_function(tstate)  93def_leave_tstate(self):  94print"leaving state",self.turtle.name() ,"of",self.mname  95  96  97def_toggle_on(self):  98print"Toggle On"  99 100def_toggle_off(self): 101print"Toggle Off" 102 103_t_off =tstate.state("Off", [_toggle_on], 104                         ["On"]) 105_t_on =tstate.state("On", [_toggle_off], 106                          ["Off"]) 107 108 109defmain(): 110big_machine =MyMachine("big") 111lil_machine =MyMachine("lil") 112 113big_machine.event_a() 114lil_machine.event_a() 115big_machine.event_a() 116lil_machine.event_a() 117big_machine.event_b() 118lil_machine.event_b() 119big_machine.event_c(4) 120lil_machine.event_c(2) 121big_machine.event_c(1) 122lil_machine.event_c(3) 123big_machine.event_b() 124lil_machine.event_b() 125big_machine.event_a() 126lil_machine.event_a() 127big_machine.event_a() 128 129big_machine.toggle() 130big_machine.toggle() 131big_machine.toggle() 132 133lil_machine.event_a() 134big_machine.event_b() 135lil_machine.event_b() 136big_machine.event_c(3) 137big_machine.event_a() 138lil_machine.event_c(2) 139lil_machine.event_a() 140big_machine.event_b() 141lil_machine.event_b() 142big_machine.event_c(7) 143lil_machine.event_c(1) 144 145print"Event A count",big_machine.a_count 146print"Event B count",big_machine.b_count 147print"Event C count",big_machine.c_count 148print"LilMachine C count",lil_machine.c_count 149 150main()

And now the imported statedefn.py

   1#   2# Support for State Machines.  ref - Design Patterns by GoF   3#  Many of the methods in these classes get called behind the scenes.   4#   5#  Notable exceptions are methods of the StateVar class.   6#   7#  See example programs for how this module is intended to be used.   8#   9classStateMachineError(Exception):  10def__init__(self,args =None):  11self.args =args  12  13classStateVar(object):  14def__init__(self,initial_state):  15self._current_state =initial_state  16self.next_state =initial_state# publicly settable in an event handling routine.  17  18defset_state(self,owner,new_state):  19'''  20        Forces a state change to new_state  21        '''  22self.next_state =new_state  23self.__to_next_state(owner)  24  25def__to_next_state(self,owner):  26'''  27        The low-level state change function which calls leave state & enter state functions as  28        needed.  29  30        LeaveState and EnterState functions are called as needed when state transitions.  31        '''  32ifself.next_stateisnotself._current_state:  33ifhasattr(self._current_state,"leave"):  34self._current_state.leave(owner)  35elifhasattr(self,"leave"):  36self.leave(owner)  37self._current_state =self.next_state  38ifhasattr(self._current_state,"enter"):  39self._current_state.enter(owner)  40elifhasattr(self,"enter"):  41self.enter(owner)  42  43def__fctn(self,func_name):  44'''  45        Returns the owning class's method for handling an event for the current state.  46        This method not for public consumption.  47        '''  48vf =self._current_state.get_fe(func_name)  49returnvf  50  51defname(self):  52'''  53        Returns the current state name.  54        '''  55returnself._current_state.name  56  57classSTState(object):  58def__init__(self,state_name):  59self.name =state_name  60self.fctn_dict = {}  61  62defset_events(self,event_list,event_hdlr_list,next_states):  63dictionary =self.fctn_dict  64ifnotnext_states:  65defset_row(event,method):  66dictionary[event] = [method,None]  67map(set_row,event_list,event_hdlr_list)  68else:  69defset_row2(event,method,next_state):  70dictionary[event] = [method,next_state]  71map(set_row2,event_list,event_hdlr_list,next_states)  72self.fctn_dict =dictionary  73  74defget_fe(self,fctn_name):  75returnself.fctn_dict[fctn_name]  76  77defmap_next_states(self,state_dict):  78''' Changes second dict value from name of state to actual state.'''  79fordeinself.fctn_dict.values():  80next_state_name =de[1]  81ifnext_state_name:  82ifnext_state_nameinstate_dict:  83de[1] =state_dict[next_state_name]  84else:  85raiseStateMachineError('Invalid Name for next state:{}'.format(next_state_name))  86  87  88classStateTable(object):  89'''  90    Magical class to define a state machine, with the help of several decorator functions  91    which follow.  92    '''  93def__init__(self,declname):  94self.machine_var =declname  95self._initial_state =None  96self._state_list = {}  97self._event_list = []  98self.need_initialize =1  99 100definitialize(self,parent): 101''' 102        Initializes the parent class's state variable for this StateTable class. 103        Must call this method in the parent' object's __init__ method.  You can have 104        Multiple state machines within a parent class. Call this method for each 105        ''' 106statevar=StateVar(self._initial_state) 107setattr(parent,self.machine_var,statevar) 108ifhasattr(self,"enter"): 109statevar.enter =self.enter 110ifhasattr(self,"leave"): 111statevar.leave =self.leave 112#Magic happens here - in the 'next state' table, translate names into state objects. 113ifself.need_initialize: 114forxstateinlist(self._state_list.values()): 115xstate.map_next_states(self._state_list) 116self.need_initialize =0 117 118defdef_state(self,event_hdlr_list,name): 119''' 120        This is used to define a state. the event handler list is a list of functions that 121        are called for corresponding events. name is the name of the state. 122        ''' 123state_table_row =STState(name) 124iflen(event_hdlr_list) !=len(self._event_list): 125raiseStateMachineError('Mismatch between number of event handlers and the methods specified for the state.') 126 127state_table_row.set_events(self._event_list,event_hdlr_list,None) 128 129ifself._initial_stateisNone: 130self._initial_state =state_table_row 131self._state_list[name] =state_table_row 132returnstate_table_row 133 134defstate(self,name,event_hdlr_list,next_states): 135state_table_row =STState(name) 136iflen(event_hdlr_list) !=len(self._event_list): 137raiseStateMachineError('Mismatch between number of event handlers and the methods specified for the state.') 138ifnext_statesisnotNoneandlen(next_states) !=len(self._event_list): 139raiseStateMachineError('Mismatch between number of event handlers and the next states specified for the state.') 140 141state_table_row.set_events(self._event_list,event_hdlr_list,next_states) 142 143ifself._initial_stateisNone: 144self._initial_state =state_table_row 145self._state_list[name] =state_table_row 146returnstate_table_row 147 148def__add_ev_hdlr(self,func_name): 149''' 150        Informs the class of an event handler to be added. We just need the name here. The 151        function name will later be associated with one of the functions in a list when a state is defined. 152        ''' 153self._event_list.append(func_name) 154 155# Decorator functions ... 156defevent_handler(state_class): 157''' 158    Declare a method that handles a type of event. 159    ''' 160defwrapper(func): 161state_class._StateTable__add_ev_hdlr(func.__name__) 162defobj_call(self, *args, **keywords): 163state_var =getattr(self,state_class.machine_var) 164funky,next_state =state_var._StateVar__fctn(func.__name__) 165ifnext_stateisnotNone: 166state_var.next_state =next_state 167rv =funky(self, *args, **keywords) 168state_var._StateVar__to_next_state(self) 169returnrv 170returnobj_call 171returnwrapper 172 173defon_enter_function(state_class): 174''' 175    Declare that this method should be called whenever a new state is entered. 176    ''' 177defwrapper(func): 178state_class.enter =func 179returnfunc 180returnwrapper 181 182defon_leave_function(state_class): 183''' 184    Declares that this method should be called whenever leaving a state. 185    ''' 186defwrapper(func): 187state_class.leave =func 188returnfunc 189returnwrapper

C++/Java-keyword-like function decorators

@abstractMethod, @deprecatedMethod, @privateMethod, @protectedMethod, @raises, @parameterTypes, @returnType

The annotations provide run-time type checking and an alternative way to document code.

The code and documentation are long, so I offer a link:http://fightingquaker.com/pyanno/

Different Decorator Forms

There are operational differences between:

This example demonstrates the operational differences between the three using a skit taken from Episode 22: Bruces.

   1fromsysimportstdout,stderr   2frompdbimportset_traceasbp   3   4classDecoTrace(object):   5'''   6    Decorator class with no arguments   7   8    This can only be used for functions or methods where the instance   9    is not necessary  10  11    '''  12  13def__init__(self,f):  14self.f =f  15  16def_showargs(self, *fargs, **kw):  17print >>stderr,'T: enter{} with args={}, kw={}'.format(self.f.__name__,str(fargs),str(kw))  18  19def_aftercall(self,status):  20print >>stderr,'T: exit{} with status={}'.format(self.f.__name__,str(status))  21  22def__call__(self, *fargs, **kw):  23'''Pass *just* function arguments to wrapped function.'''  24self._showargs(*fargs, **kw)  25ret=self.f(*fargs, **kw)  26self._aftercall(ret)  27returnret  28  29def__repr__(self):  30returnself.f.func_name  31  32  33classDecoTraceWithArgs(object):  34'''decorator class with ARGUMENTS  35  36       This can be used for unbounded functions and methods.  If this wraps a  37       class instance, then extract it and pass to the wrapped method as the  38       first arg.  39    '''  40  41def__init__(self, *dec_args, **dec_kw):  42'''The decorator arguments are passed here.  Save them for runtime.'''  43self.dec_args =dec_args  44self.dec_kw =dec_kw  45  46self.label =dec_kw.get('label','T')  47self.fid =dec_kw.get('stream',stderr)  48  49def_showargs(self, *fargs, **kw):  50  51print >>self.fid, \  52'{}: enter{} with args={}, kw={}'.format(self.label,self.f.__name__,str(fargs),str(kw))  53print >>self.fid, \  54'{}:   passing decorator args={}, kw={}'.format(self.label,str(self.dec_args),str(self.dec_kw))  55  56def_aftercall(self,status):  57print >>self.fid,'{}: exit{} with status={}'.format(self.label,self.f.__name__,str(status))  58def_showinstance(self,instance):  59print >>self.fid,'{}: instance={}'.format(self.label,instance)  60  61def__call__(self,f):  62defwrapper(*fargs, **kw):  63'''  64              Combine decorator arguments and function arguments and pass to wrapped  65              class instance-aware function/method.  66  67              Note: the first argument cannot be "self" because we get a parse error  68              "takes at least 1 argument" unless the instance is actually included in  69              the argument list, which is redundant.  If this wraps a class instance,  70              the "self" will be the first argument.  71            '''  72  73self._showargs(*fargs, **kw)  74  75# merge decorator keywords into the kw argument list  76kw.update(self.dec_kw)  77  78# Does this wrap a class instance?  79iffargsandgetattr(fargs[0],'__class__',None):  80  81# pull out the instance and combine function and  82# decorator args  83instance,fargs =fargs[0],fargs[1:]+self.dec_args  84self._showinstance(instance)  85  86# call the method  87ret=f(instance, *fargs, **kw)  88else:  89# just send in the give args and kw  90ret=f(*(fargs +self.dec_args), **kw)  91  92self._aftercall(ret)  93returnret  94  95# Save wrapped function reference  96self.f =f  97wrapper.__name__ =f.__name__  98wrapper.__dict__.update(f.__dict__)  99wrapper.__doc__ =f.__doc__ 100returnwrapper 101 102 103@DecoTrace 104defFirstBruce(*fargs, **kwargs): 105'Simple function using simple decorator.' 106iffargsandfargs[0]: 107printfargs[0] 108 109@DecoTraceWithArgs(name="Second Bruce",standardline="G'day, Bruce!") 110defSecondBruce(*fargs, **kwargs): 111'Simple function using decorator with arguments.' 112print'{}:'.format(kwargs.get('name','Unknown Bruce')) 113 114iffargsandfargs[0]: 115printfargs[0] 116else: 117printkwargs.get('standardline',None) 118 119classBruce(object): 120'Simple class.' 121 122def__init__(self,id): 123self.id =id 124 125def__str__(self): 126returnself.id 127 128def__repr__(self): 129return'Bruce' 130 131@DecoTraceWithArgs(label="Trace a class",standardline="How are yer Bruce?",stream=stdout) 132deftalk(self, *fargs, **kwargs): 133'Simple function using decorator with arguments.' 134 135print'{}:'.format(self) 136iffargsandfargs[0]: 137printfargs[0] 138else: 139printkwargs.get('standardline',None) 140 141ThirdBruce =Bruce('Third Bruce') 142 143SecondBruce() 144FirstBruce("First Bruce: Oh, Hello Bruce!") 145ThirdBruce.talk() 146FirstBruce("First Bruce: Bit crook, Bruce.") 147SecondBruce("Where's Bruce?") 148FirstBruce("First Bruce: He's not here, Bruce") 149ThirdBruce.talk("Blimey, s'hot in here, Bruce.") 150FirstBruce("First Bruce: S'hot enough to boil a monkey's bum!") 151SecondBruce("That's a strange expression, Bruce.") 152FirstBruce("First Bruce: Well Bruce, I heard the Prime Minister use it. S'hot enough to boil a monkey's bum in'ere, your Majesty,' he said and she smiled quietly to herself.") 153ThirdBruce.talk("She's a good Sheila, Bruce and not at all stuck up.")

Unimplemented function replacement

Allows you to test unimplemented code in a development environment by specifying a default argument as an argument to the decorator (or you can leave it off to specify None to be returned.

   1# Annotation wrapper annotation method   2defunimplemented(defaultval):   3if(type(defaultval) ==type(unimplemented)):   4returnlambda:None   5else:   6# Actual annotation   7defunimp_wrapper(func):   8# What we replace the function with   9defwrapper(*arg):  10returndefaultval  11returnwrapper  12returnunimp_wrapper

Redirects stdout printing to python standard logging.

   1classLogPrinter:   2'''LogPrinter class which serves to emulates a file object and logs   3       whatever it gets sent to a Logger object at the INFO level.'''   4def__init__(self):   5'''Grabs the specific logger to use for logprinting.'''   6self.ilogger =logging.getLogger('logprinter')   7il =self.ilogger   8logging.basicConfig()   9il.setLevel(logging.INFO)  10  11defwrite(self,text):  12'''Logs written output to a specific logger'''  13self.ilogger.info(text)  14  15deflogprintinfo(func):  16'''Wraps a method so that any calls made to print get logged instead'''  17defpwrapper(*arg, **kwargs):  18stdobak =sys.stdout  19lpinstance =LogPrinter()  20sys.stdout =lpinstance  21try:  22returnfunc(*arg, **kwargs)  23finally:  24sys.stdout =stdobak  25returnpwrapper

Access control

This example prevents users from getting access to places where they are not authorised to go

   1classLoginCheck:   2'''   3    This class checks whether a user   4    has logged in properly via   5    the global "check_function". If so,   6    the requested routine is called.   7    Otherwise, an alternative page is   8    displayed via the global "alt_function"   9    '''  10def__init__(self,f):  11self._f =f  12  13def__call__(self, *args):  14Status =check_function()  15ifStatusis1:  16returnself._f(*args)  17else:  18returnalt_function()  19  20defcheck_function():  21returntest  22  23defalt_function():  24return'Sorry - this is the forced behaviour'  25  26@LoginCheck  27defdisplay_members_page():  28print'This is the members page'

Example:

   1test =0   2DisplayMembersPage()   3# Displays "Sorry - this is the forced behaviour"   4   5test =1   6DisplayMembersPage()   7# Displays "This is the members page"

Events rising and handling

Please see the code and examples here:http://pypi.python.org/pypi/Decovent

Singleton

   1importfunctools   2   3defsingleton(cls):   4''' Use class as singleton. '''   5   6cls.__new_original__ =cls.__new__   7   8@functools.wraps(cls.__new__)   9defsingleton_new(cls, *args, **kw):  10it =cls.__dict__.get('__it__')  11ifitisnotNone:  12returnit  13  14cls.__it__ =it =cls.__new_original__(cls, *args, **kw)  15it.__init_original__(*args, **kw)  16returnit  17  18cls.__new__ =singleton_new  19cls.__init_original__ =cls.__init__  20cls.__init__ =object.__init__  21  22returncls  23  24#  25# Sample use:  26#  27  28@singleton  29classFoo:  30def__new__(cls):  31cls.x =10  32returnobject.__new__(cls)  33  34def__init__(self):  35assertself.x ==10  36self.x =15  37  38assertFoo().x ==15  39Foo().x =20  40assertFoo().x ==20

Asynchronous Call

   1fromQueueimportQueue   2fromthreadingimportThread   3   4classasynchronous(object):   5def__init__(self,func):   6self.func =func   7   8defthreaded(*args, **kwargs):   9self.queue.put(self.func(*args, **kwargs))  10  11self.threaded =threaded  12  13def__call__(self, *args, **kwargs):  14returnself.func(*args, **kwargs)  15  16defstart(self, *args, **kwargs):  17self.queue =Queue()  18thread =Thread(target=self.threaded,args=args,kwargs=kwargs);  19thread.start();  20returnasynchronous.Result(self.queue,thread)  21  22classNotYetDoneException(Exception):  23def__init__(self,message):  24self.message =message  25  26classResult(object):  27def__init__(self,queue,thread):  28self.queue =queue  29self.thread =thread  30  31defis_done(self):  32returnnotself.thread.is_alive()  33  34defget_result(self):  35ifnotself.is_done():  36raiseasynchronous.NotYetDoneException('the call has not yet completed its task')  37  38ifnothasattr(self,'result'):  39self.result =self.queue.get()  40  41returnself.result  42  43if__name__ =='__main__':  44# sample usage  45importtime  46  47@asynchronous  48deflong_process(num):  49time.sleep(10)  50returnnum *num  51  52result =long_process.start(12)  53  54foriinrange(20):  55printi  56time.sleep(1)  57  58ifresult.is_done():  59print"result{0}".format(result.get_result())  60  61  62result2 =long_process.start(13)  63  64try:  65print"result2{0}".format(result2.get_result())  66  67exceptasynchronous.NotYetDoneExceptionasex:  68printex.message

Class method decorator using instance

When decorating a class method, the decorator receives an function not yet bound to an instance.

The decorator can't to do anything on the instance invocating it, unless it actually is a descriptor.

   1fromfunctoolsimportwraps   2   3defdecorate(f):   4'''   5    Class method decorator specific to the instance.   6   7    It uses a descriptor to delay the definition of the   8    method wrapper.   9    '''  10classdescript(object):  11def__init__(self,f):  12self.f =f  13  14def__get__(self,instance,klass):  15ifinstanceisNone:  16# Class method was requested  17returnself.make_unbound(klass)  18returnself.make_bound(instance)  19  20defmake_unbound(self,klass):  21@wraps(self.f)  22defwrapper(*args, **kwargs):  23'''This documentation will vanish :)'''  24raiseTypeError(  25'unbound method{}() must be called with{} instance'  26'as first argument (got nothing instead)'.format(  27self.f.__name__,  28klass.__name__)  29                )  30returnwrapper  31  32defmake_bound(self,instance):  33@wraps(self.f)  34defwrapper(*args, **kwargs):  35'''This documentation will disapear :)'''  36print"Called the decorated method{} of{}".format(self.f.__name__,instance)  37returnself.f(instance, *args, **kwargs)  38# This instance does not need the descriptor anymore,  39# let it find the wrapper directly next time:  40setattr(instance,self.f.__name__,wrapper)  41returnwrapper  42  43returndescript(f)

This implementation replaces the descriptor by the actual decorated function ASAP to avoid overhead, but you could keep it to do even more (counting calls, etc...)

Another Retrying Decorator

Here's another decorator for causing a function to be retried a certain number of times. This decorator is superior IMHO because it should work with any old function that raises an exception on failure.

Features:

GIST:https://gist.github.com/2570004

   1#   2# Copyright 2012 by Jeff Laughlin Consulting LLC   3#   4# Permission is hereby granted, free of charge, to any person obtaining a copy   5# of this software and associated documentation files (the "Software"), to deal   6# in the Software without restriction, including without limitation the rights   7# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell   8# copies of the Software, and to permit persons to whom the Software is   9# furnished to do so, subject to the following conditions:  10#  11# The above copyright notice and this permission notice shall be included in  12# all copies or substantial portions of the Software.  13#  14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  15# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,  16# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE  17# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER  18# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  19# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE  20# SOFTWARE.  21  22  23importsys  24fromtimeimportsleep  25  26  27defexample_exc_handler(tries_remaining,exception,delay):  28"""Example exception handler; prints a warning to stderr.  29  30    tries_remaining: The number of tries remaining.  31    exception: The exception instance which was raised.  32    """  33print >>sys.stderr,"Caught'%s',%d tries remaining, sleeping for%s seconds" % (exception,tries_remaining,delay)  34  35  36defretries(max_tries,delay=1,backoff=2,exceptions=(Exception,),hook=None):  37"""Function decorator implementing retrying logic.  38  39    delay: Sleep this many seconds * backoff * try number after failure  40    backoff: Multiply delay by this factor after each failure  41    exceptions: A tuple of exception classes; default (Exception,)  42    hook: A function with the signature myhook(tries_remaining, exception);  43          default None  44  45    The decorator will call the function up to max_tries times if it raises  46    an exception.  47  48    By default it catches instances of the Exception class and subclasses.  49    This will recover after all but the most fatal errors. You may specify a  50    custom tuple of exception classes with the 'exceptions' argument; the  51    function will only be retried if it raises one of the specified  52    exceptions.  53  54    Additionally you may specify a hook function which will be called prior  55    to retrying with the number of remaining tries and the exception instance;  56    see given example. This is primarily intended to give the opportunity to  57    log the failure. Hook is not called after failure if no retries remain.  58    """  59defdec(func):  60deff2(*args, **kwargs):  61mydelay =delay  62tries =range(max_tries)  63tries.reverse()  64fortries_remainingintries:  65try:  66returnfunc(*args, **kwargs)  67exceptexceptionsase:  68iftries_remaining >0:  69ifhookisnotNone:  70hook(tries_remaining,e,mydelay)  71sleep(mydelay)  72mydelay =mydelay *backoff  73else:  74raise  75else:  76break  77returnf2  78returndec

Logging decorator with specified logger (or default)

This decorator will log entry and exit points of your funtion using the specified logger or it defaults to your function's module name logger.

In the current form it uses the logging.INFO level, but I can easily customized to use what ever level. Same for the entry and exit messages.

   1importfunctools,logging   2   3   4log =logging.getLogger(__name__)   5log.setLevel(logging.DEBUG)   6   7classlog_with(object):   8'''Logging decorator that allows you to log with a   9specific logger.  10'''  11# Customize these messages  12ENTRY_MESSAGE ='Entering{}'  13EXIT_MESSAGE ='Exiting{}'  14  15def__init__(self,logger=None):  16self.logger =logger  17  18def__call__(self,func):  19'''Returns a wrapper that wraps func.  20The wrapper will log the entry and exit points of the function  21with logging.INFO level.  22'''  23# set logger if it was not set earlier  24ifnotself.logger:  25logging.basicConfig()  26self.logger =logging.getLogger(func.__module__)  27  28@functools.wraps(func)  29defwrapper(*args, **kwds):  30self.logger.info(self.ENTRY_MESSAGE.format(func.__name__))# logging level .info(). Set to .debug() if you want to  31f_result =func(*args, **kwds)  32self.logger.info(self.EXIT_MESSAGE.format(func.__name__))# logging level .info(). Set to .debug() if you want to  33returnf_result  34returnwrapper

   1# Sample use and output:   2   3if__name__ =='__main__':   4logging.basicConfig()   5log =logging.getLogger('custom_log')   6log.setLevel(logging.DEBUG)   7log.info('ciao')   8   9@log_with(log)# user specified logger  10deffoo():  11print'this is foo'  12foo()  13  14@log_with()# using default logger  15deffoo2():  16print'this is foo2'  17foo2()

   1# output   2>>> ================================RESTART ================================   3>>>   4INFO:custom_log:ciao   5INFO:custom_log:Enteringfoo# uses the correct logger   6thisisfoo   7INFO:custom_log:Exitingfoo   8INFO:__main__:Enteringfoo2# uses the correct logger   9thisisfoo2  10INFO:__main__:Exitingfoo2

Lazy Thunkify

This decorator will cause any function to, instead of running its code, start a thread to run the code, returning a thunk (function with no args) that wait for the function's completion and returns the value (or raises the exception).

Useful if you have Computation A that takes x seconds and then uses Computation B, which takes y seconds. Instead of x+y seconds you only need max(x,y) seconds.

   1importthreading,sys,functools,traceback   2   3deflazy_thunkify(f):   4"""Make a function immediately return a function of no args which, when called,   5    waits for the result, which will start being processed in another thread."""   6   7@functools.wraps(f)   8deflazy_thunked(*args, **kwargs):   9wait_event =threading.Event()  10  11result = [None]  12exc = [False,None]  13  14defworker_func():  15try:  16func_result =f(*args, **kwargs)  17result[0] =func_result  18exceptException,e:  19exc[0] =True  20exc[1] =sys.exc_info()  21print"Lazy thunk has thrown an exception (will be raised on thunk()):\n%s" % (  22traceback.format_exc())  23finally:  24wait_event.set()  25  26defthunk():  27wait_event.wait()  28ifexc[0]:  29raiseexc[1][0],exc[1][1],exc[1][2]  30  31returnresult[0]  32  33threading.Thread(target=worker_func).start()  34  35returnthunk  36  37returnlazy_thunked

Example:

   1@lazy_thunkify   2defslow_double(i):   3print"Multiplying..."   4time.sleep(5)   5print"Done multiplying!"   6returni*2   7   8   9defmaybe_multiply(x):  10double_thunk =slow_double(x)  11print"Thinking..."  12time.sleep(3)  13time.sleep(3)  14time.sleep(1)  15ifx ==3:  16print"Using it!"  17res =double_thunk()  18else:  19print"Not using it."  20res =None  21returnres  22  23#both take 7 seconds  24maybe_multiply(10)  25maybe_multiply(3)

Aggregative decorators for generator functions

This could be a whole family of decorators. The aim is applying an aggregation function to the iterated outcome of a generator-functions.

Two interesting aggregators could be sum and average:

   1importfunctoolsasft   2importoperatorasop   3   4defsummed(f):   5returnlambda *xs :sum(f(*xs))   6   7defaveraged(f):   8defaux(acc,x):   9return (acc[0] +x,acc[1] +1)  10  11defout(*xs):  12s,n =ft.reduce(aux,f(*xs), (0,0))  13returns /nifn >0else0  14  15returnout

Examples for the two proposed decorators:

   1@averaged   2defproducer2():   3yield10   4yield5   5yield2.5   6yield7.5   7   8assertproducer2() == (10 +5 +2.5 +7.5) /4   9  10@summed  11defproducer1():  12yield10  13yield5  14yield2.5  15yield7.5  16  17assertproducer1() == (10 +5 +2.5 +7.5)

Function Timeout

Ever had a function take forever in weird edge cases? In one case, a function was extracting URIs from a long string using regular expressions, and sometimes it was running into a bug in the Python regexp engine and would take minutes rather than milliseconds. The best solution was to install a timeout using an alarm signal and simply abort processing. This can conveniently be wrapped in a decorator:

   1importsignal   2importfunctools   3   4classTimeoutError(Exception):pass   5   6deftimeout(seconds,error_message ='Function call timed out'):   7defdecorated(func):   8def_handle_timeout(signum,frame):   9raiseTimeoutError(error_message)  10  11defwrapper(*args, **kwargs):  12signal.signal(signal.SIGALRM,_handle_timeout)  13signal.alarm(seconds)  14try:  15result =func(*args, **kwargs)  16finally:  17signal.alarm(0)  18returnresult  19  20returnfunctools.wraps(func)(wrapper)  21  22returndecorated

Example:

   1importtime   2   3@timeout(1,'Function slow; aborted')   4defslow_function():   5time.sleep(5)

Collect Data Difference Caused by Decorated Function

It calls a user function to collect some data before and after the decorated function runs. To calculate difference it calls the difference calculator user function.

Example: checking page numbers of a print job: get the number of all printed pages from printer before and after the printing. Then calculate difference to get the number of pages printed by the the decorated function

   1importinspect   2# Just in case you want to use the name of the decorator instead of difference calculator   3# But in that case if the function decorated  more than once the collected difference will be overwritten   4   5importtime   6# Demo purposes only, the difference will be generated from time   7   8fromfunctoolsimportwraps   9  10  11defcollect_data_and_calculate_difference(data_collector,difference_calculator):  12"""Returns difference of data collected before and after the decorated function,  13    plus the original return value of the decorated function. Return type: dict.  14    Keys:  15        - function name of the decorated function  16        - name of the difference calculator function  17    Values:  18        - the original return value of decorated function  19        - difference calculated by difference_calculator functions  20    Parameters: functions to collect data, and create difference from collected data  21  22    Created: 2017  23    Author: George Fischhof  24    """  25  26current_decorator_function_name =inspect.currentframe().f_code.co_name  27# Just in case you want to use it  28  29deffunction_wrapper_because_of_parameters(decorated_function):  30difference_calculator_name =difference_calculator.__name__  31decorated_function_name =decorated_function.__name__  32  33i_am_the_first_decorator =nothasattr(decorated_function,'__wrapped__')  34  35@wraps(decorated_function)  36defwrapper(*args, **kwargs) ->dict:  37result_dict =dict()  38  39before =data_collector()  40original_result =decorated_function(*args, **kwargs)  41after =data_collector()  42  43my_collection =difference_calculator(before=before,after=after)  44  45i_am_not_first_decorator_but_first_is_similar_to_me = (  46noti_am_the_first_decorator  47andisinstance(original_result,dict)  48and (decorated_function_nameinoriginal_result)  49            )  50  51ifi_am_not_first_decorator_but_first_is_similar_to_me:  52original_result[difference_calculator_name] =my_collection  53returnoriginal_result  54else:  55result_dict[decorated_function_name] =original_result  56result_dict[difference_calculator_name] =my_collection  57returnresult_dict  58  59returnwrapper  60returnfunction_wrapper_because_of_parameters  61  62  63# Usage  64  65  66defcollect_data_or_data_series_a():  67time.sleep(0.5)  68returntime.time()  69  70  71defcollect_data_or_data_series_b():  72time.sleep(0.5)  73returntime.time()  74  75  76defcalculate_difference_on_data_series_a(before,after):  77returnafter -before  78  79  80defcalculate_difference_on_data_series_b(before,after):  81returnafter -before  82  83  84@collect_data_and_calculate_difference(  85data_collector=collect_data_or_data_series_a,  86difference_calculator=calculate_difference_on_data_series_a)  87@collect_data_and_calculate_difference(  88data_collector=collect_data_or_data_series_b,  89difference_calculator=calculate_difference_on_data_series_b)  90defdo_something_that_changes_the_collected_data():  91return'result of decorated function...'  92  93  94print(do_something_that_changes_the_collected_data())  95# result dict:  96# {'calculate_difference_on_data_series_a': 1.5010299682617188,  97# 'do_something_that_changes_the_collected_data': 'result of decorated function...',  98# 'calculate_difference_on_data_series_b': 0.5001623630523682}


CategoryDocumentation


2026-02-14 16:12
[8]ページ先頭

©2009-2026 Movatter.jp