summaryrefslogtreecommitdiff
path: root/lib/taurus/core/util/decorator
diff options
context:
space:
mode:
authorcoutinho <coutinho@esrf.fr>2015-03-26 12:21:41 +0100
committercoutinho <coutinho@esrf.fr>2015-03-26 12:21:41 +0100
commit6f2a9f4d65212ef253a5ce6fc173d52b8a470c57 (patch)
tree674c3ba22a326794b20abf345ec5e01c102a1b11 /lib/taurus/core/util/decorator
parent3d39d0a483286c6cc6abc58d5514dc5390104736 (diff)
First commit in tauruslib. Restructure according to SEP10
Diffstat (limited to 'lib/taurus/core/util/decorator')
-rw-r--r--lib/taurus/core/util/decorator/__init__.py0
-rw-r--r--lib/taurus/core/util/decorator/decorator.py119
-rw-r--r--lib/taurus/core/util/decorator/deprecated.py72
-rw-r--r--lib/taurus/core/util/decorator/memoize.py57
-rw-r--r--lib/taurus/core/util/decorator/typecheck.py158
5 files changed, 406 insertions, 0 deletions
diff --git a/lib/taurus/core/util/decorator/__init__.py b/lib/taurus/core/util/decorator/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/lib/taurus/core/util/decorator/__init__.py
diff --git a/lib/taurus/core/util/decorator/decorator.py b/lib/taurus/core/util/decorator/decorator.py
new file mode 100644
index 00000000..df109a2e
--- /dev/null
+++ b/lib/taurus/core/util/decorator/decorator.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python
+
+#############################################################################
+##
+## This file is part of Taurus
+##
+## http://taurus-scada.org
+##
+## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
+##
+## Taurus is free software: you can redistribute it and/or modify
+## it under the terms of the GNU Lesser General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## Taurus is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public License
+## along with Taurus. If not, see <http://www.gnu.org/licenses/>.
+##
+#############################################################################
+
+"""\
+Allow to use decorator either with arguments or not. Example::
+
+ @decorator
+ def apply(func, *args, **kw):
+ return func(*args, **kw)
+
+ @decorator
+ class apply:
+ def __init__(self, *args, **kw):
+ self.args = args
+ self.kw = kw
+
+ def __call__(self, func):
+ return func(*self.args, **self.kw)
+
+ #
+ # Usage in both cases:
+ #
+ @apply
+ def test():
+ return 'test'
+
+ assert test == 'test'
+
+ @apply(2, 3)
+ def test(a, b):
+ return a + b
+
+ assert test == 5"""
+
+__all__ = ["decorator"]
+
+__docformat__ = "restructuredtext"
+
+import functools
+import inspect
+
+def decorator(func):
+ """Allow to use decorator either with arguments or not. Example::
+
+ @decorator
+ def apply(func, *args, **kw):
+ return func(*args, **kw)
+
+ @decorator
+ class apply:
+ def __init__(self, *args, **kw):
+ self.args = args
+ self.kw = kw
+
+ def __call__(self, func):
+ return func(*self.args, **self.kw)
+
+ #
+ # Usage in both cases:
+ #
+ @apply
+ def test():
+ return 'test'
+
+ assert test == 'test'
+
+ @apply(2, 3)
+ def test(a, b):
+ return a + b
+
+ assert test == 5
+ """
+
+ def isFuncArg(*args, **kw):
+ return len(args) == 1 and len(kw) == 0 and (
+ inspect.isfunction(args[0]) or isinstance(args[0], type))
+
+ if isinstance(func, type):
+ def class_wrapper(*args, **kw):
+ if isFuncArg(*args, **kw):
+ return func()(*args, **kw) # create class before usage
+ return func(*args, **kw)
+ class_wrapper.__name__ = func.__name__
+ class_wrapper.__module__ = func.__module__
+ return class_wrapper
+
+ @functools.wraps(func)
+ def func_wrapper(*args, **kw):
+ if isFuncArg(*args, **kw):
+ return func(*args, **kw)
+
+ def functor(userFunc):
+ return func(userFunc, *args, **kw)
+
+ return functor
+
+ return func_wrapper
diff --git a/lib/taurus/core/util/decorator/deprecated.py b/lib/taurus/core/util/decorator/deprecated.py
new file mode 100644
index 00000000..3e4393d7
--- /dev/null
+++ b/lib/taurus/core/util/decorator/deprecated.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+
+#############################################################################
+##
+## This file is part of Taurus
+##
+## http://taurus-scada.org
+##
+## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
+##
+## Taurus is free software: you can redistribute it and/or modify
+## it under the terms of the GNU Lesser General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## Taurus is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public License
+## along with Taurus. If not, see <http://www.gnu.org/licenses/>.
+##
+#############################################################################
+
+"""This is a decorator which can be used to mark functions as deprecated.
+It will result in a warning being emitted when the function is used. Examples::
+
+ @deprecated
+ def my_func():
+ pass
+
+ @other_decorators_must_be_upper
+ @deprecated
+ def my_func():
+ pass
+"""
+
+__all__ = ["deprecated"]
+
+__docformat__ = "restructuredtext"
+
+import functools
+import warnings
+
+def deprecated(func):
+ """This is a decorator which can be used to mark functions
+ as deprecated. It will result in a warning being emitted
+ when the function is used. Examples::
+
+ @deprecated
+ def my_func():
+ pass
+
+ @other_decorators_must_be_upper
+ @deprecated
+ def my_func():
+ pass
+ """
+
+ @functools.wraps(func)
+ def new_func(*args, **kwargs):
+ warnings.warn_explicit(
+ "Call to deprecated function %(funcname)s." % {
+ 'funcname': func.__name__,
+ },
+ category=DeprecationWarning,
+ filename=func.func_code.co_filename,
+ lineno=func.func_code.co_firstlineno + 1
+ )
+ return func(*args, **kwargs)
+ return new_func \ No newline at end of file
diff --git a/lib/taurus/core/util/decorator/memoize.py b/lib/taurus/core/util/decorator/memoize.py
new file mode 100644
index 00000000..4dbb1d2e
--- /dev/null
+++ b/lib/taurus/core/util/decorator/memoize.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+
+#############################################################################
+##
+## This file is part of Taurus
+##
+## http://taurus-scada.org
+##
+## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
+##
+## Taurus is free software: you can redistribute it and/or modify
+## it under the terms of the GNU Lesser General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## Taurus is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public License
+## along with Taurus. If not, see <http://www.gnu.org/licenses/>.
+##
+#############################################################################
+
+import functools
+
+
+class memoized(object):
+ '''Decorator. Caches a function's return value each time it is called.
+ If called later with the same arguments, the cached value is returned
+ (not reevaluated).
+ '''
+
+ def __init__(self, func):
+ self.func = func
+ self.cache = {}
+
+ def __call__(self, *args):
+ try:
+ return self.cache[args]
+ except KeyError:
+ value = self.func(*args)
+ self.cache[args] = value
+ return value
+ except TypeError:
+ # uncachable -- for instance, passing a list as an argument.
+ # Better to not cache than to blow up entirely.
+ return self.func(*args)
+
+ def __repr__(self):
+ '''Return the function's docstring.'''
+ return self.func.__doc__
+
+ def __get__(self, obj, objtype):
+ '''Support instance methods.'''
+ return functools.partial(self.__call__, obj)
diff --git a/lib/taurus/core/util/decorator/typecheck.py b/lib/taurus/core/util/decorator/typecheck.py
new file mode 100644
index 00000000..932008ce
--- /dev/null
+++ b/lib/taurus/core/util/decorator/typecheck.py
@@ -0,0 +1,158 @@
+#!/usr/bin/env python
+
+#############################################################################
+##
+## This file is part of Taurus
+##
+## http://taurus-scada.org
+##
+## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
+##
+## Taurus is free software: you can redistribute it and/or modify
+## it under the terms of the GNU Lesser General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## Taurus is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public License
+## along with Taurus. If not, see <http://www.gnu.org/licenses/>.
+##
+#############################################################################
+
+"""
+One of three degrees of enforcement may be specified by passing
+the 'debug' keyword argument to the decorator:
+ 0 -- NONE: No type-checking. Decorators disabled.
+ 1 -- MEDIUM: Print warning message to stderr. (Default)
+ 2 -- STRONG: Raise TypeError with message.
+If 'debug' is not passed to the decorator, the default level is used.
+
+Example usage:
+ >>> NONE, MEDIUM, STRONG = 0, 1, 2
+ >>>
+ >>> @accepts(int, int, int)
+ ... @returns(float)
+ ... def average(x, y, z):
+ ... return (x + y + z) / 2
+ ...
+ >>> average(5.5, 10, 15.0)
+ TypeWarning: 'average' method accepts (int, int, int), but was given
+ (float, int, float)
+ 15.25
+ >>> average(5, 10, 15)
+ TypeWarning: 'average' method returns (float), but result is (int)
+ 15
+
+Needed to cast params as floats in function def (or simply divide by 2.0).
+
+ >>> TYPE_CHECK = STRONG
+ >>> @accepts(int, debug=TYPE_CHECK)
+ ... @returns(int, debug=TYPE_CHECK)
+ ... def fib(n):
+ ... if n in (0, 1): return n
+ ... return fib(n-1) + fib(n-2)
+ ...
+ >>> fib(5.3)
+ Traceback (most recent call last):
+ ...
+ TypeError: 'fib' method accepts (int), but was given (float)
+
+"""
+
+__all__ = ["accepts", "returns"]
+
+__docformat__ = "restructuredtext"
+
+import sys
+
+def accepts(*types, **kw):
+ """ Function decorator. Checks that inputs given to decorated function
+ are of the expected type.
+
+ Parameters:
+ types -- The expected types of the inputs to the decorated function.
+ Must specify type for each parameter.
+ kw -- Optional specification of 'debug' level (this is the only valid
+ keyword argument, no other should be given).
+ debug = ( 0 | 1 | 2 )
+
+ """
+ if not kw:
+ # default level: MEDIUM
+ debug = 1
+ else:
+ debug = kw['debug']
+ try:
+ def decorator(f):
+ def newf(*args):
+ if debug == 0:
+ return f(*args)
+ assert len(args) == len(types)
+ argtypes = tuple(map(type, args))
+ if argtypes != types:
+ msg = info(f.__name__, types, argtypes, 0)
+ if debug == 1:
+ print >> sys.stderr, 'TypeWarning: ', msg
+ elif debug == 2:
+ raise TypeError, msg
+ return f(*args)
+ newf.__name__ = f.__name__
+ return newf
+ return decorator
+ except KeyError, key:
+ raise KeyError, key + "is not a valid keyword argument"
+ except TypeError, msg:
+ raise TypeError, msg
+
+
+def returns(ret_type, **kw):
+ """ Function decorator. Checks that return value of decorated function
+ is of the expected type.
+
+ Parameters:
+ ret_type -- The expected type of the decorated function's return value.
+ Must specify type for each parameter.
+ kw -- Optional specification of 'debug' level (this is the only valid
+ keyword argument, no other should be given).
+ debug=(0 | 1 | 2)
+
+ """
+ try:
+ if not kw:
+ # default level: MEDIUM
+ debug = 1
+ else:
+ debug = kw['debug']
+ def decorator(f):
+ def newf(*args):
+ result = f(*args)
+ if debug == 0:
+ return result
+ res_type = type(result)
+ if res_type != ret_type:
+ msg = info(f.__name__, (ret_type,), (res_type,), 1)
+ if debug == 1:
+ print >> sys.stderr, 'TypeWarning: ', msg
+ elif debug == 2:
+ raise TypeError, msg
+ return result
+ newf.__name__ = f.__name__
+ return newf
+ return decorator
+ except KeyError, key:
+ raise KeyError, key + "is not a valid keyword argument"
+ except TypeError, msg:
+ raise TypeError, msg
+
+def info(fname, expected, actual, flag):
+ """ Convenience function returns nicely formatted error/warning msg. """
+ format = lambda types: ', '.join([str(t).split("'")[1] for t in types])
+ expected, actual = format(expected), format(actual)
+ msg = "'%s' method " % fname \
+ + ("accepts", "returns")[flag] + " (%s), but " % expected\
+ + ("was given", "result is")[flag] + " (%s)" % actual
+ return msg \ No newline at end of file