Differences between revisions 1 and 38 (spanning 37 versions)
Revision 1 as of 2004-08-10 20:46:05
Size: 247
Editor: dsl-082-082-053-236
Comment:
Revision 38 as of 2005-12-05 22:09:36
Size: 16363
Editor: rrcs-24-242-128-10
Comment:
Deletions are marked like this. Additions are marked like this.
Line 2: Line 2:
It is NOT a page to discuss about decorator syntax! It is NOT a page to discuss decorator syntax!
Line 5: Line 5:

[[TableOfContents()]]

== Property Definition ==

These decorators provide a readable way to define properties:

{{{
#!python
import sys

def propget(func):
    locals = sys._getframe(1).f_locals
    name = func.__name__
    prop = locals.get(name)
    if not isinstance(prop, property):
        prop = property(func, doc=func.__doc__)
    else:
        doc = prop.__doc__ or func.__doc__
        prop = property(func, prop.fset, prop.fdel, doc)
    return prop

def propset(func):
    locals = sys._getframe(1).f_locals
    name = func.__name__
    prop = locals.get(name)
    if not isinstance(prop, property):
        prop = property(None, func, doc=func.__doc__)
    else:
        doc = prop.__doc__ or func.__doc__
        prop = property(prop.fget, func, prop.fdel, doc)
    return prop

def propdel(func):
    locals = sys._getframe(1).f_locals
    name = func.__name__
    prop = locals.get(name)
    if not isinstance(prop, property):
        prop = property(None, None, func, doc=func.__doc__)
    else:
        prop = property(prop.fget, prop.fset, func, prop.__doc__)
    return prop

# These can be used like this:

class Example(object):

    @propget
    def myattr(self):
        return self._half * 2

    @propset
    def myattr(self, value):
        self._half = value / 2

    @propdel
    def myattr(self):
        del self._half
}}}


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

{{{
#!python
class Example(object):
    @apply
    def myattr():
        doc = """This is the doc string."""

        def fget(self):
            return self._half * 2

        def fset(self, value):
            self._half = value / 2

        def fdel(self):
            del self._half

        return property(**locals())
}}}

Yet another property decorator:

{{{
#!python
def Property(function):
    keys = 'fget', 'fset', 'fdel'
    func_locals = {'doc':function.__doc__}
    def probeFunc(frame, event, arg):
        if event == 'return':
            locals = frame.f_locals
            func_locals.update(dict((k,locals.get(k)) for k in keys))
            sys.settrace(None)
        return probeFunc
    sys.settrace(probeFunc)
    function()
    return property(**func_locals)

#====== Example =======================================================

from math import radians, degrees, pi

class Angle(object):
    def __init__(self,rad):
        self._rad = rad

    @Property
    def rad():
        '''The angle in radians'''
        def fget(self):
            return self._rad
        def fset(self,angle):
            if isinstance(angle,Angle): angle = angle.rad
            self._rad = float(angle)

    @Property
    def deg():
        '''The angle in degrees'''
        def fget(self):
            return degrees(self._rad)
        def fset(self,angle):
            if isinstance(angle,Angle): angle = angle.deg
            self._rad = radians(angle)
}}}

== Memoize ==

Here's a memoizing class.

{{{
#!python
class memoized(object):
   """Decorator that caches a function's return value each time it is called.
   If called later with the same arguments, the cached value is returned, and
   not re-evaluated.
   """
   def __init__(self, func):
      self.func = func
      self.cache = {}
   def __call__(self, *args):
      try:
         return self.cache[args]
      except KeyError:
         self.cache[args] = value = self.func(*args)
         return value
      except TypeError:
         # uncachable -- for instance, passing a list as an argument.
         # Better to not cache than to blow up entirely.
         return self.func(*args)
   def __repr__(self):
      """Return the function's docstring."""
      return self.func.__doc__
                        
@memoized
def fibonacci(n):
   "Return the nth fibonacci number."
   if n in (0, 1):
      return n
   return fibonacci(n-1) + fibonacci(n-2)

print fibonacci(12)
}}}

== Pseudo-currying ==

{{{
#!python
class curried(object):
  """
  Decorator that returns a function that keeps returning functions
  until all arguments are supplied; then the original function is
  evaluated.
  """

  def __init__(self, func, *a):
    self.func = func
    self.args = a
  def __call__(self, *a):
    args = self.args + a
    if len(args) < self.func.func_code.co_argcount:
      return curried(self.func, *args)
    else:
      return self.func(*args)


@curried
def add(a, b):
    return a+b

add1 = add(1)

print add1(2)

}}}

== Controllable DIY debug ==

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

{{{
#!python
import sys

WHAT_TO_DEBUG = set(['io', 'core']) # change to what you need

class debug:
    """ Decorator which helps to control what aspects of a program to debug
    on per-function basis. Aspects are provided as list of arguments.
    It DOESN'T slowdown functions which aren't supposed to be debugged.
    """
    def __init__(self, aspects=None):
        self.aspects = set(aspects)

    def __call__(self, f):
        if self.aspects & WHAT_TO_DEBUG:
            def newf(*args, **kwds):
                print >> sys.stderr, f.func_name, args, kwds
                f_result = f(*args, **kwds)
                print >> sys.stderr, f.func_name, "returned", f_result
                return f_result
            newf.__doc__ = f.__doc__
            return newf
        else:
            return f

@debug(['io'])
def prn(x):
    print x

@debug(['core'])
def mult(x, y):
    return x * y

prn(mult(2,2))

}}}

== Easy adding methods to a class instance ==

Credits to John Roth.

{{{
#!python
class Foo:
    def __init__(self):
        self.x = 42

foo = Foo()

def addto(instance):
    def decorator(f):
        import new
        f = new.instancemethod(f, instance, instance.__class__)
        setattr(instance, f.func_name, f)
        return f
    return decorator

@addto(foo)
def print_x(self):
    print self.x

# foo.print_x() would print "42"
}}}

== Counting function calls ==

{{{
#!python
class countcalls(object):
   "Decorator that keeps track of the number of times a function is called."
   
   __instances = {}
   
   def __init__(self, f):
      self.__f = f
      self.__numCalls = 0
      countcalls.__instances[f] = self
      
   def __call__(self, *args, **kwargs):
      self.__numCalls += 1
      return self.__f(*args, **kwargs)
      
   @staticmethod
   def count(f):
      "Return the number of times the function f was called."
      return countcalls.__instances[f].__numCalls
      
   @staticmethod
   def counts():
      "Return a dict of {function: # of calls} for all registered functions."
      return dict([(f, countcalls.count(f)) for f in countcalls.__instances])
}}}

== Generating Deprecation Warnings ==

{{{
#!python
import warnings

def deprecated(func):
    """This is a decorator which can be used to mark functions
    as deprecated. It will result in a warning being emitted
    when the function is used."""
    def newFunc(*args, **kwargs):
        warnings.warn("Call to deprecated function %s." % func.__name__,
                      category=DeprecationWarning)
        return func(*args, **kwargs)
    newFunc.__name__ = func.__name__
    newFunc.__doc__ = func.__doc__
    newFunc.__dict__.update(func.__dict__)
    return newFunc

# === Examples of use ===

@deprecated
def some_old_function(x,y):
    return x + y

class SomeClass:
    @deprecated
    def some_old_method(self, x,y):
        return x + y
}}}

== Creating Well-Behaved Decorators ==

Note: This is only one recipe. Others include inheritance
from a standard decorator (link?) and a factory function
such as [http://www.phyast.pitt.edu/~micheles/python/decorator.zip Michele Simionato's decorator module] which even preserves signature information.

{{{
#!python
def simple_decorator(decorator):
    """This decorator can be used to turn simple functions
    into well-behaved decorators, so long as the decorators
    are fairly simple. If a decorator expects a function and
    returns a function (no descriptors), and if it doesn't
    modify function attributes or docstring, then it is
    eligible to use this. Simply apply @simple_decorator to
    your decorator and it will automatically preserve the
    docstring and function attributes of functions to which
    it is applied."""
    def new_decorator(f):
        g = decorator(f)
        g.__name__ = f.__name__
        g.__doc__ = f.__doc__
        g.__dict__.update(f.__dict__)
        return g
    # Now a few lines needed to make simple_decorator itself
    # be a well-behaved decorator.
    new_decorator.__name__ = decorator.__name__
    new_decorator.__doc__ = decorator.__doc__
    new_decorator.__dict__.update(decorator.__dict__)
    return new_decorator

#
# Sample Use:
#
@simple_decorator
def mySimpleLoggingDecorator( func ):
    def logged_func( *args, **kwargs ):
        print 'calling %s' % func.__name__
        return func( *args, **kwargs )
    return logged_func

@mySimpleLoggingDecorator
def double(x):
    "Doubles a number"
    return 2*x

assert double.__name__ == 'double'
assert double.__doc__ == 'Doubles a number'
print double(155)
}}}

== Enable/Disable Decorators ==

{{{
#!python
def unchanged(func):
    "This decorator doesn't add any behavior"
    return func

def disabled(func):
    "This decorator disables the provided function, and does nothing"
    def emptyFunc(*args,**kargs):
        pass
    return emptyFunc

# define this as equivalent to unchanged, for nice symmetry with disabled
enabled = unchanged

#
# Sample use
#

globalEnableFlag = int(True)

state = (disabled, enabled)[globalEnableFlag]
@state
def specialFunctionFoo():
    print "function was enabled"
}}}


== Easy Dump of Function Arguments ==

{{{
#!python
def dumpArgs(func):
    "This decorator dumps out the arguments passed to a function before calling it"
    argnames = func.func_code.co_varnames[:func.func_code.co_argcount]
    fname = func.func_name
    def echoFunc(*args,**kwargs):
        print fname, ":", ', '.join('%s=%r' % entry
                                    for entry in zip(argnames,args) + kwargs.items())
        return func(*args, **kwargs)
    return echoFunc
    
@dumpArgs
def f1(a,b,c):
    print a + b + c
    
f1(1, 2, 3)
}}}


== Pre-/Post-Conditions ==

{{{
#!python
"""
Provide pre-/postconditions as function decorators.

Example usage:

  >>> def in_ge20(inval):
  ... assert inval >= 20, 'Input value < 20'
  ...
  >>> def out_lt30(retval, inval):
  ... assert retval < 30, 'Return value >= 30'
  ...
  >>> @precondition(in_ge20)
  ... @postcondition(out_lt30)
  ... def inc(value):
  ... return value + 1
  ...
  >>> inc(5)
  Traceback (most recent call last):
    ...
  AssertionError: Input value < 20
  >>> inc(29)
  Traceback (most recent call last):
    ...
  AssertionError: Return value >= 30
  >>> inc(20)
  21

You can define as many pre-/postconditions for a function as you
like. It is also possible to specify both types of conditions at once:

  >>> @conditions(in_ge20, out_lt30)
  ... def add1(value):
  ... return value + 1
  ...
  >>> add1(5)
  Traceback (most recent call last):
    ...
  AssertionError: Input value < 20

An interesting feature is the ability to prevent the creation of
pre-/postconditions at function definition time. This makes it
possible to use conditions for debugging and then switch them off for
distribution.

  >>> debug = False
  >>> @precondition(in_ge20, debug)
  ... def dec(value):
  ... return value - 1
  ...
  >>> dec(5)
  4
"""

__all__ = [ 'precondition', 'postcondition', 'conditions' ]

DEFAULT_ON = True

def precondition(precondition, use_conditions=DEFAULT_ON):
    return conditions(precondition, None, use_conditions)

def postcondition(postcondition, use_conditions=DEFAULT_ON):
    return conditions(None, postcondition, use_conditions)

class conditions(object):
    __slots__ = ('__precondition', '__postcondition')

    def __init__(self, pre, post, use_conditions=DEFAULT_ON):
        if not use_conditions:
            pre, post = None, None

        self.__precondition = pre
        self.__postcondition = post

    def __call__(self, function):
        # combine recursive wrappers (@precondition + @postcondition == @conditions)
        pres = set( (self.__precondition,) )
        posts = set( (self.__postcondition,) )

        # unwrap function, collect distinct pre-/post conditions
        while type(function) is FunctionWrapper:
            pres.add(function._pre)
            posts.add(function._post)
            function = function._func

        # filter out None conditions and build pairs of pre- and postconditions
        conditions = map(None, filter(None, pres), filter(None, posts))

        # add a wrapper for each pair (note that 'conditions' may be empty)
        for pre, post in conditions:
            function = FunctionWrapper(pre, post, function)

        return function

class FunctionWrapper(object):
    def __init__(self, precondition, postcondition, function):
        self._pre = precondition
        self._post = postcondition
        self._func = function

    def __call__(self, *args, **kwargs):
        precondition = self._pre
        postcondition = self._post

        if precondition:
            precondition(*args, **kwargs)
        result = self._func(*args, **kwargs)
        if postcondition:
            postcondition(result, *args, **kwargs)
        return result

def __test():
    import doctest
    doctest.testmod()

if __name__ == "__main__":
    __test()
}}}

== Decorator decorator ==

For those decorators that return local functions, this will copy attributes from the original function object:

{{{
#!python
import warnings
def decorating(func, doc=''):
    def provideatts(newFunc, ):
        newFunc.__name__ = func.__name__
        newFunc.__doc__ = (func.__doc__ or '') + '\n' + doc
        newFunc.__dict__.update(func.__dict__)
        return newFunc
    return provideatts

# Example use:
def deprecated(func):
    @decorating(func, doc='(Deprecated.)')
    def you_will_never_see_this_name(*args, **kwargs):
        warnings.warn("Call to deprecated function %s." % func.__name__,
                          category=DeprecationWarning)
        return func(*args, **kwargs)
    return you_will_never_see_this_name
}}}

== 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.

{{{
#!python
import sys
import os
import linecache

def trace(f):
    def globaltrace(frame, why, arg):
        if why == "call":
            return localtrace
        return None

    def localtrace(frame, why, arg):
        if why == "line":
            # record the file name and line number of every trace
            filename = frame.f_code.co_filename
            lineno = frame.f_lineno

            bname = os.path.basename(filename)
            print "%s(%d): %s" % (bname, lineno,
                                  linecache.getline(filename, lineno)),
        return localtrace

    def _f(*args, **kwds):
        sys.settrace(globaltrace)
        result = f(*args, **kwds)
        sys.settrace(None)
        return result

    return _f
}}}

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 use the current decorator syntax @dec)!

TableOfContents()

Property Definition

These decorators provide a readable way to define properties:

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

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

   1 class Example(object):
   2     @apply
   3     def myattr():
   4         doc = """This is the doc string."""
   5 
   6         def fget(self):
   7             return self._half * 2
   8 
   9         def fset(self, value):
  10             self._half = value / 2
  11 
  12         def fdel(self):
  13             del self._half
  14 
  15         return property(**locals())

Yet another property decorator:

   1 def Property(function):
   2     keys = 'fget', 'fset', 'fdel'
   3     func_locals = {'doc':function.__doc__}
   4     def probeFunc(frame, event, arg):
   5         if event == 'return':
   6             locals = frame.f_locals
   7             func_locals.update(dict((k,locals.get(k)) for k in keys))
   8             sys.settrace(None)
   9         return probeFunc
  10     sys.settrace(probeFunc)
  11     function()
  12     return property(**func_locals)
  13 
  14 #====== Example =======================================================
  15 
  16 from math import radians, degrees, pi
  17 
  18 class Angle(object):
  19     def __init__(self,rad):
  20         self._rad = rad
  21 
  22     @Property
  23     def rad():
  24         '''The angle in radians'''
  25         def fget(self):
  26             return self._rad
  27         def fset(self,angle):
  28             if isinstance(angle,Angle): angle = angle.rad
  29             self._rad = float(angle)
  30 
  31     @Property
  32     def deg():
  33         '''The angle in degrees'''
  34         def fget(self):
  35             return degrees(self._rad)
  36         def fset(self,angle):
  37             if isinstance(angle,Angle): angle = angle.deg
  38             self._rad = radians(angle)

Memoize

Here's a memoizing class.

   1 class memoized(object):
   2    """Decorator that caches a function's return value each time it is called.
   3    If called later with the same arguments, the cached value is returned, and
   4    not re-evaluated.
   5    """
   6    def __init__(self, func):
   7       self.func = func
   8       self.cache = {}
   9    def __call__(self, *args):
  10       try:
  11          return self.cache[args]
  12       except KeyError:
  13          self.cache[args] = value = self.func(*args)
  14          return value
  15       except TypeError:
  16          # uncachable -- for instance, passing a list as an argument.
  17          # Better to not cache than to blow up entirely.
  18          return self.func(*args)
  19    def __repr__(self):
  20       """Return the function's docstring."""
  21       return self.func.__doc__
  22                         
  23 @memoized
  24 def fibonacci(n):
  25    "Return the nth fibonacci number."
  26    if n in (0, 1):
  27       return n
  28    return fibonacci(n-1) + fibonacci(n-2)
  29 
  30 print fibonacci(12)

Pseudo-currying

   1 class curried(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 
   8   def __init__(self, func, *a):
   9     self.func = func
  10     self.args = a
  11   def __call__(self, *a):
  12     args = self.args + a
  13     if len(args) < self.func.func_code.co_argcount:
  14       return curried(self.func, *args)
  15     else:
  16       return self.func(*args)
  17 
  18 
  19 @curried
  20 def add(a, b):
  21     return a+b
  22 
  23 add1 = add(1)
  24 
  25 print add1(2)

Controllable DIY debug

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

   1 import sys
   2 
   3 WHAT_TO_DEBUG = set(['io', 'core'])  # change to what you need
   4 
   5 class debug:
   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     """
  10     def __init__(self, aspects=None):
  11         self.aspects = set(aspects)
  12 
  13     def __call__(self, f):
  14         if self.aspects & WHAT_TO_DEBUG:
  15             def newf(*args, **kwds):
  16                 print >> sys.stderr, f.func_name, args, kwds
  17                 f_result = f(*args, **kwds)
  18                 print >> sys.stderr, f.func_name, "returned", f_result
  19                 return f_result
  20             newf.__doc__ = f.__doc__
  21             return newf
  22         else:
  23             return f
  24 
  25 @debug(['io'])
  26 def prn(x):
  27     print x
  28 
  29 @debug(['core'])
  30 def mult(x, y):
  31     return x * y
  32 
  33 prn(mult(2,2))

Easy adding methods to a class instance

Credits to John Roth.

   1 class Foo:
   2     def __init__(self):
   3         self.x = 42
   4 
   5 foo = Foo()
   6 
   7 def addto(instance):
   8     def decorator(f):
   9         import new
  10         f = new.instancemethod(f, instance, instance.__class__)
  11         setattr(instance, f.func_name, f)
  12         return f
  13     return decorator
  14 
  15 @addto(foo)
  16 def print_x(self):
  17     print self.x
  18 
  19 # foo.print_x() would print "42"

Counting function calls

   1 class countcalls(object):
   2    "Decorator that keeps track of the number of times a function is called."
   3    
   4    __instances = {}
   5    
   6    def __init__(self, f):
   7       self.__f = f
   8       self.__numCalls = 0
   9       countcalls.__instances[f] = self
  10       
  11    def __call__(self, *args, **kwargs):
  12       self.__numCalls += 1
  13       return self.__f(*args, **kwargs)
  14       
  15    @staticmethod
  16    def count(f):
  17       "Return the number of times the function f was called."
  18       return countcalls.__instances[f].__numCalls
  19       
  20    @staticmethod
  21    def counts():
  22       "Return a dict of {function: # of calls} for all registered functions."
  23       return dict([(f, countcalls.count(f)) for f in countcalls.__instances])

Generating Deprecation Warnings

   1 import warnings
   2 
   3 def deprecated(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."""
   7     def newFunc(*args, **kwargs):
   8         warnings.warn("Call to deprecated function %s." % func.__name__,
   9                       category=DeprecationWarning)
  10         return func(*args, **kwargs)
  11     newFunc.__name__ = func.__name__
  12     newFunc.__doc__ = func.__doc__
  13     newFunc.__dict__.update(func.__dict__)
  14     return newFunc
  15 
  16 # === Examples of use ===
  17 
  18 @deprecated
  19 def some_old_function(x,y):
  20     return x + y
  21 
  22 class SomeClass:
  23     @deprecated
  24     def some_old_method(self, x,y):
  25         return x + y

Creating Well-Behaved Decorators

Note: This is only one recipe. Others include inheritance from a standard decorator (link?) and a factory function such as [http://www.phyast.pitt.edu/~micheles/python/decorator.zip Michele Simionato's decorator module] which even preserves signature information.

   1 def simple_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."""
  11     def new_decorator(f):
  12         g = decorator(f)
  13         g.__name__ = f.__name__
  14         g.__doc__ = f.__doc__
  15         g.__dict__.update(f.__dict__)
  16         return g
  17     # Now a few lines needed to make simple_decorator itself
  18     # be a well-behaved decorator.
  19     new_decorator.__name__ = decorator.__name__
  20     new_decorator.__doc__ = decorator.__doc__
  21     new_decorator.__dict__.update(decorator.__dict__)
  22     return new_decorator
  23 
  24 #
  25 # Sample Use:
  26 #
  27 @simple_decorator
  28 def mySimpleLoggingDecorator( func ):
  29     def logged_func( *args, **kwargs ):
  30         print 'calling %s' % func.__name__
  31         return func( *args, **kwargs )
  32     return logged_func
  33 
  34 @mySimpleLoggingDecorator
  35 def double(x):
  36     "Doubles a number"
  37     return 2*x
  38 
  39 assert double.__name__ == 'double'
  40 assert double.__doc__ == 'Doubles a number'
  41 print double(155)

Enable/Disable Decorators

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

Easy Dump of Function Arguments

   1 def dumpArgs(func):
   2     "This decorator dumps out the arguments passed to a function before calling it"
   3     argnames = func.func_code.co_varnames[:func.func_code.co_argcount]
   4     fname = func.func_name
   5     def echoFunc(*args,**kwargs):
   6         print fname, ":", ', '.join('%s=%r' % entry 
   7                                     for entry in zip(argnames,args) + kwargs.items())
   8         return func(*args, **kwargs)
   9     return echoFunc
  10     
  11 @dumpArgs
  12 def f1(a,b,c):
  13     print a + b + c
  14     
  15 f1(1, 2, 3)

Pre-/Post-Conditions

   1 """
   2 Provide pre-/postconditions as function decorators.
   3 
   4 Example 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 
  28 You can define as many pre-/postconditions for a function as you
  29 like. 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 
  40 An interesting feature is the ability to prevent the creation of
  41 pre-/postconditions at function definition time. This makes it
  42 possible to use conditions for debugging and then switch them off for
  43 distribution.
  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 
  56 DEFAULT_ON = True
  57 
  58 def precondition(precondition, use_conditions=DEFAULT_ON):
  59     return conditions(precondition, None, use_conditions)
  60 
  61 def postcondition(postcondition, use_conditions=DEFAULT_ON):
  62     return conditions(None, postcondition, use_conditions)
  63 
  64 class conditions(object):
  65     __slots__ = ('__precondition', '__postcondition')
  66 
  67     def __init__(self, pre, post, use_conditions=DEFAULT_ON):
  68         if not use_conditions:
  69             pre, post = None, None
  70 
  71         self.__precondition  = pre
  72         self.__postcondition = post
  73 
  74     def __call__(self, function):
  75         # combine recursive wrappers (@precondition + @postcondition == @conditions)
  76         pres  = set( (self.__precondition,) )
  77         posts = set( (self.__postcondition,) )
  78 
  79         # unwrap function, collect distinct pre-/post conditions
  80         while type(function) is FunctionWrapper:
  81             pres.add(function._pre)
  82             posts.add(function._post)
  83             function = function._func
  84 
  85         # filter out None conditions and build pairs of pre- and postconditions
  86         conditions = map(None, filter(None, pres), filter(None, posts))
  87 
  88         # add a wrapper for each pair (note that 'conditions' may be empty)
  89         for pre, post in conditions:
  90             function = FunctionWrapper(pre, post, function)
  91 
  92         return function
  93 
  94 class FunctionWrapper(object):
  95     def __init__(self, precondition, postcondition, function):
  96         self._pre  = precondition
  97         self._post = postcondition
  98         self._func = function
  99 
 100     def __call__(self, *args, **kwargs):
 101         precondition  = self._pre
 102         postcondition = self._post
 103 
 104         if precondition:
 105             precondition(*args, **kwargs)
 106         result = self._func(*args, **kwargs)
 107         if postcondition:
 108             postcondition(result, *args, **kwargs)
 109         return result
 110 
 111 def __test():
 112     import doctest
 113     doctest.testmod()
 114 
 115 if __name__ == "__main__":
 116     __test()

Decorator decorator

For those decorators that return local functions, this will copy attributes from the original function object:

   1 import warnings
   2 def decorating(func, doc=''):
   3     def provideatts(newFunc, ):
   4         newFunc.__name__ = func.__name__
   5         newFunc.__doc__ = (func.__doc__ or '') + '\n' + doc
   6         newFunc.__dict__.update(func.__dict__)
   7         return newFunc
   8     return provideatts
   9 
  10 # Example use:
  11 def deprecated(func):
  12     @decorating(func, doc='(Deprecated.)')
  13     def you_will_never_see_this_name(*args, **kwargs):
  14         warnings.warn("Call to deprecated function %s." % func.__name__,
  15                           category=DeprecationWarning)
  16         return func(*args, **kwargs)
  17     return you_will_never_see_this_name

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.

   1 import sys
   2 import os
   3 import linecache
   4 
   5 def trace(f):
   6     def globaltrace(frame, why, arg):
   7         if why == "call":
   8             return localtrace
   9         return None
  10 
  11     def localtrace(frame, why, arg):
  12         if why == "line":
  13             # record the file name and line number of every trace
  14             filename = frame.f_code.co_filename
  15             lineno = frame.f_lineno
  16 
  17             bname = os.path.basename(filename)
  18             print "%s(%d): %s" % (bname, lineno,
  19                                   linecache.getline(filename, lineno)),
  20         return localtrace
  21 
  22     def _f(*args, **kwds):
  23         sys.settrace(globaltrace)
  24         result = f(*args, **kwds)
  25         sys.settrace(None)
  26         return result
  27 
  28     return _f

PythonDecoratorLibrary (last edited 2017-07-04 09:44:35 by mjpieters)

Unable to edit the page? See the FrontPage for instructions.