summaryrefslogtreecommitdiffstats
path: root/deluge/decorators.py
diff options
context:
space:
mode:
Diffstat (limited to 'deluge/decorators.py')
-rw-r--r--deluge/decorators.py164
1 files changed, 164 insertions, 0 deletions
diff --git a/deluge/decorators.py b/deluge/decorators.py
new file mode 100644
index 0000000..b101712
--- /dev/null
+++ b/deluge/decorators.py
@@ -0,0 +1,164 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2010 John Garland <johnnybg+deluge@gmail.com>
+#
+# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
+# the additional special exception to link portions of this program with the OpenSSL library.
+# See LICENSE for more details.
+#
+
+from __future__ import unicode_literals
+
+import inspect
+import re
+import warnings
+from functools import wraps
+
+
+def proxy(proxy_func):
+ """
+ Factory class which returns a decorator that passes
+ the decorated function to a proxy function
+
+ :param proxy_func: the proxy function
+ :type proxy_func: function
+ """
+
+ def decorator(func):
+ @wraps(func)
+ def wrapper(*args, **kwargs):
+ return proxy_func(func, *args, **kwargs)
+
+ return wrapper
+
+ return decorator
+
+
+def overrides(*args):
+ """
+ Decorater function to specify when class methods override
+ super class methods.
+
+ When used as
+ @overrides
+ def funcname
+
+ the argument will be the funcname function.
+
+ When used as
+ @overrides(BaseClass)
+ def funcname
+
+ the argument will be the BaseClass
+
+ """
+ stack = inspect.stack()
+ if inspect.isfunction(args[0]):
+ return _overrides(stack, args[0])
+ else:
+ # One or more classes are specifed, so return a function that will be
+ # called with the real function as argument
+ def ret_func(func, **kwargs):
+ return _overrides(stack, func, explicit_base_classes=args)
+
+ return ret_func
+
+
+def _overrides(stack, method, explicit_base_classes=None):
+ # stack[0]=overrides, stack[1]=inside class def'n, stack[2]=outside class def'n
+ classes = {}
+ derived_class_locals = stack[2][0].f_locals
+
+ # Find all super classes
+ m = re.search(r'class\s(.+)\((.+)\)\s*\:', stack[2][4][0])
+ class_name = m.group(1)
+ base_classes = m.group(2)
+
+ # Handle multiple inheritance
+ base_classes = [s.strip() for s in base_classes.split(',')]
+ check_classes = base_classes
+
+ if not base_classes:
+ raise ValueError(
+ 'overrides decorator: unable to determine base class of class "%s"'
+ % class_name
+ )
+
+ def get_class(cls_name):
+ if '.' not in cls_name:
+ return derived_class_locals[cls_name]
+ else:
+ components = cls_name.split('.')
+ # obj is either a module or a class
+ obj = derived_class_locals[components[0]]
+ for c in components[1:]:
+ assert inspect.ismodule(obj) or inspect.isclass(obj)
+ obj = getattr(obj, c)
+ return obj
+
+ if explicit_base_classes:
+ # One or more base classes are explicitly given, check only those classes
+ override_classes = re.search(r'\s*@overrides\((.+)\)\s*', stack[1][4][0]).group(
+ 1
+ )
+ override_classes = [c.strip() for c in override_classes.split(',')]
+ check_classes = override_classes
+
+ for c in base_classes + check_classes:
+ classes[c] = get_class(c)
+
+ # Verify that the excplicit override class is one of base classes
+ if explicit_base_classes:
+ from itertools import product
+
+ for bc, cc in product(base_classes, check_classes):
+ if issubclass(classes[bc], classes[cc]):
+ break
+ else:
+ raise Exception(
+ 'Excplicit override class "%s" is not a super class of: %s'
+ % (explicit_base_classes, class_name)
+ )
+ if not all(hasattr(classes[cls], method.__name__) for cls in check_classes):
+ for cls in check_classes:
+ if not hasattr(classes[cls], method.__name__):
+ raise Exception(
+ 'Function override "%s" not found in superclass: %s\n%s'
+ % (
+ method.__name__,
+ cls,
+ 'File: %s:%s' % (stack[1][1], stack[1][2]),
+ )
+ )
+
+ if not any(hasattr(classes[cls], method.__name__) for cls in check_classes):
+ raise Exception(
+ 'Function override "%s" not found in any superclass: %s\n%s'
+ % (
+ method.__name__,
+ check_classes,
+ 'File: %s:%s' % (stack[1][1], stack[1][2]),
+ )
+ )
+ return method
+
+
+def deprecated(func):
+ """This is a decorator which can be used to mark function as deprecated.
+
+ It will result in a warning being emmitted when the function is used.
+
+ """
+
+ @wraps(func)
+ def depr_func(*args, **kwargs):
+ warnings.simplefilter('always', DeprecationWarning) # Turn off filter
+ warnings.warn(
+ 'Call to deprecated function {}.'.format(func.__name__),
+ category=DeprecationWarning,
+ stacklevel=2,
+ )
+ warnings.simplefilter('default', DeprecationWarning) # Reset filter
+ return func(*args, **kwargs)
+
+ return depr_func