summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormichele.simionato <devnull@localhost>2009-08-26 05:46:33 +0000
committermichele.simionato <devnull@localhost>2009-08-26 05:46:33 +0000
commit1182961a2b82500a00f948437fa7f8736537c521 (patch)
tree00621aa6737e76927329320e5ce834d3fa841b8b
parentc8708c4276a7a06470226c85863ac958b574fcd8 (diff)
downloadpython-decorator-git-1182961a2b82500a00f948437fa7f8736537c521.tar.gz
Release 3.1.2
-rwxr-xr-xCHANGES.txt3
-rw-r--r--Makefile4
-rwxr-xr-xdecorator.py23
-rw-r--r--documentation.py35
-rw-r--r--setup.py2
5 files changed, 51 insertions, 16 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 3dfed71..0827995 100755
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,6 +1,9 @@
HISTORY
----------
+3.1.2. Added attributes args, varargs, keywords and arg0, ..., argN
+ to FunctionMaker objects generated from a function; fixed another
+ Pylons-breaking bug signaled by Lawrence Oluyede (25/08/2009)
3.1.1. Fixed a bug which was breaking Pylons, signaled by
Gabriel de Perthuis, and added a test for it. (18/08/2009)
3.1. Added decorator.factory, an easy way to define families of decorators
diff --git a/Makefile b/Makefile
index 35d92a5..12fa499 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
-RST=/home/micheles/trunk/ROnline/RCommon/Python/ms/tools/rst.py
+RST=$(HOME)/trunk/ROnline/RCommon/Python/ms/tools/rst.py
rst: documentation.py
- python /home/micheles/trunk/ROnline/RCommon/Python/ms/tools/minidoc.py -dH documentation.py
+ python $(HOME)/trunk/ROnline/RCommon/Python/ms/tools/minidoc.py -dH documentation.py
pdf: /tmp/documentation.rst
$(RST) -ptd /tmp/documentation.rst; cp /tmp/documentation.pdf /tmp/documentation.html .
diff --git a/decorator.py b/decorator.py
index 5c81350..091d6ff 100755
--- a/decorator.py
+++ b/decorator.py
@@ -31,7 +31,7 @@ for the documentation.
__all__ = ["decorator", "FunctionMaker", "partial",
"deprecated", "getinfo", "new_wrapper"]
-import os, sys, re, inspect, warnings
+import os, sys, re, inspect, string, warnings
try:
from functools import partial
except ImportError: # for Python version < 2.5
@@ -58,16 +58,19 @@ class FunctionMaker(object):
def __init__(self, func=None, name=None, signature=None,
defaults=None, doc=None, module=None, funcdict=None):
if func:
- # func can also be a class or a callable, but not an instance method
+ # func can be a class or a callable, but not an instance method
self.name = func.__name__
if self.name == '<lambda>': # small hack for lambda functions
self.name = '_lambda_'
self.doc = func.__doc__
self.module = func.__module__
if inspect.isfunction(func):
+ argspec = inspect.getargspec(func)
+ self.args, self.varargs, self.keywords, self.defaults = argspec
+ for i, arg in enumerate(self.args):
+ setattr(self, 'arg%d' % i, arg)
self.signature = inspect.formatargspec(
- formatvalue=lambda val: "", *inspect.getargspec(func))[1:-1]
- self.defaults = func.func_defaults
+ formatvalue=lambda val: "", *argspec)[1:-1]
self.dict = func.__dict__.copy()
if name:
self.name = name
@@ -95,7 +98,7 @@ class FunctionMaker(object):
callermodule = sys._getframe(3).f_globals.get('__name__', '?')
func.__module__ = getattr(self, 'module', callermodule)
func.__dict__.update(kw)
-
+
def make(self, src_templ, evaldict=None, addsource=False, **attrs):
"Make a new function from a given template and update the signature"
src = src_templ % vars(self) # expand name and signature
@@ -125,7 +128,8 @@ class FunctionMaker(object):
return func
@classmethod
- def create(cls, obj, body, evaldict, defaults=None, addsource=True,**attrs):
+ def create(cls, obj, body, evaldict, defaults=None,
+ doc=None, module=None, addsource=True,**attrs):
"""
Create a function from the strings name, signature and body.
evaldict is the evaluation dictionary. If addsource is true an attribute
@@ -134,13 +138,13 @@ class FunctionMaker(object):
"""
if isinstance(obj, str): # "name(signature)"
name, rest = obj.strip().split('(', 1)
- signature = rest[:-1]
+ signature = rest[:-1] #strip a right parens
func = None
else: # a function
name = None
signature = None
func = obj
- fun = cls(func, name, signature, defaults)
+ fun = cls(func, name, signature, defaults, doc, module)
ibody = '\n'.join(' ' + line for line in body.splitlines())
return fun.make('def %(name)s(%(signature)s):\n' + ibody,
evaldict, addsource, **attrs)
@@ -162,7 +166,8 @@ def decorator(caller, func=None):
return FunctionMaker.create(
'%s(%s)' % (caller.__name__, f),
'return decorator(_call_, %s)' % f,
- dict(_call_=caller, decorator=decorator), undecorated=caller)
+ dict(_call_=caller, decorator=decorator), undecorated=caller,
+ doc=caller.__doc__, module=caller.__module__)
###################### deprecated functionality #########################
diff --git a/documentation.py b/documentation.py
index 192ebfb..96b3d45 100644
--- a/documentation.py
+++ b/documentation.py
@@ -428,12 +428,12 @@ Each call to ``write`` will create a new writer thread, but there will
be no synchronization problems since ``write`` is locked.
>>> write("data1") # doctest: +ELLIPSIS
-<Thread(write-1, started ...)>
+<Thread(write-1, started...)>
>>> time.sleep(.1) # wait a bit, so we are sure data2 is written after data1
>>> write("data2") # doctest: +ELLIPSIS
-<Thread(write-2, started ...)>
+<Thread(write-2, started...)>
>>> time.sleep(2) # wait for the writers to complete
@@ -466,6 +466,9 @@ were the function is generated by ``exec``. Here is an example:
>>> f1(1,2)
(1, 2) {}
+It is important to notice that the function body is interpolated
+before being executed, so be careful with the ``%`` sign!
+
``FunctionMaker.create`` also accepts keyword arguments and such
arguments are attached to the resulting function. This is useful
if you want to set some function attributes, for instance the
@@ -474,7 +477,7 @@ docstring ``__doc__``.
For debugging/introspection purposes it may be useful to see
the source code of the generated function; to do that, just
pass the flag ``addsource=True`` and a ``__source__`` attribute will
-be added to the decorated function:
+be added to the generated function:
.. code-block:: python
@@ -485,6 +488,24 @@ be added to the decorated function:
f(a, b)
<BLANKLINE>
+``FunctionMaker.create`` can take as first argument a string,
+as in the examples before, or a function. This is the most common
+usage, since typically you want to decorate a pre-existing
+function. A framework author may want to use directly ``FunctionMaker.create``
+instead of ``decorator``, since it gives you direct access to the body
+of the generated function. For instance, suppose you want to instrument
+the ``__init__`` methods of a set of classes, by preserving their
+signature (such use case is not made up; this is done in SQAlchemy
+and in other frameworks). When the first argument of ``FunctionMaker.create``
+is a function, a ``FunctionMaker`` object is instantiated internally,
+with attributes ``args``, ``varargs``,
+``keywords`` and ``defaults`` which are the
+the return values of the standard library function ``inspect.getargspec``.
+For each argument in the ``args`` (which is a list of strings containing
+the names of the mandatory arguments) an attribute ``arg0``, ``arg1``,
+..., ``argN`` is also generated. Finally, there is a ``signature``
+attribute, a string with the signature of the original function.
+
Notice that while I do not have plans
to change or remove the functionality provided in the
``FunctionMaker`` class, I do not guarantee that it will stay
@@ -1060,9 +1081,15 @@ def fact(n): # this is not tail-recursive
def atest_for_pylons():
"""
In version 3.1.0 decorator(caller) returned a nameless partial
- object, thus breaking Pylons. That must not happen anymore.
+ object, thus breaking Pylons. That must not happen again.
+
>>> decorator(_memoize).__name__
'_memoize'
+
+ Here is another bug of version 3.1.1 to avoid:
+
+ >>> deprecated.__doc__
+ 'A decorator for deprecated functions'
"""
if __name__ == '__main__':
diff --git a/setup.py b/setup.py
index b406954..f8dc862 100644
--- a/setup.py
+++ b/setup.py
@@ -3,7 +3,7 @@ try:
except ImportError:
from distutils.core import setup
-VERSION = '3.1.1'
+VERSION = '3.1.2'
if __name__ == '__main__':
try: