pico-ioc 2.0.1__tar.gz → 2.0.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.
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/CHANGELOG.md +26 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/PKG-INFO +1 -1
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc/__init__.py +2 -0
- pico_ioc-2.0.2/src/pico_ioc/_version.py +1 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc/api.py +50 -25
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc.egg-info/PKG-INFO +1 -1
- pico_ioc-2.0.1/src/pico_ioc/_version.py +0 -1
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/.coveragerc +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/.github/workflows/ci.yml +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/.github/workflows/publish-to-pypi.yml +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/LICENSE +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/MANIFEST.in +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/README.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/README.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/adr/README.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/adr/adr-0001-async-native.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/adr/adr-0002-tree-based-configuration.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/adr/adr-0003-context-aware-scopes.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/adr/adr-0004-observability.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/adr/adr-0005-aop.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/adr/adr-0006-eager-validation.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/adr/adr-0007-event_bus.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/adr/adr-0008-circular-dependencies.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/adr/adr-0009-flexible-provides.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/advanced-features/README.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/advanced-features/aop-interceptors.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/advanced-features/async-resolution.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/advanced-features/conditional-binding.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/advanced-features/event-bus.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/advanced-features/health-checks.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/api-reference/README.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/api-reference/container.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/api-reference/decorators.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/api-reference/glossary.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/api-reference/protocols.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/architecture/README.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/architecture/comparison.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/architecture/design-principles.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/architecture/internals.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/cookbook/README.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/cookbook/pattern-aop-feature-toggle.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/cookbook/pattern-aop-profiling.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/cookbook/pattern-aop-security.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/cookbook/pattern-aop-structured-logging.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/cookbook/pattern-cli-app.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/cookbook/pattern-cqrs.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/cookbook/pattern-dynamic-langchain.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/cookbook/pattern-hot-reload.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/cookbook/pattern-multi-tenant.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/getting-started.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/integrations/README.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/integrations/ai-langchain.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/integrations/web-django.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/integrations/web-fastapi.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/integrations/web-flask.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/observability/README.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/observability/container-context.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/observability/exporting-graph.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/observability/observers-metrics.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/overview.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/user-guide/README.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/user-guide/configuration-basic.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/user-guide/configuration-binding.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/user-guide/core-concepts.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/user-guide/qualifiers-lists.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/user-guide/scopes-lifecycle.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/user-guide/testing.md +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/pyproject.toml +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/setup.cfg +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc/aop.py +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc/config_runtime.py +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc/constants.py +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc/container.py +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc/event_bus.py +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc/exceptions.py +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc/factory.py +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc/locator.py +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc/scope.py +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc.egg-info/SOURCES.txt +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc.egg-info/dependency_links.txt +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc.egg-info/requires.txt +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc.egg-info/top_level.txt +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/test.txt +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/tests/test_configured.py +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/tests/test_container_context.py +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/tests/test_container_runtime.py +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/tests/test_event_bus.py +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/tests/test_pico_extends.py +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/tests/test_pico_integration.py +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/tests/test_provides_module_functions.py +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/tests/test_provides_static_methods.py +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/tests/test_resolution_graph.py +0 -0
- {pico_ioc-2.0.1 → pico_ioc-2.0.2}/tox.ini +0 -0
|
@@ -89,6 +89,32 @@ This version marks a significant redesign and the first major public release, es
|
|
|
89
89
|
- Encourages a lighter, more Pythonic style for simple provider declarations.
|
|
90
90
|
|
|
91
91
|
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## [2.0.2] - 2025-10-26
|
|
95
|
+
|
|
96
|
+
### Fixed 🧩
|
|
97
|
+
|
|
98
|
+
* **`@provides` Decorator Execution**
|
|
99
|
+
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`.
|
|
100
|
+
The decorator now properly registers provider metadata without invoking the function until dependency resolution time.
|
|
101
|
+
|
|
102
|
+
### Added ✨
|
|
103
|
+
|
|
104
|
+
* **`FlatDictSource` Configuration Provider**
|
|
105
|
+
Introduced a lightweight configuration source for flat in-memory dictionaries.
|
|
106
|
+
Supports optional key prefixing and case sensitivity control for simple, programmatic configuration injection.
|
|
107
|
+
|
|
108
|
+
### Internal 🔧
|
|
109
|
+
|
|
110
|
+
* Updated type imports and registration logic in `api.py` to support `Mapping` for the new configuration source.
|
|
111
|
+
* Added `FlatDictSource` to the public API (`__all__` and import namespace).
|
|
112
|
+
|
|
113
|
+
### Notes 📝
|
|
114
|
+
|
|
115
|
+
* Fully backward compatible.
|
|
116
|
+
* This patch release focuses on decorator correctness and configuration flexibility improvements.
|
|
117
|
+
|
|
92
118
|
---
|
|
93
119
|
|
|
94
120
|
## [<2.0.0]
|
|
@@ -23,6 +23,7 @@ from .api import (
|
|
|
23
23
|
ConfigSource,
|
|
24
24
|
EnvSource,
|
|
25
25
|
FileSource,
|
|
26
|
+
FlatDictSource,
|
|
26
27
|
init,
|
|
27
28
|
configured,
|
|
28
29
|
)
|
|
@@ -76,6 +77,7 @@ __all__ = [
|
|
|
76
77
|
"EnvSource",
|
|
77
78
|
"FileSource",
|
|
78
79
|
"ConfigSource",
|
|
80
|
+
"FlatDictSource",
|
|
79
81
|
"init",
|
|
80
82
|
"configured",
|
|
81
83
|
"EventBus",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '2.0.2'
|
|
@@ -6,7 +6,7 @@ import importlib
|
|
|
6
6
|
import pkgutil
|
|
7
7
|
import logging
|
|
8
8
|
from dataclasses import is_dataclass, fields, dataclass, MISSING
|
|
9
|
-
from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Tuple, Union, get_args, get_origin, Annotated, Protocol
|
|
9
|
+
from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Tuple, Union, get_args, get_origin, Annotated, Protocol, Mapping
|
|
10
10
|
from .constants import LOGGER, PICO_INFRA, PICO_NAME, PICO_KEY, PICO_META
|
|
11
11
|
from .exceptions import (
|
|
12
12
|
ProviderNotFoundError,
|
|
@@ -54,6 +54,31 @@ class FileSource:
|
|
|
54
54
|
return str(v)
|
|
55
55
|
return None
|
|
56
56
|
|
|
57
|
+
class FlatDictSource(ConfigSource):
|
|
58
|
+
def __init__(self, data: Mapping[str, Any], prefix: str = "", case_sensitive: bool = True):
|
|
59
|
+
base = dict(data)
|
|
60
|
+
if case_sensitive:
|
|
61
|
+
self._data = {str(k): v for k, v in base.items()}
|
|
62
|
+
self._prefix = prefix
|
|
63
|
+
else:
|
|
64
|
+
self._data = {str(k).upper(): v for k, v in base.items()}
|
|
65
|
+
self._prefix = prefix.upper()
|
|
66
|
+
self._case_sensitive = case_sensitive
|
|
67
|
+
|
|
68
|
+
def get(self, key: str) -> Optional[str]:
|
|
69
|
+
if not key:
|
|
70
|
+
return None
|
|
71
|
+
k = f"{self._prefix}{key}" if self._prefix else key
|
|
72
|
+
if not self._case_sensitive:
|
|
73
|
+
k = k.upper()
|
|
74
|
+
v = self._data.get(k)
|
|
75
|
+
if v is None:
|
|
76
|
+
return None
|
|
77
|
+
if isinstance(v, (str, int, float, bool)):
|
|
78
|
+
return str(v)
|
|
79
|
+
return None
|
|
80
|
+
|
|
81
|
+
|
|
57
82
|
def _meta_get(obj: Any) -> Dict[str, Any]:
|
|
58
83
|
m = getattr(obj, PICO_META, None)
|
|
59
84
|
if m is None:
|
|
@@ -132,29 +157,21 @@ def factory(
|
|
|
132
157
|
return c
|
|
133
158
|
return dec(cls) if cls else dec
|
|
134
159
|
|
|
135
|
-
def provides(
|
|
136
|
-
|
|
137
|
-
*,
|
|
138
|
-
name: Any = None,
|
|
139
|
-
qualifiers: Iterable[str] = (),
|
|
140
|
-
scope: str = "singleton",
|
|
141
|
-
primary: bool = False,
|
|
142
|
-
lazy: bool = False,
|
|
143
|
-
conditional_profiles: Iterable[str] = (),
|
|
144
|
-
conditional_require_env: Iterable[str] = (),
|
|
145
|
-
conditional_predicate: Optional[Callable[[], bool]] = None,
|
|
146
|
-
on_missing_selector: Optional[object] = None,
|
|
147
|
-
on_missing_priority: int = 0,
|
|
148
|
-
):
|
|
149
|
-
def dec(fn):
|
|
160
|
+
def provides(*dargs, **dkwargs):
|
|
161
|
+
def _apply(fn, key_hint, *, name=None, qualifiers=(), scope="singleton", primary=False, lazy=False, conditional_profiles=(), conditional_require_env=(), conditional_predicate=None, on_missing_selector=None, on_missing_priority=0):
|
|
150
162
|
target = fn.__func__ if isinstance(fn, (staticmethod, classmethod)) else fn
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
163
|
+
inferred_key = key_hint
|
|
164
|
+
if inferred_key is MISSING:
|
|
165
|
+
rt = _get_return_type(target)
|
|
166
|
+
if isinstance(rt, type):
|
|
167
|
+
inferred_key = rt
|
|
168
|
+
else:
|
|
169
|
+
inferred_key = getattr(target, "__name__", str(target))
|
|
170
|
+
setattr(target, PICO_INFRA, "provides")
|
|
171
|
+
pico_name = name if name is not None else (inferred_key if isinstance(inferred_key, str) else getattr(target, "__name__", str(target)))
|
|
172
|
+
setattr(target, PICO_NAME, pico_name)
|
|
173
|
+
setattr(target, PICO_KEY, inferred_key)
|
|
174
|
+
m = _meta_get(target)
|
|
158
175
|
m["qualifier"] = tuple(str(q) for q in qualifiers or ())
|
|
159
176
|
m["scope"] = scope
|
|
160
177
|
if primary:
|
|
@@ -169,8 +186,16 @@ def provides(
|
|
|
169
186
|
}
|
|
170
187
|
if on_missing_selector is not None:
|
|
171
188
|
m["on_missing"] = {"selector": on_missing_selector, "priority": int(on_missing_priority)}
|
|
172
|
-
return
|
|
173
|
-
|
|
189
|
+
return fn
|
|
190
|
+
|
|
191
|
+
if dargs and len(dargs) == 1 and inspect.isfunction(dargs[0]) and not dkwargs:
|
|
192
|
+
fn = dargs[0]
|
|
193
|
+
return _apply(fn, MISSING)
|
|
194
|
+
else:
|
|
195
|
+
key = dargs[0] if dargs else MISSING
|
|
196
|
+
def _decorator(fn):
|
|
197
|
+
return _apply(fn, key, **dkwargs)
|
|
198
|
+
return _decorator
|
|
174
199
|
|
|
175
200
|
class Qualifier(str):
|
|
176
201
|
__slots__ = ()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '2.0.1'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|