from typing import Tuple class RequiredAnnotationsMeta(type): """Metaclass to save all annotated public class attributes names in _required_attrs_names attribute.""" def __init__(cls, name, bases, attr_dict): super().__init__(name, bases, attr_dict) required = set() for base_cls in bases: required.update(getattr(base_cls, "_required_attrs_names", set()).copy()) required.update({name for name in getattr(cls, "__annotations__", {}).keys() if not name.startswith('_')}) cls._required_attrs_names = required class RequiredAnnotations(metaclass=RequiredAnnotationsMeta): """Base class for classes with required annotated attributes. Considers all annotated class attributes as required and checks during initialization process that all of them were set. Example: class Example1(RequiredAnnotations): a: int b: int class Example2(Example1): a = 1 c = 3 inst = Example2() AttributeError: Attributes {'b'} of class Example2 were announced as required but haven't been set. """ def __init__(self): super().__init__() diff = self._required_attrs_names.copy() - set(dir(self)) if diff: raise AttributeError(f"Attributes {diff} of class {self.__class__.__name__} were announced " f"as required but haven't been set.") class LoopRunningConfig(RequiredAnnotations): """Configuration class for the loop decorator. Attributes: APP_NAME: string name of running service. EXECUTION_TIME_DELAYS: running settings presented as tuple of tuple containing pairs of int values (seconds): execution_time_limit, delay. After processing an iteration the decorator measures its execution time and if this time <= "execution_time_limit", it makes delay equal "delay" from the same tuple. """ APP_NAME: str EXECUTION_TIME_DELAYS: Tuple[Tuple[int, int]] = ( (0, 30), (90, 60), (180, 90), (300, 120), (600, 300) ) class LockConfig(RequiredAnnotations): """Configuration class for Lock decorator. Attributes: APP_NAME: string name of running service. REDIS_LOCK_ENABLED: flag of using distributed redis locks to prevent parallel instances execution. REDIS_LOCK_TTL: int ttl for distributed lock. """ APP_NAME: str REDIS_LOCK_ENABLED: bool REDIS_LOCK_TTL: int