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?? <[email protected]>
+
+- Add upstream patch kwsyntax.patch
+
+-------------------------------------------------------------------
+Tue Jul 27 06:15:15 UTC 2021 - Dirk M??ller <[email protected]>
+
+- 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 | [email protected]|
|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 <[email protected]>
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]