codekingpro/portable-devtools
114k
1from abc import ABCMeta2 3 4class EnforceOverridesMeta(ABCMeta):5 def __new__(mcls, name, bases, namespace, **kwargs):6 # Ignore any methods defined on the metaclass when enforcing overrides.7 for method in dir(mcls):8 if not method.startswith("__") and method != "mro":9 value = getattr(mcls, method)10 if not isinstance(value, (bool, str, int, float, tuple, list, dict)):11 setattr(getattr(mcls, method), "__ignored__", True)12 13 cls = super().__new__(mcls, name, bases, namespace, **kwargs)14 for name, value in namespace.items():15 mcls._check_if_overrides_final_method(name, bases)16 if not name.startswith("__"):17 value = mcls._handle_special_value(value)18 mcls._check_if_overrides_without_overrides_decorator(name, value, bases)19 return cls20 21 @staticmethod22 def _check_if_overrides_without_overrides_decorator(name, value, bases):23 is_override = getattr(value, "__override__", False)24 for base in bases:25 base_class_method = getattr(base, name, False)26 if (27 not base_class_method28 or not callable(base_class_method)29 or getattr(base_class_method, "__ignored__", False)30 ):31 continue32 if not is_override:33 raise TypeError(34 f"Method {name} overrides method from {base} but does not have @override decorator"35 )36 37 @staticmethod38 def _check_if_overrides_final_method(name, bases):39 for base in bases:40 base_class_method = getattr(base, name, False)41 # `__final__` is added by `@final` decorator42 if getattr(base_class_method, "__final__", False):43 raise TypeError(44 f"Method {name} is finalized in {base}, it cannot be overridden"45 )46 47 @staticmethod48 def _handle_special_value(value):49 if isinstance(value, classmethod) or isinstance(value, staticmethod):50 value = value.__get__(None, dict)51 elif isinstance(value, property):52 value = value.fget53 return value54 55 56class EnforceOverrides(metaclass=EnforceOverridesMeta):57 "Use this as the parent class for your custom classes"58 pass59 