from pathlib import Path import functools from typing import Union, Callable, Dict, List, Iterable, TypeVar # some helper functions def import_file(p: Union[str, Path], name=None): p = Path(p) if name is None: name = p.stem import importlib.util spec = importlib.util.spec_from_file_location(name, p) # type: ignore foo = importlib.util.module_from_spec(spec) spec.loader.exec_module(foo) # type: ignore return foo def import_from(path, name): path = str(path) import sys try: sys.path.append(path) import importlib return importlib.import_module(name) finally: sys.path.remove(path) T = TypeVar('T') K = TypeVar('K') V = TypeVar('V') def the(l: Iterable[T]) -> T: it = iter(l) try: first = next(it) except StopIteration as ee: raise RuntimeError('Empty iterator?') assert all(e == first for e in it) return first def group_by_key(l: Iterable[T], key: Callable[[T], K]) -> Dict[K, List[T]]: res: Dict[K, List[T]] = {} for i in l: kk = key(i) lst = res.get(kk, []) lst.append(i) res[kk] = lst return res Cl = TypeVar('Cl') R = TypeVar('R') def cproperty(f: Callable[[Cl], R]) -> R: return property(functools.lru_cache(maxsize=1)(f)) # type: ignore