~ubuntu-branches/debian/sid/python-decorator/sid

« back to all changes in this revision

Viewing changes to documentation.py

  • Committer: Bazaar Package Importer
  • Author(s): Piotr Ożarowski
  • Date: 2010-05-25 21:55:03 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20100525215503-4xq1iyhjijh0t8o6
Tags: 3.2.0-1
* New upstream release
  - changelog no longer available
* python added to Build-Depends (clean rule needs it)
* debian/watch file points to PyPI now

Show diffs side-by-side

added added

removed removed

Lines of Context:
83
83
from the cache and not recomputed. There are many implementations of 
84
84
``memoize`` in http://www.python.org/moin/PythonDecoratorLibrary, 
85
85
but they do not preserve the signature.
86
 
A simple implementation for Python 2.5 could be the following (notice
 
86
A simple implementation could be the following (notice
87
87
that in general it is impossible to memoize correctly something
88
88
that depends on non-hashable arguments):
89
89
 
90
 
$$memoize25
 
90
$$memoize_uw
91
91
 
92
92
Here we used the functools.update_wrapper_ utility, which has
93
93
been added in Python 2.5 expressly to simplify the definition of decorators
100
100
The implementation above works in the sense that the decorator 
101
101
can accept functions with generic signatures; unfortunately this
102
102
implementation does *not* define a signature-preserving decorator, since in
103
 
general ``memoize25`` returns a function with a
 
103
general ``memoize_uw`` returns a function with a
104
104
*different signature* from the original function.
105
105
 
106
106
Consider for instance the following case:
107
107
 
108
108
.. code-block:: python
109
109
 
110
 
 >>> @memoize25
 
110
 >>> @memoize_uw
111
111
 ... def f1(x):
112
112
 ...     time.sleep(1) # simulate some long computation
113
113
 ...     return x
119
119
.. code-block:: python
120
120
 
121
121
 >>> from inspect import getargspec 
122
 
 >>> print getargspec(f1) 
123
 
 ([], 'args', 'kw', None)
 
122
 >>> print getargspec(f1) # I am using Python 2.6+ here
 
123
 ArgSpec(args=[], varargs='args', keywords='kw', defaults=None)
124
124
 
125
125
This means that introspection tools such as pydoc will give
126
126
wrong informations about the signature of ``f1``. This is pretty bad:
160
160
 
161
161
$$memoize
162
162
 
163
 
The difference with respect to the Python 2.5 approach, which is based
 
163
The difference with respect to the ``memoize_uw`` approach, which is based
164
164
on nested functions, is that the decorator module forces you to lift
165
165
the inner function at the outer level (*flat is better than nested*).
166
166
Moreover, you are forced to pass explicitly the function you want to
186
186
.. code-block:: python
187
187
 
188
188
 >>> print getargspec(heavy_computation) 
189
 
 ([], None, None, None)
 
189
 ArgSpec(args=[], varargs=None, keywords=None, defaults=None)
190
190
 
191
191
A ``trace`` decorator
192
192
------------------------------------------------------
219
219
.. code-block:: python
220
220
 
221
221
 >>> print getargspec(f1) 
222
 
 (['x'], None, None, None)
 
222
 ArgSpec(args=['x'], varargs=None, keywords=None, defaults=None)
223
223
 
224
224
The same decorator works with functions of any signature:
225
225
 
233
233
 calling f with args (0, 3, 2), {}
234
234
 
235
235
 >>> print getargspec(f) 
236
 
 (['x', 'y', 'z'], 'args', 'kw', (1, 2))
 
236
 ArgSpec(args=['x', 'y', 'z'], varargs='args', keywords='kw', defaults=(1, 2))
237
237
 
238
238
That includes even functions with exotic signatures like the following:
239
239
 
243
243
 ... def exotic_signature((x, y)=(1,2)): return x+y
244
244
 
245
245
 >>> print getargspec(exotic_signature)
246
 
 ([['x', 'y']], None, None, ((1, 2),))
 
246
 ArgSpec(args=[['x', 'y']], varargs=None, keywords=None, defaults=((1, 2),))
247
247
 >>> exotic_signature() 
248
248
 calling exotic_signature with args ((1, 2),), {}
249
249
 3
297
297
``decorator`` module provides a poor man replacement for
298
298
``functools.partial``.
299
299
 
300
 
There is also an easy way to create one-parameter factories of
301
 
decorators, based on the following
302
 
``decorator_factory`` utility:
303
 
 
304
 
$$decorator_factory
305
 
 
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:
311
 
 
312
 
.. code-block:: python
313
 
 
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)
319
 
 
320
 
.. code-block:: python
321
 
 
322
 
 >>> trace_factory # doctest: +ELLIPSIS
323
 
 <functools.partial object at 0x...>
324
 
 
325
 
 >>> trace = trace_factory('Calling %(name)s with args %(args)s '
326
 
 ...                        'and keywords %(kw)s')
327
 
 
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:
334
 
 
335
 
.. code-block:: python
336
 
 
337
 
 >>> @trace
338
 
 ... def func(): pass
339
 
 
340
 
 >>> func()
341
 
 Calling func with args () and keywords {}
342
 
 
343
300
``blocking``
344
301
-------------------------------------------
345
302
 
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.
429
386
 
430
 
>>> write("data1") # doctest: +ELLIPSIS
431
 
<Thread(write-1, started...)>
432
 
 
433
 
>>> time.sleep(.1) # wait a bit, so we are sure data2 is written after data1
434
 
 
435
 
>>> write("data2") # doctest: +ELLIPSIS
436
 
<Thread(write-2, started...)>
437
 
 
438
 
>>> time.sleep(2) # wait for the writers to complete
439
 
 
440
 
>>> print datalist
441
 
['data1', 'data2']
 
387
.. code-block:: python
 
388
 
 
389
 >>> write("data1") # doctest: +ELLIPSIS
 
390
 <Thread(write-1, started...)>
 
391
 
 
392
 >>> time.sleep(.1) # wait a bit, so we are sure data2 is written after data1
 
393
 
 
394
 >>> write("data2") # doctest: +ELLIPSIS
 
395
 <Thread(write-2, started...)>
 
396
 
 
397
 >>> time.sleep(2) # wait for the writers to complete
 
398
 
 
399
 >>> print datalist
 
400
 ['data1', 'data2']
442
401
 
443
402
The ``FunctionMaker`` class
444
403
---------------------------------------------------------------
772
731
Compatibility notes
773
732
---------------------------------------------------------------
774
733
 
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.
777
 
 
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:
783
 
 
784
 
$$deprecated
785
 
 
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. 
 
742
 
 
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
 
751
``FunctionMaker``.
 
752
 
 
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.
792
760
 
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).
797
 
Since there is
798
 
an easy way to define decorator factories by using ``decorator_factory``,
799
 
there is less need to use classes to implement decorator factories.
800
 
 
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.
804
768
 
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. 
820
778
 
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/
823
 
 
 
781
.. _distribute: http://packages.python.org/distribute/
 
782
.. _docutils: http://docutils.sourceforge.net/
 
783
.. _pygments: http://pygments.org/
824
784
 
825
785
LICENCE
826
786
---------------------------------------------
866
826
 
867
827
__doc__ = __doc__.replace('$VERSION', VERSION).replace('$DATE', today)
868
828
 
869
 
def decorator_factory(decfac): # partial is functools.partial
870
 
    "decorator_factory(decfac) returns a one-parameter family of decorators"
871
 
    return partial(lambda df, param: decorator(partial(df, param)), decfac)
872
 
 
873
829
def decorator_apply(dec, func):
874
830
    """
875
831
    Decorate a function by preserving the signature even if dec 
942
898
@identity_dec
943
899
def example(): pass
944
900
 
945
 
def memoize25(func):
 
901
def memoize_uw(func):
946
902
    func.cache = {}
947
903
    def memoize(*args, **kw):
948
904
        if kw: # frozenset is used to ensure hashability
972
928
def memoize(f):
973
929
    f.cache = {}
974
930
    return decorator(_memoize, f)
975
 
    
976
 
@decorator_factory
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)
981
 
        f.thread.start()
982
 
        return not_avail
983
 
    elif f.thread.isAlive():
984
 
        return not_avail
985
 
    else: # the thread is ended, return the stored result
986
 
        del f.thread
987
 
        return f.result
 
931
 
 
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)
 
937
            f.thread.start()
 
938
            return not_avail
 
939
        elif f.thread.isAlive():
 
940
            return not_avail
 
941
        else: # the thread is ended, return the stored result
 
942
            del f.thread
 
943
            return f.result
 
944
    return decorator(blocking)
988
945
 
989
946
class User(object):
990
947
    "Will just be able to see a page"
1001
958
class PermissionError(Exception):
1002
959
    pass
1003
960
 
1004
 
@decorator_factory
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)
1010
 
    else:
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)
 
967
        else:
 
968
            raise PermissionError(
 
969
                '%s does not have the permission to run %s!'
 
970
                % (userclass.__name__, func.__name__))
 
971
    return decorator(restricted)
1014
972
 
1015
973
class Action(object):
1016
974
    """
1077
1035
    if n == 0: return 1
1078
1036
    return n * fact(n-1)
1079
1037
 
1080
 
 
1081
 
def atest_for_pylons():
 
1038
def a_test_for_pylons():
1082
1039
    """
1083
1040
    In version 3.1.0 decorator(caller) returned a nameless partial
1084
1041
    object, thus breaking Pylons. That must not happen again.
1086
1043
    >>> decorator(_memoize).__name__
1087
1044
    '_memoize'
1088
1045
 
1089
 
    Here is another bug of version 3.1.1 to avoid:
 
1046
    Here is another bug of version 3.1.1 missing the docstring to avoid:
1090
1047
 
1091
 
    >>> deprecated.__doc__
1092
 
    'A decorator for deprecated functions'
 
1048
    >>> factorial.__doc__
 
1049
    'The good old factorial'
1093
1050
    """
1094
1051
 
1095
1052
if __name__ == '__main__':