From 8f7a9ed288687da7b79ef6f67673b4020ed0d561 Mon Sep 17 00:00:00 2001 From: Michele Simionato Date: Sun, 28 Nov 2010 09:39:29 +0100 Subject: Fixed func_globals in the decorated function (release 3.2.1) --- decorator/CHANGES.txt | 2 ++ decorator/documentation.py | 6 ++++-- decorator/documentation3.py | 2 +- decorator/src/decorator.py | 34 ++++++++++++++++++++-------------- decorator/test.py | 24 ++++++++++++++++++++++++ 5 files changed, 51 insertions(+), 17 deletions(-) create mode 100644 decorator/test.py diff --git a/decorator/CHANGES.txt b/decorator/CHANGES.txt index 0b75d2c..30af4b4 100644 --- a/decorator/CHANGES.txt +++ b/decorator/CHANGES.txt @@ -1,6 +1,8 @@ HISTORY ---------- +3.2.1. Now the .func_globals of the decorated function are the same of + the undecorated function, as requested by Paul Ollis (28/12/2010) 3.2. Added __version__ (thanks to Gregg Lind), removed functionality which has been deprecated for years, removed the confusing decorator_factory example and added official support for Python 3 (requested by Claus Klein). diff --git a/decorator/documentation.py b/decorator/documentation.py index 6827ca9..4e9dfba 100644 --- a/decorator/documentation.py +++ b/decorator/documentation.py @@ -502,7 +502,7 @@ $$identity_dec (see bug report 1764286_ for an explanation of what is happening). -Unfortunately the bug is still there, even in Python 2.6 and 3.0. +Unfortunately the bug is still there, even in Python 2.7 and 3.1. There is however a workaround. The decorator module adds an attribute ``.undecorated`` to the decorated function, containing a reference to the original function. The easy way to get @@ -710,7 +710,9 @@ you will get a ``NameError``: def f(_func_): return _call_(_func_, _func_) -Finally, the implementation is such that the decorated function contains +Finally, the implementation is such that the decorated function +attribute ``.func_globals`` is a *copy* of the original function +attribute. Moreover the decorated function contains a *copy* of the original function dictionary (``vars(decorated_f) is not vars(f)``): diff --git a/decorator/documentation3.py b/decorator/documentation3.py index 94641ab..8e3c95d 100644 --- a/decorator/documentation3.py +++ b/decorator/documentation3.py @@ -486,7 +486,7 @@ $$identity_dec (see bug report 1764286_ for an explanation of what is happening). -Unfortunately the bug is still there, even in Python 2.6 and 3.0. +Unfortunately the bug is still there, even in Python 2.7 and 3.1. There is however a workaround. The decorator module adds an attribute ``.undecorated`` to the decorated function, containing a reference to the original function. The easy way to get diff --git a/decorator/src/decorator.py b/decorator/src/decorator.py index a005230..5077833 100644 --- a/decorator/src/decorator.py +++ b/decorator/src/decorator.py @@ -28,11 +28,11 @@ Decorator module, see http://pypi.python.org/pypi/decorator for the documentation. """ -__version__ = '3.2.0' +__version__ = '3.2.1' __all__ = ["decorator", "FunctionMaker", "partial"] -import os, sys, re, inspect +import sys, re, inspect try: from functools import partial @@ -109,13 +109,13 @@ class FunctionMaker(object): if mo is None: raise SyntaxError('not a valid function template\n%s' % src) name = mo.group(1) # extract the function name - reserved_names = set([name] + [ - arg.strip(' *') for arg in self.signature.split(',')]) - for n, v in evaldict.iteritems(): - if n in reserved_names: + names = set([name] + [arg.strip(' *') for arg in + self.signature.split(',')]) + for n in names: + if n in ('_func_', '_call_'): raise NameError('%s is overridden in\n%s' % (n, src)) if not src.endswith('\n'): # add a newline just for safety - src += '\n' + src += '\n' # this is needed in old versions of Python try: code = compile(src, '', 'single') exec code in evaldict @@ -146,9 +146,9 @@ class FunctionMaker(object): name = None signature = None func = obj - fun = cls(func, name, signature, defaults, doc, module) + self = 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, + return self.make('def %(name)s(%(signature)s):\n' + ibody, evaldict, addsource, **attrs) def decorator(caller, func=None): @@ -157,16 +157,22 @@ def decorator(caller, func=None): decorator(caller, func) decorates a function using a caller. """ if func is not None: # returns a decorated function + evaldict = func.func_globals.copy() + evaldict['_call_'] = caller + evaldict['_func_'] = func return FunctionMaker.create( func, "return _call_(_func_, %(signature)s)", - dict(_call_=caller, _func_=func), undecorated=func) + evaldict, undecorated=func) else: # returns a decorator if isinstance(caller, partial): return partial(decorator, caller) # otherwise assume caller is a function - f = inspect.getargspec(caller)[0][0] # first arg + first = inspect.getargspec(caller)[0][0] # first arg + evaldict = caller.func_globals.copy() + evaldict['_call_'] = caller + evaldict['decorator'] = decorator return FunctionMaker.create( - '%s(%s)' % (caller.__name__, f), - 'return decorator(_call_, %s)' % f, - dict(_call_=caller, decorator=decorator), undecorated=caller, + '%s(%s)' % (caller.__name__, first), + 'return decorator(_call_, %s)' % first, + evaldict, undecorated=caller, doc=caller.__doc__, module=caller.__module__) diff --git a/decorator/test.py b/decorator/test.py new file mode 100644 index 0000000..b21fe4d --- /dev/null +++ b/decorator/test.py @@ -0,0 +1,24 @@ +""" +Some simple tests executable with nose or py.test +""" + +import os +from decorator import decorator + +@decorator +def identity(f, *a, **k): + "do nothing decorator" + return f(*a, **k) + +@identity +def f1(): + "f1" + +def test0(): + assert os.path.basename(identity.func_globals['__file__']) == 'test.py' + print identity.__doc__ + +def test1(): + assert os.path.basename(f1.func_globals['__file__']) == 'test.py' + print f1.__doc__ + -- cgit v1.2.1