diff options
| author | michele.simionato <devnull@localhost> | 2010-01-21 05:56:23 +0000 |
|---|---|---|
| committer | michele.simionato <devnull@localhost> | 2010-01-21 05:56:23 +0000 |
| commit | 4ccf1399ef31e1860b8e89f2d04b4e116ac5889b (patch) | |
| tree | 545d7e3b9ef258b19f90be0e2ca9f63f36d865b1 | |
| parent | 1182961a2b82500a00f948437fa7f8736537c521 (diff) | |
| download | python-decorator-git-4ccf1399ef31e1860b8e89f2d04b4e116ac5889b.tar.gz | |
Work for decorator version 3.2
| -rwxr-xr-x | CHANGES.txt | 3 | ||||
| -rwxr-xr-x | README.txt | 4 | ||||
| -rwxr-xr-x | decorator.py | 88 | ||||
| -rw-r--r-- | documentation.py | 120 | ||||
| -rw-r--r-- | setup.py | 2 |
5 files changed, 48 insertions, 169 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 0827995..a18d568 100755 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,9 @@ HISTORY ---------- +3.2. Added __version__, removed functionality which has been deprecated for + more than one year, removed the confusing decorator_factory example + from the documentation (21/01/2010) 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) @@ -15,6 +15,6 @@ Run $ python documentation.py
-This should work perfectly with Python 2.4 and Python 2.5 and give
-minor errors with Python 2.6 (some inner details such as the
+This should work perfectly with Python 2.7 and Python 2.6 and give a few
+expected errors with Python 2.5 and 2.4 (some inner details such as the
introduction of the ArgSpec namedtuple and Thread.__repr__ changed).
diff --git a/decorator.py b/decorator.py index 091d6ff..7d511fa 100755 --- a/decorator.py +++ b/decorator.py @@ -28,8 +28,9 @@ Decorator module, see http://pypi.python.org/pypi/decorator for the documentation. """ -__all__ = ["decorator", "FunctionMaker", "partial", - "deprecated", "getinfo", "new_wrapper"] +__version__ = '3.2.0' + +__all__ = ["decorator", "FunctionMaker", "partial"] import os, sys, re, inspect, string, warnings try: @@ -168,86 +169,3 @@ def decorator(caller, func=None): 'return decorator(_call_, %s)' % f, dict(_call_=caller, decorator=decorator), undecorated=caller, doc=caller.__doc__, module=caller.__module__) - -###################### deprecated functionality ######################### - -@decorator -def deprecated(func, *args, **kw): - "A decorator for deprecated functions" - warnings.warn( - ('Calling the deprecated function %r\n' - 'Downgrade to decorator 2.3 if you want to use this functionality') - % func.__name__, DeprecationWarning, stacklevel=3) - return func(*args, **kw) - -@deprecated -def getinfo(func): - """ - Returns an info dictionary containing: - - name (the name of the function : str) - - argnames (the names of the arguments : list) - - defaults (the values of the default arguments : tuple) - - signature (the signature : str) - - doc (the docstring : str) - - module (the module name : str) - - dict (the function __dict__ : str) - - >>> def f(self, x=1, y=2, *args, **kw): pass - - >>> info = getinfo(f) - - >>> info["name"] - 'f' - >>> info["argnames"] - ['self', 'x', 'y', 'args', 'kw'] - - >>> info["defaults"] - (1, 2) - - >>> info["signature"] - 'self, x, y, *args, **kw' - """ - assert inspect.ismethod(func) or inspect.isfunction(func) - regargs, varargs, varkwargs, defaults = inspect.getargspec(func) - argnames = list(regargs) - if varargs: - argnames.append(varargs) - if varkwargs: - argnames.append(varkwargs) - signature = inspect.formatargspec(regargs, varargs, varkwargs, defaults, - formatvalue=lambda value: "")[1:-1] - return dict(name=func.__name__, argnames=argnames, signature=signature, - defaults = func.func_defaults, doc=func.__doc__, - module=func.__module__, dict=func.__dict__, - globals=func.func_globals, closure=func.func_closure) - -@deprecated -def update_wrapper(wrapper, model, infodict=None): - "A replacement for functools.update_wrapper" - infodict = infodict or getinfo(model) - wrapper.__name__ = infodict['name'] - wrapper.__doc__ = infodict['doc'] - wrapper.__module__ = infodict['module'] - wrapper.__dict__.update(infodict['dict']) - wrapper.func_defaults = infodict['defaults'] - wrapper.undecorated = model - return wrapper - -@deprecated -def new_wrapper(wrapper, model): - """ - An improvement over functools.update_wrapper. The wrapper is a generic - callable object. It works by generating a copy of the wrapper with the - right signature and by updating the copy, not the original. - Moreovoer, 'model' can be a dictionary with keys 'name', 'doc', 'module', - 'dict', 'defaults'. - """ - if isinstance(model, dict): - infodict = model - else: # assume model is a function - infodict = getinfo(model) - assert not '_wrapper_' in infodict["argnames"], ( - '"_wrapper_" is a reserved argument name!') - src = "lambda %(signature)s: _wrapper_(%(signature)s)" % infodict - funcopy = eval(src, dict(_wrapper_=wrapper)) - return update_wrapper(funcopy, model, infodict) diff --git a/documentation.py b/documentation.py index 96b3d45..cdd4fae 100644 --- a/documentation.py +++ b/documentation.py @@ -119,8 +119,8 @@ keyword arguments: .. code-block:: python >>> from inspect import getargspec - >>> print getargspec(f1) - ([], 'args', 'kw', None) + >>> print getargspec(f1) + ArgSpec(args=[], varargs='args', keywords='kw', defaults=None) This means that introspection tools such as pydoc will give wrong informations about the signature of ``f1``. This is pretty bad: @@ -186,7 +186,7 @@ The signature of ``heavy_computation`` is the one you would expect: .. code-block:: python >>> print getargspec(heavy_computation) - ([], None, None, None) + ArgSpec(args=[], varargs=None, keywords=None, defaults=None) A ``trace`` decorator ------------------------------------------------------ @@ -219,7 +219,7 @@ and it that it has the correct signature: .. code-block:: python >>> print getargspec(f1) - (['x'], None, None, None) + ArgSpec(args=['x'], varargs=None, keywords=None, defaults=None) The same decorator works with functions of any signature: @@ -233,7 +233,7 @@ The same decorator works with functions of any signature: calling f with args (0, 3, 2), {} >>> print getargspec(f) - (['x', 'y', 'z'], 'args', 'kw', (1, 2)) + ArgSpec(args=['x', 'y', 'z'], varargs='args', keywords='kw', defaults=(1, 2)) That includes even functions with exotic signatures like the following: @@ -243,7 +243,7 @@ That includes even functions with exotic signatures like the following: ... def exotic_signature((x, y)=(1,2)): return x+y >>> print getargspec(exotic_signature) - ([['x', 'y']], None, None, ((1, 2),)) + ArgSpec(args=[['x', 'y']], varargs=None, keywords=None, defaults=((1, 2),)) >>> exotic_signature() calling exotic_signature with args ((1, 2),), {} 3 @@ -297,49 +297,6 @@ If you are using an old Python version (Python 2.4) the ``decorator`` module provides a poor man replacement for ``functools.partial``. -There is also an easy way to create one-parameter factories of -decorators, based on the following -``decorator_factory`` utility: - -$$decorator_factory - -``decorator_factory`` converts a function with signature -``(param, func, *args, **kw)`` into a one-parameter family -of decorators. Suppose for instance you want to generated different -tracing generator, with different tracing messages. -Here is how to do it: - -.. code-block:: python - - >>> @decorator_factory - ... def trace_factory(message_template, f, *args, **kw): - ... name = f.func_name - ... print message_template % locals() - ... return f(*args, **kw) - -.. code-block:: python - - >>> trace_factory # doctest: +ELLIPSIS - <functools.partial object at 0x...> - - >>> trace = trace_factory('Calling %(name)s with args %(args)s ' - ... 'and keywords %(kw)s') - -In this example the parameter (``message_template``) is -just a string, but in general it can be a tuple, a dictionary, or -a generic object, so there is no real restriction (for instance, -if you want to define a two-parameter family of decorators just -use a tuple with two arguments as parameter). -Here is an example of usage: - -.. code-block:: python - - >>> @trace - ... def func(): pass - - >>> func() - Calling func with args () and keywords {} - ``blocking`` ------------------------------------------- @@ -794,10 +751,6 @@ Finally ``decorator`` cannot be used as a class decorator and the `functionality introduced in version 2.3`_ has been removed. That means that in order to define decorator factories with classes you need to define the ``__call__`` method explicitly (no magic anymore). -Since there is -an easy way to define decorator factories by using ``decorator_factory``, -there is less need to use classes to implement decorator factories. - All these changes should not cause any trouble, since they were all rarely used features. Should you have any trouble, you can always downgrade to the 2.3 version. @@ -866,10 +819,6 @@ today = time.strftime('%Y-%m-%d') __doc__ = __doc__.replace('$VERSION', VERSION).replace('$DATE', today) -def decorator_factory(decfac): # partial is functools.partial - "decorator_factory(decfac) returns a one-parameter family of decorators" - return partial(lambda df, param: decorator(partial(df, param)), decfac) - def decorator_apply(dec, func): """ Decorate a function by preserving the signature even if dec @@ -972,19 +921,20 @@ def _memoize(func, *args, **kw): def memoize(f): f.cache = {} return decorator(_memoize, f) - -@decorator_factory -def blocking(not_avail, f, *args, **kw): - if not hasattr(f, "thread"): # no thread running - def set_result(): f.result = f(*args, **kw) - f.thread = threading.Thread(None, set_result) - f.thread.start() - return not_avail - elif f.thread.isAlive(): - return not_avail - else: # the thread is ended, return the stored result - del f.thread - return f.result + +def blocking(not_avail): + def blocking(f, *args, **kw): + if not hasattr(f, "thread"): # no thread running + def set_result(): f.result = f(*args, **kw) + f.thread = threading.Thread(None, set_result) + f.thread.start() + return not_avail + elif f.thread.isAlive(): + return not_avail + else: # the thread is ended, return the stored result + del f.thread + return f.result + return decorator(blocking) class User(object): "Will just be able to see a page" @@ -1001,16 +951,17 @@ def get_userclass(): class PermissionError(Exception): pass -@decorator_factory -def restricted(user_class, func, *args, **kw): - "Restrict access to a given class of users" - userclass = get_userclass() - if issubclass(userclass, user_class): - return func(*args, **kw) - else: - raise PermissionError( - '%s does not have the permission to run %s!' - % (userclass.__name__, func.__name__)) +def restricted(user_class): + def restricted(func, *args, **kw): + "Restrict access to a given class of users" + userclass = get_userclass() + if issubclass(userclass, user_class): + return func(*args, **kw) + else: + raise PermissionError( + '%s does not have the permission to run %s!' + % (userclass.__name__, func.__name__)) + return decorator(restricted) class Action(object): """ @@ -1077,7 +1028,6 @@ def fact(n): # this is not tail-recursive if n == 0: return 1 return n * fact(n-1) - def atest_for_pylons(): """ In version 3.1.0 decorator(caller) returned a nameless partial @@ -1092,5 +1042,13 @@ def atest_for_pylons(): 'A decorator for deprecated functions' """ +@decorator +def deprecated(func, *args, **kw): + "A decorator for deprecated functions" + warnings.warn( + 'Calling the deprecated function %r\n' % func.__name__, + DeprecationWarning, stacklevel=3) + return func(*args, **kw) + if __name__ == '__main__': import doctest; doctest.testmod() @@ -3,7 +3,7 @@ try: except ImportError: from distutils.core import setup -VERSION = '3.1.2' +from decorator import __version__ as VERSION if __name__ == '__main__': try: |
