Ticket #6700: smart_decorator.diff

File smart_decorator.diff, 3.9 KB (added by Chris Beaven, 16 years ago)
  • django/utils/decorators.py

     
    1 "Functions that help with dynamically creating decorators for views."
     1"Functions that help with creating decorators."
    22
    33import types
     4from inspect import getargspec
    45try:
    56    from functools import wraps
    67except ImportError:
    78    from django.utils.functional import wraps  # Python 2.3, 2.4 fallback.
    89
     10def smart_decorator(decorator):
     11    """
     12    Smartens up "decorators" which accept additional arguments.
     13
     14    It allows the decorator to be called correctly in any of the following
     15    formats::
     16
     17        @my_smartened_decorator(arg1, arg2)
     18        my_func = my_smartened_decorator(arg1, arg2)(my_func)
     19        my_func = my_smartened_decorator(my_func, arg1, arg2)
     20
     21    If all additional decorator arguments have defaults, it also allows for the
     22    following formats::
     23
     24        @my_smartened_decorator
     25        my_func = my_smartened_decorator(my_func)
     26    """
     27    def _simple_dec(function):
     28        return decorator(function)
     29    def _dec_with_args(*args, **kwargs):
     30        def _inner_dec(function):
     31            return decorator(function, *args, **kwargs)
     32        return wraps(decorator)(_inner_dec)
     33    def _smart_dec(function_or_value, *args, **kwargs):
     34        if not isinstance(function_or_value, types.FunctionType):
     35            args = (function_or_value,) + args
     36            return _dec_with_args(*args, **kwargs)
     37        return decorator(function_or_value, *args, **kwargs)
     38    args, varargs, varkw, defaults = getargspec(decorator)
     39    assert args, 'Decorators must take at least one argument'
     40    if not varargs and not varkw:
     41        if len(args) == 1:
     42            return wraps(decorator)(_simple_dec)
     43        if len(defaults) == len(args) - 1:
     44            return wraps(decorator)(_smart_dec)
     45    return wraps(decorator)(_dec_with_args)
     46
    947def decorator_from_middleware(middleware_class):
    1048    """
    1149    Given a middleware class (not an instance), returns a view decorator. This
  • tests/regressiontests/utils/decorators.py

     
     1r"""
     2>>> from django.utils.decorators import smart_decorator, wraps
     3
     4>>> def ping(extra_text=''):
     5...     "Simple ping method"
     6...     print extra_text or 'ping'
     7
     8>>> def my_dec(func, arg='arg1', arg2=None):
     9...     print arg
     10...     if arg2:
     11...         print 'arg2:', arg2
     12...     def _dec(*args, **kwargs):
     13...         return func(*args, **kwargs)
     14...     return wraps(func)(_dec)
     15>>> my_dec = smart_decorator(my_dec)
     16
     17# It accepts just the function
     18>>> p1 = my_dec(ping)
     19arg1
     20
     21# ... or the function and arguments
     22>>> p2 = my_dec(ping, 'two')
     23two
     24>>> p2 = my_dec(ping, arg2='two')
     25arg1
     26arg2: two
     27
     28# ... or the 2.4 syntax of (arguments)(function)
     29>>> p3 = my_dec('three')(ping)
     30three
     31
     32# In any of those cases, the actual decorated function still works fine:
     33>>> p1()
     34ping
     35>>> p1('pong')
     36pong
     37>>> p1(extra_text='pang')
     38pang
     39>>> p2()
     40ping
     41>>> p2('pong')
     42pong
     43>>> p2(extra_text='pang')
     44pang
     45>>> p3()
     46ping
     47>>> p3('pong')
     48pong
     49>>> p3(extra_text='pang')
     50pang
     51
     52>>> p1.__doc__
     53'Simple ping method'
     54>>> p2.__doc__
     55'Simple ping method'
     56>>> p3.__doc__
     57'Simple ping method'
     58"""
  • tests/regressiontests/utils/tests.py

     
    88
    99import timesince
    1010import datastructures
     11import decorators
    1112
    1213# Extra tests
    1314__test__ = {
    1415    'timesince': timesince,
    1516    'datastructures': datastructures,
     17    'decorators': decorators,
    1618}
    1719
    1820class TestUtilsHtml(TestCase):
Back to Top