import fnmatch import inspect import logging import os import re import sys import time from platform import system from functools import wraps from html import entities log = logging.getLogger(__name__) ON_WINDOWS = system() == 'Windows' PLUGINS_SUBDIR = 'plugins' # noinspection PyPep8Naming class deprecated(object): """ deprecated decorator. emits a warning on a call on an old method and call the new method anyway """ def __init__(self, new=None): self.new = new def __call__(self, old): @wraps(old) def wrapper(*args, **kwds): frame = inspect.getframeinfo(inspect.currentframe().f_back) msg = f'{frame.filename}: {frame.lineno}: ' if len(args): pref = type(args[0]).__name__ + '.' # TODO might break for individual methods else: pref = '' msg += f'call to the deprecated {pref}{old.__name__}' if self.new is not None: if type(self.new) is property: msg += f'... use the property {pref}{self.new.fget.__name__} instead' else: msg += f'... use {pref}{self.new.__name__} instead' msg += '.' logging.warning(msg) if self.new: if type(self.new) is property: return self.new.fget(*args, **kwds) return self.new(*args, **kwds) return old(*args, **kwds) wrapper.__name__ = old.__name__ wrapper.__doc__ = old.__doc__ wrapper.__dict__.update(old.__dict__) return wrapper def format_timedelta(timedelta): total_seconds = timedelta.seconds + (86400 * timedelta.days) hours, remainder = divmod(total_seconds, 3600) minutes, seconds = divmod(remainder, 60) if hours == 0 and minutes == 0: return f'{seconds:d} seconds' elif not hours: return f'{minutes:d} minutes' elif not minutes: return f'{hours:d} hours' return f'{hours:d} hours and {minutes:d} minutes' # Introspect to know from which plugin a command is implemented def get_class_for_method(meth): for cls in inspect.getmro(type(meth.__self__)): if meth.__name__ in cls.__dict__: return cls return None INVALID_VERSION_EXCEPTION = 'version %s in not in format "x.y.z" or "x.y.z-{beta,alpha,rc1,rc2...}" for example "1.2.2"' def version2tuple(version): vsplit = version.split('-') if len(vsplit) == 2: main, sub = vsplit if sub == 'alpha': sub_int = -1 elif sub == 'beta': sub_int = 0 elif sub.startswith('rc'): sub_int = int(sub[2:]) else: raise ValueError(INVALID_VERSION_EXCEPTION % version) elif len(vsplit) == 1: main = vsplit[0] sub_int = sys.maxsize else: raise ValueError(INVALID_VERSION_EXCEPTION % version) response = [int(el) for el in main.split('.')] response.append(sub_int) if len(response) != 4: raise ValueError(INVALID_VERSION_EXCEPTION % version) return tuple(response) def unescape_xml(text): """ Removes HTML or XML character references and entities from a text string. @param text The HTML (or XML) source text. @return The plain text, as a Unicode string, if necessary. """ def fixup(m): txt = m.group(0) if txt[:2] == "": # character reference try: if txt[:3] == "": return chr(int(txt[3:-1], 16)) else: return chr(int(txt[2:-1])) except ValueError: pass else: # named entity try: txt = chr(entities.name2codepoint[txt[1:-1]]) except KeyError: pass return txt # leave as is return re.sub(r'?\w+;', fixup, text) REMOVE_EOL = re.compile(r'\n') REINSERT_EOLS = re.compile(r'
||