summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichele Simionato <michele.simionato@gmail.com>2012-04-23 16:48:26 +0200
committerMichele Simionato <michele.simionato@gmail.com>2012-04-23 16:48:26 +0200
commitc9d029e4d9ba32b09462a635413fe4d80a06c9a1 (patch)
tree57e50265a6dc6ed6e254d9dc62da66e38b3a5ac9
parentdac75445820615009558537f441e0181124701e8 (diff)
downloadmicheles-c9d029e4d9ba32b09462a635413fe4d80a06c9a1.tar.gz
Fixed a bug with kwonly args in Python 3.X
-rw-r--r--decorator/CHANGES.txt2
-rw-r--r--decorator/documentation3.py28
-rw-r--r--decorator/src/decorator.py34
3 files changed, 37 insertions, 27 deletions
diff --git a/decorator/CHANGES.txt b/decorator/CHANGES.txt
index cbc1913..a319a94 100644
--- a/decorator/CHANGES.txt
+++ b/decorator/CHANGES.txt
@@ -1,6 +1,8 @@
HISTORY
----------
+3.3.3 Fixed a bug with kwonlyargs for Python 3, submitted by Chris
+ Ellison (23/04/2012)
3.3.2 Fixed a bug with __kwdefaults__ for Python 3, submitted by Chris
Ellison (01/09/2011)
3.3.1 Fixed a doctest broken for Python 3.2, as noted by
diff --git a/decorator/documentation3.py b/decorator/documentation3.py
index 8524c24..07a4e40 100644
--- a/decorator/documentation3.py
+++ b/decorator/documentation3.py
@@ -269,8 +269,6 @@ utility ``inspect.getfullargspec``, new in Python 3:
>>> argspec.kwonlyargs
[]
>>> argspec.kwonlydefaults
- >>> sorted(argspec.annotations.items())
- [('args', 'varargs'), ('kw', 'kwargs'), ('x', 'the first argument'), ('y', 'default argument')]
You can also check that the ``__annotations__`` dictionary is preserved:
@@ -279,16 +277,9 @@ You can also check that the ``__annotations__`` dictionary is preserved:
>>> f.__annotations__ == f.__wrapped__.__annotations__
True
-The two dictionaries are different objects, though
-
-.. code-block:: python
-
- >>> id(f.__annotations__) != id(f.__wrapped__.__annotations__)
- True
-
-since internally the decorator module creates an entirely new dictionary
-(it is not simply attaching the ``__annotations__`` attribute to the new
-function).
+Depending on the version of the decorator module, the two dictionaries can
+be the same object or not: you cannot rely on object identity, but you can
+rely on the content being the same.
``decorator`` is a decorator
---------------------------------------------
@@ -691,7 +682,7 @@ would require to change the CPython implementation of functions and
add an hook to make it possible to change their signature directly.
That could happen in future versions of Python (see PEP 362_) and
then the decorator module would become obsolete. However, at present,
-even in Python 3.1 it is impossible to change the function signature
+even in Python 3.2 it is impossible to change the function signature
directly, therefore the ``decorator`` module is still useful.
Actually, this is one of the main reasons why I keep maintaining
the module and releasing new versions.
@@ -1063,5 +1054,16 @@ def test_kwonlydefaults():
{'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'), {'y': 'y', 'z': 'z', 'cat': 'dog'}
+ ('y', 'z')
+ """
+
if __name__ == '__main__':
import doctest; doctest.testmod()
diff --git a/decorator/src/decorator.py b/decorator/src/decorator.py
index e7b3369..3dbbdd4 100644
--- a/decorator/src/decorator.py
+++ b/decorator/src/decorator.py
@@ -32,7 +32,7 @@ Decorator module, see http://pypi.python.org/pypi/decorator
for the documentation.
"""
-__version__ = '3.3.2'
+__version__ = '3.3.3'
__all__ = ["decorator", "FunctionMaker", "partial"]
@@ -62,7 +62,6 @@ else:
inspect.getargspec(f)
self.kwonlyargs = []
self.kwonlydefaults = None
- self.annotations = getattr(f, '__annotations__', {})
def __iter__(self):
yield self.args
yield self.varargs
@@ -90,22 +89,28 @@ class FunctionMaker(object):
self.module = func.__module__
if inspect.isfunction(func):
argspec = getfullargspec(func)
+ self.annotations = getattr(func, '__annotations__', {})
for a in ('args', 'varargs', 'varkw', 'defaults', 'kwonlyargs',
- 'kwonlydefaults', 'annotations'):
+ 'kwonlydefaults'):
setattr(self, a, getattr(argspec, a))
for i, arg in enumerate(self.args):
setattr(self, 'arg%d' % i, arg)
- self.signature = inspect.formatargspec(
- formatvalue=lambda val: "", *argspec)[1:-1]
- allargs = list(self.args)
- if self.varargs:
- allargs.append('*' + self.varargs)
- if self.varkw:
- allargs.append('**' + self.varkw)
- try:
- self.shortsignature = ', '.join(allargs)
- except TypeError: # exotic signature, valid only in Python 2.X
- self.shortsignature = self.signature
+ if sys.version < '3': # easy way
+ self.shortsignature = self.signature = \
+ inspect.formatargspec(
+ formatvalue=lambda val: "", *argspec)[1:-1]
+ else: # Python 3 way
+ self.signature = self.shortsignature = ', '.join(self.args)
+ if self.varargs:
+ self.signature += ', *' + self.varargs
+ self.shortsignature += ', *' + self.varargs
+ if self.kwonlyargs:
+ for a in self.kwonlyargs:
+ self.signature += ', %s=None' % a
+ self.shortsignature += ', %s=%s' % (a, a)
+ if self.varkw:
+ self.signature += ', **' + self.varkw
+ self.shortsignature += ', **' + self.varkw
self.dict = func.__dict__.copy()
# func=None happens when decorating a caller
if name:
@@ -132,6 +137,7 @@ class FunctionMaker(object):
func.__dict__ = getattr(self, 'dict', {})
func.func_defaults = getattr(self, 'defaults', ())
func.__kwdefaults__ = getattr(self, 'kwonlydefaults', None)
+ func.__annotations__ = getattr(self, 'annotations', None)
callermodule = sys._getframe(3).f_globals.get('__name__', '?')
func.__module__ = getattr(self, 'module', callermodule)
func.__dict__.update(kw)