diff options
author | coutinho <coutinho@esrf.fr> | 2015-03-26 12:21:41 +0100 |
---|---|---|
committer | coutinho <coutinho@esrf.fr> | 2015-03-26 12:21:41 +0100 |
commit | 6f2a9f4d65212ef253a5ce6fc173d52b8a470c57 (patch) | |
tree | 674c3ba22a326794b20abf345ec5e01c102a1b11 /lib/taurus/core/util/decorator | |
parent | 3d39d0a483286c6cc6abc58d5514dc5390104736 (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__.py | 0 | ||||
-rw-r--r-- | lib/taurus/core/util/decorator/decorator.py | 119 | ||||
-rw-r--r-- | lib/taurus/core/util/decorator/deprecated.py | 72 | ||||
-rw-r--r-- | lib/taurus/core/util/decorator/memoize.py | 57 | ||||
-rw-r--r-- | lib/taurus/core/util/decorator/typecheck.py | 158 |
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 |