* 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.
138 lines
4.6 KiB
Python
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
|