tnfr 6.0.0__py3-none-any.whl → 7.0.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.
Potentially problematic release.
This version of tnfr might be problematic. Click here for more details.
- tnfr/__init__.py +50 -5
- tnfr/__init__.pyi +0 -7
- tnfr/_compat.py +0 -1
- tnfr/_generated_version.py +34 -0
- tnfr/_version.py +44 -2
- tnfr/alias.py +14 -13
- tnfr/alias.pyi +5 -37
- tnfr/cache.py +9 -729
- tnfr/cache.pyi +8 -224
- tnfr/callback_utils.py +16 -31
- tnfr/callback_utils.pyi +3 -29
- tnfr/cli/__init__.py +17 -11
- tnfr/cli/__init__.pyi +0 -21
- tnfr/cli/arguments.py +175 -14
- tnfr/cli/arguments.pyi +5 -11
- tnfr/cli/execution.py +434 -48
- tnfr/cli/execution.pyi +14 -24
- tnfr/cli/utils.py +20 -3
- tnfr/cli/utils.pyi +5 -5
- tnfr/config/__init__.py +2 -1
- tnfr/config/__init__.pyi +2 -0
- tnfr/config/feature_flags.py +83 -0
- tnfr/config/init.py +1 -1
- tnfr/config/operator_names.py +1 -14
- tnfr/config/presets.py +6 -26
- tnfr/constants/__init__.py +10 -13
- tnfr/constants/__init__.pyi +10 -22
- tnfr/constants/aliases.py +31 -0
- tnfr/constants/core.py +4 -3
- tnfr/constants/init.py +1 -1
- tnfr/constants/metric.py +3 -3
- tnfr/dynamics/__init__.py +64 -10
- tnfr/dynamics/__init__.pyi +3 -4
- tnfr/dynamics/adaptation.py +79 -13
- tnfr/dynamics/aliases.py +10 -9
- tnfr/dynamics/coordination.py +77 -35
- tnfr/dynamics/dnfr.py +575 -274
- tnfr/dynamics/dnfr.pyi +1 -10
- tnfr/dynamics/integrators.py +47 -33
- tnfr/dynamics/integrators.pyi +0 -1
- tnfr/dynamics/runtime.py +489 -129
- tnfr/dynamics/sampling.py +2 -0
- tnfr/dynamics/selectors.py +101 -62
- tnfr/execution.py +15 -8
- tnfr/execution.pyi +5 -25
- tnfr/flatten.py +7 -3
- tnfr/flatten.pyi +1 -8
- tnfr/gamma.py +22 -26
- tnfr/gamma.pyi +0 -6
- tnfr/glyph_history.py +37 -26
- tnfr/glyph_history.pyi +1 -19
- tnfr/glyph_runtime.py +16 -0
- tnfr/glyph_runtime.pyi +9 -0
- tnfr/immutable.py +20 -15
- tnfr/immutable.pyi +4 -7
- tnfr/initialization.py +5 -7
- tnfr/initialization.pyi +1 -9
- tnfr/io.py +6 -305
- tnfr/io.pyi +13 -8
- tnfr/mathematics/__init__.py +81 -0
- tnfr/mathematics/backend.py +426 -0
- tnfr/mathematics/dynamics.py +398 -0
- tnfr/mathematics/epi.py +254 -0
- tnfr/mathematics/generators.py +222 -0
- tnfr/mathematics/metrics.py +119 -0
- tnfr/mathematics/operators.py +233 -0
- tnfr/mathematics/operators_factory.py +71 -0
- tnfr/mathematics/projection.py +78 -0
- tnfr/mathematics/runtime.py +173 -0
- tnfr/mathematics/spaces.py +247 -0
- tnfr/mathematics/transforms.py +292 -0
- tnfr/metrics/__init__.py +10 -10
- tnfr/metrics/coherence.py +123 -94
- tnfr/metrics/common.py +22 -13
- tnfr/metrics/common.pyi +42 -11
- tnfr/metrics/core.py +72 -14
- tnfr/metrics/diagnosis.py +48 -57
- tnfr/metrics/diagnosis.pyi +3 -7
- tnfr/metrics/export.py +3 -5
- tnfr/metrics/glyph_timing.py +41 -31
- tnfr/metrics/reporting.py +13 -6
- tnfr/metrics/sense_index.py +884 -114
- tnfr/metrics/trig.py +167 -11
- tnfr/metrics/trig.pyi +1 -0
- tnfr/metrics/trig_cache.py +112 -15
- tnfr/node.py +400 -17
- tnfr/node.pyi +55 -38
- tnfr/observers.py +111 -8
- tnfr/observers.pyi +0 -15
- tnfr/ontosim.py +9 -6
- tnfr/ontosim.pyi +0 -5
- tnfr/operators/__init__.py +529 -42
- tnfr/operators/__init__.pyi +14 -0
- tnfr/operators/definitions.py +350 -18
- tnfr/operators/definitions.pyi +0 -14
- tnfr/operators/grammar.py +760 -0
- tnfr/operators/jitter.py +28 -22
- tnfr/operators/registry.py +7 -12
- tnfr/operators/registry.pyi +0 -2
- tnfr/operators/remesh.py +38 -61
- tnfr/rng.py +17 -300
- tnfr/schemas/__init__.py +8 -0
- tnfr/schemas/grammar.json +94 -0
- tnfr/selector.py +3 -4
- tnfr/selector.pyi +1 -1
- tnfr/sense.py +22 -24
- tnfr/sense.pyi +0 -7
- tnfr/structural.py +504 -21
- tnfr/structural.pyi +41 -18
- tnfr/telemetry/__init__.py +23 -1
- tnfr/telemetry/cache_metrics.py +226 -0
- tnfr/telemetry/nu_f.py +423 -0
- tnfr/telemetry/nu_f.pyi +123 -0
- tnfr/tokens.py +1 -4
- tnfr/tokens.pyi +1 -6
- tnfr/trace.py +20 -53
- tnfr/trace.pyi +9 -37
- tnfr/types.py +244 -15
- tnfr/types.pyi +200 -14
- tnfr/units.py +69 -0
- tnfr/units.pyi +16 -0
- tnfr/utils/__init__.py +107 -48
- tnfr/utils/__init__.pyi +80 -11
- tnfr/utils/cache.py +1705 -65
- tnfr/utils/cache.pyi +370 -58
- tnfr/utils/chunks.py +104 -0
- tnfr/utils/chunks.pyi +21 -0
- tnfr/utils/data.py +95 -5
- tnfr/utils/data.pyi +8 -17
- tnfr/utils/graph.py +2 -4
- tnfr/utils/init.py +31 -7
- tnfr/utils/init.pyi +4 -11
- tnfr/utils/io.py +313 -14
- tnfr/{helpers → utils}/numeric.py +50 -24
- tnfr/utils/numeric.pyi +21 -0
- tnfr/validation/__init__.py +92 -4
- tnfr/validation/__init__.pyi +77 -17
- tnfr/validation/compatibility.py +79 -43
- tnfr/validation/compatibility.pyi +4 -6
- tnfr/validation/grammar.py +55 -133
- tnfr/validation/grammar.pyi +37 -8
- tnfr/validation/graph.py +138 -0
- tnfr/validation/graph.pyi +17 -0
- tnfr/validation/rules.py +161 -74
- tnfr/validation/rules.pyi +55 -18
- tnfr/validation/runtime.py +263 -0
- tnfr/validation/runtime.pyi +31 -0
- tnfr/validation/soft_filters.py +170 -0
- tnfr/validation/soft_filters.pyi +37 -0
- tnfr/validation/spectral.py +159 -0
- tnfr/validation/spectral.pyi +46 -0
- tnfr/validation/syntax.py +28 -139
- tnfr/validation/syntax.pyi +7 -4
- tnfr/validation/window.py +39 -0
- tnfr/validation/window.pyi +1 -0
- tnfr/viz/__init__.py +9 -0
- tnfr/viz/matplotlib.py +246 -0
- {tnfr-6.0.0.dist-info → tnfr-7.0.0.dist-info}/METADATA +63 -19
- tnfr-7.0.0.dist-info/RECORD +185 -0
- {tnfr-6.0.0.dist-info → tnfr-7.0.0.dist-info}/licenses/LICENSE.md +1 -1
- tnfr/constants_glyphs.py +0 -16
- tnfr/constants_glyphs.pyi +0 -12
- tnfr/grammar.py +0 -25
- tnfr/grammar.pyi +0 -13
- tnfr/helpers/__init__.py +0 -151
- tnfr/helpers/__init__.pyi +0 -66
- tnfr/helpers/numeric.pyi +0 -12
- tnfr/presets.py +0 -15
- tnfr/presets.pyi +0 -7
- tnfr/utils/io.pyi +0 -10
- tnfr/utils/validators.py +0 -130
- tnfr/utils/validators.pyi +0 -19
- tnfr-6.0.0.dist-info/RECORD +0 -157
- {tnfr-6.0.0.dist-info → tnfr-7.0.0.dist-info}/WHEEL +0 -0
- {tnfr-6.0.0.dist-info → tnfr-7.0.0.dist-info}/entry_points.txt +0 -0
- {tnfr-6.0.0.dist-info → tnfr-7.0.0.dist-info}/top_level.txt +0 -0
tnfr/utils/data.py
CHANGED
|
@@ -3,13 +3,16 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
|
+
import math
|
|
6
7
|
from collections import deque
|
|
7
8
|
from collections.abc import Collection, Iterable, Mapping, Sequence
|
|
9
|
+
from numbers import Real
|
|
8
10
|
from itertools import islice
|
|
9
11
|
from typing import Any, Callable, Iterator, TypeVar, cast
|
|
10
12
|
|
|
11
|
-
from
|
|
12
|
-
from .init import get_logger
|
|
13
|
+
from .numeric import kahan_sum_nd
|
|
14
|
+
from .init import get_logger
|
|
15
|
+
from .init import warn_once as _warn_once_factory
|
|
13
16
|
|
|
14
17
|
T = TypeVar("T")
|
|
15
18
|
|
|
@@ -24,6 +27,7 @@ _MAX_NEGATIVE_WARN_ONCE = 1024
|
|
|
24
27
|
|
|
25
28
|
__all__ = (
|
|
26
29
|
"convert_value",
|
|
30
|
+
"normalize_optional_int",
|
|
27
31
|
"MAX_MATERIALIZE_DEFAULT",
|
|
28
32
|
"normalize_materialize_limit",
|
|
29
33
|
"is_non_string_sequence",
|
|
@@ -48,7 +52,7 @@ def convert_value(
|
|
|
48
52
|
"""Attempt to convert a value and report failures."""
|
|
49
53
|
|
|
50
54
|
try:
|
|
51
|
-
|
|
55
|
+
converted = conv(value)
|
|
52
56
|
except (ValueError, TypeError) as exc:
|
|
53
57
|
if strict:
|
|
54
58
|
raise
|
|
@@ -58,6 +62,90 @@ def convert_value(
|
|
|
58
62
|
else:
|
|
59
63
|
_value_logger.log(level, "Could not convert value: %s", exc)
|
|
60
64
|
return False, None
|
|
65
|
+
if isinstance(converted, float) and not math.isfinite(converted):
|
|
66
|
+
if strict:
|
|
67
|
+
target = f"{key!r}" if key is not None else "value"
|
|
68
|
+
raise ValueError(f"Non-finite value {converted!r} for {target}")
|
|
69
|
+
level = log_level if log_level is not None else logging.DEBUG
|
|
70
|
+
if key is not None:
|
|
71
|
+
_value_logger.log(level, "Non-finite value for %r: %s", key, converted)
|
|
72
|
+
else:
|
|
73
|
+
_value_logger.log(level, "Non-finite value: %s", converted)
|
|
74
|
+
return False, None
|
|
75
|
+
return True, converted
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
_DEFAULT_SENTINELS = frozenset({"auto", "none", "null"})
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def normalize_optional_int(
|
|
82
|
+
value: Any,
|
|
83
|
+
*,
|
|
84
|
+
sentinels: Collection[str] | None = _DEFAULT_SENTINELS,
|
|
85
|
+
allow_non_positive: bool = True,
|
|
86
|
+
strict: bool = False,
|
|
87
|
+
error_message: str | None = None,
|
|
88
|
+
) -> int | None:
|
|
89
|
+
"""Normalise optional integers shared by CLI and runtime helpers.
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
----------
|
|
93
|
+
value:
|
|
94
|
+
Arbitrary object obtained from configuration, CLI options or graph
|
|
95
|
+
metadata.
|
|
96
|
+
sentinels:
|
|
97
|
+
Collection of case-insensitive strings that should be interpreted as
|
|
98
|
+
``None``. When ``None`` or empty, no sentinel mapping is applied.
|
|
99
|
+
allow_non_positive:
|
|
100
|
+
When ``False`` values ``<= 0`` are rejected and converted to ``None``.
|
|
101
|
+
strict:
|
|
102
|
+
When ``True`` invalid inputs raise :class:`ValueError` instead of
|
|
103
|
+
returning ``None``.
|
|
104
|
+
error_message:
|
|
105
|
+
Optional message used when ``strict`` mode raises due to invalid input
|
|
106
|
+
or disallowed non-positive values.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
if value is None:
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
if isinstance(value, int):
|
|
113
|
+
result = value
|
|
114
|
+
elif isinstance(value, Real):
|
|
115
|
+
result = int(value)
|
|
116
|
+
else:
|
|
117
|
+
text = str(value).strip()
|
|
118
|
+
if not text:
|
|
119
|
+
if strict:
|
|
120
|
+
raise ValueError(
|
|
121
|
+
error_message
|
|
122
|
+
or "Empty value is not allowed for configuration options."
|
|
123
|
+
)
|
|
124
|
+
return None
|
|
125
|
+
sentinel_set: set[str] | None = None
|
|
126
|
+
if sentinels:
|
|
127
|
+
sentinel_set = {s.lower() for s in sentinels}
|
|
128
|
+
lowered = text.lower()
|
|
129
|
+
if lowered in sentinel_set:
|
|
130
|
+
return None
|
|
131
|
+
try:
|
|
132
|
+
result = int(text)
|
|
133
|
+
except (TypeError, ValueError) as exc:
|
|
134
|
+
if strict:
|
|
135
|
+
raise ValueError(
|
|
136
|
+
error_message or f"Invalid integer value: {value!r}"
|
|
137
|
+
) from exc
|
|
138
|
+
return None
|
|
139
|
+
|
|
140
|
+
if not allow_non_positive and result <= 0:
|
|
141
|
+
if strict:
|
|
142
|
+
raise ValueError(
|
|
143
|
+
error_message
|
|
144
|
+
or "Non-positive values are not permitted for this option."
|
|
145
|
+
)
|
|
146
|
+
return None
|
|
147
|
+
|
|
148
|
+
return result
|
|
61
149
|
|
|
62
150
|
|
|
63
151
|
def negative_weights_warn_once(
|
|
@@ -65,7 +153,9 @@ def negative_weights_warn_once(
|
|
|
65
153
|
) -> Callable[[Mapping[str, float]], None]:
|
|
66
154
|
"""Return a ``WarnOnce`` callable for negative weight warnings."""
|
|
67
155
|
|
|
68
|
-
return _warn_once_factory(
|
|
156
|
+
return _warn_once_factory(
|
|
157
|
+
_collections_logger, NEGATIVE_WEIGHTS_MSG, maxsize=maxsize
|
|
158
|
+
)
|
|
69
159
|
|
|
70
160
|
|
|
71
161
|
def _log_negative_weights(negatives: Mapping[str, float]) -> None:
|
|
@@ -75,7 +165,7 @@ def _log_negative_weights(negatives: Mapping[str, float]) -> None:
|
|
|
75
165
|
|
|
76
166
|
|
|
77
167
|
def _resolve_negative_warn_handler(
|
|
78
|
-
warn_once: bool | Callable[[Mapping[str, float]], None]
|
|
168
|
+
warn_once: bool | Callable[[Mapping[str, float]], None],
|
|
79
169
|
) -> Callable[[Mapping[str, float]], None]:
|
|
80
170
|
"""Return a callable that logs negative weight warnings."""
|
|
81
171
|
|
tnfr/utils/data.pyi
CHANGED
|
@@ -11,7 +11,6 @@ NEGATIVE_WEIGHTS_MSG: str
|
|
|
11
11
|
|
|
12
12
|
__all__: tuple[str, ...]
|
|
13
13
|
|
|
14
|
-
|
|
15
14
|
def convert_value(
|
|
16
15
|
value: Any,
|
|
17
16
|
conv: Callable[[Any], T],
|
|
@@ -20,35 +19,31 @@ def convert_value(
|
|
|
20
19
|
key: str | None = ...,
|
|
21
20
|
log_level: int | None = ...,
|
|
22
21
|
) -> tuple[bool, T | None]: ...
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
def normalize_optional_int(
|
|
23
|
+
value: Any,
|
|
24
|
+
*,
|
|
25
|
+
sentinels: Collection[str] | None = ...,
|
|
26
|
+
allow_non_positive: bool = ...,
|
|
27
|
+
strict: bool = ...,
|
|
28
|
+
error_message: str | None = ...,
|
|
29
|
+
) -> int | None: ...
|
|
25
30
|
def negative_weights_warn_once(
|
|
26
31
|
*,
|
|
27
32
|
maxsize: int = ...,
|
|
28
33
|
) -> Callable[[Mapping[str, float]], None]: ...
|
|
29
|
-
|
|
30
|
-
|
|
31
34
|
def is_non_string_sequence(obj: Any) -> bool: ...
|
|
32
|
-
|
|
33
|
-
|
|
34
35
|
def flatten_structure(
|
|
35
36
|
obj: Any,
|
|
36
37
|
*,
|
|
37
38
|
expand: Callable[[Any], Iterable[Any] | None] | None = ...,
|
|
38
39
|
) -> Iterator[Any]: ...
|
|
39
|
-
|
|
40
|
-
|
|
41
40
|
def normalize_materialize_limit(max_materialize: int | None) -> int | None: ...
|
|
42
|
-
|
|
43
|
-
|
|
44
41
|
def ensure_collection(
|
|
45
42
|
it: Iterable[T],
|
|
46
43
|
*,
|
|
47
44
|
max_materialize: int | None = ...,
|
|
48
45
|
error_msg: str | None = ...,
|
|
49
46
|
) -> Collection[T]: ...
|
|
50
|
-
|
|
51
|
-
|
|
52
47
|
def normalize_weights(
|
|
53
48
|
dict_like: Mapping[str, Any],
|
|
54
49
|
keys: Iterable[str] | Sequence[str],
|
|
@@ -58,13 +53,9 @@ def normalize_weights(
|
|
|
58
53
|
warn_once: bool | Callable[[Mapping[str, float]], None] = ...,
|
|
59
54
|
error_on_conversion: bool = ...,
|
|
60
55
|
) -> dict[str, float]: ...
|
|
61
|
-
|
|
62
|
-
|
|
63
56
|
def normalize_counter(
|
|
64
57
|
counts: Mapping[str, float | int],
|
|
65
58
|
) -> tuple[dict[str, float], float]: ...
|
|
66
|
-
|
|
67
|
-
|
|
68
59
|
def mix_groups(
|
|
69
60
|
dist: Mapping[str, float],
|
|
70
61
|
groups: Mapping[str, Iterable[str]],
|
tnfr/utils/graph.py
CHANGED
|
@@ -18,7 +18,7 @@ __all__ = (
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def get_graph(
|
|
21
|
-
obj: GraphLike | TNFRGraph | MutableMapping[str, Any]
|
|
21
|
+
obj: GraphLike | TNFRGraph | MutableMapping[str, Any],
|
|
22
22
|
) -> MutableMapping[str, Any]:
|
|
23
23
|
"""Return the graph-level metadata mapping for ``obj``.
|
|
24
24
|
|
|
@@ -62,9 +62,7 @@ def get_graph_mapping(
|
|
|
62
62
|
return MappingProxyType(data)
|
|
63
63
|
|
|
64
64
|
|
|
65
|
-
def mark_dnfr_prep_dirty(
|
|
66
|
-
G: GraphLike | TNFRGraph | MutableMapping[str, Any]
|
|
67
|
-
) -> None:
|
|
65
|
+
def mark_dnfr_prep_dirty(G: GraphLike | TNFRGraph | MutableMapping[str, Any]) -> None:
|
|
68
66
|
"""Flag ΔNFR preparation data as stale by marking ``G.graph``.
|
|
69
67
|
|
|
70
68
|
``G`` is constrained to the :class:`~tnfr.types.GraphLike` protocol, a
|
tnfr/utils/init.py
CHANGED
|
@@ -17,7 +17,7 @@ from collections import OrderedDict
|
|
|
17
17
|
from dataclasses import dataclass, field
|
|
18
18
|
from typing import Any, Callable, Hashable, Iterable, Iterator, Literal, Mapping
|
|
19
19
|
|
|
20
|
-
from
|
|
20
|
+
from .cache import CacheManager
|
|
21
21
|
|
|
22
22
|
__all__ = (
|
|
23
23
|
"_configure_root",
|
|
@@ -86,7 +86,9 @@ class WarnOnce:
|
|
|
86
86
|
every invocation.
|
|
87
87
|
"""
|
|
88
88
|
|
|
89
|
-
def __init__(
|
|
89
|
+
def __init__(
|
|
90
|
+
self, logger: logging.Logger, msg: str, *, maxsize: int = 1024
|
|
91
|
+
) -> None:
|
|
90
92
|
self._logger = logger
|
|
91
93
|
self._msg = msg
|
|
92
94
|
self._maxsize = maxsize
|
|
@@ -295,7 +297,9 @@ def _get_success(key: str) -> Any | None:
|
|
|
295
297
|
|
|
296
298
|
with _IMPORT_CACHE_MANAGER.timer(_SUCCESS_CACHE_NAME):
|
|
297
299
|
|
|
298
|
-
def _lookup(
|
|
300
|
+
def _lookup(
|
|
301
|
+
cache: OrderedDict[str, _CacheEntry],
|
|
302
|
+
) -> OrderedDict[str, _CacheEntry]:
|
|
299
303
|
nonlocal result, hit
|
|
300
304
|
entry = cache.get(key)
|
|
301
305
|
if entry is None:
|
|
@@ -327,7 +331,9 @@ def _store_success(key: str, value: Any) -> None:
|
|
|
327
331
|
_trim_cache(_SUCCESS_CACHE_NAME, cache)
|
|
328
332
|
return cache
|
|
329
333
|
|
|
330
|
-
def _purge_failure(
|
|
334
|
+
def _purge_failure(
|
|
335
|
+
cache: OrderedDict[str, Exception],
|
|
336
|
+
) -> OrderedDict[str, Exception]:
|
|
331
337
|
if cache.pop(key, None) is not None:
|
|
332
338
|
_IMPORT_CACHE_MANAGER.increment_eviction(_FAILURE_CACHE_NAME)
|
|
333
339
|
return cache
|
|
@@ -368,7 +374,9 @@ def _store_failure(key: str, exc: Exception) -> None:
|
|
|
368
374
|
_trim_cache(_FAILURE_CACHE_NAME, cache)
|
|
369
375
|
return cache
|
|
370
376
|
|
|
371
|
-
def _purge_success(
|
|
377
|
+
def _purge_success(
|
|
378
|
+
cache: OrderedDict[str, _CacheEntry],
|
|
379
|
+
) -> OrderedDict[str, _CacheEntry]:
|
|
372
380
|
if cache.pop(key, None) is not None:
|
|
373
381
|
_IMPORT_CACHE_MANAGER.increment_eviction(_SUCCESS_CACHE_NAME)
|
|
374
382
|
return cache
|
|
@@ -549,24 +557,36 @@ class LazyImportProxy:
|
|
|
549
557
|
return self._resolve()
|
|
550
558
|
|
|
551
559
|
def __getattr__(self, item: str) -> Any:
|
|
560
|
+
"""Proxy attribute access to the resolved target."""
|
|
561
|
+
|
|
552
562
|
return getattr(self._resolve(), item)
|
|
553
563
|
|
|
554
564
|
def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
|
565
|
+
"""Invoke the resolved target with ``args``/``kwargs``."""
|
|
566
|
+
|
|
555
567
|
return self._resolve()(*args, **kwargs)
|
|
556
568
|
|
|
557
569
|
def __bool__(self) -> bool:
|
|
570
|
+
"""Return truthiness of the resolved target."""
|
|
571
|
+
|
|
558
572
|
return bool(self._resolve())
|
|
559
573
|
|
|
560
574
|
def __repr__(self) -> str: # pragma: no cover - representation helper
|
|
575
|
+
"""Return representation showing resolution status."""
|
|
576
|
+
|
|
561
577
|
target = self._resolved_target()
|
|
562
578
|
if target is self._UNRESOLVED:
|
|
563
579
|
return f"<LazyImportProxy pending={self._key!r}>"
|
|
564
580
|
return repr(target)
|
|
565
581
|
|
|
566
582
|
def __str__(self) -> str: # pragma: no cover - representation helper
|
|
583
|
+
"""Return string representation of the resolved target."""
|
|
584
|
+
|
|
567
585
|
return str(self._resolve())
|
|
568
586
|
|
|
569
587
|
def __iter__(self) -> Iterator[Any]: # pragma: no cover - passthrough helper
|
|
588
|
+
"""Yield iteration from the resolved target."""
|
|
589
|
+
|
|
570
590
|
return iter(self._resolve())
|
|
571
591
|
|
|
572
592
|
|
|
@@ -629,7 +649,9 @@ def _normalise_warm_specs(
|
|
|
629
649
|
if extra:
|
|
630
650
|
raise ValueError("'attr' can only be combined with a single module name")
|
|
631
651
|
if not isinstance(module, str):
|
|
632
|
-
raise TypeError(
|
|
652
|
+
raise TypeError(
|
|
653
|
+
"'attr' requires the first argument to be a module name string"
|
|
654
|
+
)
|
|
633
655
|
return [(module, attr)]
|
|
634
656
|
|
|
635
657
|
specs: list[_ModuleSpec]
|
|
@@ -660,7 +682,9 @@ def _normalise_warm_specs(
|
|
|
660
682
|
raise TypeError("Invalid module specification for warm_cached_import")
|
|
661
683
|
normalised.append((module_name, module_attr))
|
|
662
684
|
continue
|
|
663
|
-
raise TypeError(
|
|
685
|
+
raise TypeError(
|
|
686
|
+
"Module specifications must be strings or (module, attr) tuples"
|
|
687
|
+
)
|
|
664
688
|
|
|
665
689
|
return normalised
|
|
666
690
|
|
tnfr/utils/init.pyi
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
from collections.abc import Callable, Iterable, Iterator, Mapping
|
|
2
1
|
import logging
|
|
2
|
+
from collections.abc import Callable, Iterable, Iterator, Mapping
|
|
3
3
|
from typing import Any, Hashable, Literal
|
|
4
4
|
|
|
5
5
|
__all__: tuple[str, ...]
|
|
6
6
|
|
|
7
7
|
def __getattr__(name: str) -> Any: ...
|
|
8
8
|
|
|
9
|
-
|
|
10
9
|
class WarnOnce:
|
|
11
|
-
def __init__(
|
|
10
|
+
def __init__(
|
|
11
|
+
self, logger: logging.Logger, msg: str, *, maxsize: int = ...
|
|
12
|
+
) -> None: ...
|
|
12
13
|
def __call__(
|
|
13
14
|
self,
|
|
14
15
|
data: Mapping[Hashable, Any] | Hashable,
|
|
@@ -16,7 +17,6 @@ class WarnOnce:
|
|
|
16
17
|
) -> None: ...
|
|
17
18
|
def clear(self) -> None: ...
|
|
18
19
|
|
|
19
|
-
|
|
20
20
|
class LazyImportProxy:
|
|
21
21
|
def __init__(
|
|
22
22
|
self,
|
|
@@ -31,7 +31,6 @@ class LazyImportProxy:
|
|
|
31
31
|
def __iter__(self) -> Iterator[Any]: ...
|
|
32
32
|
def resolve(self) -> Any: ...
|
|
33
33
|
|
|
34
|
-
|
|
35
34
|
class ImportRegistry:
|
|
36
35
|
limit: int
|
|
37
36
|
failed: Mapping[str, None]
|
|
@@ -44,7 +43,6 @@ class ImportRegistry:
|
|
|
44
43
|
def clear(self) -> None: ...
|
|
45
44
|
def __contains__(self, key: str) -> bool: ...
|
|
46
45
|
|
|
47
|
-
|
|
48
46
|
EMIT_MAP: Mapping[str, Callable[[str], None]]
|
|
49
47
|
IMPORT_LOG: ImportRegistry
|
|
50
48
|
_IMPORT_STATE: ImportRegistry
|
|
@@ -52,13 +50,10 @@ _LOGGING_CONFIGURED: bool
|
|
|
52
50
|
_DEFAULT_CACHE_SIZE: int
|
|
53
51
|
_FAILED_IMPORT_LIMIT: int
|
|
54
52
|
|
|
55
|
-
|
|
56
53
|
def _configure_root() -> None: ...
|
|
57
54
|
def _reset_import_state() -> None: ...
|
|
58
55
|
def _reset_logging_state() -> None: ...
|
|
59
56
|
def _warn_failure(module: str, attr: str | None, err: Exception) -> None: ...
|
|
60
|
-
|
|
61
|
-
|
|
62
57
|
def cached_import(
|
|
63
58
|
module_name: str,
|
|
64
59
|
attr: str | None = ...,
|
|
@@ -67,8 +62,6 @@ def cached_import(
|
|
|
67
62
|
emit: Literal["warn", "log", "both"] = ...,
|
|
68
63
|
lazy: bool = ...,
|
|
69
64
|
) -> Any | None: ...
|
|
70
|
-
|
|
71
|
-
|
|
72
65
|
def warm_cached_import(
|
|
73
66
|
module: str | tuple[str, str | None] | Iterable[str | tuple[str, str | None]],
|
|
74
67
|
*extra: str | tuple[str, str | None],
|