qx-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.
qx_di-0.2.0/.gitignore ADDED
@@ -0,0 +1,56 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.pyo
5
+ *.pyd
6
+ .Python
7
+ *.so
8
+ *.egg
9
+ *.egg-info/
10
+ dist/
11
+ build/
12
+ eggs/
13
+ .eggs/
14
+ sdist/
15
+ wheels/
16
+ *.egg-link
17
+
18
+ # Virtual environments
19
+ .venv/
20
+ venv/
21
+ env/
22
+ ENV/
23
+
24
+ # uv
25
+ .uv/
26
+
27
+ # Testing
28
+ .pytest_cache/
29
+ .coverage
30
+ htmlcov/
31
+ .tox/
32
+
33
+ # Type checking
34
+ .mypy_cache/
35
+ .ruff_cache/
36
+
37
+ # IDE
38
+ .idea/
39
+ .vscode/
40
+ *.swp
41
+ *.swo
42
+
43
+ # OS
44
+ .DS_Store
45
+ Thumbs.db
46
+
47
+ # Docker
48
+ *.env.local
49
+
50
+ # Dist artifacts
51
+ dist/
52
+
53
+ # VS Code extension build artifacts
54
+ extensions/vscode/node_modules/
55
+ extensions/vscode/dist/
56
+ extensions/vscode/*.vsix
qx_di-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,52 @@
1
+ Metadata-Version: 2.4
2
+ Name: qx-di
3
+ Version: 0.2.0
4
+ Summary: Qx dependency-injection container: singleton/scoped/transient, async lifecycle, generic-aware resolution
5
+ Author: Qx Engineering
6
+ License: MIT
7
+ Requires-Python: >=3.14
8
+ Requires-Dist: qx-core
9
+ Requires-Dist: typing-extensions>=4.12.0
10
+ Description-Content-Type: text/markdown
11
+
12
+ # qx-di
13
+
14
+ Async dependency-injection container for the Qx framework. Supports `SINGLETON`, `SCOPED`, and `TRANSIENT` lifetimes with full async lifecycle, generic-aware resolution, and decorator-based registration.
15
+
16
+ ## What lives here
17
+
18
+ - **`qx.di.Container`** — resolves, caches, and scopes dependencies. Supports `register_singleton`, `register_scoped`, `register_transient`, `register_instance`, and `override`.
19
+ - **`qx.di.Scope`** — per-request child scope. Scoped registrations live exactly as long as their scope.
20
+ - **`qx.di.Lifetime`** — `SINGLETON` / `SCOPED` / `TRANSIENT` enum.
21
+ - **`qx.di.singleton` / `scoped` / `transient`** — class decorators that embed registration metadata without touching the class interface.
22
+ - **`qx.di.scan`** — discover and register all decorated classes in a package tree.
23
+ - **`qx.di.injectable`** — low-level decorator for explicit key/lifetime/factory overrides.
24
+
25
+ ## Usage
26
+
27
+ ```python
28
+ from qx.di import Container, Scope, scoped, singleton, scan
29
+
30
+ @singleton()
31
+ class Database:
32
+ def __init__(self, url: str) -> None: ...
33
+
34
+ @scoped(key=UserRepository)
35
+ class PgUserRepository(UserRepository):
36
+ def __init__(self, db: Database) -> None: ...
37
+
38
+ container = Container()
39
+ scan(container, "myapp.infrastructure")
40
+ container.register_instance(str, "postgresql+asyncpg://...")
41
+
42
+ async with container.scope() as scope:
43
+ repo = await container.resolve(UserRepository, scope=scope)
44
+ ```
45
+
46
+ ## Design rules
47
+
48
+ - **Annotation-driven** — `_call_factory` inspects `get_type_hints()` to resolve constructor parameters. No manual wiring for the common case.
49
+ - **Scope propagation** — pass the active `Scope` into `mediator.send()` and it threads through all transient factory resolutions so that `SCOPED` dependencies (e.g. `UnitOfWork`) are reachable from handlers.
50
+ - **Cycle detection** — circular dependencies raise `ResolutionError` at first resolution, not at registration time.
51
+ - **Override** — `container.override(key, instance)` replaces any registration; used in tests to swap real infrastructure for fakes.
52
+ - No reflection at module-import time. `scan()` defers all class inspection to the first `resolve()` call.
qx_di-0.2.0/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # qx-di
2
+
3
+ Async dependency-injection container for the Qx framework. Supports `SINGLETON`, `SCOPED`, and `TRANSIENT` lifetimes with full async lifecycle, generic-aware resolution, and decorator-based registration.
4
+
5
+ ## What lives here
6
+
7
+ - **`qx.di.Container`** — resolves, caches, and scopes dependencies. Supports `register_singleton`, `register_scoped`, `register_transient`, `register_instance`, and `override`.
8
+ - **`qx.di.Scope`** — per-request child scope. Scoped registrations live exactly as long as their scope.
9
+ - **`qx.di.Lifetime`** — `SINGLETON` / `SCOPED` / `TRANSIENT` enum.
10
+ - **`qx.di.singleton` / `scoped` / `transient`** — class decorators that embed registration metadata without touching the class interface.
11
+ - **`qx.di.scan`** — discover and register all decorated classes in a package tree.
12
+ - **`qx.di.injectable`** — low-level decorator for explicit key/lifetime/factory overrides.
13
+
14
+ ## Usage
15
+
16
+ ```python
17
+ from qx.di import Container, Scope, scoped, singleton, scan
18
+
19
+ @singleton()
20
+ class Database:
21
+ def __init__(self, url: str) -> None: ...
22
+
23
+ @scoped(key=UserRepository)
24
+ class PgUserRepository(UserRepository):
25
+ def __init__(self, db: Database) -> None: ...
26
+
27
+ container = Container()
28
+ scan(container, "myapp.infrastructure")
29
+ container.register_instance(str, "postgresql+asyncpg://...")
30
+
31
+ async with container.scope() as scope:
32
+ repo = await container.resolve(UserRepository, scope=scope)
33
+ ```
34
+
35
+ ## Design rules
36
+
37
+ - **Annotation-driven** — `_call_factory` inspects `get_type_hints()` to resolve constructor parameters. No manual wiring for the common case.
38
+ - **Scope propagation** — pass the active `Scope` into `mediator.send()` and it threads through all transient factory resolutions so that `SCOPED` dependencies (e.g. `UnitOfWork`) are reachable from handlers.
39
+ - **Cycle detection** — circular dependencies raise `ResolutionError` at first resolution, not at registration time.
40
+ - **Override** — `container.override(key, instance)` replaces any registration; used in tests to swap real infrastructure for fakes.
41
+ - No reflection at module-import time. `scan()` defers all class inspection to the first `resolve()` call.
@@ -0,0 +1,19 @@
1
+ [project]
2
+ name = "qx-di"
3
+ version = "0.2.0"
4
+ description = "Qx dependency-injection container: singleton/scoped/transient, async lifecycle, generic-aware resolution"
5
+ readme = "README.md"
6
+ requires-python = ">=3.14"
7
+ license = { text = "MIT" }
8
+ authors = [{ name = "Qx Engineering" }]
9
+ dependencies = [
10
+ "qx-core",
11
+ "typing-extensions>=4.12.0",
12
+ ]
13
+
14
+ [build-system]
15
+ requires = ["hatchling"]
16
+ build-backend = "hatchling.build"
17
+
18
+ [tool.hatch.build.targets.wheel]
19
+ packages = ["src/qx"]
@@ -0,0 +1,68 @@
1
+ """Qx dependency-injection container.
2
+
3
+ Public surface — import from ``qx.di``; internal layout may shift.
4
+
5
+ Quick start::
6
+
7
+ from qx.di import Container, singleton, scan
8
+
9
+ @singleton()
10
+ class Clock:
11
+ def now(self) -> datetime: ...
12
+
13
+ @scoped(key=UserRepository)
14
+ class PgUserRepository(UserRepository):
15
+ def __init__(self, db: Database): ...
16
+
17
+ container = Container()
18
+ scan(container, "myapp.infrastructure")
19
+ container.register_instance(Database, Database(url=...))
20
+
21
+ async with container.scope() as s:
22
+ repo = await container.resolve(UserRepository, scope=s)
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ from qx.di.container import (
28
+ Container,
29
+ RegistrationError,
30
+ ResolutionError,
31
+ )
32
+ from qx.di.decorators import (
33
+ Injectable,
34
+ get_metadata,
35
+ injectable,
36
+ scan,
37
+ scoped,
38
+ singleton,
39
+ transient,
40
+ )
41
+ from qx.di.providers import (
42
+ FactoryProvider,
43
+ InstanceProvider,
44
+ Lifetime,
45
+ Provider,
46
+ )
47
+ from qx.di.scope import Scope
48
+
49
+ __version__ = "0.2.0"
50
+
51
+ __all__ = [
52
+ "Container",
53
+ "FactoryProvider",
54
+ "Injectable",
55
+ "InstanceProvider",
56
+ "Lifetime",
57
+ "Provider",
58
+ "RegistrationError",
59
+ "ResolutionError",
60
+ "Scope",
61
+ "__version__",
62
+ "get_metadata",
63
+ "injectable",
64
+ "scan",
65
+ "scoped",
66
+ "singleton",
67
+ "transient",
68
+ ]