prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Refactor to drop dependency on decorator package
  ### Description
  
  We have a few decorators defined in `ietf/utils/decorator.py` that use the "decorator" package. This provides the `@decorator` decorator and a `decorate` method. The built-in Python `functools.wraps()` method can fulfill the needs here without the additional dependency. As far as I can tell we're not making use of any of the features provided by the "decorator" package.
  
  The `@decorator` mechanism also seems to interfere with Django's `@method_decorator`, which led to refactoring the `requires_api_key` decorator already.
  
  ### Code of Conduct
  
  - [X] I agree to follow the [IETF's Code of Conduct](https://github.com/ietf-tools/.github/blob/main/CODE_OF_CONDUCT.md)
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `ietf/utils/decorators.py`
  Content:
  ```
  1 # Copyright The IETF Trust 2016-2020, All Rights Reserved
  2 # -*- coding: utf-8 -*-
  3 
  4 
  5 import datetime
  6 
  7 from decorator import decorator, decorate
  8 from functools import wraps
  9 
  10 from django.conf import settings
  11 from django.contrib.auth import login
  12 from django.http import HttpResponse
  13 from django.shortcuts import render
  14 from django.utils import timezone
  15 from django.utils.encoding import force_bytes
  16 
  17 import debug                            # pyflakes:ignore
  18 
  19 from ietf.utils.test_runner import set_coverage_checking
  20 from ietf.person.models import Person, PersonalApiKey, PersonApiKeyEvent
  21 from ietf.utils import log
  22 
  23 @decorator
  24 def skip_coverage(f, *args, **kwargs):
  25     if settings.TEST_CODE_COVERAGE_CHECKER:
  26         set_coverage_checking(False)
  27         result = f(*args, **kwargs)
  28         set_coverage_checking(True)
  29         return result
  30     else:
  31         return  f(*args, **kwargs)
  32 
  33 @decorator
  34 def person_required(f, request, *args, **kwargs):
  35     if not request.user.is_authenticated:
  36         raise ValueError("The @person_required decorator should be called after @login_required.")
  37     try:
  38         request.user.person
  39     except Person.DoesNotExist:
  40         return render(request, 'registration/missing_person.html')
  41     return  f(request, *args, **kwargs)
  42 
  43 
  44 def require_api_key(f):
  45     @wraps(f)
  46     def _wrapper(request, *args, **kwargs):
  47         def err(code, text):
  48             return HttpResponse(text, status=code, content_type='text/plain')
  49         # Check method and get hash
  50         if request.method == 'POST':
  51             hash = request.POST.get('apikey')
  52         elif request.method == 'GET':
  53             hash = request.GET.get('apikey')
  54         else:
  55             return err(405, "Method not allowed")
  56         if not hash:
  57             return err(400, "Missing apikey parameter")
  58         # Check hash
  59         key = PersonalApiKey.validate_key(force_bytes(hash))
  60         if not key:
  61             return err(403, "Invalid apikey")
  62         # Check endpoint
  63         urlpath = request.META.get('PATH_INFO')
  64         if not (urlpath and urlpath == key.endpoint):
  65             return err(400, "Apikey endpoint mismatch") 
  66         # Check time since regular login
  67         person = key.person
  68         last_login = person.user.last_login
  69         if not person.user.is_staff:
  70             time_limit = (timezone.now() - datetime.timedelta(days=settings.UTILS_APIKEY_GUI_LOGIN_LIMIT_DAYS))
  71             if last_login == None or last_login < time_limit:
  72                 return err(400, "Too long since last regular login")
  73         # Log in
  74         login(request, person.user)
  75         # restore the user.last_login field, so it reflects only gui logins
  76         person.user.last_login = last_login
  77         person.user.save()
  78         # Update stats
  79         key.count += 1
  80         key.latest = timezone.now()
  81         key.save()
  82         PersonApiKeyEvent.objects.create(person=person, type='apikey_login', key=key, desc="Logged in with key ID %s, endpoint %s" % (key.id, key.endpoint))
  83         # Execute decorated function
  84         try:
  85             ret = f(request, *args, **kwargs)
  86         except AttributeError as e:
  87             log.log("Bad API call: args: %s, kwargs: %s, exception: %s" % (args, kwargs, e))
  88             return err(400, "Bad or missing parameters")
  89         return ret
  90     return _wrapper
  91 
  92 
  93 def _memoize(func, self, *args, **kwargs):
  94     '''Memoize wrapper for instance methods.  Use @lru_cache for functions.'''
  95     if kwargs:  # frozenset is used to ensure hashability
  96         key = args, frozenset(list(kwargs.items()))
  97     else:
  98         key = args
  99     # instance method, set up cache if needed
  100     if not hasattr(self, '_cache'):
  101         self._cache = {}
  102     if not func in self._cache:
  103         self._cache[func] = {}            
  104     #
  105     cache = self._cache[func]
  106     if key not in cache:
  107         cache[key] = func(self, *args, **kwargs)
  108     return cache[key]
  109 def memoize(func):
  110     if not hasattr(func, '__class__'):
  111         raise NotImplementedError("Use @lru_cache instead of memoize() for functions.")
  112     # For methods, we want the cache on the object, not on the class, in order
  113     # to not having to think about cache bloat and content becoming stale, so
  114     # we cannot set up the cache here.
  115     return decorate(func, _memoize)
  116 
  117 
  118 def ignore_view_kwargs(*args):
  119     """Ignore the specified kwargs if they are present
  120 
  121     Usage: 
  122       @ignore_view_kwargs("ignore_arg1", "ignore_arg2")
  123       def my_view(request, good_arg):
  124         ...
  125 
  126       This will allow my_view() to be used in url() paths that have zero, one, or both of
  127       ignore_arg1 and ignore_arg2 captured. These will be ignored, while good_arg will still
  128       be captured as usual.
  129     """
  130     kwargs_to_ignore = args
  131 
  132     def decorate(view):
  133         @wraps(view)
  134         def wrapped(*args, **kwargs):
  135             for kwarg in kwargs_to_ignore:
  136                 kwargs.pop(kwarg, None)
  137             return view(*args, **kwargs)
  138 
  139         return wrapped
  140 
  141     return decorate
  142 
  143 
  144
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/ietf/utils/decorators.py b/ietf/utils/decorators.py
  --- a/ietf/utils/decorators.py
  +++ b/ietf/utils/decorators.py
  @@ -4,7 +4,6 @@
   
   import datetime
   
  -from decorator import decorator, decorate
   from functools import wraps
   
   from django.conf import settings
  @@ -20,25 +19,29 @@
   from ietf.person.models import Person, PersonalApiKey, PersonApiKeyEvent
   from ietf.utils import log
   
  -@decorator
  -def skip_coverage(f, *args, **kwargs):
  -    if settings.TEST_CODE_COVERAGE_CHECKER:
  -        set_coverage_checking(False)
  -        result = f(*args, **kwargs)
  -        set_coverage_checking(True)
  -        return result
  -    else:
  -        return  f(*args, **kwargs)
  -
  -@decorator
  -def person_required(f, request, *args, **kwargs):
  -    if not request.user.is_authenticated:
  -        raise ValueError("The @person_required decorator should be called after @login_required.")
  -    try:
  -        request.user.person
  -    except Person.DoesNotExist:
  -        return render(request, 'registration/missing_person.html')
  -    return  f(request, *args, **kwargs)
  +def skip_coverage(f):
  +    @wraps(f)
  +    def _wrapper(*args, **kwargs):
  +        if settings.TEST_CODE_COVERAGE_CHECKER:
  +            set_coverage_checking(False)
  +            result = f(*args, **kwargs)
  +            set_coverage_checking(True)
  +            return result
  +        else:
  +            return  f(*args, **kwargs)
  +    return _wrapper
  +
  +def person_required(f):
  +    @wraps(f)
  +    def _wrapper(request, *args, **kwargs):
  +        if not request.user.is_authenticated:
  +            raise ValueError("The @person_required decorator should be called after @login_required.")
  +        try:
  +            request.user.person
  +        except Person.DoesNotExist:
  +            return render(request, 'registration/missing_person.html')
  +        return  f(request, *args, **kwargs)
  +    return _wrapper
   
   
   def require_api_key(f):
  @@ -90,29 +93,31 @@
       return _wrapper
   
   
  -def _memoize(func, self, *args, **kwargs):
  -    '''Memoize wrapper for instance methods.  Use @lru_cache for functions.'''
  -    if kwargs:  # frozenset is used to ensure hashability
  -        key = args, frozenset(list(kwargs.items()))
  -    else:
  -        key = args
  -    # instance method, set up cache if needed
  -    if not hasattr(self, '_cache'):
  -        self._cache = {}
  -    if not func in self._cache:
  -        self._cache[func] = {}            
  -    #
  -    cache = self._cache[func]
  -    if key not in cache:
  -        cache[key] = func(self, *args, **kwargs)
  -    return cache[key]
   def memoize(func):
  +    @wraps(func)
  +    def _memoize(self, *args, **kwargs):
  +        '''Memoize wrapper for instance methods.  Use @lru_cache for functions.'''
  +        if kwargs:  # frozenset is used to ensure hashability
  +            key = args, frozenset(list(kwargs.items()))
  +        else:
  +            key = args
  +        # instance method, set up cache if needed
  +        if not hasattr(self, '_cache'):
  +            self._cache = {}
  +        if not func in self._cache:
  +            self._cache[func] = {}            
  +        #
  +        cache = self._cache[func]
  +        if key not in cache:
  +            cache[key] = func(self, *args, **kwargs)
  +        return cache[key]
  +
       if not hasattr(func, '__class__'):
           raise NotImplementedError("Use @lru_cache instead of memoize() for functions.")
       # For methods, we want the cache on the object, not on the class, in order
       # to not having to think about cache bloat and content becoming stale, so
       # we cannot set up the cache here.
  -    return decorate(func, _memoize)
  +    return _memoize
   
   
   def ignore_view_kwargs(*args):
  
verification_info: {"golden_diff": "diff --git a/ietf/utils/decorators.py b/ietf/utils/decorators.py\n--- a/ietf/utils/decorators.py\n+++ b/ietf/utils/decorators.py\n@@ -4,7 +4,6 @@\n \n import datetime\n \n-from decorator import decorator, decorate\n from functools import wraps\n \n from django.conf import settings\n@@ -20,25 +19,29 @@\n from ietf.person.models import Person, PersonalApiKey, PersonApiKeyEvent\n from ietf.utils import log\n \n-@decorator\n-def skip_coverage(f, *args, **kwargs):\n-    if settings.TEST_CODE_COVERAGE_CHECKER:\n-        set_coverage_checking(False)\n-        result = f(*args, **kwargs)\n-        set_coverage_checking(True)\n-        return result\n-    else:\n-        return  f(*args, **kwargs)\n-\n-@decorator\n-def person_required(f, request, *args, **kwargs):\n-    if not request.user.is_authenticated:\n-        raise ValueError(\"The @person_required decorator should be called after @login_required.\")\n-    try:\n-        request.user.person\n-    except Person.DoesNotExist:\n-        return render(request, 'registration/missing_person.html')\n-    return  f(request, *args, **kwargs)\n+def skip_coverage(f):\n+    @wraps(f)\n+    def _wrapper(*args, **kwargs):\n+        if settings.TEST_CODE_COVERAGE_CHECKER:\n+            set_coverage_checking(False)\n+            result = f(*args, **kwargs)\n+            set_coverage_checking(True)\n+            return result\n+        else:\n+            return  f(*args, **kwargs)\n+    return _wrapper\n+\n+def person_required(f):\n+    @wraps(f)\n+    def _wrapper(request, *args, **kwargs):\n+        if not request.user.is_authenticated:\n+            raise ValueError(\"The @person_required decorator should be called after @login_required.\")\n+        try:\n+            request.user.person\n+        except Person.DoesNotExist:\n+            return render(request, 'registration/missing_person.html')\n+        return  f(request, *args, **kwargs)\n+    return _wrapper\n \n \n def require_api_key(f):\n@@ -90,29 +93,31 @@\n     return _wrapper\n \n \n-def _memoize(func, self, *args, **kwargs):\n-    '''Memoize wrapper for instance methods.  Use @lru_cache for functions.'''\n-    if kwargs:  # frozenset is used to ensure hashability\n-        key = args, frozenset(list(kwargs.items()))\n-    else:\n-        key = args\n-    # instance method, set up cache if needed\n-    if not hasattr(self, '_cache'):\n-        self._cache = {}\n-    if not func in self._cache:\n-        self._cache[func] = {}            \n-    #\n-    cache = self._cache[func]\n-    if key not in cache:\n-        cache[key] = func(self, *args, **kwargs)\n-    return cache[key]\n def memoize(func):\n+    @wraps(func)\n+    def _memoize(self, *args, **kwargs):\n+        '''Memoize wrapper for instance methods.  Use @lru_cache for functions.'''\n+        if kwargs:  # frozenset is used to ensure hashability\n+            key = args, frozenset(list(kwargs.items()))\n+        else:\n+            key = args\n+        # instance method, set up cache if needed\n+        if not hasattr(self, '_cache'):\n+            self._cache = {}\n+        if not func in self._cache:\n+            self._cache[func] = {}            \n+        #\n+        cache = self._cache[func]\n+        if key not in cache:\n+            cache[key] = func(self, *args, **kwargs)\n+        return cache[key]\n+\n     if not hasattr(func, '__class__'):\n         raise NotImplementedError(\"Use @lru_cache instead of memoize() for functions.\")\n     # For methods, we want the cache on the object, not on the class, in order\n     # to not having to think about cache bloat and content becoming stale, so\n     # we cannot set up the cache here.\n-    return decorate(func, _memoize)\n+    return _memoize\n \n \n def ignore_view_kwargs(*args):\n", "issue": "Refactor to drop dependency on decorator package\n### Description\n\nWe have a few decorators defined in `ietf/utils/decorator.py` that use the \"decorator\" package. This provides the `@decorator` decorator and a `decorate` method. The built-in Python `functools.wraps()` method can fulfill the needs here without the additional dependency. As far as I can tell we're not making use of any of the features provided by the \"decorator\" package.\r\n\r\nThe `@decorator` mechanism also seems to interfere with Django's `@method_decorator`, which led to refactoring the `requires_api_key` decorator already.\n\n### Code of Conduct\n\n- [X] I agree to follow the [IETF's Code of Conduct](https://github.com/ietf-tools/.github/blob/main/CODE_OF_CONDUCT.md)\n", "before_files": [{"content": "# Copyright The IETF Trust 2016-2020, All Rights Reserved\n# -*- coding: utf-8 -*-\n\n\nimport datetime\n\nfrom decorator import decorator, decorate\nfrom functools import wraps\n\nfrom django.conf import settings\nfrom django.contrib.auth import login\nfrom django.http import HttpResponse\nfrom django.shortcuts import render\nfrom django.utils import timezone\nfrom django.utils.encoding import force_bytes\n\nimport debug                            # pyflakes:ignore\n\nfrom ietf.utils.test_runner import set_coverage_checking\nfrom ietf.person.models import Person, PersonalApiKey, PersonApiKeyEvent\nfrom ietf.utils import log\n\n@decorator\ndef skip_coverage(f, *args, **kwargs):\n    if settings.TEST_CODE_COVERAGE_CHECKER:\n        set_coverage_checking(False)\n        result = f(*args, **kwargs)\n        set_coverage_checking(True)\n        return result\n    else:\n        return  f(*args, **kwargs)\n\n@decorator\ndef person_required(f, request, *args, **kwargs):\n    if not request.user.is_authenticated:\n        raise ValueError(\"The @person_required decorator should be called after @login_required.\")\n    try:\n        request.user.person\n    except Person.DoesNotExist:\n        return render(request, 'registration/missing_person.html')\n    return  f(request, *args, **kwargs)\n\n\ndef require_api_key(f):\n    @wraps(f)\n    def _wrapper(request, *args, **kwargs):\n        def err(code, text):\n            return HttpResponse(text, status=code, content_type='text/plain')\n        # Check method and get hash\n        if request.method == 'POST':\n            hash = request.POST.get('apikey')\n        elif request.method == 'GET':\n            hash = request.GET.get('apikey')\n        else:\n            return err(405, \"Method not allowed\")\n        if not hash:\n            return err(400, \"Missing apikey parameter\")\n        # Check hash\n        key = PersonalApiKey.validate_key(force_bytes(hash))\n        if not key:\n            return err(403, \"Invalid apikey\")\n        # Check endpoint\n        urlpath = request.META.get('PATH_INFO')\n        if not (urlpath and urlpath == key.endpoint):\n            return err(400, \"Apikey endpoint mismatch\") \n        # Check time since regular login\n        person = key.person\n        last_login = person.user.last_login\n        if not person.user.is_staff:\n            time_limit = (timezone.now() - datetime.timedelta(days=settings.UTILS_APIKEY_GUI_LOGIN_LIMIT_DAYS))\n            if last_login == None or last_login < time_limit:\n                return err(400, \"Too long since last regular login\")\n        # Log in\n        login(request, person.user)\n        # restore the user.last_login field, so it reflects only gui logins\n        person.user.last_login = last_login\n        person.user.save()\n        # Update stats\n        key.count += 1\n        key.latest = timezone.now()\n        key.save()\n        PersonApiKeyEvent.objects.create(person=person, type='apikey_login', key=key, desc=\"Logged in with key ID %s, endpoint %s\" % (key.id, key.endpoint))\n        # Execute decorated function\n        try:\n            ret = f(request, *args, **kwargs)\n        except AttributeError as e:\n            log.log(\"Bad API call: args: %s, kwargs: %s, exception: %s\" % (args, kwargs, e))\n            return err(400, \"Bad or missing parameters\")\n        return ret\n    return _wrapper\n\n\ndef _memoize(func, self, *args, **kwargs):\n    '''Memoize wrapper for instance methods.  Use @lru_cache for functions.'''\n    if kwargs:  # frozenset is used to ensure hashability\n        key = args, frozenset(list(kwargs.items()))\n    else:\n        key = args\n    # instance method, set up cache if needed\n    if not hasattr(self, '_cache'):\n        self._cache = {}\n    if not func in self._cache:\n        self._cache[func] = {}            \n    #\n    cache = self._cache[func]\n    if key not in cache:\n        cache[key] = func(self, *args, **kwargs)\n    return cache[key]\ndef memoize(func):\n    if not hasattr(func, '__class__'):\n        raise NotImplementedError(\"Use @lru_cache instead of memoize() for functions.\")\n    # For methods, we want the cache on the object, not on the class, in order\n    # to not having to think about cache bloat and content becoming stale, so\n    # we cannot set up the cache here.\n    return decorate(func, _memoize)\n\n\ndef ignore_view_kwargs(*args):\n    \"\"\"Ignore the specified kwargs if they are present\n\n    Usage: \n      @ignore_view_kwargs(\"ignore_arg1\", \"ignore_arg2\")\n      def my_view(request, good_arg):\n        ...\n\n      This will allow my_view() to be used in url() paths that have zero, one, or both of\n      ignore_arg1 and ignore_arg2 captured. These will be ignored, while good_arg will still\n      be captured as usual.\n    \"\"\"\n    kwargs_to_ignore = args\n\n    def decorate(view):\n        @wraps(view)\n        def wrapped(*args, **kwargs):\n            for kwarg in kwargs_to_ignore:\n                kwargs.pop(kwarg, None)\n            return view(*args, **kwargs)\n\n        return wrapped\n\n    return decorate\n\n\n", "path": "ietf/utils/decorators.py"}], "after_files": [{"content": "# Copyright The IETF Trust 2016-2020, All Rights Reserved\n# -*- coding: utf-8 -*-\n\n\nimport datetime\n\nfrom functools import wraps\n\nfrom django.conf import settings\nfrom django.contrib.auth import login\nfrom django.http import HttpResponse\nfrom django.shortcuts import render\nfrom django.utils import timezone\nfrom django.utils.encoding import force_bytes\n\nimport debug                            # pyflakes:ignore\n\nfrom ietf.utils.test_runner import set_coverage_checking\nfrom ietf.person.models import Person, PersonalApiKey, PersonApiKeyEvent\nfrom ietf.utils import log\n\ndef skip_coverage(f):\n    @wraps(f)\n    def _wrapper(*args, **kwargs):\n        if settings.TEST_CODE_COVERAGE_CHECKER:\n            set_coverage_checking(False)\n            result = f(*args, **kwargs)\n            set_coverage_checking(True)\n            return result\n        else:\n            return  f(*args, **kwargs)\n    return _wrapper\n\ndef person_required(f):\n    @wraps(f)\n    def _wrapper(request, *args, **kwargs):\n        if not request.user.is_authenticated:\n            raise ValueError(\"The @person_required decorator should be called after @login_required.\")\n        try:\n            request.user.person\n        except Person.DoesNotExist:\n            return render(request, 'registration/missing_person.html')\n        return  f(request, *args, **kwargs)\n    return _wrapper\n\n\ndef require_api_key(f):\n    @wraps(f)\n    def _wrapper(request, *args, **kwargs):\n        def err(code, text):\n            return HttpResponse(text, status=code, content_type='text/plain')\n        # Check method and get hash\n        if request.method == 'POST':\n            hash = request.POST.get('apikey')\n        elif request.method == 'GET':\n            hash = request.GET.get('apikey')\n        else:\n            return err(405, \"Method not allowed\")\n        if not hash:\n            return err(400, \"Missing apikey parameter\")\n        # Check hash\n        key = PersonalApiKey.validate_key(force_bytes(hash))\n        if not key:\n            return err(403, \"Invalid apikey\")\n        # Check endpoint\n        urlpath = request.META.get('PATH_INFO')\n        if not (urlpath and urlpath == key.endpoint):\n            return err(400, \"Apikey endpoint mismatch\") \n        # Check time since regular login\n        person = key.person\n        last_login = person.user.last_login\n        if not person.user.is_staff:\n            time_limit = (timezone.now() - datetime.timedelta(days=settings.UTILS_APIKEY_GUI_LOGIN_LIMIT_DAYS))\n            if last_login == None or last_login < time_limit:\n                return err(400, \"Too long since last regular login\")\n        # Log in\n        login(request, person.user)\n        # restore the user.last_login field, so it reflects only gui logins\n        person.user.last_login = last_login\n        person.user.save()\n        # Update stats\n        key.count += 1\n        key.latest = timezone.now()\n        key.save()\n        PersonApiKeyEvent.objects.create(person=person, type='apikey_login', key=key, desc=\"Logged in with key ID %s, endpoint %s\" % (key.id, key.endpoint))\n        # Execute decorated function\n        try:\n            ret = f(request, *args, **kwargs)\n        except AttributeError as e:\n            log.log(\"Bad API call: args: %s, kwargs: %s, exception: %s\" % (args, kwargs, e))\n            return err(400, \"Bad or missing parameters\")\n        return ret\n    return _wrapper\n\n\ndef memoize(func):\n    @wraps(func)\n    def _memoize(self, *args, **kwargs):\n        '''Memoize wrapper for instance methods.  Use @lru_cache for functions.'''\n        if kwargs:  # frozenset is used to ensure hashability\n            key = args, frozenset(list(kwargs.items()))\n        else:\n            key = args\n        # instance method, set up cache if needed\n        if not hasattr(self, '_cache'):\n            self._cache = {}\n        if not func in self._cache:\n            self._cache[func] = {}            \n        #\n        cache = self._cache[func]\n        if key not in cache:\n            cache[key] = func(self, *args, **kwargs)\n        return cache[key]\n\n    if not hasattr(func, '__class__'):\n        raise NotImplementedError(\"Use @lru_cache instead of memoize() for functions.\")\n    # For methods, we want the cache on the object, not on the class, in order\n    # to not having to think about cache bloat and content becoming stale, so\n    # we cannot set up the cache here.\n    return _memoize\n\n\ndef ignore_view_kwargs(*args):\n    \"\"\"Ignore the specified kwargs if they are present\n\n    Usage: \n      @ignore_view_kwargs(\"ignore_arg1\", \"ignore_arg2\")\n      def my_view(request, good_arg):\n        ...\n\n      This will allow my_view() to be used in url() paths that have zero, one, or both of\n      ignore_arg1 and ignore_arg2 captured. These will be ignored, while good_arg will still\n      be captured as usual.\n    \"\"\"\n    kwargs_to_ignore = args\n\n    def decorate(view):\n        @wraps(view)\n        def wrapped(*args, **kwargs):\n            for kwarg in kwargs_to_ignore:\n                kwargs.pop(kwarg, None)\n            return view(*args, **kwargs)\n\n        return wrapped\n\n    return decorate\n\n\n", "path": "ietf/utils/decorators.py"}]}