diff options
Diffstat (limited to 'docs/documentation.md')
-rw-r--r-- | docs/documentation.md | 169 |
1 files changed, 94 insertions, 75 deletions
diff --git a/docs/documentation.md b/docs/documentation.md index 9d7ad9c..858c754 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -4,9 +4,9 @@ Decorators for Humans |Author | Michele Simionato| |---|---| |E-mail | michele.simionato@gmail.com| -|Version| 5.0.4 (2021-04-03)| +|Version| 5.0.5 (2021-04-04)| |Supports| Python 3.5, 3.6, 3.7, 3.8, 3.9| -|Download page| http://pypi.python.org/pypi/decorator/5.0.4| +|Download page| http://pypi.python.org/pypi/decorator/5.0.5| |Installation| ``pip install decorator``| |License | BSD license| @@ -25,16 +25,15 @@ versions back to 2.6; versions 3.X are able to support even Python 2.5 and What's New in version 5 ----------------------- -There are no new features in version 5 of the decorator module, -except a 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 gives a very neat advantage: in case of exceptions -raised in decorated functions the traceback is nicer than it used to be. -That counts as a new feature in my book ;-) -There is also a change of logic that breaks some decorators, see the section -about caveats and limitations. +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 ----------------------- @@ -469,6 +468,89 @@ calling func with args (), {} ``` +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 + +```python + + def chatty(func, *args, **kwargs): + print(args, kwargs) + return func(*args, **kwargs) +``` + +and here is a function to decorate: + +```python + + @decorator(chatty) + def printsum(x=1, y=2): + print(x + y) +``` + +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 + +```python + + def chattywrapper(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + print(args, kwargs) + return func(*args, **kwargs) + return functools.wraps(wrapper) +``` + +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: + +```python + + @decorator(chatty, kwsyntax=True) + def printsum2(x=1, y=2): + print(x + y) +``` + +Here is how it works: + +```python +>>> printsum2(y=2, x=1) +() {'y': 2, 'x': 1} +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: + +```python +>>> printsum2(1, 2) +(1, 2) {} +3 + +``` + Decorator factories ------------------------------------------- @@ -1456,69 +1538,6 @@ not use any cache, whereas the ``singledispatch`` implementation does. Caveats and limitations ------------------------------------------- -Version 5.X breaks compatibility with the past, by making decorators -more similar to the ones that can be defined with ``functools.wraps``. -An example will make the issue clear: - -```python - - @decorator - def chatty(func, *args, **kwargs): - print(args, kwargs) - return func(*args, **kwargs) -``` - -```python - - @chatty - def printsum(x=1, y=2): - print(x + y) -``` - -In this example ``x`` and ``y`` are positional arguments with defaults. -In previous versions of the decorator module -(< 5) a call to ``printsum()`` would have passed ``args==(1, 2)`` to -the caller, with an empty ``kwargs`` dictionary. In version 5.X instead -even ``args`` is empty: - -```python ->>> printsum() -() {} -3 - -``` -``args`` become non-empty only if you pass the arguments as positional - -```python ->>> printsum(1) -(1,) {} -3 - -``` -and not if you pass them as keyword arguments: - -```python ->>> printsum(x=1) -() {'x': 1} -3 - -``` -This can be pretty confusing since non-keyword arguments are passed as -keywork arguments, but it the way it works with ``functools.wraps`` and -the way many people expect it to work. You can play with - -```python - - def chattywrapper(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - print(args, kwargs) - return func(*args, **kwargs) - return functools.wraps(wrapper) -``` - -and see that we are consistent indeed. - In the present implementation, decorators generated by ``decorator`` 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 |