This commit is contained in:
2025-10-25 13:21:06 +02:00
parent eb57506d39
commit 033ffb21f5
8388 changed files with 484789 additions and 16 deletions

View File

@@ -0,0 +1,116 @@
# coding: utf-8
# Created on: 04.01.2018
# Author: Roman Miroshnychenko aka Roman V.M. (roman1972@gmail.com)
"""
Utility functions for string conversion depending on Python version
"""
import sys
import inspect
__all__ = [
'PY2',
'py2_encode',
'py2_decode',
'encode_decode',
'patch_object',
'ModuleWrapper',
]
PY2 = sys.version_info[0] == 2 #: ``True`` for Python 2
def py2_encode(s, encoding='utf-8', errors='strict'):
"""
Encode Python 2 ``unicode`` to ``str``
In Python 3 the string is not changed.
"""
if PY2 and isinstance(s, unicode):
s = s.encode(encoding, errors)
return s
def py2_decode(s, encoding='utf-8', errors='strict'):
"""
Decode Python 2 ``str`` to ``unicode``
In Python 3 the string is not changed.
"""
if PY2 and isinstance(s, str):
s = s.decode(encoding, errors)
return s
def encode_decode(func):
"""
A decorator that encodes all unicode function arguments to UTF-8-encoded
byte strings and decodes function str return value to unicode.
This decorator is no-op in Python 3.
:param func: wrapped function or a method
:type func: types.FunctionType or types.MethodType
:return: function wrapper
:rtype: types.FunctionType
"""
if PY2:
def wrapper(*args, **kwargs):
mod_args = tuple(py2_encode(item) for item in args)
mod_kwargs = {key: py2_encode(value) for key, value
in kwargs.iteritems()}
return py2_decode(func(*mod_args, **mod_kwargs),
errors='replace')
wrapper.__name__ = 'wrapped_func_{0}'.format(func.__name__)
return wrapper
return func
def _wrap_class(cls):
class ClassWrapper(cls):
pass
ClassWrapper.__name__ = 'wrapped_class_{0}'.format(cls.__name__)
return ClassWrapper
def patch_object(obj):
"""
Applies func:`encode_decode` decorator to an object
:param obj: object for patching
:return: patched object
"""
if inspect.isbuiltin(obj):
obj = encode_decode(obj)
elif inspect.isclass(obj):
# We cannot patch methods of Kodi classes directly.
obj = _wrap_class(obj)
for memb_name, member in inspect.getmembers(obj):
# Do not patch special methods!
if (inspect.ismethoddescriptor(member) and
not memb_name.endswith('__')):
setattr(obj, memb_name, encode_decode(member))
return obj
class ModuleWrapper(object):
"""
Implements lazy patching of Kodi Python API modules
This class applies func:`encode_decode` decorator to Kodi API functions
and classes on demand when a function or a class is first used.
"""
def __init__(self, base_module):
self._base_module = base_module
def __getattr__(self, item):
if not hasattr(self._base_module, item):
raise AttributeError(
'Module {0} does not have attribute "{1}"!'.format(
self._base_module, item
)
)
obj = getattr(self._base_module, item)
obj = patch_object(obj)
setattr(self, item, obj)
return obj