diff options
| author | michele.simionato <devnull@localhost> | 2009-08-26 05:46:33 +0000 |
|---|---|---|
| committer | michele.simionato <devnull@localhost> | 2009-08-26 05:46:33 +0000 |
| commit | 1182961a2b82500a00f948437fa7f8736537c521 (patch) | |
| tree | 00621aa6737e76927329320e5ce834d3fa841b8b | |
| parent | c8708c4276a7a06470226c85863ac958b574fcd8 (diff) | |
| download | python-decorator-git-1182961a2b82500a00f948437fa7f8736537c521.tar.gz | |
Release 3.1.2
| -rwxr-xr-x | CHANGES.txt | 3 | ||||
| -rw-r--r-- | Makefile | 4 | ||||
| -rwxr-xr-x | decorator.py | 23 | ||||
| -rw-r--r-- | documentation.py | 35 | ||||
| -rw-r--r-- | setup.py | 2 |
5 files changed, 51 insertions, 16 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 3dfed71..0827995 100755 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,9 @@ HISTORY ---------- +3.1.2. Added attributes args, varargs, keywords and arg0, ..., argN + to FunctionMaker objects generated from a function; fixed another + Pylons-breaking bug signaled by Lawrence Oluyede (25/08/2009) 3.1.1. Fixed a bug which was breaking Pylons, signaled by Gabriel de Perthuis, and added a test for it. (18/08/2009) 3.1. Added decorator.factory, an easy way to define families of decorators @@ -1,7 +1,7 @@ -RST=/home/micheles/trunk/ROnline/RCommon/Python/ms/tools/rst.py +RST=$(HOME)/trunk/ROnline/RCommon/Python/ms/tools/rst.py rst: documentation.py - python /home/micheles/trunk/ROnline/RCommon/Python/ms/tools/minidoc.py -dH documentation.py + python $(HOME)/trunk/ROnline/RCommon/Python/ms/tools/minidoc.py -dH documentation.py pdf: /tmp/documentation.rst $(RST) -ptd /tmp/documentation.rst; cp /tmp/documentation.pdf /tmp/documentation.html . diff --git a/decorator.py b/decorator.py index 5c81350..091d6ff 100755 --- a/decorator.py +++ b/decorator.py @@ -31,7 +31,7 @@ for the documentation. __all__ = ["decorator", "FunctionMaker", "partial", "deprecated", "getinfo", "new_wrapper"] -import os, sys, re, inspect, warnings +import os, sys, re, inspect, string, warnings try: from functools import partial except ImportError: # for Python version < 2.5 @@ -58,16 +58,19 @@ class FunctionMaker(object): def __init__(self, func=None, name=None, signature=None, defaults=None, doc=None, module=None, funcdict=None): if func: - # func can also be a class or a callable, but not an instance method + # func can be a class or a callable, but not an instance method self.name = func.__name__ if self.name == '<lambda>': # small hack for lambda functions self.name = '_lambda_' self.doc = func.__doc__ self.module = func.__module__ if inspect.isfunction(func): + argspec = inspect.getargspec(func) + self.args, self.varargs, self.keywords, self.defaults = argspec + for i, arg in enumerate(self.args): + setattr(self, 'arg%d' % i, arg) self.signature = inspect.formatargspec( - formatvalue=lambda val: "", *inspect.getargspec(func))[1:-1] - self.defaults = func.func_defaults + formatvalue=lambda val: "", *argspec)[1:-1] self.dict = func.__dict__.copy() if name: self.name = name @@ -95,7 +98,7 @@ class FunctionMaker(object): callermodule = sys._getframe(3).f_globals.get('__name__', '?') func.__module__ = getattr(self, 'module', callermodule) func.__dict__.update(kw) - + def make(self, src_templ, evaldict=None, addsource=False, **attrs): "Make a new function from a given template and update the signature" src = src_templ % vars(self) # expand name and signature @@ -125,7 +128,8 @@ class FunctionMaker(object): return func @classmethod - def create(cls, obj, body, evaldict, defaults=None, addsource=True,**attrs): + def create(cls, obj, body, evaldict, defaults=None, + doc=None, module=None, addsource=True,**attrs): """ Create a function from the strings name, signature and body. evaldict is the evaluation dictionary. If addsource is true an attribute @@ -134,13 +138,13 @@ class FunctionMaker(object): """ if isinstance(obj, str): # "name(signature)" name, rest = obj.strip().split('(', 1) - signature = rest[:-1] + signature = rest[:-1] #strip a right parens func = None else: # a function name = None signature = None func = obj - fun = cls(func, name, signature, defaults) + fun = cls(func, name, signature, defaults, doc, module) ibody = '\n'.join(' ' + line for line in body.splitlines()) return fun.make('def %(name)s(%(signature)s):\n' + ibody, evaldict, addsource, **attrs) @@ -162,7 +166,8 @@ def decorator(caller, func=None): return FunctionMaker.create( '%s(%s)' % (caller.__name__, f), 'return decorator(_call_, %s)' % f, - dict(_call_=caller, decorator=decorator), undecorated=caller) + dict(_call_=caller, decorator=decorator), undecorated=caller, + doc=caller.__doc__, module=caller.__module__) ###################### deprecated functionality ######################### diff --git a/documentation.py b/documentation.py index 192ebfb..96b3d45 100644 --- a/documentation.py +++ b/documentation.py @@ -428,12 +428,12 @@ Each call to ``write`` will create a new writer thread, but there will be no synchronization problems since ``write`` is locked. >>> write("data1") # doctest: +ELLIPSIS -<Thread(write-1, started ...)> +<Thread(write-1, started...)> >>> time.sleep(.1) # wait a bit, so we are sure data2 is written after data1 >>> write("data2") # doctest: +ELLIPSIS -<Thread(write-2, started ...)> +<Thread(write-2, started...)> >>> time.sleep(2) # wait for the writers to complete @@ -466,6 +466,9 @@ were the function is generated by ``exec``. Here is an example: >>> f1(1,2) (1, 2) {} +It is important to notice that the function body is interpolated +before being executed, so be careful with the ``%`` sign! + ``FunctionMaker.create`` also accepts keyword arguments and such arguments are attached to the resulting function. This is useful if you want to set some function attributes, for instance the @@ -474,7 +477,7 @@ docstring ``__doc__``. For debugging/introspection purposes it may be useful to see the source code of the generated function; to do that, just pass the flag ``addsource=True`` and a ``__source__`` attribute will -be added to the decorated function: +be added to the generated function: .. code-block:: python @@ -485,6 +488,24 @@ be added to the decorated function: f(a, b) <BLANKLINE> +``FunctionMaker.create`` can take as first argument a string, +as in the examples before, or a function. This is the most common +usage, since typically you want to decorate a pre-existing +function. A framework author may want to use directly ``FunctionMaker.create`` +instead of ``decorator``, since it gives you direct access to the body +of the generated function. For instance, suppose you want to instrument +the ``__init__`` methods of a set of classes, by preserving their +signature (such use case is not made up; this is done in SQAlchemy +and in other frameworks). When the first argument of ``FunctionMaker.create`` +is a function, a ``FunctionMaker`` object is instantiated internally, +with attributes ``args``, ``varargs``, +``keywords`` and ``defaults`` which are the +the return values of the standard library function ``inspect.getargspec``. +For each argument in the ``args`` (which is a list of strings containing +the names of the mandatory arguments) an attribute ``arg0``, ``arg1``, +..., ``argN`` is also generated. Finally, there is a ``signature`` +attribute, a string with the signature of the original function. + Notice that while I do not have plans to change or remove the functionality provided in the ``FunctionMaker`` class, I do not guarantee that it will stay @@ -1060,9 +1081,15 @@ def fact(n): # this is not tail-recursive def atest_for_pylons(): """ In version 3.1.0 decorator(caller) returned a nameless partial - object, thus breaking Pylons. That must not happen anymore. + object, thus breaking Pylons. That must not happen again. + >>> decorator(_memoize).__name__ '_memoize' + + Here is another bug of version 3.1.1 to avoid: + + >>> deprecated.__doc__ + 'A decorator for deprecated functions' """ if __name__ == '__main__': @@ -3,7 +3,7 @@ try: except ImportError: from distutils.core import setup -VERSION = '3.1.1' +VERSION = '3.1.2' if __name__ == '__main__': try: |
