import re import os.path as path from Levenshtein import distance def relative_path(file, *args): """ TODO :param (str) file: :param (str) args: :rtype str :return: """ return path.realpath(path.join(path.dirname(path.realpath(file)), *args)) def read_key_safe(d, k, default=None, *, types=None, is_list=False, is_list_size=None): """ TODO :param (dict) d: source dict :param (str) k: key to read :param default: default value :param (list of type|None) types: types to check :param (bool) is_list: if the type is a list of types :param (int|None) is_list_size: size of the list to enforce or None :raises TypeError: :return: """ try: return read_key(d, k, default, types=types, is_list=is_list, is_list_size=is_list_size) except KeyError: return default def read_key(d, k, default=None, *, types=None, is_list=False, is_list_size=None): """ TODO :param (dict) d: source dict :param (str) k: key to read :param default: default value :param (list of type|None) types: types to check :param (bool) is_list: if the type is a list of types :param (int|None) is_list_size: size of the list to enforce or None :raises TypeError: :raises KeyError: :return: """ if k in d: v = d[k] if types is not None: try: check_type(v, types, is_list, is_list_size) except TypeError as e: raise TypeError(f"'{k}' is {e}") return v elif default is not None: return default else: raise KeyError(k) def check_type(obj, types, is_list=False, is_list_size=None): """ TODO :param obj: :param (list of type|None) types: types to check :param (bool) is_list: if the type is a list of types :param (int|None) is_list_size: size of the list to enforce or None :raises TypeError: :return: """ if is_list: if not is_list_of(obj, types, is_list_size): if is_list_size is None: raise TypeError(f"not a list of {is_list_size} {types[0].__name__}") else: raise TypeError(f"not a list of {types[0].__name__}") else: if not is_list_of([obj], types): raise TypeError(f"not a {types[0].__name__}") def is_list_of(obj, types, length=None): """ TODO :param obj: :param (list of type) types: :param (int) length: :rtype: bool :return: """ if not (isinstance(obj, list)): return False for item in obj: found = False for t in types: if isinstance(item, t): found = True break if not found: return False if length is not None and len(obj) != length: return False return True args_regex = re.compile('"([^"]*)"|\'([^\']*)\'|([^ ]+)') def parse_arguments(s): """ TODO :param (str) s: :rtype: list of str :return: """ return [get_found_match(m) for m in args_regex.findall(s)] def get_found_match(m): f = [g for g in m if len(g) > 0] if len(f) > 0: return f[0] return "" def find_nearest(word, wlist, threshold=5): """ TODO :param (str) word: :param (list of str) wlist: :param (int) threshold: :rtype: str | None :return: """ found = min([(distance(word, w) - abs(len(w) - len(word)), w) for w in wlist], key=lambda v: v[0]) if found[0] > threshold: return None return found[1]