Fixed #29400: Allow @register.filter on already-decorated functions
diff --git a/AUTHORS b/AUTHORS
index 088bf04e8da8c..34e729bb1f53e 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -722,6 +722,7 @@ answer newbie questions, and generally made Django that much better:
     ryankanno
     Ryan Kelly <ryan@rfk.id.au>
     Ryan Niemeyer <https://profiles.google.com/ryan.niemeyer/about>
+    Ryan Rubin <ryanmrubin@gmail.com>
     Ryno Mathee <rmathee@gmail.com>
     Sam Newman <http://www.magpiebrain.com/>
     Sander Dijkhuis <sander.dijkhuis@gmail.com>
diff --git a/django/template/base.py b/django/template/base.py
index 9dafd3eb59d46..c2376ff5a5cc8 100644
--- a/django/template/base.py
+++ b/django/template/base.py
@@ -53,7 +53,7 @@
 import logging
 import re
 from enum import Enum
-from inspect import getcallargs, getfullargspec
+from inspect import getcallargs, getfullargspec, unwrap
 
 from django.template.context import (  # NOQA: imported for backwards compatibility
     BaseContext, Context, ContextPopException, RequestContext,
@@ -707,7 +707,7 @@ def args_check(name, func, provided):
         # First argument, filter input, is implied.
         plen = len(provided) + 1
         # Check to see if a decorator is providing the real function.
-        func = getattr(func, '_decorated_function', func)
+        func = unwrap(func)
 
         args, _, _, defaults, _, _, _ = getfullargspec(func)
         alen = len(args)
diff --git a/docs/releases/2.0.6.txt b/docs/releases/2.0.6.txt
index 659c3533e7e52..5d401a7ea9be9 100644
--- a/docs/releases/2.0.6.txt
+++ b/docs/releases/2.0.6.txt
@@ -9,4 +9,5 @@ Django 2.0.6 fixes several bugs in 2.0.5.
 Bugfixes
 ========
 
-* ...
+* Fixed a regression that broke custom template filters that use decorators
+  (:ticket:`29400`).
diff --git a/tests/template_tests/templatetags/custom.py b/tests/template_tests/templatetags/custom.py
index 2e2ccf3782ab1..45acd9655ec7a 100644
--- a/tests/template_tests/templatetags/custom.py
+++ b/tests/template_tests/templatetags/custom.py
@@ -3,6 +3,7 @@
 from django import template
 from django.template.defaultfilters import stringfilter
 from django.utils.html import escape, format_html
+from django.utils.safestring import mark_safe
 
 register = template.Library()
 
@@ -13,6 +14,13 @@ def trim(value, num):
     return value[:num]
 
 
+@register.filter
+@mark_safe
+def make_data_div(value):
+    """A filter that uses a decorator (@mark_safe)."""
+    return '<div data-name="%s"></div>' % value
+
+
 @register.filter
 def noop(value, param=None):
     """A noop filter that always return its first argument and does nothing with
diff --git a/tests/template_tests/test_custom.py b/tests/template_tests/test_custom.py
index b37b5f8f1e16b..dbc5bc267d8d3 100644
--- a/tests/template_tests/test_custom.py
+++ b/tests/template_tests/test_custom.py
@@ -25,6 +25,11 @@ def test_filter(self):
             "abcde"
         )
 
+    def test_decorated_filter(self):
+        engine = Engine(libraries=LIBRARIES)
+        t = engine.from_string('{% load custom %}{{ name|make_data_div }}')
+        self.assertEqual(t.render(Context({'name': 'foo'})), '<div data-name="foo"></div>')
+
 
 class TagTestCase(SimpleTestCase):
 

`inspect.getfullargspec` is being passed the function, but if that function is decorated, `inspect.getfullargspec` [does not read `__wrapped__` attributes as of Python 3.4](https://docs.python.org/3/library/inspect.html#inspect.getfullargspec).

The function object is checked for a _decorated_function attribute, but that's not always present. 

`inspect.unwrap` will get the original function for the call to `inspect.getfullargspec`, though.