diff options
| -rwxr-xr-x | decorator.py | 17 | ||||
| -rw-r--r-- | documentation.py | 75 |
2 files changed, 45 insertions, 47 deletions
diff --git a/decorator.py b/decorator.py index 2a5c090..79c320d 100755 --- a/decorator.py +++ b/decorator.py @@ -34,12 +34,13 @@ import os, sys, re, inspect, warnings try: from functools import partial except ImportError: # for Python version < 2.5 - def partial(func, *args): + class partial(object): "A poor man replacement of partial for use in the decorator module only" - f = lambda *otherargs: func(*(args + otherargs)) - f.args = args - f.func = func - return f + def __init__(self, func, *args): + self.func = func + self.args = args + def __call__(self, *otherargs): + return self.func(*(self.args + otherargs)) DEF = re.compile('\s*def\s*([_\w][_\w\d]*)\s*\(') @@ -152,12 +153,6 @@ def decorator(caller, func=None): else: # returns a decorator return partial(decorator, caller) -def decorator_factory(decfac): - "decorator.factory(decfac) returns a one-parameter family of decorators" - return partial(lambda df, param: decorator(partial(df, param)), decfac) - -decorator.factory = decorator_factory - ###################### deprecated functionality ######################### @decorator diff --git a/documentation.py b/documentation.py index 2452333..0005d9c 100644 --- a/documentation.py +++ b/documentation.py @@ -295,25 +295,28 @@ Here is an example of usage: If you are using an old Python version (Python 2.4) the ``decorator`` module provides a poor man replacement for -``functools.partial`` as a higher order function. +``functools.partial``. There is also an easy way to create one-parameter factories of -decorators. Suppose for instance you want to generated different +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 + >>> @decorator_factory ... def trace_factory(message_template, f, *args, **kw): ... name = f.func_name ... print message_template % locals() ... return f(*args, **kw) -``decorator.factory`` converts a function with signature -``(param, func, *args, **kw)`` into a one-parameter family -of decorators. - .. code-block:: python >>> trace_factory # doctest: +ELLIPSIS @@ -770,9 +773,9 @@ 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). -Starting from version 3.1 there is -an easy way to define decorator factories by using ``decorator.factory``, -so that there is less need to use classes to implement decorator factories. +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 @@ -834,7 +837,7 @@ you are unhappy with it, send me a patch! """ from __future__ import with_statement import sys, threading, time, functools, inspect, itertools -import multiprocessing +from functools import partial from decorator import * from setup import VERSION @@ -842,6 +845,10 @@ 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 @@ -945,7 +952,7 @@ def memoize(f): f.cache = {} return decorator(_memoize, f) -@decorator.factory +@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) @@ -973,40 +980,36 @@ def get_userclass(): class PermissionError(Exception): pass -class Restricted(object): - """ - Restrict public methods and functions to a given class of users. - If instantiated twice with the same userclass return the same - object. - """ - _cache = {} - def __new__(cls, userclass): - if userclass in cls._cache: - return cls._cache[userclass] - self = cls._cache[userclass] = super(Restricted, cls).__new__(cls) - self.userclass = userclass - return self - def call(self, func, *args, **kw): - userclass = get_userclass() - if issubclass(userclass, self.userclass): - return func(*args, **kw) - else: - raise PermissionError( +@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 __call__(self, func): - return decorator(self.call, func) class Action(object): - @Restricted(User) + """ + >>> a = Action() + >>> a.view() # ok + >>> a.insert() # err + Traceback (most recent call last): + ... + PermissionError: User does not have the permission to run insert! + + """ + @restricted(User) def view(self): pass - @Restricted(PowerUser) + @restricted(PowerUser) def insert(self): pass - @Restricted(Admin) + @restricted(Admin) def delete(self): pass |
