浏览代码

Simplified @stringfilter decorator and Library with unwrap().

Nowadays we can use inspect.unwrap() to retrieve the innermost function
object when needed, and most of the uses of _decorated_function were to
access the original __name__ which is not needed because
@functools.wraps sets that attribute correctly.
Baptiste Mispelon 3 年之前
父节点
当前提交
3d7ac6420c
共有 2 个文件被更改,包括 14 次插入20 次删除
  1. 9 13
      django/template/defaultfilters.py
  2. 5 7
      django/template/library.py

+ 9 - 13
django/template/defaultfilters.py

@@ -4,6 +4,7 @@ import re
 import types
 from decimal import ROUND_HALF_UP, Context, Decimal, InvalidOperation
 from functools import wraps
+from inspect import unwrap
 from operator import itemgetter
 from pprint import pformat
 from urllib.parse import quote
@@ -37,20 +38,15 @@ def stringfilter(func):
     Decorator for filters which should only receive strings. The object
     passed as the first positional argument will be converted to a string.
     """
-    def _dec(*args, **kwargs):
-        args = list(args)
-        args[0] = str(args[0])
-        if (isinstance(args[0], SafeData) and
-                getattr(_dec._decorated_function, 'is_safe', False)):
-            return mark_safe(func(*args, **kwargs))
-        return func(*args, **kwargs)
+    @wraps(func)
+    def _dec(first, *args, **kwargs):
+        first = str(first)
+        result = func(first, *args, **kwargs)
+        if isinstance(first, SafeData) and getattr(unwrap(func), 'is_safe', False):
+            result = mark_safe(result)
+        return result
 
-    # Include a reference to the real function (used to check original
-    # arguments by the template parser, and to bear the 'is_safe' attribute
-    # when multiple decorators are applied).
-    _dec._decorated_function = getattr(func, '_decorated_function', func)
-
-    return wraps(func)(_dec)
+    return _dec
 
 
 ###################

+ 5 - 7
django/template/library.py

@@ -48,7 +48,7 @@ class Library:
             )
 
     def tag_function(self, func):
-        self.tags[getattr(func, "_decorated_function", func).__name__] = func
+        self.tags[func.__name__] = func
         return func
 
     def filter(self, name=None, filter_func=None, **flags):
@@ -83,8 +83,7 @@ class Library:
                     setattr(filter_func, attr, value)
                     # set the flag on the innermost decorated function
                     # for decorators that need it, e.g. stringfilter
-                    if hasattr(filter_func, "_decorated_function"):
-                        setattr(filter_func._decorated_function, attr, value)
+                    setattr(unwrap(filter_func), attr, value)
             filter_func._filter_name = name
             return filter_func
         else:
@@ -94,8 +93,7 @@ class Library:
             )
 
     def filter_function(self, func, **flags):
-        name = getattr(func, "_decorated_function", func).__name__
-        return self.filter(name, func, **flags)
+        return self.filter(func.__name__, func, **flags)
 
     def simple_tag(self, func=None, takes_context=None, name=None):
         """
@@ -107,7 +105,7 @@ class Library:
         """
         def dec(func):
             params, varargs, varkw, defaults, kwonly, kwonly_defaults, _ = getfullargspec(unwrap(func))
-            function_name = (name or getattr(func, '_decorated_function', func).__name__)
+            function_name = (name or func.__name__)
 
             @functools.wraps(func)
             def compile_func(parser, token):
@@ -144,7 +142,7 @@ class Library:
         """
         def dec(func):
             params, varargs, varkw, defaults, kwonly, kwonly_defaults, _ = getfullargspec(unwrap(func))
-            function_name = (name or getattr(func, '_decorated_function', func).__name__)
+            function_name = name or func.__name__
 
             @functools.wraps(func)
             def compile_func(parser, token):