diff options
| author | Michele Simionato <michele.simionato@gmail.com> | 2017-01-15 10:06:58 +0100 |
|---|---|---|
| committer | Michele Simionato <michele.simionato@gmail.com> | 2017-01-15 10:06:58 +0100 |
| commit | 8608a4672a5c4ae390040d56ef049980a144c280 (patch) | |
| tree | 61d474c04ae721cf5c51a4b9963f3c93973329ec /src | |
| parent | 7d24926595c7719a7118e8b821a865f33a0445c0 (diff) | |
| download | python-decorator-git-8608a4672a5c4ae390040d56ef049980a144c280.tar.gz | |
Tested with Python 3.6
Diffstat (limited to 'src')
| -rw-r--r-- | src/decorator.py | 39 | ||||
| -rw-r--r-- | src/tests/documentation.py | 49 |
2 files changed, 47 insertions, 41 deletions
diff --git a/src/decorator.py b/src/decorator.py index 09a466e..6362c02 100644 --- a/src/decorator.py +++ b/src/decorator.py @@ -1,6 +1,6 @@ # ######################### LICENSE ############################ # -# Copyright (c) 2005-2016, Michele Simionato +# Copyright (c) 2005-2017, Michele Simionato # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -40,7 +40,7 @@ import operator import itertools import collections -__version__ = '4.0.10' +__version__ = '4.0.11' if sys.version >= '3': from inspect import getfullargspec @@ -48,21 +48,13 @@ if sys.version >= '3': def get_init(cls): return cls.__init__ else: - class getfullargspec(object): - "A quick and dirty replacement for getfullargspec for Python 2.X" - def __init__(self, f): - self.args, self.varargs, self.varkw, self.defaults = \ - inspect.getargspec(f) - self.kwonlyargs = [] - self.kwonlydefaults = None - - def __iter__(self): - yield self.args - yield self.varargs - yield self.varkw - yield self.defaults + FullArgSpec = collections.namedtuple( + 'FullArgSpec', 'args varargs varkw defaults ' + 'kwonlyargs kwonlydefaults') - getargspec = inspect.getargspec + 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__ @@ -78,7 +70,7 @@ def getargspec(f): return ArgSpec(spec.args, spec.varargs, spec.varkw, spec.defaults) -DEF = re.compile('\s*def\s*([_\w][_\w\d]*)\s*\(') +DEF = re.compile(r'\s*def\s*([_\w][_\w\d]*)\s*\(') # basic functionality @@ -92,6 +84,9 @@ class FunctionMaker(object): # Atomic get-and-increment provided by the GIL _compile_count = itertools.count() + # make pylint happy + args = varargs = varkw = defaults = kwonlyargs = kwonlydefaults = () + def __init__(self, func=None, name=None, signature=None, defaults=None, doc=None, module=None, funcdict=None): self.shortsignature = signature @@ -154,8 +149,8 @@ class FunctionMaker(object): func.__name__ = self.name func.__doc__ = getattr(self, 'doc', None) func.__dict__ = getattr(self, 'dict', {}) - func.__defaults__ = getattr(self, 'defaults', ()) - func.__kwdefaults__ = getattr(self, 'kwonlydefaults', None) + func.__defaults__ = self.defaults + func.__kwdefaults__ = self.kwonlydefaults or None func.__annotations__ = getattr(self, 'annotations', None) try: frame = sys._getframe(3) @@ -346,7 +341,7 @@ def dispatch_on(*dispatch_args): ras = [[] for _ in range(len(dispatch_args))] for types_ in typemap: for t, type_, ra in zip(types, types_, ras): - if issubclass(t, type_) and type_ not in t.__mro__: + if issubclass(t, type_) and type_ not in t.mro(): append(type_, ra) return [set(ra) for ra in ras] @@ -363,9 +358,9 @@ def dispatch_on(*dispatch_args): 'Ambiguous dispatch for %s: %s' % (t, vas)) elif n_vas == 1: va, = vas - mro = type('t', (t, va), {}).__mro__[1:] + mro = type('t', (t, va), {}).mro()[1:] else: - mro = t.__mro__ + mro = t.mro() lists.append(mro[:-1]) # discard t and object return lists diff --git a/src/tests/documentation.py b/src/tests/documentation.py index e651281..6b5d90c 100644 --- a/src/tests/documentation.py +++ b/src/tests/documentation.py @@ -1,6 +1,6 @@ from __future__ import print_function -doc = r""" +doc = r"""\ The ``decorator`` module ============================================================= @@ -33,12 +33,12 @@ trivial to distribute the module as an universal wheel_ since 2to3 is no more required. Since Python 2.5 has been released 9 years ago, 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 to 3.5. +3.4.2. The current version supports all Python releases from 2.6 up to 3.6. .. _wheel: http://pythonwheels.com/ -What's New ---------------------- +What's New in version 4 +----------------------- - **New documentation** There is now a single manual for all Python versions, so I took the @@ -596,7 +596,7 @@ 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 have considered this acceptable, since ``inspect.getsource`` +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: @@ -725,10 +725,12 @@ $$XMLWriter Here, you want to dispatch on the *second* argument; the first is already taken by ``self``. The ``dispatch_on`` decorator factory allows you to specify -the dispatch argument simplpy by passing its name as a string. (Note +the dispatch argument simply by passing its name as a string. (Note that if you misspell the name you will get an error.) -The decorated function decorated is turned into a generic function, +The decorated function `write` is turned into a generic function ( +`write` is a function at the idea it is decorated; it will be turned +into a method later, at class instantiation time), and it is called if there are no more specialized implementations. Usually, default functions should raise a ``NotImplementedError``, thus @@ -972,7 +974,7 @@ not use any cache, whereas the ``singledispatch`` implementation does. Caveats and limitations ------------------------------------------- -One thing you should be aware of is the performance penalty of decorators. +One thing you should be aware of, is the performance penalty of decorators. The worse case is shown by the following example: .. code-block:: bash @@ -1003,9 +1005,9 @@ plain function is five times slower:: 1000000 loops, best of 3: 0.278 usec per loop Of course, a real life function probably does something more useful -than ``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. +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. @@ -1036,7 +1038,7 @@ But since the function is decorated, the traceback is longer: 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 +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 @@ -1056,9 +1058,9 @@ 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 the -function (which has rightly been deprecated) will see the wrong -signature. +``inspect.getfullargspec``; so, all documentation tools using +``inspect.getfullargspec`` - which has been rightly deprecated - +will see the wrong signature. .. _362: http://www.python.org/dev/peps/pep-0362 @@ -1066,7 +1068,13 @@ 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.) +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. 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_ project by Graeme Dumpleton. + +.. _wrapt: https://wrapt.readthedocs.io/en/latest/ There is a strange quirk when decorating functions with keyword arguments, if one of the arguments has the same name used in the @@ -1109,7 +1117,10 @@ or to change the implementation like so: This avoids the need to name the first argument, so the problem simply disappears. This is a technique that you should keep in mind -when writing decorators for functions with keyword arguments. +when writing decorators for functions with keyword arguments. Also, +notice that lately I have come to believe that decorating functions with +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``: @@ -1146,10 +1157,10 @@ a (shallow) copy of the original function dictionary: .. _docutils: http://docutils.sourceforge.net/ .. _pygments: http://pygments.org/ -LICENSE +LICENSE (2-clause BSD) --------------------------------------------- -Copyright (c) 2005-2016, Michele Simionato +Copyright (c) 2005-2017, Michele Simionato All rights reserved. Redistribution and use in source and binary forms, with or without |
