deluge/deluge/decorators.py
Calum Lind 84802da29b [Py2to3] Force unicode_literals and fix related issues
* Added `from __future__ import unicode_literals` to every file so
   now all strings in code are forced to be unicode strings unless
   marked as byte string `b'str'` or encoded to byte string `'str'.encode('utf-8')`.

   This is a large change but we have been working towards the goal of unicode
   strings passed in the code so decoding external input and encoding
   output as byte strings (where applicable).

   Note that in Python 2 the `str` type still refers to byte strings.

 * Replaced the use of `str` for `basestring` in isinstance comparison as
   this was the original intention but breaks code when encoutering unicode strings.

 * Marked byte strings in gtkui as the conversion to utf8 is not always handled, mostly
   related to gobject signal names.
2017-02-22 12:36:32 +00:00

138 lines
4.6 KiB
Python

# -*- 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