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