297
297
``decorator`` module provides a poor man replacement for
298
298
``functools.partial``.
300
There is also an easy way to create one-parameter factories of
301
decorators, based on the following
302
``decorator_factory`` utility:
306
``decorator_factory`` converts a function with signature
307
``(param, func, *args, **kw)`` into a one-parameter family
308
of decorators. Suppose for instance you want to generated different
309
tracing generator, with different tracing messages.
310
Here is how to do it:
312
.. code-block:: python
314
>>> @decorator_factory
315
... def trace_factory(message_template, f, *args, **kw):
316
... name = f.func_name
317
... print message_template % locals()
318
... return f(*args, **kw)
320
.. code-block:: python
322
>>> trace_factory # doctest: +ELLIPSIS
323
<functools.partial object at 0x...>
325
>>> trace = trace_factory('Calling %(name)s with args %(args)s '
326
... 'and keywords %(kw)s')
328
In this example the parameter (``message_template``) is
329
just a string, but in general it can be a tuple, a dictionary, or
330
a generic object, so there is no real restriction (for instance,
331
if you want to define a two-parameter family of decorators just
332
use a tuple with two arguments as parameter).
333
Here is an example of usage:
335
.. code-block:: python
341
Calling func with args () and keywords {}
344
301
-------------------------------------------
427
384
Each call to ``write`` will create a new writer thread, but there will
428
385
be no synchronization problems since ``write`` is locked.
430
>>> write("data1") # doctest: +ELLIPSIS
431
<Thread(write-1, started...)>
433
>>> time.sleep(.1) # wait a bit, so we are sure data2 is written after data1
435
>>> write("data2") # doctest: +ELLIPSIS
436
<Thread(write-2, started...)>
438
>>> time.sleep(2) # wait for the writers to complete
387
.. code-block:: python
389
>>> write("data1") # doctest: +ELLIPSIS
390
<Thread(write-1, started...)>
392
>>> time.sleep(.1) # wait a bit, so we are sure data2 is written after data1
394
>>> write("data2") # doctest: +ELLIPSIS
395
<Thread(write-2, started...)>
397
>>> time.sleep(2) # wait for the writers to complete
443
402
The ``FunctionMaker`` class
444
403
---------------------------------------------------------------
772
731
Compatibility notes
773
732
---------------------------------------------------------------
775
Version 3.0 is a complete rewrite of the original implementation.
776
It is mostly compatible with the past, a part for a few differences.
778
First of all, the utilites ``get_info`` and ``new_wrapper``, available
779
in the 2.X versions, have been deprecated and they will be removed
780
in the future. For the moment, using them raises a ``DeprecationWarning``.
781
Incidentally, the functionality has been implemented through a
782
decorator which makes a good example for this documentation:
786
``get_info`` has been removed since it was little used and since it had
787
to be changed anyway to work with Python 3.0; ``new_wrapper`` has been
788
removed since it was useless: its major use case (converting
789
signature changing decorators to signature preserving decorators)
790
has been subsumed by ``decorator_apply``
791
and the other use case can be managed with the ``FunctionMaker``.
734
Version 3.2 is the first version of the ``decorator`` module to officially
735
support Python 3. Actually, the module has supported Python 3 from
736
the beginning, via the ``2to3`` conversion tool, but this step has
737
been now integrated in the build process, thanks to the distribute_
738
project, the Python 3-compatible replacement of easy_install.
739
The hard work (for me) has been converting the documentation and the
740
doctests. This has been possible only now that docutils_ and pygments_
741
have been ported to Python 3.
743
The ``decorator`` module *per se* does not contain any change, apart
744
from the removal of the functions ``get_info`` and ``new_wrapper``,
745
which have been deprecated for years. ``get_info`` has been removed
746
since it was little used and since it had to be changed anyway to work
747
with Python 3.0; ``new_wrapper`` has been removed since it was
748
useless: its major use case (converting signature changing decorators
749
to signature preserving decorators) has been subsumed by
750
``decorator_apply`` and the other use case can be managed with the
753
There are a few changes in the documentation: I removed the
754
``decorator_factory`` example, which was confusing some of my users,
755
and I removed the part about exotic signatures in the Python 3
756
documentation, since Python 3 does not support them.
757
Notice that there is no support for Python 3 `function annotations`_
758
since it seems premature at the moment, when most people are
759
still using Python 2.X.
793
761
Finally ``decorator`` cannot be used as a class decorator and the
794
762
`functionality introduced in version 2.3`_ has been removed. That
795
763
means that in order to define decorator factories with classes you
796
764
need to define the ``__call__`` method explicitly (no magic anymore).
798
an easy way to define decorator factories by using ``decorator_factory``,
799
there is less need to use classes to implement decorator factories.
801
765
All these changes should not cause any trouble, since they were
802
766
all rarely used features. Should you have any trouble, you can always
803
767
downgrade to the 2.3 version.
805
The examples shown here have been tested with Python 2.5. Python 2.4
769
The examples shown here have been tested with Python 2.6. Python 2.4
806
770
is also supported - of course the examples requiring the ``with``
807
statement will not work there. Python 2.6 works fine, but if you
808
run the examples here in the interactive interpreter
771
statement will not work there. Python 2.5 works fine, but if you
772
run the examples in the interactive interpreter
809
773
you will notice a few differences since
810
774
``getargspec`` returns an ``ArgSpec`` namedtuple instead of a regular
811
775
tuple. That means that running the file
812
``documentation.py`` under Python 2.5 will a few errors, but
813
they are not serious. Python 3.0 is kind of supported too.
814
Simply run the script ``2to3`` on the module
815
``decorator.py`` and you will get a version of the code running
816
with Python 3.0 (at least, I did some simple checks and it seemed
817
to work). However there is no support for `function annotations`_ yet
818
since it seems premature at this moment (most people are
819
still using Python 2.5).
776
``documentation.py`` under Python 2.5 will print a few errors, but
777
they are not serious.
821
779
.. _functionality introduced in version 2.3: http://www.phyast.pitt.edu/~micheles/python/documentation.html#class-decorators-and-decorator-factories
822
780
.. _function annotations: http://www.python.org/dev/peps/pep-3107/
781
.. _distribute: http://packages.python.org/distribute/
782
.. _docutils: http://docutils.sourceforge.net/
783
.. _pygments: http://pygments.org/
826
786
---------------------------------------------
974
930
return decorator(_memoize, f)
977
def blocking(not_avail, f, *args, **kw):
978
if not hasattr(f, "thread"): # no thread running
979
def set_result(): f.result = f(*args, **kw)
980
f.thread = threading.Thread(None, set_result)
983
elif f.thread.isAlive():
985
else: # the thread is ended, return the stored result
932
def blocking(not_avail):
933
def blocking(f, *args, **kw):
934
if not hasattr(f, "thread"): # no thread running
935
def set_result(): f.result = f(*args, **kw)
936
f.thread = threading.Thread(None, set_result)
939
elif f.thread.isAlive():
941
else: # the thread is ended, return the stored result
944
return decorator(blocking)
989
946
class User(object):
990
947
"Will just be able to see a page"
1001
958
class PermissionError(Exception):
1005
def restricted(user_class, func, *args, **kw):
1006
"Restrict access to a given class of users"
1007
userclass = get_userclass()
1008
if issubclass(userclass, user_class):
1009
return func(*args, **kw)
1011
raise PermissionError(
1012
'%s does not have the permission to run %s!'
1013
% (userclass.__name__, func.__name__))
961
def restricted(user_class):
962
def restricted(func, *args, **kw):
963
"Restrict access to a given class of users"
964
userclass = get_userclass()
965
if issubclass(userclass, user_class):
966
return func(*args, **kw)
968
raise PermissionError(
969
'%s does not have the permission to run %s!'
970
% (userclass.__name__, func.__name__))
971
return decorator(restricted)
1015
973
class Action(object):