pico-ioc 1.3.0__py3-none-any.whl → 1.5.0__py3-none-any.whl
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/__init__.py +28 -5
- pico_ioc/_state.py +60 -25
- pico_ioc/_version.py +1 -1
- pico_ioc/api.py +38 -56
- pico_ioc/builder.py +68 -100
- pico_ioc/config.py +332 -0
- pico_ioc/container.py +26 -44
- pico_ioc/decorators.py +15 -29
- pico_ioc/infra.py +196 -0
- pico_ioc/interceptors.py +59 -33
- pico_ioc/policy.py +102 -189
- pico_ioc/proxy.py +22 -24
- pico_ioc/resolver.py +12 -40
- pico_ioc/scanner.py +42 -67
- pico_ioc/scope.py +41 -0
- {pico_ioc-1.3.0.dist-info → pico_ioc-1.5.0.dist-info}/METADATA +15 -1
- pico_ioc-1.5.0.dist-info/RECORD +23 -0
- pico_ioc-1.3.0.dist-info/RECORD +0 -20
- {pico_ioc-1.3.0.dist-info → pico_ioc-1.5.0.dist-info}/WHEEL +0 -0
- {pico_ioc-1.3.0.dist-info → pico_ioc-1.5.0.dist-info}/licenses/LICENSE +0 -0
- {pico_ioc-1.3.0.dist-info → pico_ioc-1.5.0.dist-info}/top_level.txt +0 -0
pico_ioc/scanner.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
2
|
import importlib
|
|
3
3
|
import inspect
|
|
4
4
|
import logging
|
|
@@ -6,7 +6,7 @@ import pkgutil
|
|
|
6
6
|
from types import ModuleType
|
|
7
7
|
from typing import Any, Callable, Optional, Tuple, List, Iterable
|
|
8
8
|
|
|
9
|
-
from .plugins import run_plugin_hook
|
|
9
|
+
from .plugins import run_plugin_hook, PicoPlugin
|
|
10
10
|
from .container import PicoContainer, Binder
|
|
11
11
|
from .decorators import (
|
|
12
12
|
COMPONENT_FLAG,
|
|
@@ -17,14 +17,13 @@ from .decorators import (
|
|
|
17
17
|
PROVIDES_LAZY,
|
|
18
18
|
COMPONENT_TAGS,
|
|
19
19
|
PROVIDES_TAGS,
|
|
20
|
-
|
|
20
|
+
INFRA_META,
|
|
21
21
|
)
|
|
22
22
|
from .proxy import ComponentProxy
|
|
23
23
|
from .resolver import Resolver
|
|
24
|
-
from .plugins import PicoPlugin
|
|
25
24
|
from . import _state
|
|
26
25
|
from .utils import _provider_from_class, _provider_from_callable
|
|
27
|
-
|
|
26
|
+
from .config import is_config_component, build_component_instance, ConfigRegistry
|
|
28
27
|
|
|
29
28
|
def scan_and_configure(
|
|
30
29
|
package_or_name: Any,
|
|
@@ -32,103 +31,66 @@ def scan_and_configure(
|
|
|
32
31
|
*,
|
|
33
32
|
exclude: Optional[Callable[[str], bool]] = None,
|
|
34
33
|
plugins: Tuple[PicoPlugin, ...] = (),
|
|
35
|
-
) -> tuple[int, int, list[tuple[
|
|
36
|
-
"""
|
|
37
|
-
Scan a package, bind components/factories, and collect interceptor declarations.
|
|
38
|
-
Returns: (component_count, factory_count, interceptor_decls)
|
|
39
|
-
|
|
40
|
-
interceptor_decls contains entries of the form:
|
|
41
|
-
- (cls, meta) for class-level @interceptor on a class
|
|
42
|
-
- (fn, meta) for module-level function with @interceptor
|
|
43
|
-
- ((owner_cls, fn), meta) for methods on a class decorated with @interceptor
|
|
44
|
-
"""
|
|
34
|
+
) -> tuple[int, int, list[tuple[type, dict]]]:
|
|
45
35
|
package = _as_module(package_or_name)
|
|
46
36
|
logging.info("Scanning in '%s'...", getattr(package, "__name__", repr(package)))
|
|
47
|
-
|
|
48
37
|
binder = Binder(container)
|
|
49
38
|
resolver = Resolver(container)
|
|
50
|
-
|
|
51
39
|
run_plugin_hook(plugins, "before_scan", package, binder)
|
|
52
|
-
|
|
53
|
-
comp_classes, factory_classes, interceptor_decls = _collect_decorated(
|
|
40
|
+
comp_classes, factory_classes, infra_decls = _collect_decorated(
|
|
54
41
|
package=package,
|
|
55
42
|
exclude=exclude,
|
|
56
43
|
plugins=plugins,
|
|
57
44
|
binder=binder,
|
|
58
45
|
)
|
|
59
|
-
|
|
60
46
|
run_plugin_hook(plugins, "after_scan", package, binder)
|
|
61
|
-
|
|
62
47
|
_register_component_classes(classes=comp_classes, container=container, resolver=resolver)
|
|
63
48
|
_register_factory_classes(factory_classes=factory_classes, container=container, resolver=resolver)
|
|
64
|
-
|
|
65
|
-
return len(comp_classes), len(factory_classes), interceptor_decls
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
# -------------------- Helpers --------------------
|
|
49
|
+
return len(comp_classes), len(factory_classes), infra_decls
|
|
69
50
|
|
|
70
51
|
def _as_module(package_or_name: Any) -> ModuleType:
|
|
71
52
|
if isinstance(package_or_name, str):
|
|
72
53
|
return importlib.import_module(package_or_name)
|
|
73
54
|
if hasattr(package_or_name, "__spec__"):
|
|
74
|
-
return package_or_name
|
|
55
|
+
return package_or_name
|
|
75
56
|
raise TypeError("package_or_name must be a module or importable package name (str).")
|
|
76
57
|
|
|
77
|
-
|
|
78
58
|
def _iter_package_modules(package: ModuleType) -> Iterable[str]:
|
|
79
|
-
"""Yield fully-qualified module names under a package (recursive)."""
|
|
80
59
|
try:
|
|
81
|
-
pkg_path = package.__path__
|
|
60
|
+
pkg_path = package.__path__
|
|
82
61
|
except Exception:
|
|
83
62
|
return
|
|
84
63
|
prefix = package.__name__ + "."
|
|
85
64
|
for _finder, name, _is_pkg in pkgutil.walk_packages(pkg_path, prefix):
|
|
86
65
|
yield name
|
|
87
66
|
|
|
88
|
-
|
|
89
67
|
def _collect_decorated(
|
|
90
68
|
*,
|
|
91
69
|
package: ModuleType,
|
|
92
70
|
exclude: Optional[Callable[[str], bool]],
|
|
93
71
|
plugins: Tuple[PicoPlugin, ...],
|
|
94
72
|
binder: Binder,
|
|
95
|
-
) -> Tuple[List[type], List[type], List[tuple[
|
|
73
|
+
) -> Tuple[List[type], List[type], List[tuple[type, dict]]]:
|
|
96
74
|
comps: List[type] = []
|
|
97
75
|
facts: List[type] = []
|
|
98
|
-
|
|
76
|
+
infras: List[tuple[type, dict]] = []
|
|
99
77
|
|
|
100
78
|
def _collect_from_class(cls: type):
|
|
101
|
-
# Class decorators
|
|
102
79
|
if getattr(cls, COMPONENT_FLAG, False):
|
|
103
80
|
comps.append(cls)
|
|
104
81
|
elif getattr(cls, FACTORY_FLAG, False):
|
|
105
82
|
facts.append(cls)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
# Method-level interceptors
|
|
113
|
-
for _nm, fn in inspect.getmembers(cls, predicate=inspect.isfunction):
|
|
114
|
-
meta_m = getattr(fn, INTERCEPTOR_META, None)
|
|
115
|
-
if meta_m:
|
|
116
|
-
# Preserve the owner to allow proper binding (self) later
|
|
117
|
-
interceptors.append(((cls, fn), dict(meta_m)))
|
|
83
|
+
infra_m = getattr(cls, INFRA_META, None)
|
|
84
|
+
if infra_m:
|
|
85
|
+
infras.append((cls, dict(infra_m)))
|
|
86
|
+
for _nm, _fn in inspect.getmembers(cls, predicate=inspect.isfunction):
|
|
87
|
+
pass
|
|
118
88
|
|
|
119
89
|
def _visit_module(module: ModuleType):
|
|
120
|
-
# Classes
|
|
121
90
|
for _name, obj in inspect.getmembers(module, inspect.isclass):
|
|
122
91
|
run_plugin_hook(plugins, "visit_class", module, obj, binder)
|
|
123
92
|
_collect_from_class(obj)
|
|
124
93
|
|
|
125
|
-
# Module-level functions that declare interceptors
|
|
126
|
-
for _name, fn in inspect.getmembers(module, predicate=inspect.isfunction):
|
|
127
|
-
meta = getattr(fn, INTERCEPTOR_META, None)
|
|
128
|
-
if meta:
|
|
129
|
-
interceptors.append((fn, dict(meta)))
|
|
130
|
-
|
|
131
|
-
# Walk submodules
|
|
132
94
|
for mod_name in _iter_package_modules(package):
|
|
133
95
|
if exclude and exclude(mod_name):
|
|
134
96
|
logging.info("Skipping module %s (excluded)", mod_name)
|
|
@@ -140,11 +102,10 @@ def _collect_decorated(
|
|
|
140
102
|
continue
|
|
141
103
|
_visit_module(module)
|
|
142
104
|
|
|
143
|
-
# Also visit the root module itself (in case it's a single-file module)
|
|
144
105
|
if not hasattr(package, "__path__"):
|
|
145
106
|
_visit_module(package)
|
|
146
107
|
|
|
147
|
-
return comps, facts,
|
|
108
|
+
return comps, facts, infras
|
|
148
109
|
|
|
149
110
|
def _register_component_classes(
|
|
150
111
|
*,
|
|
@@ -156,10 +117,17 @@ def _register_component_classes(
|
|
|
156
117
|
key = getattr(cls, COMPONENT_KEY, cls)
|
|
157
118
|
is_lazy = bool(getattr(cls, COMPONENT_LAZY, False))
|
|
158
119
|
tags = tuple(getattr(cls, COMPONENT_TAGS, ()))
|
|
159
|
-
|
|
120
|
+
if is_config_component(cls):
|
|
121
|
+
registry: ConfigRegistry | None = getattr(container, "_config_registry", None)
|
|
122
|
+
def _prov(_c=cls, _reg=registry):
|
|
123
|
+
if _reg is None:
|
|
124
|
+
raise RuntimeError(f"No config registry found to build {_c.__name__}")
|
|
125
|
+
return build_component_instance(_c, _reg)
|
|
126
|
+
provider = (lambda p=_prov: ComponentProxy(p)) if is_lazy else _prov
|
|
127
|
+
else:
|
|
128
|
+
provider = _provider_from_class(cls, resolver=resolver, lazy=is_lazy)
|
|
160
129
|
container.bind(key, provider, lazy=is_lazy, tags=tags)
|
|
161
130
|
|
|
162
|
-
|
|
163
131
|
def _register_factory_classes(
|
|
164
132
|
*,
|
|
165
133
|
factory_classes: List[type],
|
|
@@ -168,7 +136,6 @@ def _register_factory_classes(
|
|
|
168
136
|
) -> None:
|
|
169
137
|
for fcls in factory_classes:
|
|
170
138
|
try:
|
|
171
|
-
# Prevent accidental container access recursion while constructing factories
|
|
172
139
|
tok_res = _state._resolving.set(True)
|
|
173
140
|
try:
|
|
174
141
|
finst = resolver.create_instance(fcls)
|
|
@@ -177,21 +144,29 @@ def _register_factory_classes(
|
|
|
177
144
|
except Exception:
|
|
178
145
|
logging.exception("Error in factory %s", fcls.__name__)
|
|
179
146
|
continue
|
|
180
|
-
|
|
181
|
-
for attr_name,
|
|
147
|
+
raw_dict = getattr(fcls, "__dict__", {})
|
|
148
|
+
for attr_name, attr in inspect.getmembers(fcls):
|
|
149
|
+
func = None
|
|
150
|
+
raw = raw_dict.get(attr_name, None)
|
|
151
|
+
if isinstance(raw, classmethod):
|
|
152
|
+
func = raw.__func__
|
|
153
|
+
elif isinstance(raw, staticmethod):
|
|
154
|
+
func = raw.__func__
|
|
155
|
+
elif inspect.isfunction(attr):
|
|
156
|
+
func = attr
|
|
157
|
+
if func is None:
|
|
158
|
+
continue
|
|
182
159
|
provided_key = getattr(func, PROVIDES_KEY, None)
|
|
183
160
|
if provided_key is None:
|
|
184
161
|
continue
|
|
185
|
-
|
|
186
162
|
is_lazy = bool(getattr(func, PROVIDES_LAZY, False))
|
|
187
163
|
tags = tuple(getattr(func, PROVIDES_TAGS, ()))
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
164
|
+
if isinstance(raw, (classmethod, staticmethod)):
|
|
165
|
+
bound = getattr(fcls, attr_name)
|
|
166
|
+
else:
|
|
167
|
+
bound = getattr(finst, attr_name, func.__get__(finst, fcls))
|
|
191
168
|
prov = _provider_from_callable(bound, owner_cls=fcls, resolver=resolver, lazy=is_lazy)
|
|
192
|
-
|
|
193
169
|
if isinstance(provided_key, type):
|
|
194
|
-
# Mark for aliasing policy pipeline and ensure uniqueness of the provider key
|
|
195
170
|
try:
|
|
196
171
|
setattr(prov, "_pico_alias_for", provided_key)
|
|
197
172
|
except Exception:
|
pico_ioc/scope.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Optional
|
|
4
|
+
|
|
5
|
+
from .container import PicoContainer
|
|
6
|
+
|
|
7
|
+
class ScopedContainer(PicoContainer):
|
|
8
|
+
def __init__(self, built_container: PicoContainer, base: Optional[PicoContainer], strict: bool):
|
|
9
|
+
super().__init__(providers=getattr(built_container, "_providers", {}).copy())
|
|
10
|
+
self._active_profiles = getattr(built_container, "_active_profiles", ())
|
|
11
|
+
base_method_its = getattr(base, "_method_interceptors", ()) if base else ()
|
|
12
|
+
base_container_its = getattr(base, "_container_interceptors", ()) if base else ()
|
|
13
|
+
self._method_interceptors = base_method_its
|
|
14
|
+
self._container_interceptors = base_container_its
|
|
15
|
+
self._seen_interceptor_types = {type(it) for it in base_container_its}
|
|
16
|
+
for it in getattr(built_container, "_method_interceptors", ()):
|
|
17
|
+
self.add_method_interceptor(it)
|
|
18
|
+
for it in getattr(built_container, "_container_interceptors", ()):
|
|
19
|
+
self.add_container_interceptor(it)
|
|
20
|
+
self._base = base
|
|
21
|
+
self._strict = strict
|
|
22
|
+
if base:
|
|
23
|
+
self._singletons.update(getattr(base, "_singletons", {}))
|
|
24
|
+
|
|
25
|
+
def __enter__(self): return self
|
|
26
|
+
def __exit__(self, exc_type, exc, tb): return False
|
|
27
|
+
|
|
28
|
+
def has(self, key: Any) -> bool:
|
|
29
|
+
if super().has(key): return True
|
|
30
|
+
if not self._strict and self._base is not None:
|
|
31
|
+
return self._base.has(key)
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
def get(self, key: Any):
|
|
35
|
+
try:
|
|
36
|
+
return super().get(key)
|
|
37
|
+
except NameError as e:
|
|
38
|
+
if not self._strict and self._base is not None and self._base.has(key):
|
|
39
|
+
return self._base.get(key)
|
|
40
|
+
raise e
|
|
41
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pico-ioc
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0
|
|
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
|
|
@@ -221,6 +221,20 @@ tox
|
|
|
221
221
|
|
|
222
222
|
---
|
|
223
223
|
|
|
224
|
+
## 📜 Overview
|
|
225
|
+
|
|
226
|
+
See [OVERVIEW.md](.llm/OVERVIEW.md) Just need a quick summary?
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## 🔔 Important Changes
|
|
230
|
+
|
|
231
|
+
### 1.5.0 (2025-09-17)
|
|
232
|
+
- Introduced **`@infrastructure`** classes for bootstrap-time configuration.
|
|
233
|
+
→ They can query the model, add interceptors, wrap/replace providers, and adjust tags/qualifiers.
|
|
234
|
+
- Added new **around-style interceptors** (`MethodInterceptor.invoke`, `ContainerInterceptor.around_*`) with deterministic ordering.
|
|
235
|
+
- **Removed legacy `@interceptor` API** (before/after/error style). All interceptors must be migrated to the new contracts.
|
|
236
|
+
|
|
237
|
+
---
|
|
224
238
|
## 📜 Changelog
|
|
225
239
|
|
|
226
240
|
See [CHANGELOG.md](./CHANGELOG.md) for version history.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
pico_ioc/__init__.py,sha256=Nlj8wsCtOz1uGC9eg7GICsGc4EecU2t9AkY74E1hx_s,1400
|
|
2
|
+
pico_ioc/_state.py,sha256=C98XQZIfKy98j8fzR730eUCoqSnCkRkxUS4bH7mp73c,2154
|
|
3
|
+
pico_ioc/_version.py,sha256=wShy9YfBfroz0HjRH_aNNehkEu1_PLsd_GjTU5aCDPk,22
|
|
4
|
+
pico_ioc/api.py,sha256=-zouzi8Uel7IXpiBYwra6ctqT71LLslcvU1trS4kKh8,8359
|
|
5
|
+
pico_ioc/builder.py,sha256=lwhKliYYonar4JOUi-Nfa8sej06mwSUp0bvKBJLAYes,9007
|
|
6
|
+
pico_ioc/config.py,sha256=J3k7_2vRB2HCpikzeMzT4Ut9COFM4kcydkwZorncqSk,12317
|
|
7
|
+
pico_ioc/container.py,sha256=Chbi8Mhz_OlhD03tf1l4hQq2yGDpELL0eNKtjyjn0us,6389
|
|
8
|
+
pico_ioc/decorators.py,sha256=LSMW5DYfQIV2y60E4tIVprMDUc4M7qCNCe7Pz5nN0Hg,3448
|
|
9
|
+
pico_ioc/infra.py,sha256=IvYA2kliG6opMG8SXX6eneWCQ6tBfSeSCiLq0-f9rJ8,7924
|
|
10
|
+
pico_ioc/interceptors.py,sha256=ZJjIRyTgHh9CTRpNZwTjM8iKvMLIw5OBQ7DrpF29HHY,3465
|
|
11
|
+
pico_ioc/plugins.py,sha256=GP7WEMshggQ-FEjiShkcuLrSMxfueUnhbY9I8PcIyPU,1039
|
|
12
|
+
pico_ioc/policy.py,sha256=p7maTHNfU-zoaz3j7CY4P3ry-bYfaGxAOklcTAuF6dY,8648
|
|
13
|
+
pico_ioc/proxy.py,sha256=2XR5mwNTEpYzZFUCcFJW1psSgOgEFooeO91_2wdnRnk,6403
|
|
14
|
+
pico_ioc/public_api.py,sha256=E3sArCoI1xxkIw7xQBvLYAWcIoVJjcq1s0kH-0qIVDE,2383
|
|
15
|
+
pico_ioc/resolver.py,sha256=Fi4dHY4NuqxNxZwVVtBR1lBnhZSTBd3dDXzr8lRNe-g,4191
|
|
16
|
+
pico_ioc/scanner.py,sha256=ieJ1A2UPWLqfybjPdWQowEx7ZCkN9693pa8sUc83LGE,6612
|
|
17
|
+
pico_ioc/scope.py,sha256=pdSKcO14jt7_rB5ymLpbBI9qS9FbdvTGZfPj3Mc0Znc,1705
|
|
18
|
+
pico_ioc/utils.py,sha256=OyhOKnyepwGQ_uQKlQLt-fymEV1bQ6hCq4Me7h3dfco,1002
|
|
19
|
+
pico_ioc-1.5.0.dist-info/licenses/LICENSE,sha256=N1_nOvHTM6BobYnOTNXiQkroDqCEi6EzfGBv8lWtyZ0,1077
|
|
20
|
+
pico_ioc-1.5.0.dist-info/METADATA,sha256=11GMlO6Z1S-PhiLBBHD5gTLfrpTxAR8q-HC6zFfHDwk,10844
|
|
21
|
+
pico_ioc-1.5.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
22
|
+
pico_ioc-1.5.0.dist-info/top_level.txt,sha256=_7_RLu616z_dtRw16impXn4Mw8IXe2J4BeX5912m5dQ,9
|
|
23
|
+
pico_ioc-1.5.0.dist-info/RECORD,,
|
pico_ioc-1.3.0.dist-info/RECORD
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
pico_ioc/__init__.py,sha256=Vl0nnRou3BZY1QSUOSyZ8-PFIFrz979DAzhXp0KYvIg,1014
|
|
2
|
-
pico_ioc/_state.py,sha256=XG3Q8NsRN-Di5MrWn6kLzJXg25PXp2_qAzfgytNoP-s,1021
|
|
3
|
-
pico_ioc/_version.py,sha256=zi_LaUT_OsChAtsPXbOeRpQkCohSsOyeXfavQPM0GoE,22
|
|
4
|
-
pico_ioc/api.py,sha256=PbR9_VX3ipxgv7-vKq1TyjKC75RqyDr65QUCG4i7eeI,9172
|
|
5
|
-
pico_ioc/builder.py,sha256=QPn1yC3JQYkjV08XpCAIFs95Bi0VQG5NZnSXKx4IgsI,10730
|
|
6
|
-
pico_ioc/container.py,sha256=YCrjAhChHtDcPBg1Zz0PnMbY-6x6V313nA1liPNaUaM,6306
|
|
7
|
-
pico_ioc/decorators.py,sha256=jhJxpaR9wCeBsNm1W0ziTCF3C0LGhu866-9x4IJ9-4U,3581
|
|
8
|
-
pico_ioc/interceptors.py,sha256=-ZH-AG4h_6vUgTJGbP8YESlODhOEkdC6r82GHTRrKxk,1978
|
|
9
|
-
pico_ioc/plugins.py,sha256=GP7WEMshggQ-FEjiShkcuLrSMxfueUnhbY9I8PcIyPU,1039
|
|
10
|
-
pico_ioc/policy.py,sha256=XQs8Nr7aTq0xbNPGaeFiBgJCI65r5rXK4yQdhhQuLjM,12054
|
|
11
|
-
pico_ioc/proxy.py,sha256=ZQ0g5QT32QV_v72N9oUWtQRnTh5An66GPtvaF1HtNp8,6187
|
|
12
|
-
pico_ioc/public_api.py,sha256=E3sArCoI1xxkIw7xQBvLYAWcIoVJjcq1s0kH-0qIVDE,2383
|
|
13
|
-
pico_ioc/resolver.py,sha256=qCzyjsfq59b_XEa5LNAgWzPniqoJYxyG-mXn2fzqFsk,5063
|
|
14
|
-
pico_ioc/scanner.py,sha256=VjIW6e2nsYuI1pm38RX8yIGs7SUrvuKHPN5vgTss3h4,7257
|
|
15
|
-
pico_ioc/utils.py,sha256=OyhOKnyepwGQ_uQKlQLt-fymEV1bQ6hCq4Me7h3dfco,1002
|
|
16
|
-
pico_ioc-1.3.0.dist-info/licenses/LICENSE,sha256=N1_nOvHTM6BobYnOTNXiQkroDqCEi6EzfGBv8lWtyZ0,1077
|
|
17
|
-
pico_ioc-1.3.0.dist-info/METADATA,sha256=GRIv4XBIAY8vFFXv0BVFyT4lVqryoUEQU9qF6jOlftc,10259
|
|
18
|
-
pico_ioc-1.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
19
|
-
pico_ioc-1.3.0.dist-info/top_level.txt,sha256=_7_RLu616z_dtRw16impXn4Mw8IXe2J4BeX5912m5dQ,9
|
|
20
|
-
pico_ioc-1.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|