summaryrefslogtreecommitdiff
path: root/decorator.py
diff options
context:
space:
mode:
Diffstat (limited to 'decorator.py')
-rwxr-xr-xdecorator.py129
1 files changed, 129 insertions, 0 deletions
diff --git a/decorator.py b/decorator.py
new file mode 100755
index 0000000..e9a35e0
--- /dev/null
+++ b/decorator.py
@@ -0,0 +1,129 @@
+########################## LICENCE ###############################
+
+## Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## Redistributions in bytecode form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+## INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+## BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+## OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+## TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+## USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+## DAMAGE.
+
+"""
+Decorator module, see http://pypi.python.org/pypi/decorator
+for the documentation.
+"""
+
+## The basic trick is to generate the source code for the decorated function
+## with the right signature and to evaluate it.
+
+__all__ = ["decorator", "makefn", "getsignature", "upgrade_dec"]
+
+import os, sys, re, inspect, warnings
+from tempfile import mkstemp
+
+DEF = re.compile('\s*def\s*([_\w][_\w\d]*)\s*\(')
+
+def _callermodule(level=2):
+ return sys._getframe(level).f_globals.get('__name__', '?')
+
+def getsignature(func):
+ "Return the signature of a function as a string"
+ argspec = inspect.getargspec(func)
+ return inspect.formatargspec(formatvalue=lambda val: "", *argspec)[1:-1]
+
+class FuncData(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
+ self.name = func.__name__
+ self.doc = func.__doc__
+ self.module = func.__module__
+ if inspect.isfunction(func):
+ self.signature = getsignature(func)
+ self.defaults = func.func_defaults
+ self.dict = func.__dict__
+ if name:
+ self.name = name
+ if signature:
+ self.signature = signature
+ if defaults:
+ self.defaults = defaults
+ if doc:
+ self.doc = doc
+ if module:
+ self.module = module
+ if funcdict:
+ self.dict = funcdict
+
+ def update(self, func, **kw):
+ func.__name__ = getattr(self, 'name', 'noname')
+ func.__doc__ = getattr(self, 'doc', None)
+ func.__dict__ = getattr(self, 'dict', {})
+ func.func_defaults = getattr(self, 'defaults', None)
+ func.__module__ = getattr(self, 'module', _callermodule())
+ func.__dict__.update(kw)
+ return func
+
+ def __getitem__(self, name):
+ return getattr(self, name)
+
+def makefn(src, funcdata, save_source=True, **evaldict):
+ src += os.linesep # add a newline just for safety
+ name = DEF.match(src).group(1) # extract the function name from the source
+ if save_source:
+ fhandle, fname = mkstemp()
+ os.write(fhandle, src)
+ os.close(fhandle)
+ else:
+ fname = '?'
+ code = compile(src, fname, 'single')
+ exec code in evaldict
+ func = evaldict[name]
+ return funcdata.update(func, __source__=src)
+
+def decorator(caller, func=None):
+ """
+ decorator(caller) converts a caller function into a decorator;
+ decorator(caller, func) is akin to decorator(caller)(func).
+ """
+ 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__
+ return makefn(src, FuncData(caller), save_source=False,
+ caller=caller, decorator=decorator)
+
+@decorator
+def deprecated(func, *args, **kw):
+ "A decorator for deprecated functions"
+ warnings.warn('Calling the deprecated function %r' % func.__name__,
+ DeprecationWarning, stacklevel=3)
+ return func(*args, **kw)
+
+def upgrade_dec(dec):
+ def new_dec(func):
+ fd = FuncData(func)
+ src = '''def %(name)s(%(signature)s):
+ return decorated(%(signature)s)''' % fd
+ return makefn(src, fd, save_source=False, decorated=dec(func))
+ return FuncData(dec).update(new_dec)