summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormichele.simionato <devnull@localhost>2008-12-09 07:15:54 +0000
committermichele.simionato <devnull@localhost>2008-12-09 07:15:54 +0000
commit49f5428fcbdc2ad712eddd2ce131a4540364dac7 (patch)
tree64497e15e92b6bc2cf8b432d40985258a7897354
parent7b391ceaf4d46bd2fe1464379fc48d6b3c8b6b01 (diff)
downloadpython-decorator-git-49f5428fcbdc2ad712eddd2ce131a4540364dac7.tar.gz
Many improvements
-rwxr-xr-xdecorator.py35
-rw-r--r--documentation.py126
2 files changed, 58 insertions, 103 deletions
diff --git a/decorator.py b/decorator.py
index e9a35e0..39debfa 100755
--- a/decorator.py
+++ b/decorator.py
@@ -93,25 +93,28 @@ def makefn(src, funcdata, save_source=True, **evaldict):
func = evaldict[name]
return funcdata.update(func, __source__=src)
-def decorator(caller, func=None):
+def decorator_apply(caller, func):
+ "decorator.apply(caller, func) is akin to decorator(caller)(func)"
+ fd = FuncData(func)
+ name = fd.name
+ signature = fd.signature
+ for arg in signature.split(','):
+ argname = arg.strip(' *')
+ assert not argname in('_func_', '_call_'), (
+ '%s is a reserved argument name!' % argname)
+ src = """def %(name)s(%(signature)s):
+ return _call_(_func_, %(signature)s)""" % locals()
+ return makefn(src, fd, save_source=False, _func_=func, _call_=caller)
+
+def decorator(caller):
"""
- decorator(caller) converts a caller function into a decorator;
- decorator(caller, func) is akin to decorator(caller)(func).
+ decorator(caller) converts a caller function into a decorator.
"""
- if func:
- fd = FuncData(func)
- name = fd.name
- signature = fd.signature
- for arg in signature.split(','):
- argname = arg.strip(' *')
- assert not argname in('_func_', '_call_'), (
- '%s is a reserved argument name!' % argname)
- src = """def %(name)s(%(signature)s):
- return _call_(_func_, %(signature)s)""" % locals()
- return makefn(src, fd, save_source=False, _func_=func, _call_=caller)
- src = 'def %s(func): return decorator(caller, func)' % caller.__name__
+ src = 'def %s(func): return appl(caller, func)' % caller.__name__
return makefn(src, FuncData(caller), save_source=False,
- caller=caller, decorator=decorator)
+ caller=caller, appl=decorator_apply)
+
+decorator.apply = decorator_apply
@decorator
def deprecated(func, *args, **kw):
diff --git a/documentation.py b/documentation.py
index 3d5f88a..dadb231 100644
--- a/documentation.py
+++ b/documentation.py
@@ -14,12 +14,13 @@ The ``decorator`` module
Introduction
------------------------------------------------
-Python 2.4 decorators are an interesting example of why syntactic sugar
-matters: in principle, their introduction changed nothing, since they do
-not provide any new functionality which was not already present in the
-language; in practice, their introduction has significantly changed the way
-we structure our programs in Python. I believe the change is for the best,
-and that decorators are a great idea since:
+Python decorators are an interesting example of why syntactic sugar
+matters. In principle, their introduction in Python 2.4 changed
+nothing, since they do not provide any new functionality which was not
+already present in the language; in practice, their introduction has
+significantly changed the way we structure our programs in Python. I
+believe the change is for the best, and that decorators are a great
+idea since:
* decorators help reducing boilerplate code;
* decorators help separation of concerns;
@@ -145,12 +146,15 @@ First of all, you must import ``decorator``:
>>> from decorator import decorator
-Then you must define an helper function with signature ``(f, *args, **kw)``
+Then you must define a helper function with signature ``(f, *args, **kw)``
which calls the original function ``f`` with arguments ``args`` and ``kw``
and implements the tracing capability:
$$_trace
+At this point you can define your decorator in terms of the helper function
+via ``decorator.apply``:
+
$$trace
Therefore, you can write the following:
@@ -192,7 +196,8 @@ That includes even functions with exotic signatures like the following:
calling exotic_signature with args ((1, 2),), {}
3
-Notice that exotic signatures have been disabled in Python 3.0.
+Notice that the support for exotic signatures has been deprecated
+in Python 2.6 and removed in Python 3.0.
``decorator`` is a decorator
---------------------------------------------
@@ -274,38 +279,6 @@ tuple(kwargs.iteritems()))`` as key for the memoize dictionary.
Notice that in general it is impossible to memoize correctly something
that depends on mutable arguments.
-``locked``
----------------------------------------------------------------
-
-There are good use cases for decorators is in multithreaded programming.
-For instance, a ``locked`` decorator can remove the boilerplate
-for acquiring/releasing locks [#]_.
-
-.. [#] In Python 2.5, the preferred way to manage locking is via
- the ``with`` statement: http://docs.python.org/lib/with-locks.html
-
-To show an example of usage, suppose one wants to write some data to
-an external resource which can be accessed by a single user at once
-(for instance a printer). Then the access to the writing function must
-be locked:
-
-.. code-block:: python
-
- import time
-
- datalist = [] # for simplicity the written data are stored into a list.
-
- @locked
- def write(data):
- "Writing to a sigle-access resource"
- time.sleep(1)
- datalist.append(data)
-
-
-Since the writing function is locked, we are guaranteed that at any given time
-there is at most one writer. An example multithreaded program that invokes
-``write`` and prints the datalist is shown in the next section.
-
``delayed`` and ``threaded``
--------------------------------------------
@@ -350,8 +323,28 @@ to deserve a name:
threaded = delayed(0)
Threaded procedures will be executed in a separated thread as soon
-as they are called. Here is an example using the ``write``
-routine defined before:
+as they are called. Here is an example.
+
+Suppose one wants to write some data to
+an external resource which can be accessed by a single user at once
+(for instance a printer). Then the access to the writing function must
+be locked:
+
+.. code-block:: python
+
+ import time
+
+ datalist = [] # for simplicity the written data are stored into a list.
+
+ def write(data):
+ "Writing to a sigle-access resource"
+ with threading.Lock():
+ time.sleep(1)
+ datalist.append(data)
+
+
+Since the writing function is locked, we are guaranteed that at any given time
+there is at most one writer. Here is an example.
>>> @threaded
... def writedata(data):
@@ -406,35 +399,6 @@ Please wait ...
>>> print read_data()
some data
-``redirecting_stdout``
--------------------------------------------
-
-Decorators help in removing the boilerplate associated to ``try .. finally``
-blocks. We saw the case of ``locked``; here is another example:
-
-$$redirecting_stdout
-
-Here is an example of usage:
-
->>> from StringIO import StringIO
-
->>> out = StringIO()
-
->>> @redirecting_stdout(out)
-... def helloworld():
-... print "hello, world!"
-
->>> helloworld()
-
->>> out.getvalue()
-'hello, world!\n'
-
-Similar tricks can be used to remove the boilerplate associate with
-transactional databases. I think you got the idea, so I will leave
-the transactional example as an exercise for the reader. Of course
-in Python 2.5 these use cases can also be addressed with the ``with``
-statement.
-
Class decorators and decorator factories
--------------------------------------------------------------------
@@ -529,9 +493,8 @@ of the original function. If not, you will get an error at calling
time, not at decoration time.
With ``new_wrapper`` at your disposal, it is a breeze to define an utility
-to upgrade old-style decorators to signature-preserving decorators:
+to upgrade old-style decorators to signature-preserving decorators.
-$$upgrade_dec
``tail_recursive``
------------------------------------------------------------
@@ -760,7 +723,7 @@ def _trace(f, *args, **kw):
print "calling %s with args %s, %s" % (f.func_name, args, kw)
return f(*args, **kw)
def trace(f):
- return decorator(_trace, f)
+ return decorator.apply(_trace, f)
def delayed(nsec):
def call(proc, *args, **kw):
@@ -793,7 +756,7 @@ def _memoize(func, *args, **kw):
def memoize(f):
f.cache = {}
- return decorator(_memoize, f)
+ return decorator.apply(_memoize, f)
@decorator
def locked(func, *args, **kw):
@@ -821,17 +784,6 @@ def blocking(not_avail="Not Available"):
return f.result
return decorator(call)
-def redirecting_stdout(new_stdout):
- def call(func, *args, **kw):
- save_stdout = sys.stdout
- sys.stdout = new_stdout
- try:
- result = func(*args, **kw)
- finally:
- sys.stdout = save_stdout
- return result
- return decorator(call)
-
class User(object):
"Will just be able to see a page"
@@ -869,7 +821,7 @@ class Restricted(object):
'%s does not have the permission to run %s!'
% (userclass.__name__, func.__name__))
def __call__(self, func):
- return decorator(self.call, func)
+ return decorator.apply(self.call, func)
class Action(object):