summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichele Simionato <michele.simionato@gmail.com>2018-04-15 14:35:10 +0200
committerMichele Simionato <michele.simionato@gmail.com>2018-04-15 14:35:10 +0200
commit9e5235962d8d7e67f41a1ef7b7d70b1cc7e55d0b (patch)
tree2207a5150cdd7d4b8c5d4a832a238cfa91be39cf
parentc7aa9cb24de431c73ddb5a5fa024a0e68c0eb3c9 (diff)
downloadpython-decorator-git-9e5235962d8d7e67f41a1ef7b7d70b1cc7e55d0b.tar.gz
Updated docs
-rw-r--r--CHANGES.md8
-rw-r--r--docs/tests.documentation.rst73
-rw-r--r--src/decorator.py2
-rw-r--r--src/tests/documentation.py36
4 files changed, 80 insertions, 39 deletions
diff --git a/CHANGES.md b/CHANGES.md
index cbf5be8..1d83b99 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -3,6 +3,14 @@ HISTORY
## Unreleased
+## 4.3.0 (2018-04-15)
+
+Extended the decorator family facility to work with positional
+arguments and updated the documentation. Removed
+`decorator.getargspec` and provided `decorator.getfullargspec`
+instead. This is convenient for users of Python 2.6/2.7, the others
+can just use `inspect.getfullargspec`.
+
## 4.2.1 (2018-01-14)
Fixed a regression breaking IPython reported by https://github.com/spapini .
diff --git a/docs/tests.documentation.rst b/docs/tests.documentation.rst
index 97f264c..c6d606c 100644
--- a/docs/tests.documentation.rst
+++ b/docs/tests.documentation.rst
@@ -3,9 +3,9 @@ The ``decorator`` module
:Author: Michele Simionato
:E-mail: michele.simionato@gmail.com
-:Version: 4.2.1 (2018-01-14)
+:Version: 4.3.0 (2018-04-15)
:Supports: Python 2.6, 2.7, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6
-:Download page: http://pypi.python.org/pypi/decorator/4.2.1
+:Download page: http://pypi.python.org/pypi/decorator/4.3.0
:Installation: ``pip install decorator``
:License: BSD license
@@ -211,9 +211,9 @@ keyword arguments:
.. code-block:: python
- >>> from decorator import getargspec # akin to inspect.getargspec
- >>> print(getargspec(f1))
- ArgSpec(args=[], varargs='args', varkw='kw', defaults=None)
+ >>> from decorator 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
@@ -228,10 +228,9 @@ calling the function with more than one argument raises an error:
...
TypeError: f1() takes exactly 1 positional argument (2 given)
-Notice that ``inspect.getargspec`` and ``inspect.getfullargspec``
-will give the wrong signature. This even occurs in Python 3.5,
-although both functions were deprecated in that release.
-
+Notice that ``inspect.getfullargspec``
+will give the wrong signature, even in the latest Python, i.e. version 3.6
+at the time of writing.
The solution
-----------------------------------------
@@ -306,8 +305,8 @@ The signature of ``heavy_computation`` is the one you would expect:
.. code-block:: python
- >>> print(getargspec(heavy_computation))
- ArgSpec(args=[], varargs=None, varkw=None, defaults=None)
+ >>> print(getfullargspec(heavy_computation))
+ FullArgSpec(args=[], varargs=None, varkw=None, defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={})
A ``trace`` decorator
------------------------------------------------------
@@ -348,8 +347,8 @@ It is immediate to verify that ``f1`` works...
.. code-block:: python
- >>> print(getargspec(f1))
- ArgSpec(args=['x'], varargs=None, varkw=None, defaults=None)
+ >>> print(getfullargspec(f1))
+ FullArgSpec(args=['x'], varargs=None, varkw=None, defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={})
The decorator works with functions of any signature:
@@ -362,8 +361,8 @@ The decorator works with functions of any signature:
>>> f(0, 3)
calling f with args (0, 3, 2), {}
- >>> print(getargspec(f))
- ArgSpec(args=['x', 'y', 'z'], varargs='args', varkw='kw', defaults=(1, 2))
+ >>> print(getfullargspec(f))
+ FullArgSpec(args=['x', 'y', 'z'], varargs='args', varkw='kw', defaults=(1, 2), kwonlyargs=[], kwonlydefaults=None, annotations={})
Function annotations
---------------------------------------------
@@ -384,7 +383,7 @@ Here is an example:
In order to introspect functions with annotations, one needs the
utility ``inspect.getfullargspec`` (introduced in Python 3, then
-deprecated in Python 3.5, in favor of ``inspect.signature``):
+deprecated in Python 3.5, then undeprecated in Python 3.6):
.. code-block:: python
@@ -503,7 +502,7 @@ https://www.python.org/dev/peps/pep-0557/) I finally gave up.
The example below will show how it works in practice.
-``blocking``
+Decorator factories
-------------------------------------------
Sometimes one has to deal with blocking resources, such as ``stdin``.
@@ -535,7 +534,7 @@ available. For instance:
.. code-block:: python
- >>> @blocking(msg="Please wait ...")
+ >>> @blocking("Please wait ...")
... def read_data():
... time.sleep(3) # simulate a blocking resource
... return "some data"
@@ -555,6 +554,38 @@ available. For instance:
>>> print(read_data())
some data
+Decorator factories are most useful to framework builders. Here is an example
+that gives an idea of how you could manage permissions in a Web framework:
+
+.. code-block:: python
+
+ @decorator
+ def restricted(func, user_class=User, *args, **kw):
+ "Restrict access to a given class of users"
+ self = args[0]
+ if isinstance(self.user, user_class):
+ return func(*args, **kw)
+ else:
+ raise PermissionError(
+ '%s does not have the permission to run %s!'
+ % (self.user, func.__name__))
+
+.. code-block:: python
+
+ class Action(object):
+ @restricted(User)
+ def view(self):
+ pass
+
+ @restricted(PowerUser)
+ def insert(self):
+ pass
+
+ @restricted(Admin)
+ def delete(self):
+ pass
+
+
``decorator(cls)``
--------------------------------------------
@@ -753,7 +784,7 @@ Here is what happens:
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.getargspec``.)
+ ``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.
@@ -769,8 +800,8 @@ followed by a tuple of defaults:
>>> f1 = FunctionMaker.create(
... 'f1(a, b)', 'f(a, b)', dict(f=f), addsource=True, defaults=(None,))
- >>> print(getargspec(f1))
- ArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=(None,))
+ >>> print(getfullargspec(f1))
+ FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=(None,), kwonlyargs=[], kwonlydefaults=None, annotations={})
Getting the source code
diff --git a/src/decorator.py b/src/decorator.py
index b31356a..44303ee 100644
--- a/src/decorator.py
+++ b/src/decorator.py
@@ -40,7 +40,7 @@ import operator
import itertools
import collections
-__version__ = '4.2.1'
+__version__ = '4.3.0'
if sys.version >= '3':
from inspect import getfullargspec
diff --git a/src/tests/documentation.py b/src/tests/documentation.py
index 2588768..0d8ebf5 100644
--- a/src/tests/documentation.py
+++ b/src/tests/documentation.py
@@ -395,7 +395,7 @@ https://www.python.org/dev/peps/pep-0557/) I finally gave up.
The example below will show how it works in practice.
-``blocking``
+Decorator factories
-------------------------------------------
Sometimes one has to deal with blocking resources, such as ``stdin``.
@@ -432,6 +432,12 @@ available. For instance:
>>> print(read_data())
some data
+Decorator factories are most useful to framework builders. Here is an example
+that gives an idea of how you could manage permissions in a Web framework:
+
+$$restricted
+$$Action
+
``decorator(cls)``
--------------------------------------------
@@ -1495,35 +1501,31 @@ class Admin(PowerUser):
"Will be able to delete pages too"
-def get_userclass():
- return User
-
-
class PermissionError(Exception):
- pass
+ """
+ >>> a = Action()
+ >>> a.user = User()
+ >>> a.view() # ok
+ >>> a.insert() # doctest: +IGNORE_EXCEPTION_DETAIL
+ Traceback (most recent call last):
+ ...
+ PermissionError: User does not have the permission to run insert!
+ """
@decorator
def restricted(func, user_class=User, *args, **kw):
"Restrict access to a given class of users"
- userclass = get_userclass()
- if issubclass(userclass, user_class):
+ self = args[0]
+ if isinstance(self.user, user_class):
return func(*args, **kw)
else:
raise PermissionError(
'%s does not have the permission to run %s!'
- % (userclass.__name__, func.__name__))
+ % (self.user, func.__name__))
class Action(object):
- """
- >>> a = Action()
- >>> a.view() # ok
- >>> a.insert() # doctest: +IGNORE_EXCEPTION_DETAIL
- Traceback (most recent call last):
- ...
- PermissionError: User does not have the permission to run insert!
- """
@restricted(User)
def view(self):
pass