web/lib/django/utils/decorators.py
author ymh <ymh.work@gmail.com>
Wed, 02 Jun 2010 18:57:35 +0200
changeset 38 77b6da96e6f1
permissions -rw-r--r--
update django
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
38
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     1
"Functions that help with dynamically creating decorators for views."
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     2
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     3
import types
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     4
try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     5
    from functools import wraps, update_wrapper, WRAPPER_ASSIGNMENTS
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     6
except ImportError:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     7
    from django.utils.functional import wraps, update_wrapper, WRAPPER_ASSIGNMENTS  # Python 2.4 fallback.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     8
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     9
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    10
def method_decorator(decorator):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    11
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    12
    Converts a function decorator into a method decorator
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    13
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    14
    def _dec(func):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    15
        def _wrapper(self, *args, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    16
            def bound_func(*args2, **kwargs2):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    17
                return func(self, *args2, **kwargs2)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    18
            # bound_func has the signature that 'decorator' expects i.e.  no
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    19
            # 'self' argument, but it is a closure over self so it can call
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    20
            # 'func' correctly.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    21
            return decorator(bound_func)(*args, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    22
        return wraps(func)(_wrapper)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    23
    update_wrapper(_dec, decorator)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    24
    # Change the name to aid debugging.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    25
    _dec.__name__ = 'method_decorator(%s)' % decorator.__name__
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    26
    return _dec
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    27
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    28
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    29
def decorator_from_middleware_with_args(middleware_class):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    30
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    31
    Like decorator_from_middleware, but returns a function
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    32
    that accepts the arguments to be passed to the middleware_class.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    33
    Use like::
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    34
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    35
         cache_page = decorator_from_middleware_with_args(CacheMiddleware)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    36
         # ...
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    37
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    38
         @cache_page(3600)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    39
         def my_view(request):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    40
             # ...
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    41
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    42
    return make_middleware_decorator(middleware_class)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    43
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    44
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    45
def decorator_from_middleware(middleware_class):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    46
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    47
    Given a middleware class (not an instance), returns a view decorator. This
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    48
    lets you use middleware functionality on a per-view basis. The middleware
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    49
    is created with no params passed.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    50
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    51
    return make_middleware_decorator(middleware_class)()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    52
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    53
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    54
def available_attrs(fn):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    55
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    56
    Return the list of functools-wrappable attributes on a callable.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    57
    This is required as a workaround for http://bugs.python.org/issue3445.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    58
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    59
    return tuple(a for a in WRAPPER_ASSIGNMENTS if hasattr(fn, a))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    60
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    61
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    62
def make_middleware_decorator(middleware_class):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    63
    def _make_decorator(*m_args, **m_kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    64
        middleware = middleware_class(*m_args, **m_kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    65
        def _decorator(view_func):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    66
            def _wrapped_view(request, *args, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    67
                if hasattr(middleware, 'process_request'):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    68
                    result = middleware.process_request(request)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    69
                    if result is not None:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    70
                        return result
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    71
                if hasattr(middleware, 'process_view'):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    72
                    result = middleware.process_view(request, view_func, args, kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    73
                    if result is not None:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    74
                        return result
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    75
                try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    76
                    response = view_func(request, *args, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    77
                except Exception, e:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    78
                    if hasattr(middleware, 'process_exception'):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    79
                        result = middleware.process_exception(request, e)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    80
                        if result is not None:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    81
                            return result
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    82
                    raise
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    83
                if hasattr(middleware, 'process_response'):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    84
                    result = middleware.process_response(request, response)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    85
                    if result is not None:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    86
                        return result
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    87
                return response
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    88
            return wraps(view_func, assigned=available_attrs(view_func))(_wrapped_view)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    89
        return _decorator
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    90
    return _make_decorator