pyiv 0.3.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.
- pyiv/__init__.py +137 -0
- pyiv/binder.py +203 -0
- pyiv/binder_impl.py +178 -0
- pyiv/chain.py +79 -0
- pyiv/clock.py +411 -0
- pyiv/command.py +581 -0
- pyiv/config.py +555 -0
- pyiv/console.py +2143 -0
- pyiv/datetime_service.py +115 -0
- pyiv/factory.py +133 -0
- pyiv/filesystem.py +801 -0
- pyiv/injector.py +707 -0
- pyiv/key.py +223 -0
- pyiv/members.py +235 -0
- pyiv/multibinder.py +291 -0
- pyiv/network/__init__.py +20 -0
- pyiv/network/base.py +137 -0
- pyiv/network/clients.py +202 -0
- pyiv/optional.py +174 -0
- pyiv/provider.py +288 -0
- pyiv/py.typed +0 -0
- pyiv/reflection.py +362 -0
- pyiv/scope.py +302 -0
- pyiv/serde/__init__.py +47 -0
- pyiv/serde/base.py +125 -0
- pyiv/serde/encodings.py +373 -0
- pyiv/serde/json.py +33 -0
- pyiv/singleton.py +121 -0
- pyiv-0.3.0.dist-info/METADATA +101 -0
- pyiv-0.3.0.dist-info/RECORD +32 -0
- pyiv-0.3.0.dist-info/WHEEL +4 -0
- pyiv-0.3.0.dist-info/licenses/LICENSE +26 -0
pyiv/__init__.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""Guice-style dependency injection for Python.
|
|
2
|
+
|
|
3
|
+
pyiv provides type-based constructor injection, scopes, qualified keys, and
|
|
4
|
+
built-in test doubles. Runtime has zero third-party dependencies. Python 3.8+.
|
|
5
|
+
|
|
6
|
+
Key Features:
|
|
7
|
+
|
|
8
|
+
- Type-based constructor injection from annotations
|
|
9
|
+
- Scopes (per-injector and process-wide singletons, plus custom Scope)
|
|
10
|
+
- Qualified keys and a fluent Binder API
|
|
11
|
+
- Reflection to discover implementations in a package
|
|
12
|
+
- Test doubles for Clock, Filesystem, Console, and DateTimeService
|
|
13
|
+
- Zero runtime dependencies
|
|
14
|
+
|
|
15
|
+
Quick Start:
|
|
16
|
+
|
|
17
|
+
>>> from pyiv import Config, get_injector
|
|
18
|
+
>>> class Database:
|
|
19
|
+
... pass
|
|
20
|
+
>>> class PostgreSQL(Database):
|
|
21
|
+
... pass
|
|
22
|
+
>>> class MyConfig(Config):
|
|
23
|
+
... def configure(self):
|
|
24
|
+
... self.register(Database, PostgreSQL)
|
|
25
|
+
>>> injector = get_injector(MyConfig)
|
|
26
|
+
>>> isinstance(injector.inject(Database), PostgreSQL)
|
|
27
|
+
True
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from pyiv.binder import Binder, BindingBuilder
|
|
31
|
+
from pyiv.chain import ChainHandler, ChainType
|
|
32
|
+
from pyiv.clock import Clock, RealClock, SyntheticClock, Timer
|
|
33
|
+
from pyiv.config import Config
|
|
34
|
+
from pyiv.console import (
|
|
35
|
+
BaseConsole,
|
|
36
|
+
Console,
|
|
37
|
+
FileConsole,
|
|
38
|
+
MemoryConsole,
|
|
39
|
+
MockConsole,
|
|
40
|
+
PTYConsole,
|
|
41
|
+
RealConsole,
|
|
42
|
+
)
|
|
43
|
+
from pyiv.datetime_service import DateTimeService, MockDateTimeService, PythonDateTimeService
|
|
44
|
+
from pyiv.factory import BaseFactory, Factory, SimpleFactory
|
|
45
|
+
from pyiv.filesystem import Filesystem, MemoryFilesystem, RealFilesystem
|
|
46
|
+
from pyiv.injector import Injector, get_injector
|
|
47
|
+
from pyiv.key import Key, Named, Qualifier
|
|
48
|
+
from pyiv.members import InjectorMembersInjector, MembersInjector
|
|
49
|
+
from pyiv.multibinder import ListMultibinder, Multibinder, SetMultibinder
|
|
50
|
+
from pyiv.network import HTTPClient, HTTPSClient, NetworkClient
|
|
51
|
+
from pyiv.optional import get_optional_type, is_optional_type
|
|
52
|
+
from pyiv.provider import (
|
|
53
|
+
BaseProvider,
|
|
54
|
+
FactoryProvider,
|
|
55
|
+
InjectorProvider,
|
|
56
|
+
InstanceProvider,
|
|
57
|
+
Provider,
|
|
58
|
+
)
|
|
59
|
+
from pyiv.reflection import ReflectionConfig
|
|
60
|
+
from pyiv.scope import GlobalSingletonScope, NoScope, Scope, SingletonScope
|
|
61
|
+
from pyiv.serde import Base64SerDe, JSONSerDe, NoOpSerDe, PickleSerDe, SerDe, XMLSerDe, YAMLSerDe
|
|
62
|
+
from pyiv.singleton import GlobalSingletonRegistry, SingletonType
|
|
63
|
+
|
|
64
|
+
# Command interface (optional import)
|
|
65
|
+
try:
|
|
66
|
+
from pyiv.command import CLICommand, Command, CommandRunner, ServiceCommand
|
|
67
|
+
|
|
68
|
+
_has_commands = True
|
|
69
|
+
except ImportError:
|
|
70
|
+
_has_commands = False
|
|
71
|
+
|
|
72
|
+
__version__ = "0.3.0"
|
|
73
|
+
__all__ = [
|
|
74
|
+
"Config",
|
|
75
|
+
"ReflectionConfig",
|
|
76
|
+
"Injector",
|
|
77
|
+
"get_injector",
|
|
78
|
+
"ChainType",
|
|
79
|
+
"ChainHandler",
|
|
80
|
+
"Filesystem",
|
|
81
|
+
"RealFilesystem",
|
|
82
|
+
"MemoryFilesystem",
|
|
83
|
+
"Console",
|
|
84
|
+
"BaseConsole",
|
|
85
|
+
"RealConsole",
|
|
86
|
+
"MemoryConsole",
|
|
87
|
+
"FileConsole",
|
|
88
|
+
"PTYConsole",
|
|
89
|
+
"MockConsole",
|
|
90
|
+
"Clock",
|
|
91
|
+
"RealClock",
|
|
92
|
+
"SyntheticClock",
|
|
93
|
+
"Timer",
|
|
94
|
+
"DateTimeService",
|
|
95
|
+
"PythonDateTimeService",
|
|
96
|
+
"MockDateTimeService",
|
|
97
|
+
"Factory",
|
|
98
|
+
"BaseFactory",
|
|
99
|
+
"SimpleFactory",
|
|
100
|
+
"SerDe",
|
|
101
|
+
"JSONSerDe",
|
|
102
|
+
"Base64SerDe",
|
|
103
|
+
"XMLSerDe",
|
|
104
|
+
"YAMLSerDe",
|
|
105
|
+
"PickleSerDe",
|
|
106
|
+
"NoOpSerDe",
|
|
107
|
+
"NetworkClient",
|
|
108
|
+
"HTTPClient",
|
|
109
|
+
"HTTPSClient",
|
|
110
|
+
"SingletonType",
|
|
111
|
+
"GlobalSingletonRegistry",
|
|
112
|
+
# New interfaces
|
|
113
|
+
"Provider",
|
|
114
|
+
"BaseProvider",
|
|
115
|
+
"InjectorProvider",
|
|
116
|
+
"InstanceProvider",
|
|
117
|
+
"FactoryProvider",
|
|
118
|
+
"Scope",
|
|
119
|
+
"NoScope",
|
|
120
|
+
"SingletonScope",
|
|
121
|
+
"GlobalSingletonScope",
|
|
122
|
+
"Key",
|
|
123
|
+
"Named",
|
|
124
|
+
"Qualifier",
|
|
125
|
+
"Binder",
|
|
126
|
+
"BindingBuilder",
|
|
127
|
+
"MembersInjector",
|
|
128
|
+
"InjectorMembersInjector",
|
|
129
|
+
"Multibinder",
|
|
130
|
+
"SetMultibinder",
|
|
131
|
+
"ListMultibinder",
|
|
132
|
+
"is_optional_type",
|
|
133
|
+
"get_optional_type",
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
if _has_commands:
|
|
137
|
+
__all__.extend(["Command", "ServiceCommand", "CLICommand", "CommandRunner"])
|
pyiv/binder.py
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""Binder interface for fluent configuration API.
|
|
2
|
+
|
|
3
|
+
This module provides the Binder interface for configuring dependency bindings
|
|
4
|
+
in a fluent, decoupled way. The Binder separates the configuration API from
|
|
5
|
+
the implementation, making Config more testable and enabling programmatic
|
|
6
|
+
configuration.
|
|
7
|
+
|
|
8
|
+
**What Problem Does This Solve?**
|
|
9
|
+
|
|
10
|
+
Binders solve the configuration API design problem:
|
|
11
|
+
|
|
12
|
+
- **Fluent API**: Method chaining for readable, expressive configuration
|
|
13
|
+
- **Separation of Concerns**: Configuration API separated from implementation
|
|
14
|
+
- **Testability**: Mock binders for testing configuration logic
|
|
15
|
+
- **Programmatic Configuration**: Build configurations dynamically
|
|
16
|
+
- **Better Readability**: ``binder.bind(X).to(Y).in_scope(Z)`` is clearer than nested calls
|
|
17
|
+
|
|
18
|
+
**Real-World Use Cases:**
|
|
19
|
+
|
|
20
|
+
- **Dynamic Configuration**: Build configurations based on environment variables
|
|
21
|
+
- **Configuration Testing**: Mock binders to test configuration logic
|
|
22
|
+
- **Modular Configuration**: Compose configurations from multiple sources
|
|
23
|
+
- **Framework Integration**: Provide fluent APIs for framework-specific configuration
|
|
24
|
+
|
|
25
|
+
Architecture:
|
|
26
|
+
- Binder: Protocol defining the binder interface
|
|
27
|
+
- BindingBuilder: Fluent builder for configuring bindings
|
|
28
|
+
- ConfigBinder: Concrete binder implementation used by Config
|
|
29
|
+
|
|
30
|
+
Usage Examples:
|
|
31
|
+
|
|
32
|
+
Basic Fluent Configuration:
|
|
33
|
+
>>> from pyiv.binder import Binder
|
|
34
|
+
>>> from pyiv.scope import SingletonScope
|
|
35
|
+
>>> from pyiv import Config, get_injector
|
|
36
|
+
>>>
|
|
37
|
+
>>> class Database:
|
|
38
|
+
... pass
|
|
39
|
+
>>>
|
|
40
|
+
>>> class PostgreSQL(Database):
|
|
41
|
+
... pass
|
|
42
|
+
>>>
|
|
43
|
+
>>> class Logger:
|
|
44
|
+
... pass
|
|
45
|
+
>>>
|
|
46
|
+
>>> class FileLogger(Logger):
|
|
47
|
+
... pass
|
|
48
|
+
>>>
|
|
49
|
+
>>> class MyConfig(Config):
|
|
50
|
+
... def configure(self):
|
|
51
|
+
... binder = self.get_binder()
|
|
52
|
+
... # Fluent API - easy to read and chain
|
|
53
|
+
... binder.bind(Database).to(PostgreSQL)
|
|
54
|
+
... binder.bind(Logger).to(FileLogger).in_scope(SingletonScope())
|
|
55
|
+
>>>
|
|
56
|
+
>>> injector = get_injector(MyConfig)
|
|
57
|
+
>>> db = injector.inject(Database)
|
|
58
|
+
>>> isinstance(db, PostgreSQL)
|
|
59
|
+
True
|
|
60
|
+
|
|
61
|
+
Binding to Instances:
|
|
62
|
+
>>> from pyiv import Config, get_injector
|
|
63
|
+
>>>
|
|
64
|
+
>>> class Cache:
|
|
65
|
+
... def __init__(self):
|
|
66
|
+
... self.data = {}
|
|
67
|
+
>>>
|
|
68
|
+
>>> cache = Cache()
|
|
69
|
+
>>> class InstanceConfig(Config):
|
|
70
|
+
... def configure(self):
|
|
71
|
+
... self.get_binder().bind_instance(Cache, cache)
|
|
72
|
+
>>>
|
|
73
|
+
>>> injector = get_injector(InstanceConfig)
|
|
74
|
+
>>> injector.inject(Cache) is cache
|
|
75
|
+
True
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
from typing import Any, Generic, Protocol, Type, TypeVar
|
|
79
|
+
|
|
80
|
+
from pyiv.key import Key
|
|
81
|
+
from pyiv.provider import Provider
|
|
82
|
+
from pyiv.scope import Scope
|
|
83
|
+
|
|
84
|
+
T = TypeVar("T")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class BindingBuilder(Protocol, Generic[T]):
|
|
88
|
+
"""Fluent builder for configuring bindings.
|
|
89
|
+
|
|
90
|
+
This builder provides a fluent API for configuring how a type should
|
|
91
|
+
be bound. It supports chaining methods to configure the binding.
|
|
92
|
+
|
|
93
|
+
Example:
|
|
94
|
+
>>> from pyiv import Config, get_injector
|
|
95
|
+
>>> from pyiv.scope import SingletonScope
|
|
96
|
+
>>> class Database:
|
|
97
|
+
... pass
|
|
98
|
+
>>> class PostgreSQL(Database):
|
|
99
|
+
... pass
|
|
100
|
+
>>> class MyConfig(Config):
|
|
101
|
+
... def configure(self):
|
|
102
|
+
... self.get_binder().bind(Database).to(PostgreSQL).in_scope(SingletonScope())
|
|
103
|
+
>>> isinstance(get_injector(MyConfig).inject(Database), PostgreSQL)
|
|
104
|
+
True
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def to(self, implementation: Type[T]) -> "BindingBuilder[T]":
|
|
108
|
+
"""Bind to a concrete implementation.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
implementation: The concrete class to bind to
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Self for method chaining
|
|
115
|
+
"""
|
|
116
|
+
...
|
|
117
|
+
|
|
118
|
+
def to_instance(self, instance: T) -> "BindingBuilder[T]":
|
|
119
|
+
"""Bind to a pre-created instance.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
instance: The instance to bind to
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Self for method chaining
|
|
126
|
+
"""
|
|
127
|
+
...
|
|
128
|
+
|
|
129
|
+
def to_provider(self, provider: Provider[T]) -> "BindingBuilder[T]":
|
|
130
|
+
"""Bind to a provider.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
provider: The provider to use for instance creation
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
Self for method chaining
|
|
137
|
+
"""
|
|
138
|
+
...
|
|
139
|
+
|
|
140
|
+
def in_scope(self, scope: Scope) -> "BindingBuilder[T]":
|
|
141
|
+
"""Set the scope for this binding.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
scope: The scope to use
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
Self for method chaining
|
|
148
|
+
"""
|
|
149
|
+
...
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class Binder(Protocol):
|
|
153
|
+
"""Protocol for binder implementations.
|
|
154
|
+
|
|
155
|
+
Binders provide a fluent API for configuring dependency bindings.
|
|
156
|
+
They separate the configuration API from the implementation, making
|
|
157
|
+
Config more testable and enabling programmatic configuration.
|
|
158
|
+
|
|
159
|
+
Example::
|
|
160
|
+
|
|
161
|
+
class MyBinder(Binder):
|
|
162
|
+
def bind(self, abstract):
|
|
163
|
+
return BindingBuilder(...)
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
def bind(self, abstract: Type[T]) -> BindingBuilder[T]:
|
|
167
|
+
"""Start a binding configuration.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
abstract: The abstract type to bind
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
A binding builder for fluent configuration
|
|
174
|
+
"""
|
|
175
|
+
...
|
|
176
|
+
|
|
177
|
+
def bind_key(self, key: Key[T]) -> BindingBuilder[T]:
|
|
178
|
+
"""Start a binding configuration with a qualified key.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
key: The qualified key to bind
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
A binding builder for fluent configuration
|
|
185
|
+
"""
|
|
186
|
+
...
|
|
187
|
+
|
|
188
|
+
def bind_instance(self, abstract: Type[T], instance: T) -> None:
|
|
189
|
+
"""Bind to a pre-created instance.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
abstract: The abstract type
|
|
193
|
+
instance: The pre-created instance
|
|
194
|
+
"""
|
|
195
|
+
...
|
|
196
|
+
|
|
197
|
+
def install(self, config: Any) -> None:
|
|
198
|
+
"""Install another configuration module.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
config: Another Config instance to install
|
|
202
|
+
"""
|
|
203
|
+
...
|
pyiv/binder_impl.py
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""Concrete Binder implementation for Config.
|
|
2
|
+
|
|
3
|
+
This module provides the ConfigBinder implementation that works with Config
|
|
4
|
+
to provide a fluent configuration API.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, Generic, List, Optional, Type, TypeVar
|
|
8
|
+
|
|
9
|
+
from pyiv.binder import Binder, BindingBuilder
|
|
10
|
+
from pyiv.config import Config
|
|
11
|
+
from pyiv.key import Key
|
|
12
|
+
from pyiv.provider import InjectorProvider, InstanceProvider, Provider
|
|
13
|
+
from pyiv.scope import NoScope, Scope
|
|
14
|
+
|
|
15
|
+
T = TypeVar("T")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ConfigBindingBuilder(BindingBuilder[T]):
|
|
19
|
+
"""Binding builder implementation for Config."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, config: Config, abstract: Type[T]):
|
|
22
|
+
"""Initialize binding builder.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
config: The config to register bindings with
|
|
26
|
+
abstract: The abstract type being bound
|
|
27
|
+
"""
|
|
28
|
+
self._config = config
|
|
29
|
+
self._abstract = abstract
|
|
30
|
+
self._implementation: Optional[Type[T]] = None
|
|
31
|
+
self._instance: Optional[T] = None
|
|
32
|
+
self._provider: Optional[Provider[T]] = None
|
|
33
|
+
self._scope: Optional[Scope] = None
|
|
34
|
+
|
|
35
|
+
def to(self, implementation: Type[T]) -> "ConfigBindingBuilder[T]":
|
|
36
|
+
"""Bind to a concrete implementation.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
implementation: The concrete class to bind to
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Self for method chaining
|
|
43
|
+
"""
|
|
44
|
+
self._implementation = implementation
|
|
45
|
+
self._finalize()
|
|
46
|
+
return self
|
|
47
|
+
|
|
48
|
+
def to_instance(self, instance: T) -> "ConfigBindingBuilder[T]":
|
|
49
|
+
"""Bind to a pre-created instance.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
instance: The instance to bind to
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Self for method chaining
|
|
56
|
+
"""
|
|
57
|
+
self._instance = instance
|
|
58
|
+
self._finalize()
|
|
59
|
+
return self
|
|
60
|
+
|
|
61
|
+
def to_provider(self, provider: Provider[T]) -> "ConfigBindingBuilder[T]":
|
|
62
|
+
"""Bind to a provider.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
provider: The provider to use for instance creation
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
Self for method chaining
|
|
69
|
+
"""
|
|
70
|
+
self._provider = provider
|
|
71
|
+
self._finalize()
|
|
72
|
+
return self
|
|
73
|
+
|
|
74
|
+
def in_scope(self, scope: Scope) -> "ConfigBindingBuilder[T]":
|
|
75
|
+
"""Set the scope for this binding.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
scope: The scope to use
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Self for method chaining
|
|
82
|
+
"""
|
|
83
|
+
self._scope = scope
|
|
84
|
+
# Update scope if already finalized
|
|
85
|
+
if hasattr(self, "_finalized") and getattr(self, "_finalized", False):
|
|
86
|
+
self._config._scopes[self._abstract] = scope
|
|
87
|
+
return self
|
|
88
|
+
|
|
89
|
+
def _finalize(self) -> None:
|
|
90
|
+
"""Finalize the binding registration."""
|
|
91
|
+
if hasattr(self, "_finalized") and getattr(self, "_finalized", False):
|
|
92
|
+
return
|
|
93
|
+
|
|
94
|
+
if self._instance is not None:
|
|
95
|
+
self._config.register_instance(self._abstract, self._instance)
|
|
96
|
+
if self._scope is not None:
|
|
97
|
+
self._config._scopes[self._abstract] = self._scope
|
|
98
|
+
elif self._provider is not None:
|
|
99
|
+
self._config.register_provider(self._abstract, self._provider)
|
|
100
|
+
if self._scope is not None:
|
|
101
|
+
self._config._scopes[self._abstract] = self._scope
|
|
102
|
+
elif self._implementation is not None:
|
|
103
|
+
self._config.register(
|
|
104
|
+
self._abstract,
|
|
105
|
+
self._implementation,
|
|
106
|
+
scope=self._scope if self._scope is not None else NoScope(),
|
|
107
|
+
)
|
|
108
|
+
else:
|
|
109
|
+
# Don't raise error - allow chaining
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
self._finalized = True
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class ConfigBinder(Binder):
|
|
116
|
+
"""Concrete Binder implementation for Config."""
|
|
117
|
+
|
|
118
|
+
def __init__(self, config: Config):
|
|
119
|
+
"""Initialize binder.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
config: The config to register bindings with
|
|
123
|
+
"""
|
|
124
|
+
self._config = config
|
|
125
|
+
self._builders: List[ConfigBindingBuilder[Any]] = []
|
|
126
|
+
|
|
127
|
+
def bind(self, abstract: Type[T]) -> BindingBuilder[T]:
|
|
128
|
+
"""Start a binding configuration.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
abstract: The abstract type to bind
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
A binding builder for fluent configuration
|
|
135
|
+
"""
|
|
136
|
+
builder = ConfigBindingBuilder(self._config, abstract)
|
|
137
|
+
self._builders.append(builder)
|
|
138
|
+
return builder
|
|
139
|
+
|
|
140
|
+
def bind_key(self, key: Key[T]) -> BindingBuilder[T]:
|
|
141
|
+
"""Start a binding configuration with a qualified key.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
key: The qualified key to bind
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
A binding builder for fluent configuration
|
|
148
|
+
"""
|
|
149
|
+
# For qualified keys, we need a special builder
|
|
150
|
+
builder: ConfigBindingBuilder[Any] = ConfigBindingBuilder(self._config, key.type) # type: ignore[arg-type]
|
|
151
|
+
builder._key = key # type: ignore[attr-defined] # Store the key
|
|
152
|
+
self._builders.append(builder)
|
|
153
|
+
return builder
|
|
154
|
+
|
|
155
|
+
def bind_instance(self, abstract: Type[T], instance: T) -> None:
|
|
156
|
+
"""Bind to a pre-created instance.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
abstract: The abstract type
|
|
160
|
+
instance: The pre-created instance
|
|
161
|
+
"""
|
|
162
|
+
self._config.register_instance(abstract, instance)
|
|
163
|
+
|
|
164
|
+
def install(self, config: Any) -> None:
|
|
165
|
+
"""Install another configuration module.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
config: Another Config instance to install
|
|
169
|
+
"""
|
|
170
|
+
# This would require merging configs, which is complex
|
|
171
|
+
# For now, we'll raise NotImplementedError
|
|
172
|
+
raise NotImplementedError("Config installation not yet implemented")
|
|
173
|
+
|
|
174
|
+
def finalize(self) -> None:
|
|
175
|
+
"""Finalize all pending bindings."""
|
|
176
|
+
for builder in self._builders:
|
|
177
|
+
builder._finalize()
|
|
178
|
+
self._builders.clear()
|
pyiv/chain.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Chain of Responsibility pattern for pyiv.
|
|
2
|
+
|
|
3
|
+
This module provides a general chain of responsibility system that can be used
|
|
4
|
+
for various purposes: encoding, hashing, sorting, etc. Each chain type has its
|
|
5
|
+
own interface and implementations.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from enum import Enum
|
|
10
|
+
from typing import Any, TypeVar
|
|
11
|
+
|
|
12
|
+
T = TypeVar("T")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ChainType(Enum):
|
|
16
|
+
"""Types of chain of responsibility handlers.
|
|
17
|
+
|
|
18
|
+
Each chain type represents a different category of handlers:
|
|
19
|
+
- ENCODING: Serialization/deserialization (SerDe)
|
|
20
|
+
- HASHING: Hash function implementations
|
|
21
|
+
- SORTING: Sorting algorithm implementations
|
|
22
|
+
- NETWORK_CLIENT: Network protocol clients (HTTP, HTTPS, etc.)
|
|
23
|
+
- etc.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
ENCODING = "encoding"
|
|
27
|
+
HASHING = "hashing"
|
|
28
|
+
SORTING = "sorting"
|
|
29
|
+
NETWORK_CLIENT = "network_client"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ChainHandler(ABC):
|
|
33
|
+
"""Abstract base class for chain of responsibility handlers.
|
|
34
|
+
|
|
35
|
+
Chain handlers process requests in a chain. Each handler can either:
|
|
36
|
+
- Handle the request and return a result
|
|
37
|
+
- Pass the request to the next handler in the chain
|
|
38
|
+
- Reject the request
|
|
39
|
+
|
|
40
|
+
Subclasses must implement:
|
|
41
|
+
- chain_type: The type of chain this handler belongs to
|
|
42
|
+
- handler_type: The specific handler type identifier (e.g., "json", "md5", "quicksort")
|
|
43
|
+
- handle(): Process the request
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
@abstractmethod
|
|
48
|
+
def chain_type(self) -> ChainType:
|
|
49
|
+
"""Return the chain type this handler belongs to.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
The ChainType enum value
|
|
53
|
+
"""
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
@abstractmethod
|
|
58
|
+
def handler_type(self) -> str:
|
|
59
|
+
"""Return the handler type identifier.
|
|
60
|
+
|
|
61
|
+
This identifies the specific implementation (e.g., "json", "md5", "quicksort").
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
A string identifying the handler type
|
|
65
|
+
"""
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
@abstractmethod
|
|
69
|
+
def handle(self, request: Any, **kwargs) -> Any:
|
|
70
|
+
"""Handle a request.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
request: The request to handle
|
|
74
|
+
**kwargs: Additional keyword arguments
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
The result of handling the request
|
|
78
|
+
"""
|
|
79
|
+
pass
|