pico-ioc 1.3.0__tar.gz → 2.1.2__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.
Files changed (139) hide show
  1. {pico_ioc-1.3.0 → pico_ioc-2.1.2}/.github/workflows/ci.yml +1 -1
  2. pico_ioc-2.1.2/.github/workflows/docs.yml +104 -0
  3. pico_ioc-2.1.2/CHANGELOG.md +222 -0
  4. pico_ioc-2.1.2/PKG-INFO +352 -0
  5. pico_ioc-2.1.2/README.md +302 -0
  6. pico_ioc-2.1.2/docs/LEARN.md +103 -0
  7. pico_ioc-2.1.2/docs/README.md +154 -0
  8. pico_ioc-2.1.2/docs/adr/README.md +40 -0
  9. pico_ioc-2.1.2/docs/adr/adr-0001-async-native.md +91 -0
  10. pico_ioc-2.1.2/docs/adr/adr-0002-tree-based-configuration.md +62 -0
  11. pico_ioc-2.1.2/docs/adr/adr-0003-context-aware-scopes.md +96 -0
  12. pico_ioc-2.1.2/docs/adr/adr-0004-observability.md +35 -0
  13. pico_ioc-2.1.2/docs/adr/adr-0005-aop.md +44 -0
  14. pico_ioc-2.1.2/docs/adr/adr-0006-eager-validation.md +106 -0
  15. pico_ioc-2.1.2/docs/adr/adr-0007-event_bus.md +39 -0
  16. pico_ioc-2.1.2/docs/adr/adr-0008-circular-dependencies.md +53 -0
  17. pico_ioc-2.1.2/docs/adr/adr-0009-flexible-provides.md +163 -0
  18. pico_ioc-2.1.2/docs/adr/adr-0010-unified-configuration.md +180 -0
  19. pico_ioc-2.1.2/docs/advanced-features/README.md +16 -0
  20. pico_ioc-2.1.2/docs/advanced-features/aop-interceptors.md +366 -0
  21. pico_ioc-2.1.2/docs/advanced-features/async-resolution.md +230 -0
  22. pico_ioc-2.1.2/docs/advanced-features/conditional-binding.md +230 -0
  23. pico_ioc-2.1.2/docs/advanced-features/event-bus.md +302 -0
  24. pico_ioc-2.1.2/docs/advanced-features/health-checks.md +172 -0
  25. pico_ioc-2.1.2/docs/api-reference/README.md +13 -0
  26. pico_ioc-2.1.2/docs/api-reference/container.md +214 -0
  27. pico_ioc-2.1.2/docs/api-reference/decorators.md +141 -0
  28. pico_ioc-2.1.2/docs/api-reference/event_bus.md +140 -0
  29. pico_ioc-2.1.2/docs/api-reference/glossary.md +126 -0
  30. pico_ioc-2.1.2/docs/api-reference/protocols.md +154 -0
  31. pico_ioc-2.1.2/docs/architecture/README.md +64 -0
  32. pico_ioc-2.1.2/docs/architecture/comparison.md +101 -0
  33. pico_ioc-2.1.2/docs/architecture/design-principles.md +97 -0
  34. pico_ioc-2.1.2/docs/architecture/internals.md +204 -0
  35. pico_ioc-2.1.2/docs/cookbook/README.md +69 -0
  36. pico_ioc-2.1.2/docs/cookbook/pattern-aop-feature-toggle.md +261 -0
  37. pico_ioc-2.1.2/docs/cookbook/pattern-aop-profiling.md +168 -0
  38. pico_ioc-2.1.2/docs/cookbook/pattern-aop-security.md +318 -0
  39. pico_ioc-2.1.2/docs/cookbook/pattern-aop-structured-logging.md +262 -0
  40. pico_ioc-2.1.2/docs/cookbook/pattern-cli-app.md +206 -0
  41. pico_ioc-2.1.2/docs/cookbook/pattern-config-overrides.md +149 -0
  42. pico_ioc-2.1.2/docs/cookbook/pattern-cqrs.md +245 -0
  43. pico_ioc-2.1.2/docs/cookbook/pattern-hot-reload.md +285 -0
  44. pico_ioc-2.1.2/docs/cookbook/pattern-multi-tenant.md +241 -0
  45. pico_ioc-2.1.2/docs/getting-started.md +205 -0
  46. pico_ioc-2.1.2/docs/javascripts/extra.js +10 -0
  47. pico_ioc-2.1.2/docs/observability/README.md +61 -0
  48. pico_ioc-2.1.2/docs/observability/container-context.md +208 -0
  49. pico_ioc-2.1.2/docs/observability/exporting-graph.md +129 -0
  50. pico_ioc-2.1.2/docs/observability/observers-metrics.md +185 -0
  51. pico_ioc-2.1.2/docs/overview.md +191 -0
  52. pico_ioc-2.1.2/docs/requirements.txt +6 -0
  53. pico_ioc-2.1.2/docs/stylesheets/extra.css +53 -0
  54. pico_ioc-2.1.2/docs/user-guide/README.md +14 -0
  55. pico_ioc-2.1.2/docs/user-guide/configuration-basic.md +71 -0
  56. pico_ioc-2.1.2/docs/user-guide/configuration-binding.md +307 -0
  57. pico_ioc-2.1.2/docs/user-guide/core-concepts.md +344 -0
  58. pico_ioc-2.1.2/docs/user-guide/qualifiers-lists.md +216 -0
  59. pico_ioc-2.1.2/docs/user-guide/scopes-lifecycle.md +274 -0
  60. pico_ioc-2.1.2/docs/user-guide/testing.md +278 -0
  61. pico_ioc-2.1.2/mkdocs.yml +189 -0
  62. {pico_ioc-1.3.0 → pico_ioc-2.1.2}/pyproject.toml +8 -2
  63. pico_ioc-2.1.2/src/pico_ioc/__init__.py +94 -0
  64. pico_ioc-2.1.2/src/pico_ioc/_version.py +1 -0
  65. pico_ioc-2.1.2/src/pico_ioc/analysis.py +135 -0
  66. pico_ioc-2.1.2/src/pico_ioc/aop.py +289 -0
  67. pico_ioc-2.1.2/src/pico_ioc/api.py +171 -0
  68. pico_ioc-2.1.2/src/pico_ioc/component_scanner.py +166 -0
  69. pico_ioc-2.1.2/src/pico_ioc/config_builder.py +87 -0
  70. pico_ioc-2.1.2/src/pico_ioc/config_registrar.py +236 -0
  71. pico_ioc-2.1.2/src/pico_ioc/config_runtime.py +330 -0
  72. pico_ioc-2.1.2/src/pico_ioc/constants.py +10 -0
  73. pico_ioc-2.1.2/src/pico_ioc/container.py +539 -0
  74. pico_ioc-2.1.2/src/pico_ioc/decorators.py +193 -0
  75. pico_ioc-2.1.2/src/pico_ioc/dependency_validator.py +103 -0
  76. pico_ioc-2.1.2/src/pico_ioc/event_bus.py +222 -0
  77. pico_ioc-2.1.2/src/pico_ioc/exceptions.py +71 -0
  78. pico_ioc-2.1.2/src/pico_ioc/factory.py +49 -0
  79. pico_ioc-2.1.2/src/pico_ioc/locator.py +135 -0
  80. pico_ioc-2.1.2/src/pico_ioc/provider_selector.py +35 -0
  81. pico_ioc-2.1.2/src/pico_ioc/registrar.py +188 -0
  82. pico_ioc-2.1.2/src/pico_ioc/scope.py +166 -0
  83. pico_ioc-2.1.2/src/pico_ioc.egg-info/PKG-INFO +352 -0
  84. pico_ioc-2.1.2/src/pico_ioc.egg-info/SOURCES.txt +106 -0
  85. pico_ioc-2.1.2/src/pico_ioc.egg-info/requires.txt +6 -0
  86. pico_ioc-2.1.2/tests/test_collection_injection.py +151 -0
  87. pico_ioc-2.1.2/tests/test_config_value.py +160 -0
  88. pico_ioc-2.1.2/tests/test_configured.py +404 -0
  89. pico_ioc-2.1.2/tests/test_container_context.py +51 -0
  90. pico_ioc-2.1.2/tests/test_container_runtime.py +110 -0
  91. pico_ioc-2.1.2/tests/test_container_self_injection.py +27 -0
  92. pico_ioc-2.1.2/tests/test_event_bus.py +107 -0
  93. pico_ioc-2.1.2/tests/test_pico_extends.py +465 -0
  94. pico_ioc-2.1.2/tests/test_pico_integration.py +352 -0
  95. pico_ioc-2.1.2/tests/test_protocol_resolution_and_graph.py +53 -0
  96. pico_ioc-2.1.2/tests/test_provides_module_functions.py +96 -0
  97. pico_ioc-2.1.2/tests/test_provides_static_methods.py +39 -0
  98. pico_ioc-2.1.2/tests/test_proxy_unit.py +288 -0
  99. pico_ioc-2.1.2/tests/test_resolution_graph.py +63 -0
  100. pico_ioc-2.1.2/tests/test_scope.py +310 -0
  101. {pico_ioc-1.3.0 → pico_ioc-2.1.2}/tox.ini +9 -2
  102. pico_ioc-1.3.0/.llm/ARCHITECTURE.md +0 -387
  103. pico_ioc-1.3.0/.llm/DECISIONS.md +0 -174
  104. pico_ioc-1.3.0/.llm/GUIDE.md +0 -466
  105. pico_ioc-1.3.0/.llm/GUIDE_CQRS.md +0 -110
  106. pico_ioc-1.3.0/.llm/GUIDE_CREATING_PLUGINS_AND_INTERCEPTORS.md +0 -182
  107. pico_ioc-1.3.0/.llm/OVERVIEW.md +0 -143
  108. pico_ioc-1.3.0/CHANGELOG.md +0 -95
  109. pico_ioc-1.3.0/PKG-INFO +0 -235
  110. pico_ioc-1.3.0/README.md +0 -190
  111. pico_ioc-1.3.0/src/pico_ioc/__init__.py +0 -45
  112. pico_ioc-1.3.0/src/pico_ioc/_state.py +0 -40
  113. pico_ioc-1.3.0/src/pico_ioc/_version.py +0 -1
  114. pico_ioc-1.3.0/src/pico_ioc/api.py +0 -240
  115. pico_ioc-1.3.0/src/pico_ioc/builder.py +0 -242
  116. pico_ioc-1.3.0/src/pico_ioc/container.py +0 -186
  117. pico_ioc-1.3.0/src/pico_ioc/decorators.py +0 -134
  118. pico_ioc-1.3.0/src/pico_ioc/interceptors.py +0 -50
  119. pico_ioc-1.3.0/src/pico_ioc/plugins.py +0 -28
  120. pico_ioc-1.3.0/src/pico_ioc/policy.py +0 -332
  121. pico_ioc-1.3.0/src/pico_ioc/proxy.py +0 -117
  122. pico_ioc-1.3.0/src/pico_ioc/public_api.py +0 -76
  123. pico_ioc-1.3.0/src/pico_ioc/resolver.py +0 -129
  124. pico_ioc-1.3.0/src/pico_ioc/scanner.py +0 -203
  125. pico_ioc-1.3.0/src/pico_ioc/utils.py +0 -25
  126. pico_ioc-1.3.0/src/pico_ioc.egg-info/PKG-INFO +0 -235
  127. pico_ioc-1.3.0/src/pico_ioc.egg-info/SOURCES.txt +0 -39
  128. pico_ioc-1.3.0/tests/test_api.py +0 -145
  129. pico_ioc-1.3.0/tests/test_decorators_and_policy.py +0 -216
  130. pico_ioc-1.3.0/tests/test_fingerprint_public.py +0 -27
  131. pico_ioc-1.3.0/tests/test_interceptors_autoreg.py +0 -214
  132. pico_ioc-1.3.0/tests/test_scope.py +0 -80
  133. {pico_ioc-1.3.0 → pico_ioc-2.1.2}/.coveragerc +0 -0
  134. {pico_ioc-1.3.0 → pico_ioc-2.1.2}/.github/workflows/publish-to-pypi.yml +0 -0
  135. {pico_ioc-1.3.0 → pico_ioc-2.1.2}/LICENSE +0 -0
  136. {pico_ioc-1.3.0 → pico_ioc-2.1.2}/MANIFEST.in +0 -0
  137. {pico_ioc-1.3.0 → pico_ioc-2.1.2}/setup.cfg +0 -0
  138. {pico_ioc-1.3.0 → pico_ioc-2.1.2}/src/pico_ioc.egg-info/dependency_links.txt +0 -0
  139. {pico_ioc-1.3.0 → pico_ioc-2.1.2}/src/pico_ioc.egg-info/top_level.txt +0 -0
@@ -13,7 +13,7 @@ jobs:
13
13
  strategy:
14
14
  fail-fast: false
15
15
  matrix:
16
- python-version: [ "3.10", "3.11", "3.12", "3.13" ]
16
+ python-version: [ "3.10", "3.11", "3.12", "3.13", "3.14" ]
17
17
 
18
18
  steps:
19
19
  - name: Checkout
@@ -0,0 +1,104 @@
1
+ name: Deploy Documentation
2
+
3
+ on:
4
+ push:
5
+ branches: [ main, master ]
6
+ paths:
7
+ - 'docs/**'
8
+ - 'mkdocs.yml'
9
+ - '.github/workflows/docs.yml'
10
+ pull_request:
11
+ branches: [ main, master ]
12
+ paths:
13
+ - 'docs/**'
14
+ - 'mkdocs.yml'
15
+ workflow_dispatch:
16
+
17
+ permissions:
18
+ actions: read
19
+ contents: write
20
+ pages: write
21
+ id-token: write
22
+
23
+ concurrency:
24
+ group: "pages"
25
+ cancel-in-progress: false
26
+
27
+ jobs:
28
+ validate:
29
+ if: github.event_name == 'pull_request'
30
+ runs-on: ubuntu-latest
31
+ steps:
32
+ - uses: actions/checkout@v4
33
+ with:
34
+ fetch-depth: 0
35
+
36
+ - uses: actions/setup-python@v5
37
+ with:
38
+ python-version: '3.11'
39
+ cache: 'pip'
40
+
41
+ - name: Install dependencies
42
+ run: |
43
+ pip install --upgrade pip
44
+ pip install -r docs/requirements.txt
45
+
46
+ - name: Validate documentation build
47
+ run: mkdocs build --strict
48
+
49
+ - name: Check for broken links (best-effort)
50
+ run: |
51
+ pip install linkchecker
52
+ python -m http.server --directory site 8000 &
53
+ sleep 3
54
+ linkchecker http://127.0.0.1:8000 --check-extern || true
55
+
56
+ deploy:
57
+ if: github.event_name != 'pull_request'
58
+ runs-on: ubuntu-latest
59
+ environment:
60
+ name: github-pages
61
+ url: ${{ steps.deployment.outputs.page_url }}
62
+
63
+ steps:
64
+ - uses: actions/checkout@v4
65
+ with:
66
+ fetch-depth: 0
67
+
68
+ - uses: actions/setup-python@v5
69
+ with:
70
+ python-version: '3.11'
71
+ cache: 'pip'
72
+
73
+ - name: Install dependencies
74
+ run: |
75
+ pip install --upgrade pip
76
+ pip install -r docs/requirements.txt
77
+
78
+ - name: Build documentation
79
+ run: |
80
+ mkdocs build --strict --verbose
81
+ echo "Built on $(date -u)" > site/build-info.txt
82
+ echo "Commit: ${{ github.sha }}" >> site/build-info.txt
83
+
84
+ - name: Upload artifact
85
+ uses: actions/upload-pages-artifact@v4
86
+ with:
87
+ path: './site'
88
+
89
+ - name: Deploy to GitHub Pages
90
+ id: deployment
91
+ uses: actions/deploy-pages@v4
92
+
93
+ - name: Comment on commit (optional)
94
+ if: success()
95
+ uses: actions/github-script@v7
96
+ with:
97
+ script: |
98
+ github.rest.repos.createCommitComment({
99
+ owner: context.repo.owner,
100
+ repo: context.repo.repo,
101
+ commit_sha: context.sha,
102
+ body: `✅ Documentation deployed successfully!\n\n📚 View at: ${'${{ steps.deployment.outputs.page_url }}'}`
103
+ })
104
+
@@ -0,0 +1,222 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on Keep a Changelog (https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to Semantic Versioning (https://semver.org/spec/v2.0.html).
7
+
8
+ ---
9
+
10
+ ## [2.1.1] - 2025-11-02
11
+
12
+ ### Added
13
+
14
+ * ✨ Inline Field Overrides: Added support for overriding configuration fields directly in `@configured` classes using `Annotated[..., Value(...)]` (documented in ADR-0010).
15
+ * Container Self-Injection: `PicoContainer` is now registered as a singleton and can be injected into other components.
16
+ * Flexible `@provides`: `@provides` now supports module-level functions, `@staticmethod`, and `@classmethod` (ADR-0009), allowing for stateless providers without requiring a `@factory` instance.
17
+ * New `websocket` Scope: Added a new default scope named `websocket` to the `ScopeManager`, based on `contextvars`.
18
+
19
+ ### Changed
20
+
21
+ * Simplified `custom_scopes` API: `init(custom_scopes=...)` now accepts an `Iterable[str]` of scope names (instead of a dict), automatically registering them as `ContextVarScope`.
22
+ * Strict Async Validation (Singletons): `init()` will now raise a `ConfigurationError` if a non-lazy (eager) singleton requires an async `@configure` method. This forces the developer to either use `aget()` or mark the component as `lazy=True`.
23
+ * Async Validation (Other Scopes): Components with other scopes (e.g., `request`) that have an async `@configure` will now raise an `AsyncResolutionError` if retrieved with `get()` (sync) but will resolve correctly with `aget()` (async).
24
+
25
+ ### Fixed
26
+
27
+ * EventBus Thread-Safety: Fixed a bug to ensure `EventBus.post()` is fully thread-safe and correctly handles the worker's event loop context.
28
+ * Lazy Proxy and Async: The `UnifiedComponentProxy` (used for `lazy=True`) now correctly detects async `@configure` methods and raises an `AsyncResolutionError` if resolved synchronously.
29
+ * Python Version: Updated `requires-python` to `>=3.10` to reflect actual dependencies (like `contextvars` and `typing.Annotated`) and prevent installation errors.
30
+ * Internal Wiring: Cleaned up the import flow and internal logic in `api.init()` and `registrar.finalize()`.
31
+
32
+ ### Docs
33
+
34
+ * User Guide & ADRs: Updated all documentation (User Guide, Glossary, ADRs) to reflect the unified configuration API (ADR-0010), removing all references to the old `@configuration` decorator.
35
+ * API Reference: Updated the `init()` signature in `container.md` to show the change to `custom_scopes` and document the `validate_only` and `observers` parameters.
36
+ * Simplified `architecture/comparison.md` and corrected minor errors.
37
+
38
+ ### Tests
39
+
40
+ * Added `test_container_self_injection.py` to verify container self-injection.
41
+ * Updated assertions in log and stats tests to reflect new eager initialization and scope semantics.
42
+
43
+ ---
44
+
45
+ ## [2.1.0] - 2025-10-28
46
+
47
+ ### Added ✨
48
+
49
+ * Unified Configuration Builder (`configuration(...)`): Introduced a new top-level function `configuration(...)` that accepts various sources (`EnvSource`, `DictSource`, `JsonTreeSource`, `YamlTreeSource`, `FlatDictSource`, etc.) and `overrides` to produce an immutable `ContextConfig` object. This centralizes configuration definition and precedence rules (ADR-0010).
50
+ * `ContextConfig` Object: A new object encapsulating the fully processed configuration state, passed to `init(config=...)`.
51
+ * Enhanced `@configured` Decorator:
52
+ * Added `mapping: Literal["auto", "flat", "tree"]` parameter to explicitly control binding strategy.
53
+ * Implemented `"auto"` detection: uses `"tree"` if any field is complex (dataclass, list, dict, Union), otherwise uses `"flat"`.
54
+ * Supports unified normalization rules for keys (e.g., `ENV_VAR_NAME` <-> `field_name`, `ENV_VAR__NESTED` <-> `parent.nested`).
55
+ * Integrates seamlessly with both flat and tree sources managed by `ContextConfig`.
56
+
57
+ ### Changed ⚠️
58
+
59
+ * `init()` Signature: The `config` and `tree_config` parameters have been removed. Configuration is now passed solely through the new `config: Optional[ContextConfig]` parameter (ADR-0010).
60
+ * Configuration Precedence: Configuration loading and precedence are now strictly defined by the order of sources passed to the `configuration(...)` builder, followed by its `overrides` parameter, and finally `Annotated[..., Value(...)]` (ADR-0010).
61
+
62
+ ### Removed ❌
63
+
64
+ * `@configuration` Decorator: This decorator, previously used for flat key-value binding, has been completely removed in favor of the unified `@configured` decorator (ADR-0010).
65
+ * Separate `config` (flat) and `tree_config` arguments in `init()`.
66
+
67
+ ### Documentation 📚
68
+
69
+ * Updated all documentation (User Guide, API Reference, Examples, ADRs) to reflect the unified configuration system based on `@configured`, `configuration(...)`, and `ContextConfig`.
70
+ * Added `docs/specs/spec-configuration.md` detailing the unified configuration rules.
71
+ * Added migration notes related to removing `@configuration`.
72
+
73
+ ### Breaking Changes ⚠️
74
+
75
+ * This release introduces significant breaking changes related to configuration management, requiring migration from the old `@configuration` decorator and `init()` arguments to the new `@configured` modes and `configuration(...)` builder (ADR-0010).
76
+
77
+ ---
78
+
79
+ ## [2.0.3] - 2025-10-26
80
+
81
+ ### Fixed
82
+
83
+ - Injection now falls back from an annotated key to the parameter name when the annotated key is unbound, enabling resolution against string-key providers registered via `@provides("name")`.
84
+ - `ProviderNotFoundError` includes the requesting origin (component or key) to aid debugging and test assertions.
85
+ - Unified sync/async resolution path in `PicoContainer`:
86
+ - `get()` raises `AsyncResolutionError` if a provider returns an awaitable, guiding users to `aget()`.
87
+ - `aget()` awaits awaitables and applies aspects and caching consistently.
88
+ - Observers receive accurate resolve timings in both paths.
89
+
90
+ ### Added
91
+
92
+ - `AsyncResolutionError` to signal misuse of `get()` when a provider is async.
93
+ - More informative tracer notes for parameter binding.
94
+
95
+ ### Internal
96
+
97
+ - `ComponentFactory.get()` now accepts an `origin` to enrich `ProviderNotFoundError` messages.
98
+ - Lazy proxy creation calls `factory.get(key, origin="lazy")` to attribute provenance.
99
+ - Public API exports updated to include `AsyncResolutionError`.
100
+
101
+ ### Compatibility
102
+
103
+ - No public API breaking changes. Internal factory signature changed but remains encapsulated within the container/registrar.
104
+
105
+ ---
106
+
107
+ ## [2.0.2] - 2025-10-26
108
+
109
+ ### Fixed 🧩
110
+
111
+ * `@provides` Decorator Execution
112
+ Corrected an issue where the `@provides` decorator executed its wrapped function prematurely during module import, leading to runtime errors like `TypeError: Service() takes no arguments`.
113
+ The decorator now properly registers provider metadata without invoking the function until dependency resolution time.
114
+
115
+ ### Added ✨
116
+
117
+ * `FlatDictSource` Configuration Provider
118
+ Introduced a lightweight configuration source for flat in-memory dictionaries.
119
+ Supports optional key prefixing and case sensitivity control for simple, programmatic configuration injection.
120
+
121
+ ### Internal 🔧
122
+
123
+ * Updated type imports and registration logic in `api.py` to support `Mapping` for the new configuration source.
124
+ * Added `FlatDictSource` to the public API (`__all__` and import namespace).
125
+
126
+ ### Notes 📝
127
+
128
+ * Fully backward compatible.
129
+ * This patch release focuses on decorator correctness and configuration flexibility improvements.
130
+
131
+ ---
132
+
133
+ ## [2.0.1] - 2025-10-25
134
+
135
+ ### Added ✨
136
+
137
+ - ADR-0009: Flexible `@provides` Support
138
+ Implemented support for using `@provides` in additional contexts:
139
+ - `@staticmethod` methods within `@factory` classes
140
+ - `@classmethod` methods within `@factory` classes
141
+ - Module-level functions
142
+ These new provider types are discovered automatically during module scanning and participate fully in dependency resolution, validation, and graph generation.
143
+
144
+ - Dependency Graph and Validation Enhancements
145
+ - `_build_resolution_graph` now includes edges for all `@provides` functions, regardless of where they are defined.
146
+ - Fail-fast validation checks now cover static/class/module-level providers, reporting missing bindings consistently.
147
+ - Scope inference and promotion logic apply equally to these new provider types.
148
+
149
+ ### Documentation 📚
150
+
151
+ - Expanded `docs/overview.md` to document the new flexible provider options (`@staticmethod`, `@classmethod`, module-level functions).
152
+ - Updated `docs/guide.md` with practical examples showing when to use each style of provider.
153
+ - Linked ADR-0009 for design rationale and migration guidance.
154
+
155
+ ### Notes 📝
156
+
157
+ - This is a minor feature release introducing a major ergonomics improvement (ADR-0009).
158
+ - Fully backward compatible with existing factories, components, and configuration mechanisms.
159
+ - Encourages a lighter, more Pythonic style for simple provider declarations.
160
+
161
+
162
+ ---
163
+
164
+ ## [2.0.0] - 2025-10-23
165
+
166
+ This version marks a significant redesign and the first major public release, establishing the core architecture and feature set based on the principles outlined in the Architecture Decision Records (ADRs).
167
+
168
+ ### 🚀 Highlights
169
+
170
+ * Async-Native Core: Introduced first-class `async`/`await` support across component resolution (`container.aget`), initialization (`__ainit__`), lifecycle hooks (`@configure`, `@cleanup`), AOP interceptors, and the event bus (ADR-0001).
171
+ * Tree-Based Configuration: Added `@configured` decorator and `TreeSource` protocol for binding complex, nested configuration (YAML/JSON) to dataclass graphs, including interpolation and type coercion (ADR-0002).
172
+ * Context-Aware Scopes: Implemented `contextvars`-based scopes (e.g., `"request"`, `"session"`) for managing component lifecycles tied to specific contexts (ADR-0003).
173
+ * Observability Features: Integrated container context (`container_id`, `as_current`), basic stats (`container.stats()`), observer protocol (`ContainerObserver`), and dependency graph export (`container.export_graph()`) (ADR-0004).
174
+ * Aspect-Oriented Programming (AOP): Implemented method interception via `MethodInterceptor` protocol and `@intercepted_by` decorator, using a dynamic proxy (`UnifiedComponentProxy`) (ADR-0005).
175
+ * Eager Startup Validation: Added fail-fast validation during `init()` to detect missing dependencies and configuration errors before runtime (ADR-0006).
176
+ * Built-in Event Bus: Included an asynchronous, in-process event bus (`EventBus`, `@subscribe`, `AutoSubscriberMixin`) for decoupled communication (ADR-0007).
177
+ * Explicit Circular Dependency Handling: Implemented detection and fail-fast for circular dependencies, requiring explicit resolution patterns (ADR-0008).
178
+ * Unified Decorator API: Consolidated component metadata into parameterized decorators (`@component`, `@factory`, `@provides`), removing older stacked decorators (ADR-0009).
179
+
180
+ ### ✨ Added
181
+
182
+ * Core registration decorators: `@component`, `@factory`, `@provides`.
183
+ * Configuration decorators: `@configuration` (flat key-value) and `@configured` (tree-based).
184
+ * Lifecycle decorators: `@configure`, `@cleanup`.
185
+ * AOP decorator: `@intercepted_by`.
186
+ * Event bus decorator: `@subscribe`.
187
+ * Health check decorator: `@health`.
188
+ * Async resolution: `container.aget()` and `__ainit__` convention.
189
+ * Async cleanup: `container.cleanup_all_async()`.
190
+ * Qualifier support (`Qualifier` class) for list injection (`Annotated[List[Type], Qualifier(...)]`).
191
+ * Support for `lazy=True` parameter for deferred component instantiation.
192
+ * Conditional binding parameters (`conditional_profiles`, `conditional_require_env`, `conditional_predicate`).
193
+ * Fallback binding parameters (`on_missing_selector`, `on_missing_priority`).
194
+ * Primary selection parameter (`primary=True`).
195
+ * Testing support via `init(overrides={...})` and `init(profiles=(...))`.
196
+ * Container context management (`as_current`, `get_current`, `shutdown`, `all_containers`).
197
+ * Scope management API (`activate_scope`, `deactivate_scope`, `scope` context manager).
198
+ * Configuration sources: `EnvSource`, `FileSource` (flat); `JsonTreeSource`, `YamlTreeSource`, `DictSource` (tree).
199
+ * Protocols for extension: `MethodInterceptor`, `ContainerObserver`, `ScopeProtocol`, `ConfigSource`, `TreeSource`.
200
+
201
+ ### ⚠️ Breaking Changes
202
+
203
+ * Complete redesign compared to any prior internal/unreleased versions. APIs are not backward compatible.
204
+ * Requires Python 3.10+.
205
+
206
+ ### 📚 Docs
207
+
208
+ * Established new documentation structure including ADRs, Architecture, User Guide, Advanced Features, Cookbook, Integrations, and API Reference.
209
+
210
+ ### 🧪 Testing
211
+
212
+ * Added comprehensive test suite covering core features, async behavior, AOP, configuration, scopes, and error handling.
213
+ * Introduced patterns for testing with overrides and profiles.
214
+
215
+ ---
216
+
217
+ ## [<2.0.0]
218
+
219
+ * Internal development and prototyping phase. Basic dependency injection concepts established. Architecture significantly reworked for the v2.0.0 release.
220
+
221
+
222
+ ---
@@ -0,0 +1,352 @@
1
+ Metadata-Version: 2.4
2
+ Name: pico-ioc
3
+ Version: 2.1.2
4
+ Summary: A minimalist, zero-dependency Inversion of Control (IoC) container for Python.
5
+ Author-email: David Perez Cabrera <dperezcabrera@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 David Pérez Cabrera
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/dperezcabrera/pico-ioc
29
+ Project-URL: Repository, https://github.com/dperezcabrera/pico-ioc
30
+ Project-URL: Issue Tracker, https://github.com/dperezcabrera/pico-ioc/issues
31
+ Keywords: ioc,di,dependency injection,inversion of control,decorator
32
+ Classifier: Development Status :: 4 - Beta
33
+ Classifier: Programming Language :: Python :: 3
34
+ Classifier: Programming Language :: Python :: 3 :: Only
35
+ Classifier: Programming Language :: Python :: 3.10
36
+ Classifier: Programming Language :: Python :: 3.11
37
+ Classifier: Programming Language :: Python :: 3.12
38
+ Classifier: Programming Language :: Python :: 3.13
39
+ Classifier: Programming Language :: Python :: 3.14
40
+ Classifier: License :: OSI Approved :: MIT License
41
+ Classifier: Operating System :: OS Independent
42
+ Requires-Python: >=3.10
43
+ Description-Content-Type: text/markdown
44
+ License-File: LICENSE
45
+ Provides-Extra: yaml
46
+ Requires-Dist: PyYAML; extra == "yaml"
47
+ Provides-Extra: graphviz
48
+ Requires-Dist: graphviz; extra == "graphviz"
49
+ Dynamic: license-file
50
+
51
+ # 📦 Pico-IoC: A Robust, Async-Native IoC Container for Python
52
+
53
+ [![PyPI](https://img.shields.io/pypi/v/pico-ioc.svg)](https://pypi.org/project/pico-ioc/)
54
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/dperezcabrera/pico-ioc)
55
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
56
+ ![CI (tox matrix)](https://github.com/dperezcabrera/pico-ioc/actions/workflows/ci.yml/badge.svg)
57
+ [![codecov](https://codecov.io/gh/dperezcabrera/pico-ioc/branch/main/graph/badge.svg)](https://codecov.io/gh/dperezcabrera/pico-ioc)
58
+ [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=dperezcabrera_pico-ioc&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=dperezcabrera_pico-ioc)
59
+ [![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=dperezcabrera_pico-ioc&metric=duplicated_lines_density)](https://sonarcloud.io/summary/new_code?id=dperezcabrera_pico-ioc)
60
+ [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=dperezcabrera_pico-ioc&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=dperezcabrera_pico-ioc)
61
+ [![PyPI Downloads](https://static.pepy.tech/personalized-badge/pico-ioc?period=monthly&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=Monthly+downloads)](https://pepy.tech/projects/pico-ioc)
62
+ [![Docs](https://img.shields.io/badge/Docs-pico--ioc-blue?style=flat&logo=readthedocs&logoColor=white)](https://dperezcabrera.github.io/pico-ioc/)
63
+ [![Interactive Lab](https://img.shields.io/badge/Learn-online-green?style=flat&logo=python&logoColor=white)](https://dperezcabrera.github.io/learn-pico-ioc/)
64
+
65
+
66
+ **Pico-IoC** is a **lightweight, async-ready, decorator-driven IoC container** built for clarity, testability, and performance.
67
+ It brings Inversion of Control and dependency injection to Python in a deterministic, modern, and framework-agnostic way.
68
+
69
+ > 🐍 Requires Python 3.10+
70
+
71
+ ---
72
+
73
+ ## ⚖️ Core Principles
74
+
75
+ - Single Purpose – Do one thing: dependency management.
76
+ - Declarative – Use simple decorators (`@component`, `@factory`, `@provides`, `@configured`) instead of complex config files.
77
+ - Deterministic – No hidden scanning or side-effects; everything flows from an explicit `init()`.
78
+ - Async-Native – Fully supports async providers, async lifecycle hooks (`__ainit__`), and async interceptors.
79
+ - Fail-Fast – Detects missing bindings and circular dependencies at bootstrap (`init()`).
80
+ - Testable by Design – Use `overrides` and `profiles` to swap components instantly.
81
+ - Zero Core Dependencies – Built entirely on the Python standard library. Optional features may require external packages (see Installation).
82
+
83
+ ---
84
+
85
+ ## 🚀 Why Pico-IoC?
86
+
87
+ As Python systems evolve, wiring dependencies by hand becomes fragile and unmaintainable.
88
+ Pico-IoC eliminates that friction by letting you declare how components relate — not how they’re created.
89
+
90
+ | Feature | Manual Wiring | With Pico-IoC |
91
+ | :-------------- | :----------------------------- | :------------------------------ |
92
+ | Object creation | `svc = Service(Repo(Config()))` | `svc = container.get(Service)` |
93
+ | Replacing deps | Monkey-patch | `overrides={Repo: FakeRepo()}` |
94
+ | Coupling | Tight | Loose |
95
+ | Testing | Painful | Instant |
96
+ | Async support | Manual | Built-in (`aget`, `__ainit__`) |
97
+
98
+ ---
99
+
100
+ ## 🧩 Highlights (v2.0+)
101
+
102
+ - Unified Configuration: Use `@configured` to bind both flat (ENV-like) and tree (YAML/JSON) sources via the `configuration(...)` builder (ADR-0010).
103
+ - Async-aware AOP system: Method interceptors via `@intercepted_by`.
104
+ - Scoped resolution: singleton, prototype, request, session, transaction, and custom scopes.
105
+ - `UnifiedComponentProxy`: Transparent `lazy=True` and AOP proxy supporting serialization.
106
+ - Tree-based configuration runtime: Advanced mapping with reusable adapters and discriminators (`Annotated[Union[...], Discriminator(...)]`).
107
+ - Observable container context: Built-in stats, health checks (`@health`), observer hooks (`ContainerObserver`), dependency graph export (`export_graph`), and async cleanup.
108
+
109
+ ---
110
+
111
+ ## 📦 Installation
112
+
113
+ ```bash
114
+ pip install pico-ioc
115
+ ```
116
+
117
+ Optional extras:
118
+
119
+ - YAML configuration support (requires PyYAML)
120
+
121
+ ```bash
122
+ pip install pico-ioc[yaml]
123
+ ```
124
+
125
+ -----
126
+
127
+ ## ⚙️ Quick Example (Unified Configuration)
128
+
129
+ ```python
130
+ import os
131
+ from dataclasses import dataclass
132
+ from pico_ioc import component, configured, configuration, init, EnvSource
133
+
134
+ # 1. Define configuration with @configured
135
+ @configured(prefix="APP_", mapping="auto") # Auto-detects flat mapping
136
+ @dataclass
137
+ class Config:
138
+ db_url: str = "sqlite:///demo.db"
139
+
140
+ # 2. Define components
141
+ @component
142
+ class Repo:
143
+ def __init__(self, cfg: Config): # Inject config
144
+ self.cfg = cfg
145
+ def fetch(self):
146
+ return f"fetching from {self.cfg.db_url}"
147
+
148
+ @component
149
+ class Service:
150
+ def __init__(self, repo: Repo): # Inject Repo
151
+ self.repo = repo
152
+ def run(self):
153
+ return self.repo.fetch()
154
+
155
+ # --- Example Setup ---
156
+ os.environ['APP_DB_URL'] = 'postgresql://user:pass@host/db'
157
+
158
+ # 3. Build configuration context
159
+ config_ctx = configuration(
160
+ EnvSource(prefix="") # Read APP_DB_URL from environment
161
+ )
162
+
163
+ # 4. Initialize container
164
+ container = init(modules=[__name__], config=config_ctx) # Pass context via 'config'
165
+
166
+ # 5. Get and use the service
167
+ svc = container.get(Service)
168
+ print(svc.run())
169
+
170
+ # --- Cleanup ---
171
+ del os.environ['APP_DB_URL']
172
+ ```
173
+
174
+ Output:
175
+
176
+ ```
177
+ fetching from postgresql://user:pass@host/db
178
+ ```
179
+
180
+ -----
181
+
182
+ ## 🧪 Testing with Overrides
183
+
184
+ ```python
185
+ class FakeRepo:
186
+ def fetch(self): return "fake-data"
187
+
188
+ # Build configuration context (might be empty or specific for test)
189
+ test_config_ctx = configuration()
190
+
191
+ # Use overrides during init
192
+ container = init(
193
+ modules=[__name__],
194
+ config=test_config_ctx,
195
+ overrides={Repo: FakeRepo()} # Replace Repo with FakeRepo
196
+ )
197
+
198
+ svc = container.get(Service)
199
+ assert svc.run() == "fake-data"
200
+ ```
201
+
202
+ -----
203
+
204
+ ## 🧰 Profiles
205
+
206
+ Use profiles to enable/disable components or configuration branches conditionally.
207
+
208
+ ```python
209
+ # Enable "test" profile when bootstrapping the container
210
+ container = init(
211
+ modules=[__name__],
212
+ profiles=["test"]
213
+ )
214
+ ```
215
+
216
+ Profiles are typically referenced in decorators or configuration mappings to include/exclude components and bindings.
217
+
218
+ -----
219
+
220
+ ## ⚡ Async Components
221
+
222
+ Pico-IoC supports async lifecycle and resolution.
223
+
224
+ ```python
225
+ import asyncio
226
+ from pico_ioc import component, init
227
+
228
+ @component
229
+ class AsyncRepo:
230
+ async def __ainit__(self):
231
+ # e.g., open async connections
232
+ self.ready = True
233
+
234
+ async def fetch(self):
235
+ return "async-data"
236
+
237
+ async def main():
238
+ container = init(modules=[__name__])
239
+ repo = await container.aget(AsyncRepo) # Async resolution
240
+ print(await repo.fetch())
241
+
242
+ asyncio.run(main())
243
+ ```
244
+
245
+ - `__ainit__` runs after construction if defined.
246
+ - Use `container.aget(Type)` to resolve components that require async initialization or whose providers are async.
247
+
248
+ -----
249
+
250
+ ## 🩺 Lifecycle & AOP
251
+
252
+ ```python
253
+ import time
254
+ from pico_ioc import component, init, intercepted_by, MethodInterceptor, MethodCtx
255
+
256
+ # Define an interceptor component
257
+ @component
258
+ class LogInterceptor(MethodInterceptor):
259
+ def invoke(self, ctx: MethodCtx, call_next):
260
+ print(f"→ calling {ctx.cls.__name__}.{ctx.name}")
261
+ start = time.perf_counter()
262
+ try:
263
+ res = call_next(ctx)
264
+ duration = (time.perf_counter() - start) * 1000
265
+ print(f"← {ctx.cls.__name__}.{ctx.name} done ({duration:.2f}ms)")
266
+ return res
267
+ except Exception as e:
268
+ duration = (time.perf_counter() - start) * 1000
269
+ print(f"← {ctx.cls.__name__}.{ctx.name} failed ({duration:.2f}ms): {e}")
270
+ raise
271
+
272
+ @component
273
+ class Demo:
274
+ @intercepted_by(LogInterceptor) # Apply the interceptor
275
+ def work(self):
276
+ print(" Working...")
277
+ time.sleep(0.01)
278
+ return "ok"
279
+
280
+ # Initialize container (must scan module containing interceptor too)
281
+ c = init(modules=[__name__])
282
+ result = c.get(Demo).work()
283
+ print(f"Result: {result}")
284
+ ```
285
+
286
+ Output:
287
+
288
+ ```
289
+ → calling Demo.work
290
+ Working...
291
+ ← Demo.work done (10.xxms)
292
+ Result: ok
293
+ ```
294
+
295
+ -----
296
+
297
+ ## 👁️ Observability & Cleanup
298
+
299
+ - Export a dependency graph in DOT format:
300
+
301
+ ```python
302
+ c = init(modules=[...])
303
+ dot = c.export_graph() # Returns DOT graph as a string
304
+ with open("dependencies.dot", "w") as f:
305
+ f.write(dot)
306
+ ```
307
+
308
+ - Health checks:
309
+ - Annotate health probes inside components with `@health` for container-level reporting.
310
+ - The container exposes health information that can be queried in observability tooling.
311
+
312
+ - Container cleanup:
313
+ - For sync components: `container.close()`
314
+ - For async components/resources: `await container.aclose()`
315
+
316
+ Use cleanup in application shutdown hooks to release resources deterministically.
317
+
318
+ -----
319
+
320
+ ## 📖 Documentation
321
+
322
+ The full documentation is available within the `docs/` directory of the project repository. Start with `docs/README.md` for navigation.
323
+
324
+ - Getting Started: `docs/getting-started.md`
325
+ - User Guide: `docs/user-guide/README.md`
326
+ - Advanced Features: `docs/advanced-features/README.md`
327
+ - Observability: `docs/observability/README.md`
328
+ - Cookbook (Patterns): `docs/cookbook/README.md`
329
+ - Architecture: `docs/architecture/README.md`
330
+ - API Reference: `docs/api-reference/README.md`
331
+ - ADR Index: `docs/adr/README.md`
332
+
333
+ -----
334
+
335
+ ## 🧩 Development
336
+
337
+ ```bash
338
+ pip install tox
339
+ tox
340
+ ```
341
+
342
+ -----
343
+
344
+ ## 🧾 Changelog
345
+
346
+ See [CHANGELOG.md](./CHANGELOG.md) — Significant redesigns and features in v2.0+.
347
+
348
+ -----
349
+
350
+ ## 📜 License
351
+
352
+ MIT — [LICENSE](https://opensource.org/licenses/MIT)