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.
Files changed (93) hide show
  1. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/CHANGELOG.md +26 -0
  2. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/PKG-INFO +1 -1
  3. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc/__init__.py +2 -0
  4. pico_ioc-2.0.2/src/pico_ioc/_version.py +1 -0
  5. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc/api.py +50 -25
  6. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc.egg-info/PKG-INFO +1 -1
  7. pico_ioc-2.0.1/src/pico_ioc/_version.py +0 -1
  8. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/.coveragerc +0 -0
  9. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/.github/workflows/ci.yml +0 -0
  10. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/.github/workflows/publish-to-pypi.yml +0 -0
  11. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/LICENSE +0 -0
  12. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/MANIFEST.in +0 -0
  13. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/README.md +0 -0
  14. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/README.md +0 -0
  15. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/adr/README.md +0 -0
  16. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/adr/adr-0001-async-native.md +0 -0
  17. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/adr/adr-0002-tree-based-configuration.md +0 -0
  18. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/adr/adr-0003-context-aware-scopes.md +0 -0
  19. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/adr/adr-0004-observability.md +0 -0
  20. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/adr/adr-0005-aop.md +0 -0
  21. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/adr/adr-0006-eager-validation.md +0 -0
  22. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/adr/adr-0007-event_bus.md +0 -0
  23. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/adr/adr-0008-circular-dependencies.md +0 -0
  24. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/adr/adr-0009-flexible-provides.md +0 -0
  25. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/advanced-features/README.md +0 -0
  26. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/advanced-features/aop-interceptors.md +0 -0
  27. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/advanced-features/async-resolution.md +0 -0
  28. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/advanced-features/conditional-binding.md +0 -0
  29. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/advanced-features/event-bus.md +0 -0
  30. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/advanced-features/health-checks.md +0 -0
  31. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/api-reference/README.md +0 -0
  32. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/api-reference/container.md +0 -0
  33. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/api-reference/decorators.md +0 -0
  34. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/api-reference/glossary.md +0 -0
  35. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/api-reference/protocols.md +0 -0
  36. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/architecture/README.md +0 -0
  37. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/architecture/comparison.md +0 -0
  38. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/architecture/design-principles.md +0 -0
  39. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/architecture/internals.md +0 -0
  40. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/cookbook/README.md +0 -0
  41. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/cookbook/pattern-aop-feature-toggle.md +0 -0
  42. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/cookbook/pattern-aop-profiling.md +0 -0
  43. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/cookbook/pattern-aop-security.md +0 -0
  44. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/cookbook/pattern-aop-structured-logging.md +0 -0
  45. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/cookbook/pattern-cli-app.md +0 -0
  46. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/cookbook/pattern-cqrs.md +0 -0
  47. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/cookbook/pattern-dynamic-langchain.md +0 -0
  48. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/cookbook/pattern-hot-reload.md +0 -0
  49. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/cookbook/pattern-multi-tenant.md +0 -0
  50. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/getting-started.md +0 -0
  51. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/integrations/README.md +0 -0
  52. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/integrations/ai-langchain.md +0 -0
  53. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/integrations/web-django.md +0 -0
  54. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/integrations/web-fastapi.md +0 -0
  55. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/integrations/web-flask.md +0 -0
  56. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/observability/README.md +0 -0
  57. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/observability/container-context.md +0 -0
  58. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/observability/exporting-graph.md +0 -0
  59. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/observability/observers-metrics.md +0 -0
  60. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/overview.md +0 -0
  61. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/user-guide/README.md +0 -0
  62. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/user-guide/configuration-basic.md +0 -0
  63. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/user-guide/configuration-binding.md +0 -0
  64. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/user-guide/core-concepts.md +0 -0
  65. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/user-guide/qualifiers-lists.md +0 -0
  66. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/user-guide/scopes-lifecycle.md +0 -0
  67. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/docs/user-guide/testing.md +0 -0
  68. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/pyproject.toml +0 -0
  69. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/setup.cfg +0 -0
  70. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc/aop.py +0 -0
  71. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc/config_runtime.py +0 -0
  72. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc/constants.py +0 -0
  73. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc/container.py +0 -0
  74. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc/event_bus.py +0 -0
  75. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc/exceptions.py +0 -0
  76. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc/factory.py +0 -0
  77. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc/locator.py +0 -0
  78. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc/scope.py +0 -0
  79. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc.egg-info/SOURCES.txt +0 -0
  80. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc.egg-info/dependency_links.txt +0 -0
  81. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc.egg-info/requires.txt +0 -0
  82. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/src/pico_ioc.egg-info/top_level.txt +0 -0
  83. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/test.txt +0 -0
  84. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/tests/test_configured.py +0 -0
  85. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/tests/test_container_context.py +0 -0
  86. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/tests/test_container_runtime.py +0 -0
  87. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/tests/test_event_bus.py +0 -0
  88. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/tests/test_pico_extends.py +0 -0
  89. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/tests/test_pico_integration.py +0 -0
  90. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/tests/test_provides_module_functions.py +0 -0
  91. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/tests/test_provides_static_methods.py +0 -0
  92. {pico_ioc-2.0.1 → pico_ioc-2.0.2}/tests/test_resolution_graph.py +0 -0
  93. {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]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pico-ioc
3
- Version: 2.0.1
3
+ Version: 2.0.2
4
4
  Summary: A minimalist, zero-dependency Inversion of Control (IoC) container for Python.
5
5
  Author-email: David Perez Cabrera <dperezcabrera@gmail.com>
6
6
  License: MIT License
@@ -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
- key: Any,
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
- @functools.wraps(target)
152
- def w(*a, **k):
153
- return target(*a, **k)
154
- setattr(w, PICO_INFRA, "provides")
155
- setattr(w, PICO_NAME, name if name is not None else key)
156
- setattr(w, PICO_KEY, key)
157
- m = _meta_get(w)
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 w
173
- return dec
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pico-ioc
3
- Version: 2.0.1
3
+ Version: 2.0.2
4
4
  Summary: A minimalist, zero-dependency Inversion of Control (IoC) container for Python.
5
5
  Author-email: David Perez Cabrera <dperezcabrera@gmail.com>
6
6
  License: MIT License
@@ -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