Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-decorator for openSUSE:Factory checked in at 2021-08-11 11:46:59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-decorator (Old) and /work/SRC/openSUSE:Factory/.python-decorator.new.1899 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-decorator" Wed Aug 11 11:46:59 2021 rev:23 rq:910347 version:5.0.9 Changes: -------- --- /work/SRC/openSUSE:Factory/python-decorator/python-decorator.changes 2020-03-27 00:24:30.340232740 +0100 +++ /work/SRC/openSUSE:Factory/.python-decorator.new.1899/python-decorator.changes 2021-08-11 11:47:01.369760525 +0200 @@ -1,0 +2,19 @@ +Thu Aug 5 15:25:53 UTC 2021 - Mark??ta Machov?? <mmach...@suse.com> + +- Add upstream patch kwsyntax.patch + +------------------------------------------------------------------- +Tue Jul 27 06:15:15 UTC 2021 - Dirk M??ller <dmuel...@suse.com> + +- update to 5.0.9: + * Fixed a test breaking PyPy. Restored support for Sphinx. + * Made the decorator module more robust when decorating builtin functions + lacking dunder attributes, like `dict.__setitem__`. + * The decorator module was not passing correctly the defaults inside the + `*args` tuple, thanks to Dan Shult for the fix. + * The decorator module was not copying the __module__ attribute anymore. + * Dropped support for Python < 3.5 with a substantial simplification of + the code base (now building a decorator does not require calling "exec"). + Added a way to mimic functools.wraps-generated decorators. + +------------------------------------------------------------------- Old: ---- decorator-4.4.2.tar.gz New: ---- decorator-5.0.9.tar.gz kwsyntax.patch ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-decorator.spec ++++++ --- /var/tmp/diff_new_pack.bygCBI/_old 2021-08-11 11:47:02.633759005 +0200 +++ /var/tmp/diff_new_pack.bygCBI/_new 2021-08-11 11:47:02.637759000 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-decorator # -# Copyright (c) 2020 SUSE LLC +# Copyright (c) 2021 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,14 +19,17 @@ # Please submit bugfixes or comments via http://bugs.opensuse.org/ # %{?!python_module:%define python_module() python-%{**} python3-%{**}} +%global skip_python2 1 Name: python-decorator -Version: 4.4.2 +Version: 5.0.9 Release: 0 Summary: Decorators for Humans License: BSD-2-Clause Group: Development/Languages/Python URL: https://github.com/micheles/decorator Source: https://files.pythonhosted.org/packages/source/d/decorator/decorator-%{version}.tar.gz +# PATCH-FIX-UPSTREAM https://github.com/micheles/decorator/commit/817d070db3c9cc5900d118837c533c039982b050 Fixed decorator.decorator not passing kwsyntax +Patch0: kwsyntax.patch BuildRequires: %{python_module setuptools} BuildRequires: dos2unix BuildRequires: fdupes @@ -45,6 +48,7 @@ %prep %setup -q -n decorator-%{version} +%autopatch -p1 %build %python_build ++++++ decorator-4.4.2.tar.gz -> decorator-5.0.9.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/decorator-4.4.2/CHANGES.md new/decorator-5.0.9/CHANGES.md --- old/decorator-4.4.2/CHANGES.md 2020-02-29 06:22:24.000000000 +0100 +++ new/decorator-5.0.9/CHANGES.md 2021-05-16 06:06:15.000000000 +0200 @@ -3,6 +3,34 @@ ## unreleased +## 5.0.9 (2021-05-16) + +Fixed a test breaking PyPy. Restored support for Sphinx. + +## 5.0.8 (2021-05-15) + +Made the decorator module more robust when decorating builtin functions +lacking dunder attributes, like `dict.__setitem__`. + +## 5.0.7 (2021-04-14) + +The decorator module was not passing correctly the defaults inside the +`*args` tuple, thanks to Dan Shult for the fix. Also fixed some mispellings +in the documentation and integrated codespell in the CI, thanks to +Christian Clauss. + +## 5.0.6 (2021-04-08) + +The decorator module was not copying the __module__ attribute anymore. Thanks to +Nikolay Markov for the notice. + +## 5.0.5 (2021-04-04) + +Dropped support for Python < 3.5 with a substantial simplification of +the code base (now building a decorator does not require calling "exec"). +Added a way to mimic functools.wraps-generated decorators. +Ported the Continuous Integration from Travis to GitHub. + ## 4.4.2 (2020-02-29) Sylvan Mosberger (https://github.com/Infinisil) contributed a patch to diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/decorator-4.4.2/PKG-INFO new/decorator-5.0.9/PKG-INFO --- old/decorator-4.4.2/PKG-INFO 2020-02-29 06:24:41.000000000 +0100 +++ new/decorator-5.0.9/PKG-INFO 2021-05-16 06:08:31.094942800 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.2 Name: decorator -Version: 4.4.2 +Version: 5.0.9 Summary: Decorators for Humans Home-page: https://github.com/micheles/decorator Author: Michele Simionato @@ -113,17 +113,12 @@ Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.6 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.2 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: Utilities -Requires-Python: >=2.6, !=3.0.*, !=3.1.* +Requires-Python: >=3.5 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/decorator-4.4.2/setup.cfg new/decorator-5.0.9/setup.cfg --- old/decorator-4.4.2/setup.cfg 2020-02-29 06:24:41.000000000 +0100 +++ new/decorator-5.0.9/setup.cfg 2021-05-16 06:08:31.094942800 +0200 @@ -1,6 +1,3 @@ -[bdist_wheel] -universal = 1 - [upload_docs] upload-dir = docs diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/decorator-4.4.2/setup.py new/decorator-5.0.9/setup.py --- old/decorator-4.4.2/setup.py 2019-10-27 08:59:44.000000000 +0100 +++ new/decorator-5.0.9/setup.py 2021-04-02 13:31:16.000000000 +0200 @@ -18,23 +18,18 @@ py_modules=['decorator'], keywords="decorators generic utility", platforms=["All"], - python_requires='>=2.6, !=3.0.*, !=3.1.*', + python_requires='>=3.5', classifiers=['Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Natural Language :: English', 'Operating System :: OS Independent', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.6', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.2', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: Implementation :: CPython', 'Topic :: Software Development :: Libraries', 'Topic :: Utilities'], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/decorator-4.4.2/src/decorator.egg-info/PKG-INFO new/decorator-5.0.9/src/decorator.egg-info/PKG-INFO --- old/decorator-4.4.2/src/decorator.egg-info/PKG-INFO 2020-02-29 06:24:40.000000000 +0100 +++ new/decorator-5.0.9/src/decorator.egg-info/PKG-INFO 2021-05-16 06:08:30.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.2 Name: decorator -Version: 4.4.2 +Version: 5.0.9 Summary: Decorators for Humans Home-page: https://github.com/micheles/decorator Author: Michele Simionato @@ -113,17 +113,12 @@ Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.6 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.2 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: Utilities -Requires-Python: >=2.6, !=3.0.*, !=3.1.* +Requires-Python: >=3.5 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/decorator-4.4.2/src/decorator.py new/decorator-5.0.9/src/decorator.py --- old/decorator-4.4.2/src/decorator.py 2020-02-29 06:20:33.000000000 +0100 +++ new/decorator-5.0.9/src/decorator.py 2021-05-16 06:05:29.000000000 +0200 @@ -1,6 +1,6 @@ # ######################### LICENSE ############################ # -# Copyright (c) 2005-2018, Michele Simionato +# Copyright (c) 2005-2021, Michele Simionato # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -28,55 +28,26 @@ # DAMAGE. """ -Decorator module, see http://pypi.python.org/pypi/decorator +Decorator module, see +https://github.com/micheles/decorator/blob/master/docs/documentation.md for the documentation. """ -from __future__ import print_function - import re import sys import inspect import operator import itertools -import collections - -__version__ = '4.4.2' - -if sys.version_info >= (3,): - from inspect import getfullargspec - - def get_init(cls): - return cls.__init__ -else: - FullArgSpec = collections.namedtuple( - 'FullArgSpec', 'args varargs varkw defaults ' - 'kwonlyargs kwonlydefaults annotations') - - def getfullargspec(f): - "A quick and dirty replacement for getfullargspec for Python 2.X" - return FullArgSpec._make(inspect.getargspec(f) + ([], None, {})) - - def get_init(cls): - return cls.__init__.__func__ - -try: - iscoroutinefunction = inspect.iscoroutinefunction -except AttributeError: - # let's assume there are no coroutine functions in old Python - def iscoroutinefunction(f): - return False -try: - from inspect import isgeneratorfunction -except ImportError: - # assume no generator function in old Python versions - def isgeneratorfunction(caller): - return False +from contextlib import _GeneratorContextManager +from inspect import getfullargspec, iscoroutinefunction, isgeneratorfunction +__version__ = '5.0.9' DEF = re.compile(r'\s*def\s*([_\w][_\w\d]*)\s*\(') +POS = inspect.Parameter.POSITIONAL_OR_KEYWORD +EMPTY = inspect.Parameter.empty -# basic functionality +# this is not used anymore in the core, but kept for backward compatibility class FunctionMaker(object): """ An object with the ability to create functions with a given signature. @@ -143,7 +114,9 @@ raise TypeError('You are decorating a non function: %s' % func) def update(self, func, **kw): - "Update the signature of func with the data in self" + """ + Update the signature of func with the data in self + """ func.__name__ = self.name func.__doc__ = getattr(self, 'doc', None) func.__dict__ = getattr(self, 'dict', {}) @@ -160,7 +133,9 @@ func.__dict__.update(kw) def make(self, src_templ, evaldict=None, addsource=False, **attrs): - "Make a new function from a given template and update the signature" + """ + Make a new function from a given template and update the signature + """ src = src_templ % vars(self) # expand name and signature evaldict = evaldict or {} mo = DEF.search(src) @@ -221,105 +196,112 @@ return self.make(body, evaldict, addsource, **attrs) -def decorate(func, caller, extras=()): +def fix(args, kwargs, sig): """ - decorate(func, caller) decorates a function using a caller. - If the caller is a generator function, the resulting function - will be a generator function. + Fix args and kwargs to be consistent with the signature """ - evaldict = dict(_call_=caller, _func_=func) - es = '' - for i, extra in enumerate(extras): - ex = '_e%d_' % i - evaldict[ex] = extra - es += ex + ', ' - - if '3.5' <= sys.version < '3.6': - # with Python 3.5 isgeneratorfunction returns True for all coroutines - # however we know that it is NOT possible to have a generator - # coroutine in python 3.5: PEP525 was not there yet - generatorcaller = isgeneratorfunction( - caller) and not iscoroutinefunction(caller) - else: - generatorcaller = isgeneratorfunction(caller) - if generatorcaller: - fun = FunctionMaker.create( - func, "for res in _call_(_func_, %s%%(shortsignature)s):\n" - " yield res" % es, evaldict, __wrapped__=func) + ba = sig.bind(*args, **kwargs) + ba.apply_defaults() # needed for test_dan_schult + return ba.args, ba.kwargs + + +def decorate(func, caller, extras=(), kwsyntax=False): + """ + Decorates a function/generator/coroutine using a caller. + If kwsyntax is True calling the decorated functions with keyword + syntax will pass the named arguments inside the ``kw`` dictionary, + even if such argument are positional, similarly to what functools.wraps + does. By default kwsyntax is False and the the arguments are untouched. + """ + sig = inspect.signature(func) + if iscoroutinefunction(caller): + async def fun(*args, **kw): + if not kwsyntax: + args, kw = fix(args, kw, sig) + return await caller(func, *(extras + args), **kw) + elif isgeneratorfunction(caller): + def fun(*args, **kw): + if not kwsyntax: + args, kw = fix(args, kw, sig) + for res in caller(func, *(extras + args), **kw): + yield res else: - fun = FunctionMaker.create( - func, "return _call_(_func_, %s%%(shortsignature)s)" % es, - evaldict, __wrapped__=func) - if hasattr(func, '__qualname__'): - fun.__qualname__ = func.__qualname__ + def fun(*args, **kw): + if not kwsyntax: + args, kw = fix(args, kw, sig) + return caller(func, *(extras + args), **kw) + fun.__name__ = func.__name__ + fun.__doc__ = func.__doc__ + fun.__wrapped__ = func + fun.__signature__ = sig + fun.__qualname__ = func.__qualname__ + # builtin functions like defaultdict.__setitem__ lack many attributes + try: + fun.__defaults__ = func.__defaults__ + except AttributeError: + pass + try: + fun.__kwdefaults__ = func.__kwdefaults__ + except AttributeError: + pass + try: + fun.__annotations__ = func.__annotations__ + except AttributeError: + pass + try: + fun.__module__ = func.__module__ + except AttributeError: + pass + try: + fun.__dict__.update(func.__dict__) + except AttributeError: + pass return fun -def decorator(caller, _func=None): - """decorator(caller) converts a caller function into a decorator""" +def decorator(caller, _func=None, kwsyntax=False): + """ + decorator(caller) converts a caller function into a decorator + """ if _func is not None: # return a decorated function # this is obsolete behavior; you should use decorate instead return decorate(_func, caller) # else return a decorator function - defaultargs, defaults = '', () - if inspect.isclass(caller): - name = caller.__name__.lower() - doc = 'decorator(%s) converts functions/generators into ' \ - 'factories of %s objects' % (caller.__name__, caller.__name__) - elif inspect.isfunction(caller): - if caller.__name__ == '<lambda>': - name = '_lambda_' + sig = inspect.signature(caller) + dec_params = [p for p in sig.parameters.values() if p.kind is POS] + + def dec(func=None, *args, **kw): + na = len(args) + 1 + extras = args + tuple(kw.get(p.name, p.default) + for p in dec_params[na:] + if p.default is not EMPTY) + if func is None: + return lambda func: decorate(func, caller, extras, kwsyntax) else: - name = caller.__name__ - doc = caller.__doc__ - nargs = caller.__code__.co_argcount - ndefs = len(caller.__defaults__ or ()) - defaultargs = ', '.join(caller.__code__.co_varnames[nargs-ndefs:nargs]) - if defaultargs: - defaultargs += ',' - defaults = caller.__defaults__ - else: # assume caller is an object with a __call__ method - name = caller.__class__.__name__.lower() - doc = caller.__call__.__doc__ - evaldict = dict(_call=caller, _decorate_=decorate) - dec = FunctionMaker.create( - '%s(func, %s)' % (name, defaultargs), - 'if func is None: return lambda func: _decorate_(func, _call, (%s))\n' - 'return _decorate_(func, _call, (%s))' % (defaultargs, defaultargs), - evaldict, doc=doc, module=caller.__module__, __wrapped__=caller) - if defaults: - dec.__defaults__ = (None,) + defaults + return decorate(func, caller, extras, kwsyntax) + dec.__signature__ = sig.replace(parameters=dec_params) + dec.__name__ = caller.__name__ + dec.__doc__ = caller.__doc__ + dec.__wrapped__ = caller + dec.__qualname__ = caller.__qualname__ + dec.__kwdefaults__ = getattr(caller, '__kwdefaults__', None) + dec.__dict__.update(caller.__dict__) return dec # ####################### contextmanager ####################### # -try: # Python >= 3.2 - from contextlib import _GeneratorContextManager -except ImportError: # Python >= 2.5 - from contextlib import GeneratorContextManager as _GeneratorContextManager - class ContextManager(_GeneratorContextManager): - def __call__(self, func): - """Context manager decorator""" - return FunctionMaker.create( - func, "with _self_: return _func_(%(shortsignature)s)", - dict(_self_=self, _func_=func), __wrapped__=func) - - -init = getfullargspec(_GeneratorContextManager.__init__) -n_args = len(init.args) -if n_args == 2 and not init.varargs: # (self, genobj) Python 2.7 - def __init__(self, g, *a, **k): - return _GeneratorContextManager.__init__(self, g(*a, **k)) - ContextManager.__init__ = __init__ -elif n_args == 2 and init.varargs: # (self, gen, *a, **k) Python 3.4 - pass -elif n_args == 4: # (self, gen, args, kwds) Python 3.5 def __init__(self, g, *a, **k): return _GeneratorContextManager.__init__(self, g, a, k) - ContextManager.__init__ = __init__ + + def __call__(self, func): + def caller(f, *a, **k): + with self: + return f(*a, **k) + return decorate(func, caller) + _contextmanager = decorator(ContextManager) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/decorator-4.4.2/src/tests/documentation.py new/decorator-5.0.9/src/tests/documentation.py --- old/decorator-4.4.2/src/tests/documentation.py 2020-02-29 06:10:07.000000000 +0100 +++ new/decorator-5.0.9/src/tests/documentation.py 2021-04-11 07:52:37.000000000 +0200 @@ -1,16 +1,12 @@ -from __future__ import print_function import sys +import inspect import threading import time import functools import itertools import collections -try: - import collections.abc as c -except ImportError: - c = collections - collections.abc = collections -from decorator import (decorator, decorate, FunctionMaker, contextmanager, +import collections.abc as c +from decorator import (decorator, decorate, FunctionMaker, dispatch_on, __version__) doc = r"""Decorators for Humans @@ -20,7 +16,7 @@ |---|---| |E-mail | michele.simion...@gmail.com| |Version| $VERSION ($DATE)| -|Supports| Python 2.6, 2.7, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8| +|Supports| Python 3.5, 3.6, 3.7, 3.8, 3.9| |Download page| http://pypi.python.org/pypi/decorator/$VERSION| |Installation| ``pip install decorator``| |License | BSD license| @@ -30,29 +26,32 @@ The ``decorator`` module is over ten years old, but still alive and kicking. It is used by several frameworks (IPython, scipy, authkit, -pylons, pycuda, sugar, ...) and has been stable for a *long* -time. It is your best option if you want to preserve the signature of -decorated functions in a consistent way across Python -releases. Version 4 is fully compatible with the past, except for -one thing: support for Python 2.4 and 2.5 has been dropped. That -decision made it possible to use a single code base both for Python -2.X and Python 3.X. This is a *huge* bonus, since I could remove over -2,000 lines of duplicated documentation/doctests. Having to maintain -separate docs for Python 2 and Python 3 effectively stopped any -development on the module for several years. Moreover, it is now -trivial to distribute the module as an universal - [wheel](http://pythonwheels.com) since 2to3 is no more -required. Since Python 2.5 has been released ages ago (in 2006), I felt that -it was reasonable to drop the support for it. If you need to support -ancient versions of Python, stick with the decorator module version -3.4.2. The current version supports all Python releases from 2.6 up. +pylons, pycuda, sugar, ...) and has been stable for a *long* time. It +is your best option if you want to preserve the signature of decorated +functions in a consistent way across Python releases. Versions 5.X +supports Python versions greater than 3.4, versions 4.X supports Python +versions back to 2.6; versions 3.X are able to support even Python 2.5 and +2.4. + +What's New in version 5 +----------------------- + +Version 5 of the decorator module features a major simplification of +the code base made possible by dropping support for Python releases +older than 3.5. From that version the ``Signature`` object works well +enough that it is possible to fix the signature of a decorated +function without resorting to ``exec`` tricks. The simplification +has a very neat advantage: in case of exceptions raised in decorated +functions the traceback is nicer than it used to be. Moreover, it is +now possible to mimic the behavior of decorators defined with +``functool.wraps``: see the section about the ``kwsyntax`` flag below. What's New in version 4 ----------------------- - **New documentation** There is now a single manual for all Python versions, so I took the - opportunity to overhaul the documentation and to move it to readthedocs.org. + opportunity to overhaul the documentation. Even if you are a long-time user, you may want to revisit the docs, since several examples have been improved. @@ -94,8 +93,8 @@ matters. In principle, their introduction in Python 2.4 changed nothing, since they did not provide any new functionality which was not already present in the language. In practice, their introduction has -significantly changed the way we structure our programs in Python. I -believe the change is for the best, and that decorators are a great +significantly changed the way we structure our programs. +I believe the change is for the best, and that decorators are a great idea since: * decorators help reducing boilerplate code; @@ -176,10 +175,6 @@ ``__name__``, ``__doc__``, ``__module__``, and ``__dict__`` to the decorated function by hand). -Here is an example of usage: - -$$f1 - This works insofar as the decorator accepts functions with generic signatures. Unfortunately, it is *not* a signature-preserving decorator, since ``memoize_uw`` generally returns a function with a *different signature* @@ -194,17 +189,17 @@ keyword arguments: ```python ->>> from decorator import getfullargspec +>>> from inspect import getfullargspec >>> print(getfullargspec(f1)) FullArgSpec(args=[], varargs='args', varkw='kw', defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={}) ``` -This means that introspection tools (like ``pydoc``) will give false -information about the signature of ``f1`` -- unless you are using -Python 3.5. This is pretty bad: ``pydoc`` will tell you that the -function accepts the generic signature ``*args, **kw``, but -calling the function with more than one argument raises an error: +This means that introspection tools like ``getfullargspec`` will give +you false information about the signature of ``f1`` This is pretty bad: +``getfullargspec`` says that the function accepts the generic +signature ``*args, **kw``, but calling the function with more than one +argument raises an error: ```python >>> f1(0, 1) # doctest: +IGNORE_EXCEPTION_DETAIL @@ -214,9 +209,8 @@ ``` -Notice that ``inspect.getfullargspec`` -will give the wrong signature, even in the latest Python, i.e. version 3.6 -at the time of writing. +Notice that ``pydoc`` will give the right signature, but only in Python +versions greater than 3.5. The solution ----------------------------------------- @@ -315,16 +309,17 @@ ```python >>> @trace -... def f(x, y=1, z=2, *args, **kw): +... def f(x, y=1, *args, **kw): ... pass >>> f(0, 3) -calling f with args (0, 3, 2), {} +calling f with args (0, 3), {} >>> print(getfullargspec(f)) -FullArgSpec(args=['x', 'y', 'z'], varargs='args', varkw='kw', defaults=(1, 2), kwonlyargs=[], kwonlydefaults=None, annotations={}) +FullArgSpec(args=['x', 'y'], varargs='args', varkw='kw', defaults=(1,), kwonlyargs=[], kwonlydefaults=None, annotations={}) ``` + $FUNCTION_ANNOTATIONS ``decorator.decorator`` @@ -340,8 +335,6 @@ ```python >>> from decorator import decorator ->>> print(decorator.__doc__) -decorator(caller) converts a caller function into a decorator ``` The ``decorator`` function can be used as a signature-changing @@ -380,6 +373,69 @@ ``` +Mimicking the behavior of functools.wrap +---------------------------------------- + +Often people are confused by the decorator module since, contrarily +to ``functools.wraps`` in the standard library, it tries very hard +to keep the semantics of the arguments: in particular, positional arguments +stay positional even if they are called with the keyword argument syntax. +An example will make the issue clear. Here is a simple caller + +$$chatty + +and here is a function to decorate: + +$$printsum + +In this example ``x`` and ``y`` are positional arguments (with +defaults). From the caller perspective, it does not matter if the user +calls them as named arguments, they will stay inside the ``args`` +tuple and not inside the ``kwargs`` dictionary: + +```python +>>> printsum(y=2, x=1) +(1, 2) [] +3 + +``` + +This is quite different from the behavior of ``functools.wraps``; if you +define the decorator as follows + +$$chattywrapper + +you will see that calling ``printsum`` with named arguments will pass +such arguments to ``kwargs``, while ``args`` will be the empty tuple. +Since version 5 of the decorator module it is possible to mimic that +behavior by using the ``kwsyntax`` flag: + +$$printsum2 + +Here is how it works: + +```python +>>> printsum2(y=2, x=1) +() [('x', 1), ('y', 2)] +3 + +``` + +This is exactly what the ``chattywrapper`` decorator would print: +positional arguments are seen as keyword arguments, but only if the +client code calls them with the keyword syntax. Otherwise they stay +positional, i.e. they belongs to the ``args`` tuple and not to ``kwargs``: + +```python +>>> printsum2(1, 2) +(1, 2) [] +3 + +``` + +Decorator factories +------------------------------------------- + The `decorator` function can also be used to define factories of decorators, i.e. functions returning decorators. In general you can just write something like this: @@ -392,9 +448,8 @@ ``` This is fully general but requires an additional level of nesting. For this -reason since version 4.2 there is a facility to build -decorator factories by using a single caller with default arguments i.e. -writing something like this: +reason since version 4.2 there is a facility to build decorator factories by +using a single caller with default arguments: ```python def caller(f, param1=default1, param2=default2, ..., *args, **kw): @@ -408,22 +463,17 @@ of the family which uses the default values for all parameters. Such decorator can be written as ``decfactory()`` with no parameters specified; moreover, as a shortcut, it is also possible to elide the parenthesis, -a feature much requested by the users. For years I have been opposite -to this feature request, since having explicit parenthesis to me is more clear +a feature much requested by the users. For years I have been opposing +the request, since having explicit parenthesis to me is more clear and less magic; however once this feature entered in decorators of the Python standard library (I am referring to the [dataclass decorator]( https://www.python.org/dev/peps/pep-0557/)) I finally gave up. -The example below will show how it works in practice. - -Decorator factories -------------------------------------------- - -Sometimes one has to deal with blocking resources, such as ``stdin``. -Sometimes it is better to receive a "busy" message than just blocking -everything. -This can be accomplished with a suitable family of decorators (decorator -factory), parameterize by a string, the busy message: +The example below shows how it works in practice. The goal is to +convert a function relying on a blocking resource into a function +returning a "busy" message if the resource is not available. +This can be accomplished with a suitable family of decorators +parameterize by a string, the busy message: $$blocking @@ -529,8 +579,7 @@ ``` ...then ``before_after`` is a factory function that returns -``GeneratorContextManager`` objects, which provide the -use of the ``with`` statement: +``GeneratorContextManager`` objects, usable with the ``with`` statement: ```python >>> with before_after('BEFORE', 'AFTER'): @@ -564,17 +613,10 @@ The ``ba`` decorator basically inserts a ``with ba:`` block inside the function. -However, there are two issues: - -1. ``GeneratorContextManager`` objects are only callable in Python 3.2, - so the previous example breaks in older versions of Python. - (You can solve this by installing ``contextlib2``, which backports - the Python 3 functionality to Python 2.) - -2. ``GeneratorContextManager`` objects do not preserve the signature of - the decorated functions. The decorated ``hello`` function above will - have the generic signature ``hello(*args, **kwargs)``, but fails if - called with more than zero arguments. +However ``GeneratorContextManager`` objects do not preserve the signature of +the decorated functions. The decorated ``hello`` function above will +have the generic signature ``hello(*args, **kwargs)``, but fails if +called with more than zero arguments. For these reasons, the `decorator` module, starting from release 3.4, offers a ``decorator.contextmanager`` decorator that solves both problems, @@ -588,14 +630,13 @@ The ``FunctionMaker`` class --------------------------------------------------------------- -You may wonder how the functionality of the ``decorator`` module -is implemented. The basic building block is -a ``FunctionMaker`` class. It generates on-the-fly functions +The ``decorator`` module also provides a ``FunctionMaker`` class, which +is able to generate on-the-fly functions with a given name and signature from a function template passed as a string. If you're just writing ordinary decorators, then you probably won't -need to use ``FunctionMaker`` directly. But in some circumstances, it +need to use ``FunctionMaker``. But in some circumstances, it can be handy. You will see an example shortly--in the implementation of a cool decorator utility (``decorator_apply``). @@ -655,9 +696,9 @@ - If first argument of ``FunctionMaker.create`` is a function, an instance of ``FunctionMaker`` is created with the attributes - ``args``, ``varargs``, ``keywords``, and ``defaults``. - (These mirror the return values of the standard library's - ``inspect.getfullargspec``.) + ``args``, ``varargs``, ``keywords``, and ``defaults`` + (these mirror the return values of the standard library's + ``inspect.getfullargspec``). - For each item in ``args`` (a list of strings of the names of all required arguments), an attribute ``arg0``, ``arg1``, ..., ``argN`` is also generated. @@ -685,28 +726,6 @@ decorated functions. In IPython, this means that the usual ``??`` trick will give you the (right on the spot) message ``Dynamically generated function. No source code available``. - -In the past, I considered this acceptable, since ``inspect.getsource`` -does not really work with "regular" decorators. In those cases, -``inspect.getsource`` gives you the wrapper source code, which is probably -not what you want: - -$$identity_dec -$$example - -```python ->>> import inspect ->>> print(inspect.getsource(example)) - def wrapper(*args, **kw): - return func(*args, **kw) -<BLANKLINE> - -``` - -(See bug report [1764286](http://bugs.python.org/issue1764286) -for an explanation of what is happening). -Unfortunately the bug still exists in all versions of Python < 3.5. - However, there is a workaround. The decorated function has the ``__wrapped__`` attribute, pointing to the original function. The simplest way to get the source code is to call ``inspect.getsource`` on the undecorated function: @@ -791,11 +810,10 @@ Python 3.5 coroutines ----------------------- -I am personally not using Python 3.5 coroutines yet, because at work we are -still maintaining compatibility with Python 2.7. However, some users requested -support for coroutines and since version 4.1 the decorator module has it. -You should consider the support experimental and kindly report issues if -you find any. +I am personally not using Python 3.5 coroutines yet. However, some +users requested support for coroutines and since version 4.1 the +decorator module has it. You should consider the support experimental +and kindly report issues if you find any. Here I will give a single example of usage. Suppose you want to log the moment a coroutine starts and the moment it stops for debugging purposes. You could @@ -807,7 +825,7 @@ from asyncio import get_event_loop, sleep, wait from decorator import decorator - @decorator +@decorator async def log_start_stop(coro, *args, **kwargs): logging.info('Starting %s%s', coro.__name__, args) t0 = time.time() @@ -855,8 +873,8 @@ return get_event_loop().run_until_complete(coro(*args, **kw)) ``` -Notice the diffence: the caller in ``log_start_stop`` was a coroutine -function and the associate decorator was converting coroutines->coroutines; +Notice the difference: the caller in ``log_start_stop`` was a coroutine +function and the associate decorator was converting coroutines in coroutines; the caller in ``coro_to_func`` is a regular function and converts coroutines -> functions. @@ -982,7 +1000,7 @@ ``` -You can introspect the precedence used by the dispath algorithm by +You can introspect the precedence used by the dispatch algorithm by calling ``.dispatch_info(*types)``: ```python @@ -1146,106 +1164,12 @@ Caveats and limitations ------------------------------------------- -One thing you should be aware of, is the performance penalty of decorators. -The worse case is shown by the following example: - -```bash - $ cat performance.sh - python3 -m timeit -s " - from decorator import decorator - - @decorator - def do_nothing(func, *args, **kw): - return func(*args, **kw) - - @do_nothing - def f(): - pass - " "f()" - - python3 -m timeit -s " - def f(): - pass - " "f()" - -``` -On my laptop, using the ``do_nothing`` decorator instead of the -plain function is five times slower: - -```bash - $ bash performance.sh - 1000000 loops, best of 3: 1.39 usec per loop - 1000000 loops, best of 3: 0.278 usec per loop -``` -Of course, a real life function probably does something more useful -than the function ``f`` here, so the real life performance penalty -*could* be negligible. As always, the only way to know if there is a -penalty in your specific use case is to measure it. - -More importantly, you should be aware that decorators will make your -tracebacks longer and more difficult to understand. - -Consider this example: - -```python ->>> @trace -... def f(): -... 1/0 - -``` - -Calling ``f()`` gives you a ``ZeroDivisionError``. -But since the function is decorated, the traceback is longer: - -```python ->>> f() # doctest: +ELLIPSIS -Traceback (most recent call last): - ... - File "<string>", line 2, in f - File "<doctest __main__[22]>", line 4, in trace - return f(*args, **kw) - File "<doctest __main__[51]>", line 3, in f - 1/0 -ZeroDivisionError: ... - -``` - -You see here the inner call to the decorator ``trace``, which calls -``f(*args, **kw)``, and a reference to ``File "<string>", line 2, in f``. - -This latter reference is due to the fact that, internally, the decorator -module uses ``exec`` to generate the decorated function. Notice that -``exec`` is *not* responsible for the performance penalty, since is the -called *only once* (at function decoration time); it is *not* called -each time the decorated function is called. - -Presently, there is no clean way to avoid ``exec``. A clean solution -would require changing the CPython implementation, by -adding a hook to functions (to allow changing their signature directly). - -Even in Python 3.5, it is impossible to change the -function signature directly. Thus, the ``decorator`` module is -still useful! As a matter of fact, this is the main reason why I still -maintain the module and release new versions. - -It should be noted that in Python 3.5, a *lot* of improvements have -been made: you can decorate a function with -``func_tools.update_wrapper``, and ``pydoc`` will see the correct -signature. Unfortunately, the function will still have an incorrect -signature internally, as you can see by using -``inspect.getfullargspec``; so, all documentation tools using -``inspect.getfullargspec`` - which has been rightly deprecated - -will see the wrong signature. - In the present implementation, decorators generated by ``decorator`` -can only be used on user-defined Python functions or methods. -They cannot be used on generic callable objects or built-in functions, -due to limitations of the standard library's ``inspect`` module, especially -for Python 2. In Python 3.5, many such limitations have been removed, but -I still think that it is cleaner and safer to decorate only functions and -coroutines. If you want to decorate things like classmethods/staticmethods -and general callables - which I will never support in the decorator module - -I suggest you to look at the [wrapt](https://wrapt.readthedocs.io/en/latest/) +can only be used on user-defined Python functions, methods or coroutines. +I have no interest in decorating generic callable objects. If you want to +decorate things like classmethods/staticmethods and general callables - +which I will never support in the decorator module - I suggest you +to look at the [wrapt](https://wrapt.readthedocs.io/en/latest/) project by Graeme Dumpleton. There is a strange quirk when decorating functions with keyword @@ -1270,7 +1194,7 @@ The error message looks really strange... until you realize that the caller function `_memoize` uses `func` as first argument, so there is a confusion between the positional argument and the -keywork arguments. +keyword arguments. The solution is to change the name of the first argument in `_memoize`, or to change the implementation like so: @@ -1297,22 +1221,7 @@ keyword arguments is not such a good idea, and you may want not to do that. -On a similar note, there is a restriction on argument names. For instance, -if you name an argument ``_call_`` or ``_func_``, you will get a ``NameError``: - -```python ->>> @trace -... def f(_func_): print(f) -... -Traceback (most recent call last): - ... -NameError: _func_ is overridden in -def f(_func_): - return _call_(_func_, _func_) - -``` - -Finally, the implementation is such that the decorated function makes +The implementation is such that the decorated function makes a (shallow) copy of the original function dictionary: ```python @@ -1330,6 +1239,43 @@ ``` +Finally, you should be aware of the performance penalty of decorators. +The worse case is shown by the following example: + +```bash + $ cat performance.sh + python3 -m timeit -s " + from decorator import decorator + + @decorator + def do_nothing(func, *args, **kw): + return func(*args, **kw) + + @do_nothing + def f(): + pass + " "f()" + + python3 -m timeit -s " + def f(): + pass + " "f()" + +``` +On my laptop, using the ``do_nothing`` decorator instead of the +plain function is five times slower: + +```bash + $ bash performance.sh + 1000000 loops, best of 3: 1.39 usec per loop + 1000000 loops, best of 3: 0.278 usec per loop +``` + +Of course, a real life function probably does something more useful +than the function ``f`` here, so the real life performance penalty +*could* be negligible. As always, the only way to know if there is a +penalty in your specific use case is to measure it. + LICENSE (2-clause BSD) --------------------------------------------- @@ -1384,8 +1330,8 @@ ``` -In order to introspect functions with annotations, one needs the -utility ``inspect.getfullargspec`` (introduced in Python 3, then +In order to introspect functions with annotations, one needs +``inspect.getfullargspec`` (introduced in Python 3, then deprecated in Python 3.5, then undeprecated in Python 3.6): ```python @@ -1420,7 +1366,6 @@ Another attribute copied from the original function is ``__qualname__``, the qualified name. This attribute was introduced in Python 3.3. """ - if sys.version_info < (3,): function_annotations = '' @@ -1654,71 +1599,48 @@ """ -if sys.version_info >= (3,): # tests for signatures specific to Python 3 +def test_kwonlydefaults(): + """ + >>> @trace + ... def f(arg, defarg=1, *args, kwonly=2): pass + ... + >>> f.__kwdefaults__ + {'kwonly': 2} + """ + + +def test_kwonlyargs(): + """ + >>> @trace + ... def func(a, b, *args, y=2, z=3, **kwargs): + ... return y, z + ... + >>> func('a', 'b', 'c', 'd', 'e', y='y', z='z', cat='dog') + calling func with args ('a', 'b', 'c', 'd', 'e'), {'cat': 'dog', 'y': 'y', 'z': 'z'} + ('y', 'z') + """ + - def test_kwonlydefaults(): - """ - >>> @trace - ... def f(arg, defarg=1, *args, kwonly=2): pass - ... - >>> f.__kwdefaults__ - {'kwonly': 2} - """ - - def test_kwonlyargs(): - """ - >>> @trace - ... def func(a, b, *args, y=2, z=3, **kwargs): - ... return y, z - ... - >>> func('a', 'b', 'c', 'd', 'e', y='y', z='z', cat='dog') - calling func with args ('a', 'b', 'c', 'd', 'e'), {'cat': 'dog', 'y': 'y', 'z': 'z'} - ('y', 'z') - """ - - def test_kwonly_no_args(): - """# this was broken with decorator 3.3.3 - >>> @trace - ... def f(**kw): pass - ... - >>> f() - calling f with args (), {} - """ - - def test_kwonly_star_notation(): - """ - >>> @trace - ... def f(*, a=1, **kw): pass - ... - >>> import inspect - >>> inspect.getfullargspec(f) - FullArgSpec(args=[], varargs=None, varkw='kw', defaults=None, kwonlyargs=['a'], kwonlydefaults={'a': 1}, annotations={}) - """ - - -@contextmanager -def before_after(before, after): - print(before) - yield - print(after) - - -ba = before_after('BEFORE', 'AFTER') # ContextManager instance - - -@ba -def hello(user): - """ - >>> ba.__class__.__name__ - 'ContextManager' - >>> hello('michele') - BEFORE - hello michele - AFTER +def test_kwonly_no_args(): + """# this was broken with decorator 3.3.3 + >>> @trace + ... def f(**kw): pass + ... + >>> f() + calling f with args (), {} """ - print('hello %s' % user) +def test_kwonly_star_notation(): + """ + >>> @trace + ... def f(*, a=1, **kw): pass + ... + >>> import inspect + >>> inspect.getfullargspec(f) + FullArgSpec(args=[], varargs=None, varkw='kw', defaults=None, kwonlyargs=['a'], kwonlydefaults={'a': 1}, annotations={}) + """ + # ####################### multiple dispatch ############################ # @@ -1892,6 +1814,73 @@ time.sleep(.1) +def chatty(func, *args, **kwargs): + print(args, sorted(kwargs.items())) + return func(*args, **kwargs) + + +@decorator(chatty) +def printsum(x=1, y=2): + print(x + y) + + +@decorator(chatty, kwsyntax=True) +def printsum2(x=1, y=2): + print(x + y) + + +def chattywrapper(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + print(args, kwargs) + return func(*args, **kwargs) + return functools.wraps(wrapper) + +# ####################### changing the signature ########################## # + + +# see https://github.com/micheles/decorator/pull/85 +def to_method(f): + """ + Takes a function with signature (..., context) and returns a new + function with signature (self, ...) to be used a a method in a + class with a .context attribute. + """ + sig = inspect.signature(f) + params = list(sig.parameters.values()) + assert params[-1].name == 'context' + self = inspect.Parameter('self', inspect.Parameter.POSITIONAL_OR_KEYWORD) + params.insert(0, self) # insert self + del params[-1] # remove context + newsig = '%s%s' % (f.__name__, sig.replace(parameters=params)) + return FunctionMaker.create( + newsig, 'context = self.context; return _func_%s' % sig, + dict(_func_=f)) + + +def foo(x, context=None): + return x + + +def bar(x, y, context): + return x + y + + +class Client: + def __init__(self, context): + self.context = context + + +def test_change_sig(): + """ + >>> Client.foo = to_method(foo) + >>> Client.bar = to_method(bar) + >>> c = Client(None) + >>> assert c.foo(1) == 1 + >>> assert c.bar(1, 2) == 3 + """ + + if __name__ == '__main__': import doctest doctest.testmod() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/decorator-4.4.2/src/tests/test.py new/decorator-5.0.9/src/tests/test.py --- old/decorator-4.4.2/src/tests/test.py 2020-02-29 05:50:53.000000000 +0100 +++ new/decorator-5.0.9/src/tests/test.py 2021-05-16 05:34:45.000000000 +0200 @@ -1,21 +1,15 @@ -from __future__ import absolute_import import sys import doctest import unittest import decimal import inspect -import functools -import collections -from collections import defaultdict -try: - c = collections.abc -except AttributeError: - c = collections +from asyncio import get_event_loop +from collections import defaultdict, ChainMap, abc as c from decorator import dispatch_on, contextmanager, decorator try: - from . import documentation as doc -except (ImportError, ValueError, SystemError): # depending on the py-version - import documentation as doc + from . import documentation as doc # good with pytest +except ImportError: + import documentation as doc # good with `python src/tests/test.py` @contextmanager @@ -29,22 +23,21 @@ raise Exception('Expected %s' % etype.__name__) -if sys.version_info >= (3, 5): - exec('''from asyncio import get_event_loop - @decorator async def before_after(coro, *args, **kwargs): return "<before>" + (await coro(*args, **kwargs)) + "<after>" + @decorator def coro_to_func(coro, *args, **kw): return get_event_loop().run_until_complete(coro(*args, **kw)) + class CoroutineTestCase(unittest.TestCase): def test_before_after(self): @before_after async def coro(x): - return x + return x self.assertTrue(inspect.iscoroutinefunction(coro)) out = get_event_loop().run_until_complete(coro('x')) self.assertEqual(out, '<before>x<after>') @@ -55,7 +48,6 @@ return x self.assertFalse(inspect.iscoroutinefunction(coro)) self.assertEqual(coro('x'), 'x') -''') def gen123(): @@ -80,28 +72,42 @@ err = doctest.testmod(doc)[0] self.assertEqual(err, 0) + def test_copy_dunder_attrs(self): + traced = doc.trace(doc.foo) + self.assertIn('documentation', traced.__module__) + self.assertEqual(traced.__annotations__, {}) + self.assertEqual(traced.__defaults__, (None,)) + def test_singledispatch1(self): - if hasattr(functools, 'singledispatch'): - with assertRaises(RuntimeError): - doc.singledispatch_example1() + with assertRaises(RuntimeError): + doc.singledispatch_example1() def test_singledispatch2(self): - if hasattr(functools, 'singledispatch'): - doc.singledispatch_example2() + doc.singledispatch_example2() + + def test_context_manager(self): + + @contextmanager + def before_after(before, after): + print(before) + yield + print(after) + + @before_after('BEFORE', 'AFTER') + def hello_user(user): + print('hello %s' % user) + + argspec = inspect.getfullargspec(hello_user) + self.assertEqual(argspec.args, ['user']) class ExtraTestCase(unittest.TestCase): def test_qualname(self): - if sys.version_info >= (3, 3): - self.assertEqual(doc.hello.__qualname__, 'hello') - else: - with assertRaises(AttributeError): - doc.hello.__qualname__ + self.assertEqual(doc.operation1.__qualname__, 'operation1') def test_signature(self): - if hasattr(inspect, 'signature'): - sig = inspect.signature(doc.f1) - self.assertEqual(str(sig), '(x)') + sig = inspect.signature(doc.f1) + self.assertEqual(str(sig), '(x)') def test_unique_filenames(self): @decorator @@ -125,10 +131,13 @@ @d1 def f1(x, y, z): pass - self.assertNotEqual(d1.__code__.co_filename, d2.__code__.co_filename) - self.assertNotEqual(f1.__code__.co_filename, f2.__code__.co_filename) - self.assertNotEqual(f1_orig.__code__.co_filename, - f1.__code__.co_filename) + + self.assertEqual(d1.__code__.co_filename, + d2.__code__.co_filename) + self.assertEqual(f1.__code__.co_filename, + f2.__code__.co_filename) + self.assertEqual(f1_orig.__code__.co_filename, + f1.__code__.co_filename) def test_no_first_arg(self): @decorator @@ -137,17 +146,19 @@ @example def func(**kw): + "Docstring" return kw # there is no confusion when passing args as a keyword argument self.assertEqual(func(args='a'), {'args': 'a'}) + self.assertEqual(func.__doc__, "Docstring") def test_decorator_factory(self): # similar to what IPython is doing in traitlets.config.application @decorator def catch_config_error(method, app, *args, **kwargs): return method(app) - catch_config_error(lambda app: None) + catch_config_error(lambda app, **kw: None)(1) def test_add1(self): # similar to what IPython is doing in traitlets.config.application @@ -159,6 +170,29 @@ return x self.assertEqual(add(f, 2)(0), 2) + def test_dan_schult(self): + # see https://github.com/micheles/decorator/issues/120 + @decorator + def prnt(func, index=0, *args, **kw): + print(args[index]) + return func(*args, **kw) + + @prnt(index=2) # print the value of the third argument + def f(a, b, c=None): + return [a, b, c] + + self.assertEqual(f(0, 1), [0, 1, None]) + + def test_slow_wrapper(self): + # see https://github.com/micheles/decorator/issues/123 + dd = defaultdict(list) + doc.trace(defaultdict.__setitem__)(dd, 'x', [1]) + self.assertEqual(dd['x'], [1]) + doc.trace(defaultdict.__delitem__)(dd, 'x') + self.assertEqual(dd['x'], []) + # NB: defaultdict.__getitem__ has no signature and cannot be + # decorated in CPython, while it is regular in PyPy + # ################### test dispatch_on ############################# # # adapted from test_functools in Python 3.5 @@ -290,14 +324,13 @@ self.assertEqual(g(f), "sized") self.assertEqual(g(t), "sized") - if hasattr(c, 'ChainMap'): - g.register(c.ChainMap)(lambda obj: "chainmap") - # irrelevant ABCs registered - self.assertEqual(g(d), "mutablemapping") - self.assertEqual(g(l), "sized") - self.assertEqual(g(s), "sized") - self.assertEqual(g(f), "sized") - self.assertEqual(g(t), "sized") + g.register(ChainMap)(lambda obj: "chainmap") + # irrelevant ABCs registered + self.assertEqual(g(d), "mutablemapping") + self.assertEqual(g(l), "sized") + self.assertEqual(g(s), "sized") + self.assertEqual(g(f), "sized") + self.assertEqual(g(t), "sized") g.register(c.MutableSequence)(lambda obj: "mutablesequence") self.assertEqual(g(d), "mutablemapping") ++++++ kwsyntax.patch ++++++ >From 817d070db3c9cc5900d118837c533c039982b050 Mon Sep 17 00:00:00 2001 From: Michele Simionato <michele.simion...@gmail.com> Date: Fri, 28 May 2021 08:30:09 +0200 Subject: [PATCH] Fixed decorator.decorator not passing kwsyntax --- CHANGES.md | 2 ++ src/decorator.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 596559b..876df34 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,8 @@ HISTORY ## unreleased +decorator.decorator was not passing the kwsyntax. + ## 5.0.9 (2021-05-16) Fixed a test breaking PyPy. Restored support for Sphinx. diff --git a/src/decorator.py b/src/decorator.py index 438dff6..dab0d7c 100644 --- a/src/decorator.py +++ b/src/decorator.py @@ -265,7 +265,7 @@ def decorator(caller, _func=None, kwsyntax=False): """ if _func is not None: # return a decorated function # this is obsolete behavior; you should use decorate instead - return decorate(_func, caller) + return decorate(_func, caller, (), kwsyntax) # else return a decorator function sig = inspect.signature(caller) dec_params = [p for p in sig.parameters.values() if p.kind is POS]