interface-contract 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- interface_contract/__init__.py +68 -0
- interface_contract/_core.py +7 -0
- interface_contract/adapters.py +7 -0
- interface_contract/mypy_plugin.py +68 -0
- interface_contract/py.typed +0 -0
- interface_contract-0.4.0.dist-info/METADATA +305 -0
- interface_contract-0.4.0.dist-info/RECORD +15 -0
- interface_contract-0.4.0.dist-info/WHEEL +5 -0
- interface_contract-0.4.0.dist-info/licenses/LICENSE +21 -0
- interface_contract-0.4.0.dist-info/top_level.txt +2 -0
- strict_interface/__init__.py +85 -0
- strict_interface/_core.py +798 -0
- strict_interface/adapters.py +109 -0
- strict_interface/mypy_plugin.py +5 -0
- strict_interface/py.typed +0 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Small, explicit adapter registry for interface contracts."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from typing import Any, TypeVar
|
|
7
|
+
|
|
8
|
+
from ._core import InterfaceError, is_interface, satisfies
|
|
9
|
+
|
|
10
|
+
_T = TypeVar("_T")
|
|
11
|
+
_MISSING = object()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AdaptationError(InterfaceError):
|
|
15
|
+
"""Raised when no adapter exists or an adapter returns an invalid object."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AdapterRegistry:
|
|
19
|
+
"""Map ``(source type, target interface)`` pairs to adapter factories."""
|
|
20
|
+
|
|
21
|
+
def __init__(self) -> None:
|
|
22
|
+
self._factories: dict[tuple[type, type], Callable[[Any], Any]] = {}
|
|
23
|
+
|
|
24
|
+
def register(
|
|
25
|
+
self,
|
|
26
|
+
source: type,
|
|
27
|
+
target: type,
|
|
28
|
+
factory: Callable[[Any], Any] | None = None,
|
|
29
|
+
) -> Callable[[Any], Any]:
|
|
30
|
+
"""Register a factory directly or use the method as a decorator."""
|
|
31
|
+
if not isinstance(source, type):
|
|
32
|
+
raise TypeError("source bir sinif olmali")
|
|
33
|
+
if not is_interface(target):
|
|
34
|
+
raise TypeError("target bir Interface olmali")
|
|
35
|
+
|
|
36
|
+
def store(candidate: Callable[[Any], Any]) -> Callable[[Any], Any]:
|
|
37
|
+
if not callable(candidate):
|
|
38
|
+
raise TypeError("adapter factory cagrilabilir olmali")
|
|
39
|
+
self._factories[(source, target)] = candidate
|
|
40
|
+
return candidate
|
|
41
|
+
|
|
42
|
+
return store if factory is None else store(factory)
|
|
43
|
+
|
|
44
|
+
def unregister(self, source: type, target: type) -> bool:
|
|
45
|
+
"""Remove one registration and report whether it existed."""
|
|
46
|
+
return self._factories.pop((source, target), None) is not None
|
|
47
|
+
|
|
48
|
+
def _factory_for(self, source: type, target: type) -> Callable[[Any], Any] | None:
|
|
49
|
+
for candidate in source.__mro__:
|
|
50
|
+
factory = self._factories.get((candidate, target))
|
|
51
|
+
if factory is not None:
|
|
52
|
+
return factory
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
def can_adapt(self, obj: Any, target: type) -> bool:
|
|
56
|
+
"""Return whether ``obj`` already satisfies or has an adapter to ``target``."""
|
|
57
|
+
if not is_interface(target):
|
|
58
|
+
raise TypeError("target bir Interface olmali")
|
|
59
|
+
return satisfies(obj, target) or self._factory_for(type(obj), target) is not None
|
|
60
|
+
|
|
61
|
+
def adapt(self, obj: Any, target: type, *, default: Any = _MISSING) -> Any:
|
|
62
|
+
"""Adapt ``obj`` to ``target`` and validate the produced object."""
|
|
63
|
+
if not is_interface(target):
|
|
64
|
+
raise TypeError("target bir Interface olmali")
|
|
65
|
+
if satisfies(obj, target):
|
|
66
|
+
return obj
|
|
67
|
+
|
|
68
|
+
factory = self._factory_for(type(obj), target)
|
|
69
|
+
if factory is None:
|
|
70
|
+
if default is not _MISSING:
|
|
71
|
+
return default
|
|
72
|
+
raise AdaptationError(
|
|
73
|
+
f"{type(obj).__name__} -> {target.__name__} adapter'i kayitli degil."
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
result = factory(obj)
|
|
77
|
+
if not satisfies(result, target):
|
|
78
|
+
raise AdaptationError(
|
|
79
|
+
f"{type(obj).__name__} -> {target.__name__} adapter'i "
|
|
80
|
+
f"gecersiz {type(result).__name__} dondurdu."
|
|
81
|
+
)
|
|
82
|
+
return result
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
default_registry = AdapterRegistry()
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def register_adapter(
|
|
89
|
+
source: type,
|
|
90
|
+
target: type,
|
|
91
|
+
factory: Callable[[Any], Any] | None = None,
|
|
92
|
+
) -> Callable[[Any], Any]:
|
|
93
|
+
"""Register an adapter in the process-wide default registry."""
|
|
94
|
+
return default_registry.register(source, target, factory)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def unregister_adapter(source: type, target: type) -> bool:
|
|
98
|
+
"""Remove an adapter from the process-wide default registry."""
|
|
99
|
+
return default_registry.unregister(source, target)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def adapt(obj: _T, target: type, *, default: Any = _MISSING) -> Any:
|
|
103
|
+
"""Adapt through the process-wide default registry."""
|
|
104
|
+
return default_registry.adapt(obj, target, default=default)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def can_adapt(obj: Any, target: type) -> bool:
|
|
108
|
+
"""Check the process-wide default registry."""
|
|
109
|
+
return default_registry.can_adapt(obj, target)
|
|
File without changes
|