|
3 | 3 |
|
4 | 4 | """Abstract Base Classes (ABCs) according to PEP 3119.""" |
5 | 5 |
|
| 6 | +import inspect |
6 | 7 |
|
7 | 8 | def abstractmethod(funcobj): |
8 | 9 | """A decorator indicating abstract methods. |
@@ -186,3 +187,35 @@ class ABC(metaclass=ABCMeta): |
186 | 187 | inheritance. |
187 | 188 | """ |
188 | 189 | __slots__ = () |
| 190 | + |
| 191 | + |
| 192 | +class InterfaceMeta(ABCMeta): |
| 193 | + """Metaclass to force implementation of methods in derived classes with the same signature.""" |
| 194 | + |
| 195 | + def __new__(mcs, name, bases, namespace): |
| 196 | + # If it is not a base class (Interface), check implementation |
| 197 | + if bases: |
| 198 | + for base in bases: |
| 199 | + if isinstance(base, InterfaceMeta): |
| 200 | + for attr_name, attr_value in base.__dict__.items(): |
| 201 | + # Checks if the base attribute is a method and not a variable |
| 202 | + if callable(attr_value) and not attr_name.startswith('__'): |
| 203 | + if attr_name not in namespace: |
| 204 | + raise TypeError(f"Class '{name}' must implement method '{attr_name}' of interface '{base.__name__}'") |
| 205 | + |
| 206 | + # Compare method signatures |
| 207 | + base_signature = inspect.signature(attr_value) |
| 208 | + derived_signature = inspect.signature(namespace[attr_name]) |
| 209 | + |
| 210 | + if base_signature != derived_signature: |
| 211 | + raise TypeError( |
| 212 | + f"Method '{attr_name}' in class '{name}' does not match the interface signature.\n" |
| 213 | + f"Expected: {base_signature}\n" |
| 214 | + f"Got: {derived_signature}" |
| 215 | + ) |
| 216 | + return super().__new__(mcs, name, bases, namespace) |
| 217 | + |
| 218 | + |
| 219 | +class Interface(metaclass=InterfaceMeta): |
| 220 | + """Base for all interfaces.""" |
| 221 | + pass |
0 commit comments