pico-ioc 0.4.0__tar.gz → 0.5.1__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.
@@ -0,0 +1,17 @@
1
+ [run]
2
+ branch = True
3
+ source =
4
+ pico_ioc
5
+ omit =
6
+ */_version.py
7
+
8
+ [paths]
9
+ pico_ioc =
10
+ src/pico_ioc
11
+ .tox/*/lib/*/site-packages/pico_ioc
12
+ .tox/*/site-packages/pico_ioc
13
+ */site-packages/pico_ioc
14
+
15
+ [report]
16
+ show_missing = True
17
+
@@ -0,0 +1,102 @@
1
+ name: CI & Coverage
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ tests:
11
+ name: Tests (Python ${{ matrix.python-version }})
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ python-version: [ "3.8", "3.9", "3.10", "3.11", "3.12", "3.13" ]
17
+
18
+ steps:
19
+ - name: Checkout
20
+ uses: actions/checkout@v4
21
+ with:
22
+ fetch-depth: 0
23
+
24
+ - name: Set up Python ${{ matrix.python-version }}
25
+ uses: actions/setup-python@v5
26
+ with:
27
+ python-version: ${{ matrix.python-version }}
28
+ cache: pip
29
+
30
+ - name: Install tox
31
+ run: |
32
+ python -m pip install --upgrade pip
33
+ pip install tox
34
+
35
+ - name: Run tests with coverage (writes to repo root)
36
+ shell: bash
37
+ run: |
38
+ set -euxo pipefail
39
+ tox -e cov
40
+ # Rename to per-version file for artifact + merge job
41
+ mv .coverage .coverage.${{ matrix.python-version }}
42
+ ls -la .coverage.${{ matrix.python-version }}
43
+
44
+ - name: Upload .coverage artifact
45
+ uses: actions/upload-artifact@v4
46
+ with:
47
+ name: coverage-${{ matrix.python-version }}
48
+ path: .coverage.${{ matrix.python-version }}
49
+ include-hidden-files: true
50
+ if-no-files-found: error
51
+
52
+ coverage-merge:
53
+ name: Merge coverage
54
+ runs-on: ubuntu-latest
55
+ needs: tests
56
+ steps:
57
+ - name: Checkout (for repo context)
58
+ uses: actions/checkout@v4
59
+
60
+ - name: Download all coverage artifacts
61
+ uses: actions/download-artifact@v4
62
+ with:
63
+ path: coverage-reports
64
+
65
+ - name: Combine coverage and generate reports
66
+ shell: bash
67
+ run: |
68
+ set -euxo pipefail
69
+ python -m pip install coverage
70
+ files=$(find coverage-reports -type f -name ".coverage.*" -print)
71
+ if [ -z "$files" ]; then
72
+ echo "No coverage data files found"; exit 1
73
+ fi
74
+ coverage combine $files
75
+ coverage report -m --rcfile=.coveragerc
76
+ coverage xml -o coverage.xml --rcfile=.coveragerc
77
+ coverage html -d htmlcov --rcfile=.coveragerc
78
+
79
+ - name: Upload combined coverage XML
80
+ uses: actions/upload-artifact@v4
81
+ with:
82
+ name: coverage-xml
83
+ path: coverage.xml
84
+ if-no-files-found: error
85
+
86
+ - name: Upload HTML coverage report
87
+ uses: actions/upload-artifact@v4
88
+ with:
89
+ name: coverage-html
90
+ path: htmlcov
91
+ if-no-files-found: error
92
+
93
+ - name: Upload to Codecov
94
+ if: always() && !cancelled()
95
+ uses: codecov/codecov-action@v5
96
+ continue-on-error: true
97
+ with:
98
+ files: coverage.xml
99
+ fail_ci_if_error: false
100
+ token: ${{ secrets.CODECOV_TOKEN }}
101
+ slug: dperezcabrera/pico-ioc
102
+
@@ -29,4 +29,8 @@ jobs:
29
29
 
30
30
  - name: Publish to PyPI
31
31
  uses: pypa/gh-action-pypi-publish@release/v1
32
+ with:
33
+ skip-existing: true
34
+ verify-metadata: true
35
+ attestations: false
32
36
 
pico_ioc-0.5.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 David Pérez Cabrera
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,24 @@
1
+ # Incluir ficheros importantes
2
+ include README.md
3
+ include LICENSE
4
+
5
+ # Incluir todo el código fuente
6
+ recursive-include src *.py
7
+
8
+ # Incluir datos de tests si quieres que otros puedan correrlos desde el sdist
9
+ # (opcional — puedes quitarlo si no quieres incluir tests en PyPI)
10
+ recursive-include tests *.py
11
+
12
+ # Excluir cosas innecesarias
13
+ exclude .gitignore
14
+ exclude Dockerfile*
15
+ exclude docker-compose*.yml
16
+ exclude Makefile
17
+
18
+ # Excluir carpetas de build, virtualenvs, caches, etc.
19
+ prune build
20
+ prune dist
21
+ prune .tox
22
+ prune .pytest_cache
23
+ prune __pycache__
24
+
@@ -0,0 +1,284 @@
1
+ Metadata-Version: 2.4
2
+ Name: pico-ioc
3
+ Version: 0.5.1
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.8
36
+ Classifier: Programming Language :: Python :: 3.9
37
+ Classifier: Programming Language :: Python :: 3.10
38
+ Classifier: Programming Language :: Python :: 3.11
39
+ Classifier: Programming Language :: Python :: 3.12
40
+ Classifier: Programming Language :: Python :: 3.13
41
+ Classifier: License :: OSI Approved :: MIT License
42
+ Classifier: Operating System :: OS Independent
43
+ Requires-Python: >=3.8
44
+ Description-Content-Type: text/markdown
45
+ License-File: LICENSE
46
+ Dynamic: license-file
47
+
48
+ Got it ✅
49
+ Here’s your **updated README.md in full English**, keeping all original sections but now including the **name-first resolution** feature and new tests section.
50
+
51
+ ---
52
+
53
+ ````markdown
54
+ # 📦 Pico-IoC: A Minimalist IoC Container for Python
55
+
56
+ [![PyPI](https://img.shields.io/pypi/v/pico-ioc.svg)](https://pypi.org/project/pico-ioc/)
57
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
58
+ ![CI (tox matrix)](https://github.com/dperezcabrera/pico-ioc/actions/workflows/ci.yml/badge.svg)
59
+ [![codecov](https://codecov.io/gh/dperezcabrera/pico-ioc/branch/main/graph/badge.svg)](https://codecov.io/gh/dperezcabrera/pico-ioc)
60
+ [![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)
61
+ [![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)
62
+ [![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)
63
+
64
+ **Pico-IoC** is a tiny, zero-dependency, decorator-based Inversion of Control container for Python.
65
+ Build loosely-coupled, testable apps without manual wiring. Inspired by the Spring ecosystem.
66
+
67
+ ---
68
+
69
+ ## ✨ Key Features
70
+
71
+ * **Zero dependencies** — pure Python.
72
+ * **Decorator API** — `@component`, `@factory_component`, `@provides`.
73
+ * **Auto discovery** — scans a package and registers components.
74
+ * **Eager by default, fail-fast** — non-lazy bindings are instantiated immediately after `init()`. Missing deps fail startup.
75
+ * **Opt-in lazy** — set `lazy=True` to defer creation (wrapped in `ComponentProxy`).
76
+ * **Factories** — encapsulate complex creation logic.
77
+ * **Smart resolution order** — **parameter name** takes precedence over **type annotation**, then **MRO fallback**, then **string(name)**.
78
+ * **Re-entrancy guard** — prevents `get()` during scanning.
79
+ * **Auto-exclude caller** — `init()` skips the calling module to avoid double scanning.
80
+
81
+ ---
82
+
83
+ ## 📦 Installation
84
+
85
+ ```bash
86
+ pip install pico-ioc
87
+ ````
88
+
89
+ ---
90
+
91
+ ## 🚀 Quick Start
92
+
93
+ ```python
94
+ from pico_ioc import component, init
95
+
96
+ @component
97
+ class AppConfig:
98
+ def get_db_url(self):
99
+ return "postgresql://user:pass@host/db"
100
+
101
+ @component
102
+ class DatabaseService:
103
+ def __init__(self, config: AppConfig):
104
+ self._cs = config.get_db_url()
105
+ def get_data(self):
106
+ return f"Data from {self._cs}"
107
+
108
+ container = init(__name__) # blueprint runs here (eager + fail-fast)
109
+ db = container.get(DatabaseService)
110
+ print(db.get_data())
111
+ ```
112
+
113
+ ---
114
+
115
+ ## 🧩 Custom Component Keys
116
+
117
+ ```python
118
+ from pico_ioc import component, init
119
+
120
+ @component(name="config") # custom key
121
+ class AppConfig:
122
+ db_url = "postgresql://user:pass@localhost/db"
123
+
124
+ @component
125
+ class Repository:
126
+ def __init__(self, config: "config"): # resolve by NAME
127
+ self.url = config.db_url
128
+
129
+ container = init(__name__)
130
+ print(container.get("config").db_url)
131
+ ```
132
+
133
+ ---
134
+
135
+ ## 🏭 Factories and `@provides`
136
+
137
+ * Default is **eager** (`lazy=False`). Eager bindings are constructed at the end of `init()`.
138
+ * Use `lazy=True` for on-first-use creation via `ComponentProxy`.
139
+
140
+ ```python
141
+ from pico_ioc import factory_component, provides, init
142
+
143
+ COUNTER = {"value": 0}
144
+
145
+ @factory_component
146
+ class ServicesFactory:
147
+ @provides(key="heavy_service", lazy=True)
148
+ def heavy(self):
149
+ COUNTER["value"] += 1
150
+ return {"payload": "hello"}
151
+
152
+ container = init(__name__)
153
+ svc = container.get("heavy_service") # not created yet
154
+ print(COUNTER["value"]) # 0
155
+ print(svc["payload"]) # triggers creation
156
+ print(COUNTER["value"]) # 1
157
+ ```
158
+
159
+ ---
160
+
161
+ ## 🧠 Dependency Resolution Order (Updated in v0.5.0)
162
+
163
+ Starting with **v0.5.0**, Pico-IoC enforces **name-first resolution**:
164
+
165
+ 1. **Parameter name** (highest priority)
166
+ 2. **Exact type annotation**
167
+ 3. **MRO fallback** (walk base classes)
168
+ 4. **String(name)**
169
+
170
+ This means that if a dependency could match both by name and type, **the name match wins**.
171
+
172
+ Example:
173
+
174
+ ```python
175
+ from pico_ioc import component, factory_component, provides, init
176
+
177
+ class BaseType: ...
178
+ class Impl(BaseType): ...
179
+
180
+ @component(name="inject_by_name")
181
+ class InjectByName:
182
+ def __init__(self):
183
+ self.value = "by-name"
184
+
185
+ @factory_component
186
+ class NameVsTypeFactory:
187
+ @provides("choose", lazy=True)
188
+ def make(self, inject_by_name, hint: BaseType = None):
189
+ return inject_by_name.value
190
+
191
+ container = init(__name__)
192
+ assert container.get("choose") == "by-name"
193
+ ```
194
+
195
+ ---
196
+
197
+ ## ⚡ Eager vs. Lazy (Blueprint Behavior)
198
+
199
+ At the end of `init()`, Pico-IoC performs a **blueprint**:
200
+
201
+ * **Eager** (`lazy=False`, default): instantiated immediately; failures stop startup.
202
+ * **Lazy** (`lazy=True`): returns a `ComponentProxy`; instantiated on first real use.
203
+
204
+ **Lifecycle:**
205
+
206
+ ```
207
+ ┌───────────────────────┐
208
+ │ init() │
209
+ └───────────────────────┘
210
+
211
+
212
+ ┌───────────────────────┐
213
+ │ Scan & bind deps │
214
+ └───────────────────────┘
215
+
216
+
217
+ ┌─────────────────────────────┐
218
+ │ Blueprint instantiates all │
219
+ │ non-lazy (eager) beans │
220
+ └─────────────────────────────┘
221
+
222
+ ┌───────────────────────┐
223
+ │ Container ready │
224
+ └───────────────────────┘
225
+ ```
226
+
227
+ ---
228
+
229
+ ## 🛠 API Reference
230
+
231
+ ### `init(root, *, exclude=None, auto_exclude_caller=True) -> PicoContainer`
232
+
233
+ Scan and bind components in `root` (str module name or module).
234
+ Skips the calling module if `auto_exclude_caller=True`.
235
+ Runs blueprint (instantiate all `lazy=False` bindings).
236
+
237
+ ### `@component(cls=None, *, name=None, lazy=False)`
238
+
239
+ Register a class as a component.
240
+ Use `name` for a custom key.
241
+ Set `lazy=True` to defer creation.
242
+
243
+ ### `@factory_component`
244
+
245
+ Mark a class as a component factory (its methods can `@provides` bindings).
246
+
247
+ ### `@provides(key, *, lazy=False)`
248
+
249
+ Declare that a factory method provides a component under `key`.
250
+ Set `lazy=True` for deferred creation (`ComponentProxy`).
251
+
252
+ ---
253
+
254
+ ## 🧪 Testing
255
+
256
+ ```bash
257
+ pip install tox
258
+ tox
259
+ ```
260
+
261
+ **New in v0.5.0:**
262
+ Additional tests verify:
263
+
264
+ * Name vs. type precedence.
265
+ * Mixed binding key resolution in factories.
266
+ * Eager vs. lazy instantiation edge cases.
267
+
268
+ ---
269
+
270
+ ## 🔌 Extensibility: Plugins, Binder, and Lifecycle Hooks
271
+
272
+ From `v0.4.0` onward, Pico-IoC can be cleanly extended without patching the core.
273
+
274
+ *(plugin API docs unchanged from before)*
275
+
276
+ ---
277
+
278
+ ## 📜 License
279
+
280
+ MIT — see [LICENSE](https://opensource.org/licenses/MIT)
281
+
282
+ ```
283
+
284
+
@@ -0,0 +1,237 @@
1
+ Got it ✅
2
+ Here’s your **updated README.md in full English**, keeping all original sections but now including the **name-first resolution** feature and new tests section.
3
+
4
+ ---
5
+
6
+ ````markdown
7
+ # 📦 Pico-IoC: A Minimalist IoC Container for Python
8
+
9
+ [![PyPI](https://img.shields.io/pypi/v/pico-ioc.svg)](https://pypi.org/project/pico-ioc/)
10
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
11
+ ![CI (tox matrix)](https://github.com/dperezcabrera/pico-ioc/actions/workflows/ci.yml/badge.svg)
12
+ [![codecov](https://codecov.io/gh/dperezcabrera/pico-ioc/branch/main/graph/badge.svg)](https://codecov.io/gh/dperezcabrera/pico-ioc)
13
+ [![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)
14
+ [![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)
15
+ [![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)
16
+
17
+ **Pico-IoC** is a tiny, zero-dependency, decorator-based Inversion of Control container for Python.
18
+ Build loosely-coupled, testable apps without manual wiring. Inspired by the Spring ecosystem.
19
+
20
+ ---
21
+
22
+ ## ✨ Key Features
23
+
24
+ * **Zero dependencies** — pure Python.
25
+ * **Decorator API** — `@component`, `@factory_component`, `@provides`.
26
+ * **Auto discovery** — scans a package and registers components.
27
+ * **Eager by default, fail-fast** — non-lazy bindings are instantiated immediately after `init()`. Missing deps fail startup.
28
+ * **Opt-in lazy** — set `lazy=True` to defer creation (wrapped in `ComponentProxy`).
29
+ * **Factories** — encapsulate complex creation logic.
30
+ * **Smart resolution order** — **parameter name** takes precedence over **type annotation**, then **MRO fallback**, then **string(name)**.
31
+ * **Re-entrancy guard** — prevents `get()` during scanning.
32
+ * **Auto-exclude caller** — `init()` skips the calling module to avoid double scanning.
33
+
34
+ ---
35
+
36
+ ## 📦 Installation
37
+
38
+ ```bash
39
+ pip install pico-ioc
40
+ ````
41
+
42
+ ---
43
+
44
+ ## 🚀 Quick Start
45
+
46
+ ```python
47
+ from pico_ioc import component, init
48
+
49
+ @component
50
+ class AppConfig:
51
+ def get_db_url(self):
52
+ return "postgresql://user:pass@host/db"
53
+
54
+ @component
55
+ class DatabaseService:
56
+ def __init__(self, config: AppConfig):
57
+ self._cs = config.get_db_url()
58
+ def get_data(self):
59
+ return f"Data from {self._cs}"
60
+
61
+ container = init(__name__) # blueprint runs here (eager + fail-fast)
62
+ db = container.get(DatabaseService)
63
+ print(db.get_data())
64
+ ```
65
+
66
+ ---
67
+
68
+ ## 🧩 Custom Component Keys
69
+
70
+ ```python
71
+ from pico_ioc import component, init
72
+
73
+ @component(name="config") # custom key
74
+ class AppConfig:
75
+ db_url = "postgresql://user:pass@localhost/db"
76
+
77
+ @component
78
+ class Repository:
79
+ def __init__(self, config: "config"): # resolve by NAME
80
+ self.url = config.db_url
81
+
82
+ container = init(__name__)
83
+ print(container.get("config").db_url)
84
+ ```
85
+
86
+ ---
87
+
88
+ ## 🏭 Factories and `@provides`
89
+
90
+ * Default is **eager** (`lazy=False`). Eager bindings are constructed at the end of `init()`.
91
+ * Use `lazy=True` for on-first-use creation via `ComponentProxy`.
92
+
93
+ ```python
94
+ from pico_ioc import factory_component, provides, init
95
+
96
+ COUNTER = {"value": 0}
97
+
98
+ @factory_component
99
+ class ServicesFactory:
100
+ @provides(key="heavy_service", lazy=True)
101
+ def heavy(self):
102
+ COUNTER["value"] += 1
103
+ return {"payload": "hello"}
104
+
105
+ container = init(__name__)
106
+ svc = container.get("heavy_service") # not created yet
107
+ print(COUNTER["value"]) # 0
108
+ print(svc["payload"]) # triggers creation
109
+ print(COUNTER["value"]) # 1
110
+ ```
111
+
112
+ ---
113
+
114
+ ## 🧠 Dependency Resolution Order (Updated in v0.5.0)
115
+
116
+ Starting with **v0.5.0**, Pico-IoC enforces **name-first resolution**:
117
+
118
+ 1. **Parameter name** (highest priority)
119
+ 2. **Exact type annotation**
120
+ 3. **MRO fallback** (walk base classes)
121
+ 4. **String(name)**
122
+
123
+ This means that if a dependency could match both by name and type, **the name match wins**.
124
+
125
+ Example:
126
+
127
+ ```python
128
+ from pico_ioc import component, factory_component, provides, init
129
+
130
+ class BaseType: ...
131
+ class Impl(BaseType): ...
132
+
133
+ @component(name="inject_by_name")
134
+ class InjectByName:
135
+ def __init__(self):
136
+ self.value = "by-name"
137
+
138
+ @factory_component
139
+ class NameVsTypeFactory:
140
+ @provides("choose", lazy=True)
141
+ def make(self, inject_by_name, hint: BaseType = None):
142
+ return inject_by_name.value
143
+
144
+ container = init(__name__)
145
+ assert container.get("choose") == "by-name"
146
+ ```
147
+
148
+ ---
149
+
150
+ ## ⚡ Eager vs. Lazy (Blueprint Behavior)
151
+
152
+ At the end of `init()`, Pico-IoC performs a **blueprint**:
153
+
154
+ * **Eager** (`lazy=False`, default): instantiated immediately; failures stop startup.
155
+ * **Lazy** (`lazy=True`): returns a `ComponentProxy`; instantiated on first real use.
156
+
157
+ **Lifecycle:**
158
+
159
+ ```
160
+ ┌───────────────────────┐
161
+ │ init() │
162
+ └───────────────────────┘
163
+
164
+
165
+ ┌───────────────────────┐
166
+ │ Scan & bind deps │
167
+ └───────────────────────┘
168
+
169
+
170
+ ┌─────────────────────────────┐
171
+ │ Blueprint instantiates all │
172
+ │ non-lazy (eager) beans │
173
+ └─────────────────────────────┘
174
+
175
+ ┌───────────────────────┐
176
+ │ Container ready │
177
+ └───────────────────────┘
178
+ ```
179
+
180
+ ---
181
+
182
+ ## 🛠 API Reference
183
+
184
+ ### `init(root, *, exclude=None, auto_exclude_caller=True) -> PicoContainer`
185
+
186
+ Scan and bind components in `root` (str module name or module).
187
+ Skips the calling module if `auto_exclude_caller=True`.
188
+ Runs blueprint (instantiate all `lazy=False` bindings).
189
+
190
+ ### `@component(cls=None, *, name=None, lazy=False)`
191
+
192
+ Register a class as a component.
193
+ Use `name` for a custom key.
194
+ Set `lazy=True` to defer creation.
195
+
196
+ ### `@factory_component`
197
+
198
+ Mark a class as a component factory (its methods can `@provides` bindings).
199
+
200
+ ### `@provides(key, *, lazy=False)`
201
+
202
+ Declare that a factory method provides a component under `key`.
203
+ Set `lazy=True` for deferred creation (`ComponentProxy`).
204
+
205
+ ---
206
+
207
+ ## 🧪 Testing
208
+
209
+ ```bash
210
+ pip install tox
211
+ tox
212
+ ```
213
+
214
+ **New in v0.5.0:**
215
+ Additional tests verify:
216
+
217
+ * Name vs. type precedence.
218
+ * Mixed binding key resolution in factories.
219
+ * Eager vs. lazy instantiation edge cases.
220
+
221
+ ---
222
+
223
+ ## 🔌 Extensibility: Plugins, Binder, and Lifecycle Hooks
224
+
225
+ From `v0.4.0` onward, Pico-IoC can be cleanly extended without patching the core.
226
+
227
+ *(plugin API docs unchanged from before)*
228
+
229
+ ---
230
+
231
+ ## 📜 License
232
+
233
+ MIT — see [LICENSE](https://opensource.org/licenses/MIT)
234
+
235
+ ```
236
+
237
+