From d7b0ee97a44c328708de97127d10e5bf793cac24 Mon Sep 17 00:00:00 2001
From: Andreas Kloeckner <inform@tiker.net>
Date: Fri, 2 Apr 2021 17:46:02 -0500
Subject: [PATCH] Drop dependency on, included obsolete copy of 'decorator'
 pypi module

---
 README.rst           |   1 -
 pytools/__init__.py  |  74 ++++++++++----------
 pytools/decorator.py | 159 -------------------------------------------
 pytools/obj_array.py |  28 ++++++--
 setup.cfg            |   1 -
 setup.py             |   1 -
 test/test_pytools.py |  14 ++++
 7 files changed, 74 insertions(+), 204 deletions(-)
 delete mode 100644 pytools/decorator.py

diff --git a/README.rst b/README.rst
index 31038d4..5bb3a52 100644
--- a/README.rst
+++ b/README.rst
@@ -19,7 +19,6 @@ nonetheless, here's what's on offer:
 * A ton of small tool functions such as `len_iterable`, `argmin`,
   tuple generation, permutation generation, ASCII table pretty printing,
   GvR's monkeypatch_xxx() hack, the elusive `flatten`, and much more.
-* Michele Simionato's decorator module
 * A time-series logging module, `pytools.log`.
 * Batch job submission, `pytools.batchjob`.
 * A lexer, `pytools.lex`.
diff --git a/pytools/__init__.py b/pytools/__init__.py
index c7bb9d5..1010a3e 100644
--- a/pytools/__init__.py
+++ b/pytools/__init__.py
@@ -38,9 +38,6 @@ import builtins
 from sys import intern
 
 
-decorator_module = __import__("decorator", level=0)
-my_decorator = decorator_module.decorator
-
 # These are deprecated and will go away in 2022.
 all = builtins.all
 any = builtins.any
@@ -625,43 +622,48 @@ def memoize(*args: F, **kwargs: Any) -> F:
             % ", ".join(list(kwargs.keys())))
 
     if key_func is not None:
-        @my_decorator
-        def _deco(func, *args, **kwargs):
-            # by Michele Simionato
-            # http://www.phyast.pitt.edu/~micheles/python/
-            key = key_func(*args, **kwargs)
-            try:
-                return func._memoize_dic[key]  # pylint: disable=protected-access
-            except AttributeError:
-                # _memoize_dic doesn't exist yet.
-                result = func(*args, **kwargs)
-                func._memoize_dic = {key: result}  # pylint: disable=protected-access
-                return result
-            except KeyError:
-                result = func(*args, **kwargs)
-                func._memoize_dic[key] = result  # pylint: disable=protected-access
-                return result
+        def _decorator(func):
+            def wrapper(*args, **kwargs):
+                key = key_func(*args, **kwargs)
+                try:
+                    return func._memoize_dic[key]  # noqa: E501 # pylint: disable=protected-access
+                except AttributeError:
+                    # _memoize_dic doesn't exist yet.
+                    result = func(*args, **kwargs)
+                    func._memoize_dic = {key: result}  # noqa: E501 # pylint: disable=protected-access
+                    return result
+                except KeyError:
+                    result = func(*args, **kwargs)
+                    func._memoize_dic[key] = result  # noqa: E501 # pylint: disable=protected-access
+                    return result
+
+            from functools import update_wrapper
+            update_wrapper(wrapper, func)
+            return wrapper
+
     else:
-        @my_decorator
-        def _deco(func, *args):
-            # by Michele Simionato
-            # http://www.phyast.pitt.edu/~micheles/python/
-            try:
-                return func._memoize_dic[args]  # pylint: disable=protected-access
-            except AttributeError:
-                # _memoize_dic doesn't exist yet.
-                result = func(*args)
-                func._memoize_dic = {args: result}  # pylint:disable=protected-access
-                return result
-            except KeyError:
-                result = func(*args)
-                func._memoize_dic[args] = result  # pylint: disable=protected-access
-                return result
+        def _decorator(func):
+            def wrapper(*args):
+                try:
+                    return func._memoize_dic[args]  # noqa: E501 # pylint: disable=protected-access
+                except AttributeError:
+                    # _memoize_dic doesn't exist yet.
+                    result = func(*args)
+                    func._memoize_dic = {args: result}  # noqa: E501 # pylint:disable=protected-access
+                    return result
+                except KeyError:
+                    result = func(*args)
+                    func._memoize_dic[args] = result  # noqa: E501 # pylint: disable=protected-access
+                    return result
+
+            from functools import update_wrapper
+            update_wrapper(wrapper, func)
+            return wrapper
 
     if not args:
-        return _deco
+        return _decorator
     if callable(args[0]) and len(args) == 1:
-        return _deco(args[0])
+        return _decorator(args[0])
     raise TypeError(
         "memoize received unexpected position arguments: %s" % args)
 
diff --git a/pytools/decorator.py b/pytools/decorator.py
deleted file mode 100644
index c053456..0000000
--- a/pytools/decorator.py
+++ /dev/null
@@ -1,159 +0,0 @@
-# Python decorator module
-# by Michele Simionato
-# http://www.phyast.pitt.edu/~micheles/python/
-
-## The basic trick is to generate the source code for the decorated function
-## with the right signature and to evaluate it.
-## Uncomment the statement 'print >> sys.stderr, func_src'  in _decorate
-## to understand what is going on.
-
-__all__ = ["decorator", "update_wrapper", "getinfo"]
-
-import inspect
-
-def getinfo(func):
-    """
-    Returns an info dictionary containing:
-    - name (the name of the function : str)
-    - argnames (the names of the arguments : list)
-    - defaults (the values of the default arguments : tuple)
-    - signature (the signature : str)
-    - doc (the docstring : str)
-    - module (the module name : str)
-    - dict (the function __dict__ : str)
-
-    >>> def f(self, x=1, y=2, *args, **kw): pass
-
-    >>> info = getinfo(f)
-
-    >>> info["name"]
-    'f'
-    >>> info["argnames"]
-    ['self', 'x', 'y', 'args', 'kw']
-
-    >>> info["defaults"]
-    (1, 2)
-
-    >>> info["signature"]
-    'self, x, y, *args, **kw'
-    """
-    assert inspect.ismethod(func) or inspect.isfunction(func)
-    regargs, varargs, varkwargs, defaults = inspect.getargspec(func)
-    argnames = list(regargs)
-    if varargs:
-        argnames.append(varargs)
-    if varkwargs:
-        argnames.append(varkwargs)
-    signature = inspect.formatargspec(regargs, varargs, varkwargs, defaults,
-                                      formatvalue=lambda value: "")[1:-1]
-    return dict(name=func.__name__, argnames=argnames, signature=signature,
-                defaults = func.__defaults__, doc=func.__doc__,
-                module=func.__module__, dict=func.__dict__,
-                globals=func.__globals__, closure=func.__closure__)
-
-def update_wrapper(wrapper, wrapped, create=False):
-    """
-    An improvement over functools.update_wrapper. By default it works the
-    same, but if the 'create' flag is set, generates a copy of the wrapper
-    with the right signature and update the copy, not the original.
-    Moreovoer, 'wrapped' can be a dictionary with keys 'name', 'doc', 'module',
-    'dict', 'defaults'.
-    """
-    if isinstance(wrapped, dict):
-        infodict = wrapped
-    else: # assume wrapped is a function
-        infodict = getinfo(wrapped)
-    assert not '_wrapper_' in infodict["argnames"], \
-           '"_wrapper_" is a reserved argument name!'
-    if create: # create a brand new wrapper with the right signature
-        src = "lambda %(signature)s: _wrapper_(%(signature)s)" % infodict
-        # import sys; print >> sys.stderr, src # for debugging purposes
-        wrapper = eval(src, dict(_wrapper_=wrapper))
-    try:
-        wrapper.__name__ = infodict['name']
-    except: # Python version < 2.4
-        pass
-    wrapper.__doc__ = infodict['doc']
-    wrapper.__module__ = infodict['module']
-    wrapper.__dict__.update(infodict['dict'])
-    wrapper.__defaults__ = infodict['defaults']
-    return wrapper
-
-# the real meat is here
-def _decorator(caller, func):
-    if not (inspect.ismethod(func) or inspect.isfunction(func)):
-        # skip all the fanciness, just do what works
-        return lambda *args, **kwargs: caller(func, *args, **kwargs)
-
-    infodict = getinfo(func)
-    argnames = infodict['argnames']
-    assert not ('_call_' in argnames or '_func_' in argnames), \
-           'You cannot use _call_ or _func_ as argument names!'
-    src = "lambda %(signature)s: _call_(_func_, %(signature)s)" % infodict
-    dec_func = eval(src, dict(_func_=func, _call_=caller))
-    return update_wrapper(dec_func, func)
-
-def decorator(caller, func=None):
-    """
-    General purpose decorator factory: takes a caller function as
-    input and returns a decorator with the same attributes.
-    A caller function is any function like this::
-
-     def caller(func, *args, **kw):
-         # do something
-         return func(*args, **kw)
-
-    Here is an example of usage:
-
-    >>> @decorator
-    ... def chatty(f, *args, **kw):
-    ...     print("Calling %r" % f.__name__)
-    ...     return f(*args, **kw)
-
-    >>> chatty.__name__
-    'chatty'
-
-    >>> @chatty
-    ... def f(): pass
-    ...
-    >>> f()
-    Calling 'f'
-
-    For sake of convenience, the decorator factory can also be called with
-    two arguments. In this casem ``decorator(caller, func)`` is just a
-    shortcut for ``decorator(caller)(func)``.
-    """
-    from warnings import warn
-    warn("pytools.decorator is deprecated and will be removed in pytools 12. "
-            "Use the 'decorator' module directly instead.",
-            DeprecationWarning, stacklevel=2)
-
-    if func is None: # return a decorator function
-        return update_wrapper(lambda f : _decorator(caller, f), caller)
-    else: # return a decorated function
-        return _decorator(caller, func)
-
-if __name__ == "__main__":
-    import doctest; doctest.testmod()
-
-#######################     LEGALESE    ##################################
-
-##   Redistributions of source code must retain the above copyright 
-##   notice, this list of conditions and the following disclaimer.
-##   Redistributions in bytecode form must reproduce the above copyright
-##   notice, this list of conditions and the following disclaimer in
-##   the documentation and/or other materials provided with the
-##   distribution.
-
-##   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-##   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-##   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-##   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-##   HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-##   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-##   BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-##   OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-##   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-##   TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-##   USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
-##   DAMAGE.
diff --git a/pytools/obj_array.py b/pytools/obj_array.py
index 86b92f3..fc9a0a0 100644
--- a/pytools/obj_array.py
+++ b/pytools/obj_array.py
@@ -21,8 +21,8 @@ THE SOFTWARE.
 """
 
 import numpy as np
+from functools import partial, update_wrapper
 from warnings import warn
-from pytools import my_decorator as decorator
 
 
 __doc__ = """
@@ -142,7 +142,10 @@ def obj_array_vectorize(f, ary):
         return f(ary)
 
 
-obj_array_vectorized = decorator(obj_array_vectorize)
+def obj_array_vectorized(f):
+    wrapper = partial(obj_array_vectorize, f)
+    update_wrapper(wrapper, f)
+    return wrapper
 
 
 def rec_obj_array_vectorize(f, ary):
@@ -168,7 +171,10 @@ def rec_obj_array_vectorize(f, ary):
         return f(ary)
 
 
-rec_obj_array_vectorized = decorator(rec_obj_array_vectorize)
+def rec_obj_array_vectorized(f):
+    wrapper = partial(rec_obj_array_vectorized, f)
+    update_wrapper(wrapper, f)
+    return wrapper
 
 
 def obj_array_vectorize_n_args(f, *args):
@@ -207,7 +213,11 @@ def obj_array_vectorize_n_args(f, *args):
     return result
 
 
-obj_array_vectorized_n_args = decorator(obj_array_vectorize_n_args)
+def obj_array_vectorized_n_args(f):
+    wrapper = partial(obj_array_vectorize_n_args, f)
+    update_wrapper(wrapper, f)
+    return f
+
 
 
 # {{{ workarounds for https://github.com/numpy/numpy/issues/1740
@@ -365,7 +375,10 @@ def with_object_array_or_scalar(f, field, obj_array_only=False):
         return f(field)
 
 
-as_oarray_func = decorator(with_object_array_or_scalar)
+def as_oarray_func(f):
+    wrapper = partial(with_object_array_or_scalar, f)
+    update_wrapper(wrapper, f)
+    return wrapper
 
 
 def with_object_array_or_scalar_n_args(f, *args):
@@ -398,7 +411,10 @@ def with_object_array_or_scalar_n_args(f, *args):
         return f(*args)
 
 
-as_oarray_func_n_args = decorator(with_object_array_or_scalar_n_args)
+def as_oarray_func_n_args(f):
+    wrapper = partial(with_object_array_or_scalar_n_args, f)
+    update_wrapper(wrapper, f)
+    return wrapper
 
 
 def oarray_real(ary):
diff --git a/setup.cfg b/setup.cfg
index f2e50e9..7b12a48 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,7 +1,6 @@
 [flake8]
 ignore = E126,E127,E128,E123,E226,E241,E242,E265,E402,W503,E731
 max-line-length=85
-exclude=pytools/arithmetic_container.py,pytools/decorator.py
 
 inline-quotes = "
 docstring-quotes = "
diff --git a/setup.py b/setup.py
index ed73fd6..e77140c 100644
--- a/setup.py
+++ b/setup.py
@@ -38,7 +38,6 @@ setup(name="pytools",
       python_requires="~=3.6",
 
       install_requires=[
-          "decorator>=3.2.0",
           "appdirs>=1.4.0",
           "numpy>=1.6.0",
           "dataclasses>=0.7;python_version<='3.6'"
diff --git a/test/test_pytools.py b/test/test_pytools.py
index a7f2e13..fb4af28 100644
--- a/test/test_pytools.py
+++ b/test/test_pytools.py
@@ -120,6 +120,20 @@ def test_memoize():
     from pytools import memoize
     count = [0]
 
+    @memoize
+    def f(i, j):
+        count[0] += 1
+        return i + j
+
+    assert f(1, 2) == 3
+    assert f(1, 2) == 3
+    assert count[0] == 1
+
+
+def test_memoize_with_kwargs():
+    from pytools import memoize
+    count = [0]
+
     @memoize(use_kwargs=True)
     def f(i, j=1):
         count[0] += 1
-- 
GitLab