Django

Code

Changeset 7153

Show
Ignore:
Timestamp:
02/25/08 00:02:35 (6 months ago)
Author:
gwilson
Message:

Fixed #5701 -- Fixed decorators to take the name, attributes, and docstring of the function they decorate by adding a modified version of the functools.wraps function from Python 2.5. wraps has been altered to work with Django's curry function and with Python 2.3, which doesn't allow assignment of a function's __name__ attribute. This fixes severaly annoyances, such as the online documentation for template filters served by the admin app. This change is backwards incompatible if, for some reason, you were relying on the name of a Django decorator instead of the function it decorates.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/contrib/admin/views/decorators.py

    r7131 r7153  
     1import base64 
     2import md5 
     3import cPickle as pickle 
     4try: 
     5    from functools import wraps 
     6except ImportError: 
     7    from django.utils.functional import wraps  # Python 2.3, 2.4 fallback. 
     8 
    19from django import http, template 
    210from django.conf import settings 
     
    614from django.utils.translation import ugettext_lazy, ugettext as _ 
    715from django.utils.safestring import mark_safe 
    8 import base64, md5 
    9 import cPickle as pickle 
    1016 
    1117ERROR_MESSAGE = ugettext_lazy("Please enter a correct username and password. Note that both fields are case-sensitive.") 
     
    105111                return _display_login_form(request, ERROR_MESSAGE) 
    106112 
    107     return _checklogin 
     113    return wraps(view_func)(_checklogin) 
  • django/trunk/django/contrib/auth/decorators.py

    r6659 r7153  
     1try: 
     2    from functools import wraps, update_wrapper 
     3except ImportError: 
     4    from django.utils.functional import wraps, update_wrapper  # Python 2.3, 2.4 fallback. 
     5 
    16from django.contrib.auth import REDIRECT_FIELD_NAME 
    27from django.http import HttpResponseRedirect 
     
    5257        self.login_url = login_url 
    5358        self.redirect_field_name = redirect_field_name 
    54         self.__name__ = view_func.__name__ 
     59        update_wrapper(self, view_func) 
    5560         
    5661    def __get__(self, obj, cls=None): 
  • django/trunk/django/template/defaultfilters.py

    r7033 r7153  
    33import re 
    44import random as random_module 
     5try: 
     6    from functools import wraps 
     7except ImportError: 
     8    from django.utils.functional import wraps  # Python 2.3, 2.4 fallback. 
    59 
    610from django.template import Variable, Library 
     
    3640        if hasattr(func, attr): 
    3741            setattr(_dec, attr, getattr(func, attr)) 
    38     return _dec 
     42    return wraps(func)(_dec) 
    3943 
    4044################### 
  • django/trunk/django/utils/decorators.py

    r5631 r7153  
    22 
    33import types 
     4try: 
     5    from functools import wraps 
     6except ImportError: 
     7    from django.utils.functional import wraps  # Python 2.3, 2.4 fallback. 
    48 
    59def decorator_from_middleware(middleware_class): 
     
    5458                    return result 
    5559            return response 
    56         return _wrapped_view 
     60        return wraps(view_func)(_wrapped_view) 
    5761    return _decorator_from_middleware 
  • django/trunk/django/utils/functional.py

    r6587 r7153  
     1# License for code in this file that was taken from Python 2.5. 
     2 
     3# PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 
     4# -------------------------------------------- 
     5# 
     6# 1. This LICENSE AGREEMENT is between the Python Software Foundation 
     7# ("PSF"), and the Individual or Organization ("Licensee") accessing and 
     8# otherwise using this software ("Python") in source or binary form and 
     9# its associated documentation. 
     10# 
     11# 2. Subject to the terms and conditions of this License Agreement, PSF 
     12# hereby grants Licensee a nonexclusive, royalty-free, world-wide 
     13# license to reproduce, analyze, test, perform and/or display publicly, 
     14# prepare derivative works, distribute, and otherwise use Python 
     15# alone or in any derivative version, provided, however, that PSF's 
     16# License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 
     17# 2001, 2002, 2003, 2004, 2005, 2006, 2007 Python Software Foundation; 
     18# All Rights Reserved" are retained in Python alone or in any derivative 
     19# version prepared by Licensee. 
     20# 
     21# 3. In the event Licensee prepares a derivative work that is based on 
     22# or incorporates Python or any part thereof, and wants to make 
     23# the derivative work available to others as provided herein, then 
     24# Licensee hereby agrees to include in any such work a brief summary of 
     25# the changes made to Python. 
     26# 
     27# 4. PSF is making Python available to Licensee on an "AS IS" 
     28# basis.  PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR 
     29# IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND 
     30# DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS 
     31# FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT 
     32# INFRINGE ANY THIRD PARTY RIGHTS. 
     33# 
     34# 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 
     35# FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS 
     36# A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, 
     37# OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 
     38# 
     39# 6. This License Agreement will automatically terminate upon a material 
     40# breach of its terms and conditions. 
     41# 
     42# 7. Nothing in this License Agreement shall be deemed to create any 
     43# relationship of agency, partnership, or joint venture between PSF and 
     44# Licensee.  This License Agreement does not grant permission to use PSF 
     45# trademarks or trade name in a trademark sense to endorse or promote 
     46# products or services of Licensee, or any third party. 
     47# 
     48# 8. By copying, installing or otherwise using Python, Licensee 
     49# agrees to be bound by the terms and conditions of this License 
     50# Agreement. 
     51 
     52 
    153def curry(_curried_func, *args, **kwargs): 
    254    def _curried(*moreargs, **morekwargs): 
    355        return _curried_func(*(args+moreargs), **dict(kwargs, **morekwargs)) 
    456    return _curried 
     57 
     58### Begin from Python 2.5 functools.py ######################################## 
     59 
     60# Summary of changes made to the Python 2.5 code below: 
     61#   * swapped ``partial`` for ``curry`` to maintain backwards-compatibility 
     62#     in Django. 
     63#   * Wrapped the ``setattr`` call in ``update_wrapper`` with a try-except 
     64#     block to make it compatible with Python 2.3, which doesn't allow 
     65#     assigning to ``__name__``. 
     66 
     67# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Python Software Foundation. 
     68# All Rights Reserved. 
     69 
     70############################################################################### 
     71 
     72# update_wrapper() and wraps() are tools to help write 
     73# wrapper functions that can handle naive introspection 
     74 
     75WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__') 
     76WRAPPER_UPDATES = ('__dict__',) 
     77def update_wrapper(wrapper, 
     78                   wrapped, 
     79                   assigned = WRAPPER_ASSIGNMENTS, 
     80                   updated = WRAPPER_UPDATES): 
     81    """Update a wrapper function to look like the wrapped function 
     82 
     83       wrapper is the function to be updated 
     84       wrapped is the original function 
     85       assigned is a tuple naming the attributes assigned directly 
     86       from the wrapped function to the wrapper function (defaults to 
     87       functools.WRAPPER_ASSIGNMENTS) 
     88       updated is a tuple naming the attributes off the wrapper that 
     89       are updated with the corresponding attribute from the wrapped 
     90       function (defaults to functools.WRAPPER_UPDATES) 
     91    """ 
     92    for attr in assigned: 
     93        try: 
     94            setattr(wrapper, attr, getattr(wrapped, attr)) 
     95        except TypeError: # Python 2.3 doesn't allow assigning to __name__. 
     96            pass 
     97    for attr in updated: 
     98        getattr(wrapper, attr).update(getattr(wrapped, attr)) 
     99    # Return the wrapper so this can be used as a decorator via curry() 
     100    return wrapper 
     101 
     102def wraps(wrapped, 
     103          assigned = WRAPPER_ASSIGNMENTS, 
     104          updated = WRAPPER_UPDATES): 
     105    """Decorator factory to apply update_wrapper() to a wrapper function 
     106 
     107       Returns a decorator that invokes update_wrapper() with the decorated 
     108       function as the wrapper argument and the arguments to wraps() as the 
     109       remaining arguments. Default arguments are as for update_wrapper(). 
     110       This is a convenience function to simplify applying curry() to 
     111       update_wrapper(). 
     112    """ 
     113    return curry(update_wrapper, wrapped=wrapped, 
     114                 assigned=assigned, updated=updated) 
     115 
     116### End from Python 2.5 functools.py ########################################## 
    5117 
    6118def memoize(func, cache, num_args): 
     
    19131        cache[mem_args] = result 
    20132        return result 
    21     return wrapper 
     133    return wraps(func)(wrapper) 
    22134 
    23135class Promise(object): 
     
    111223        return __proxy__(args, kw) 
    112224 
    113     return __wrapper__ 
     225    return wraps(func)(__wrapper__) 
    114226 
    115227def allow_lazy(func, *resultclasses): 
     
    127239            return func(*args, **kwargs) 
    128240        return lazy(func, *resultclasses)(*args, **kwargs) 
    129     return wrapper 
     241    return wraps(func)(wrapper) 
  • django/trunk/django/views/decorators/cache.py

    r4265 r7153  
    1111account on caching -- just like the middleware does. 
    1212""" 
     13 
     14try: 
     15    from functools import wraps 
     16except ImportError: 
     17    from django.utils.functional import wraps  # Python 2.3, 2.4 fallback. 
    1318 
    1419from django.utils.decorators import decorator_from_middleware 
     
    2732            return response 
    2833 
    29         return _cache_controlled 
     34        return wraps(viewfunc)(_cache_controlled) 
    3035 
    3136    return _cache_controller 
     
    4045        add_never_cache_headers(response) 
    4146        return response 
    42     return _wrapped_view_func 
     47    return wraps(view_func)(_wrapped_view_func) 
  • django/trunk/django/views/decorators/http.py

    r4265 r7153  
    22Decorators for views based on HTTP headers. 
    33""" 
     4 
     5try: 
     6    from functools import wraps 
     7except ImportError: 
     8    from django.utils.functional import wraps  # Python 2.3, 2.4 fallback. 
    49 
    510from django.utils.decorators import decorator_from_middleware 
     
    2530                return HttpResponseNotAllowed(request_method_list) 
    2631            return func(request, *args, **kwargs) 
    27         return inner 
     32        return wraps(func)(inner) 
    2833    return decorator 
    2934 
  • django/trunk/django/views/decorators/vary.py

    r4265 r7153  
     1try: 
     2    from functools import wraps 
     3except ImportError: 
     4    from django.utils.functional import wraps  # Python 2.3, 2.4 fallback. 
     5 
    16from django.utils.cache import patch_vary_headers 
    27 
     
    1722            patch_vary_headers(response, headers) 
    1823            return response 
    19         return inner_func 
     24        return wraps(func)(inner_func) 
    2025    return decorator 
    2126 
     
    3338        patch_vary_headers(response, ('Cookie',)) 
    3439        return response 
    35     return inner_func 
     40    return wraps(func)(inner_func)