svcs-di 0.2.0__tar.gz
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.
- svcs_di-0.2.0/PKG-INFO +13 -0
- svcs_di-0.2.0/pyproject.toml +57 -0
- svcs_di-0.2.0/src/svcs_di/__init__.py +44 -0
- svcs_di-0.2.0/src/svcs_di/_container_mixin.py +77 -0
- svcs_di-0.2.0/src/svcs_di/auto.py +682 -0
- svcs_di-0.2.0/src/svcs_di/hopscotch_registry.py +353 -0
- svcs_di-0.2.0/src/svcs_di/injector_container.py +129 -0
- svcs_di-0.2.0/src/svcs_di/injectors/__init__.py +49 -0
- svcs_di-0.2.0/src/svcs_di/injectors/_helpers.py +110 -0
- svcs_di-0.2.0/src/svcs_di/injectors/decorators.py +132 -0
- svcs_di-0.2.0/src/svcs_di/injectors/hopscotch.py +369 -0
- svcs_di-0.2.0/src/svcs_di/injectors/keyword.py +221 -0
- svcs_di-0.2.0/src/svcs_di/injectors/keyword.pyi +36 -0
- svcs_di-0.2.0/src/svcs_di/injectors/locator.py +570 -0
- svcs_di-0.2.0/src/svcs_di/injectors/scanning.py +398 -0
- svcs_di-0.2.0/src/svcs_di/py.typed +0 -0
svcs_di-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: svcs-di
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Dependency injection using svcs
|
|
5
|
+
Classifier: Development Status :: 3 - Alpha
|
|
6
|
+
Classifier: Intended Audience :: Developers
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
9
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
10
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
11
|
+
Classifier: Typing :: Typed
|
|
12
|
+
Requires-Dist: svcs
|
|
13
|
+
Requires-Python: >=3.14
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "svcs-di"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "Dependency injection using svcs"
|
|
5
|
+
requires-python = ">=3.14"
|
|
6
|
+
dependencies = [
|
|
7
|
+
"svcs"
|
|
8
|
+
]
|
|
9
|
+
classifiers = [
|
|
10
|
+
"Development Status :: 3 - Alpha",
|
|
11
|
+
"Intended Audience :: Developers",
|
|
12
|
+
"Programming Language :: Python :: 3",
|
|
13
|
+
"Programming Language :: Python :: 3.14",
|
|
14
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
15
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
16
|
+
"Typing :: Typed",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[dependency-groups]
|
|
20
|
+
dev = [
|
|
21
|
+
"coverage>=7.13.0",
|
|
22
|
+
"furo>=2025.12.19",
|
|
23
|
+
"linkify-it-py>=2.0.3",
|
|
24
|
+
"myst-parser",
|
|
25
|
+
"pyrefly>=0.46.1",
|
|
26
|
+
"pyright>=1.1.407",
|
|
27
|
+
"pytest>=8.4.2",
|
|
28
|
+
"pytest-cov>=4.1.0",
|
|
29
|
+
"pytest-run-parallel>=0.8.1",
|
|
30
|
+
"pytest-timeout>=2.4.0",
|
|
31
|
+
"pytest-xdist>=3.8.0",
|
|
32
|
+
"ruff>=0.14.10",
|
|
33
|
+
"sphinx>=8",
|
|
34
|
+
"sphinx-autobuild>=2025.8.25",
|
|
35
|
+
"sybil[pytest]>=8.4.2",
|
|
36
|
+
"ty>=0.0.6",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[tool.pytest.ini_options]
|
|
40
|
+
testpaths = ["tests", "src"]
|
|
41
|
+
python_files = ["test_*.py"]
|
|
42
|
+
addopts = "-p no:doctest -m \"not slow\""
|
|
43
|
+
pythonpath = ["examples"]
|
|
44
|
+
markers = [
|
|
45
|
+
"slow: marks tests as slow (deselect with '-m \"not slow\"')",
|
|
46
|
+
"freethreaded: marks tests that verify free-threading compatibility",
|
|
47
|
+
]
|
|
48
|
+
# Free-threading safety: timeout tests to detect hangs/deadlocks
|
|
49
|
+
timeout = 60
|
|
50
|
+
faulthandler_timeout = 120
|
|
51
|
+
|
|
52
|
+
[tool.ty.environment]
|
|
53
|
+
extra-paths = ["examples/scanning"]
|
|
54
|
+
|
|
55
|
+
[build-system]
|
|
56
|
+
requires = ["uv_build>=0.8.17,<0.9.0"]
|
|
57
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""
|
|
2
|
+
svcs-di: Minimal automatic dependency injection helper for svcs.
|
|
3
|
+
|
|
4
|
+
This package provides a thin layer on top of svcs for automatic dependency
|
|
5
|
+
resolution based on type hints, with explicit opt-in via the Inject[T] marker.
|
|
6
|
+
|
|
7
|
+
Key exports:
|
|
8
|
+
- auto: Factory function generator for automatic dependency injection
|
|
9
|
+
- auto_async: Async factory function generator for async dependencies
|
|
10
|
+
- Inject: Type marker for parameters that should be injected from container
|
|
11
|
+
- Resource: Type marker for resource injection from HopscotchContainer
|
|
12
|
+
- Injector: Protocol defining the injector interface
|
|
13
|
+
- AsyncInjector: Protocol defining the async injector interface
|
|
14
|
+
- DefaultInjector: Default synchronous injector implementation
|
|
15
|
+
- DefaultAsyncInjector: Default asynchronous injector implementation
|
|
16
|
+
- InjectionTarget: Type alias for class or callable targets (type[T] | Callable[..., T])
|
|
17
|
+
- AsyncInjectionTarget: Type alias for async targets (type[T] | Callable[..., Awaitable[T]])
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from svcs_di.auto import (
|
|
21
|
+
AsyncInjectionTarget,
|
|
22
|
+
AsyncInjector,
|
|
23
|
+
DefaultAsyncInjector,
|
|
24
|
+
DefaultInjector,
|
|
25
|
+
Inject,
|
|
26
|
+
InjectionTarget,
|
|
27
|
+
Injector,
|
|
28
|
+
Resource,
|
|
29
|
+
auto,
|
|
30
|
+
auto_async,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"auto",
|
|
35
|
+
"auto_async",
|
|
36
|
+
"Inject",
|
|
37
|
+
"Resource",
|
|
38
|
+
"Injector",
|
|
39
|
+
"AsyncInjector",
|
|
40
|
+
"DefaultInjector",
|
|
41
|
+
"DefaultAsyncInjector",
|
|
42
|
+
"InjectionTarget",
|
|
43
|
+
"AsyncInjectionTarget",
|
|
44
|
+
]
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Mixin providing shared inject/ainject methods for container classes.
|
|
3
|
+
|
|
4
|
+
This module contains InjectorMixin which provides the core implementation
|
|
5
|
+
for inject() and ainject() methods used by InjectorContainer and HopscotchContainer.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from svcs_di.auto import AsyncInjector, Injector
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class InjectorMixin:
|
|
14
|
+
"""
|
|
15
|
+
Mixin providing inject/ainject methods for containers.
|
|
16
|
+
|
|
17
|
+
This mixin provides _do_inject and _do_ainject methods that implement
|
|
18
|
+
the core injection logic. Subclasses should define their own inject()
|
|
19
|
+
and ainject() methods that delegate to these with appropriate injector_kwargs.
|
|
20
|
+
|
|
21
|
+
Attributes:
|
|
22
|
+
injector: The synchronous injector class to use.
|
|
23
|
+
async_injector: The asynchronous injector class to use.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
injector: type[Injector] | None
|
|
27
|
+
async_injector: type[AsyncInjector] | None
|
|
28
|
+
|
|
29
|
+
def _do_inject[T](
|
|
30
|
+
self,
|
|
31
|
+
svc_type: type[T],
|
|
32
|
+
injector_kwargs: dict[str, Any],
|
|
33
|
+
**kwargs: Any,
|
|
34
|
+
) -> T:
|
|
35
|
+
"""
|
|
36
|
+
Core sync injection implementation.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
svc_type: The service type to resolve.
|
|
40
|
+
injector_kwargs: Kwargs to pass to injector constructor (e.g., resource).
|
|
41
|
+
**kwargs: Kwargs to pass through to the target callable.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
The resolved service instance.
|
|
45
|
+
|
|
46
|
+
Raises:
|
|
47
|
+
ValueError: If no injector is configured.
|
|
48
|
+
"""
|
|
49
|
+
if self.injector is None:
|
|
50
|
+
raise ValueError("Cannot inject without an injector configured")
|
|
51
|
+
return self.injector(container=self, **injector_kwargs)(svc_type, **kwargs)
|
|
52
|
+
|
|
53
|
+
async def _do_ainject[T](
|
|
54
|
+
self,
|
|
55
|
+
svc_type: type[T],
|
|
56
|
+
injector_kwargs: dict[str, Any],
|
|
57
|
+
**kwargs: Any,
|
|
58
|
+
) -> T:
|
|
59
|
+
"""
|
|
60
|
+
Core async injection implementation.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
svc_type: The service type to resolve.
|
|
64
|
+
injector_kwargs: Kwargs to pass to injector constructor (e.g., resource).
|
|
65
|
+
**kwargs: Kwargs to pass through to the target callable.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
The resolved service instance.
|
|
69
|
+
|
|
70
|
+
Raises:
|
|
71
|
+
ValueError: If no async injector is configured.
|
|
72
|
+
"""
|
|
73
|
+
if self.async_injector is None:
|
|
74
|
+
raise ValueError("Cannot inject without an async injector configured")
|
|
75
|
+
return await self.async_injector(container=self, **injector_kwargs)(
|
|
76
|
+
svc_type, **kwargs
|
|
77
|
+
)
|