numbox 0.3.2__py3-none-any.whl → 0.3.4__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 numbox might be problematic. Click here for more details.
- numbox/__init__.py +1 -1
- numbox/core/variable/variable.py +85 -9
- {numbox-0.3.2.dist-info → numbox-0.3.4.dist-info}/METADATA +1 -1
- {numbox-0.3.2.dist-info → numbox-0.3.4.dist-info}/RECORD +7 -7
- {numbox-0.3.2.dist-info → numbox-0.3.4.dist-info}/LICENSE +0 -0
- {numbox-0.3.2.dist-info → numbox-0.3.4.dist-info}/WHEEL +0 -0
- {numbox-0.3.2.dist-info → numbox-0.3.4.dist-info}/top_level.txt +0 -0
numbox/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '0.3.
|
|
1
|
+
__version__ = '0.3.4'
|
numbox/core/variable/variable.py
CHANGED
|
@@ -3,7 +3,7 @@ import warnings
|
|
|
3
3
|
from collections import defaultdict
|
|
4
4
|
from dataclasses import dataclass, field
|
|
5
5
|
from typing import (
|
|
6
|
-
Any, Callable, Dict, List, Mapping, Set, Tuple, TypeAlias, Union
|
|
6
|
+
Any, Callable, Dict, Iterator, List, Mapping, Set, Tuple, TypeAlias, Union
|
|
7
7
|
)
|
|
8
8
|
|
|
9
9
|
|
|
@@ -23,7 +23,7 @@ _null = _Null()
|
|
|
23
23
|
QUAL_SEP = "."
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
def
|
|
26
|
+
def make_qual_name(namespace_name: str, var_name: str) -> str:
|
|
27
27
|
""" Each `Variable` instance is best contained in a
|
|
28
28
|
mapping namespace, with the given `namespace_name`.
|
|
29
29
|
This function thereby returns qualified name of the
|
|
@@ -55,6 +55,9 @@ class External:
|
|
|
55
55
|
self._vars[name] = variable
|
|
56
56
|
return variable
|
|
57
57
|
|
|
58
|
+
def __iter__(self) -> Iterator['Variable']:
|
|
59
|
+
return iter(self._vars.values())
|
|
60
|
+
|
|
58
61
|
|
|
59
62
|
@dataclass(frozen=True)
|
|
60
63
|
class Variable:
|
|
@@ -92,6 +95,8 @@ class Variable:
|
|
|
92
95
|
these variables.
|
|
93
96
|
:param formula: (optional) function that calculates value
|
|
94
97
|
of this `Variable` from its sources.
|
|
98
|
+
:param metadata: any possible metadata associated with
|
|
99
|
+
this variable.
|
|
95
100
|
:param cacheable: (default `False`) when `True`, the
|
|
96
101
|
corresponding `Value` (see below) will be cached during
|
|
97
102
|
calculation by the `id` of the corresponding Python object
|
|
@@ -102,6 +107,7 @@ class Variable:
|
|
|
102
107
|
source: str = field(default="")
|
|
103
108
|
inputs: Mapping[str, str] = field(default_factory=lambda: {})
|
|
104
109
|
formula: Callable = field(default=None)
|
|
110
|
+
metadata: str | None = field(default=None)
|
|
105
111
|
cacheable: bool = field(default=False)
|
|
106
112
|
|
|
107
113
|
def __hash__(self):
|
|
@@ -111,7 +117,7 @@ class Variable:
|
|
|
111
117
|
return isinstance(other, Variable) and self.name == other.name and self.source == other.source
|
|
112
118
|
|
|
113
119
|
def qual_name(self) -> str:
|
|
114
|
-
return
|
|
120
|
+
return make_qual_name(self.source, self.name)
|
|
115
121
|
|
|
116
122
|
|
|
117
123
|
class Variables:
|
|
@@ -140,6 +146,9 @@ class Variables:
|
|
|
140
146
|
"""
|
|
141
147
|
return self.variables[variable_name]
|
|
142
148
|
|
|
149
|
+
def __iter__(self) -> Iterator[Variable]:
|
|
150
|
+
return iter(self.variables.values())
|
|
151
|
+
|
|
143
152
|
|
|
144
153
|
@dataclass
|
|
145
154
|
class Value:
|
|
@@ -169,8 +178,8 @@ class CompiledNode:
|
|
|
169
178
|
inputs: List[Variable]
|
|
170
179
|
|
|
171
180
|
def __post_init__(self):
|
|
172
|
-
if self.variable.formula
|
|
173
|
-
raise RuntimeError(f"{self.variable} contains
|
|
181
|
+
if self.variable.formula and not self.inputs:
|
|
182
|
+
raise RuntimeError(f"{self.variable} contains formula but no inputs, how come?")
|
|
174
183
|
|
|
175
184
|
def __hash__(self):
|
|
176
185
|
return hash((self.variable.source, self.variable.name))
|
|
@@ -209,7 +218,6 @@ class CompiledGraph:
|
|
|
209
218
|
source and then from the name of the variable within that
|
|
210
219
|
source to the variable's actual value.
|
|
211
220
|
:param values: runtime storage of all values, instance of `Values`.
|
|
212
|
-
:return:
|
|
213
221
|
"""
|
|
214
222
|
self._assign_external_values(external_values, values)
|
|
215
223
|
self._calculate(self.ordered_nodes, values)
|
|
@@ -238,7 +246,7 @@ class CompiledGraph:
|
|
|
238
246
|
for var_name, variable in variables.items():
|
|
239
247
|
if var_name not in provided:
|
|
240
248
|
raise KeyError(
|
|
241
|
-
f"Missing value for external variable '{
|
|
249
|
+
f"Missing value for external variable '{make_qual_name(source_name, var_name)}'"
|
|
242
250
|
)
|
|
243
251
|
values.get(variable).value = provided[var_name]
|
|
244
252
|
|
|
@@ -295,7 +303,7 @@ class CompiledGraph:
|
|
|
295
303
|
for src, vals in changed.items():
|
|
296
304
|
for name, val in vals.items():
|
|
297
305
|
variable = self.required_external_variables.get(src, {}).get(name)
|
|
298
|
-
qual =
|
|
306
|
+
qual = make_qual_name(src, name)
|
|
299
307
|
if variable is None:
|
|
300
308
|
try:
|
|
301
309
|
variable = next(n.variable for n in self.ordered_nodes if n.variable.qual_name() == qual)
|
|
@@ -409,7 +417,7 @@ class Graph:
|
|
|
409
417
|
if isinstance(source, External):
|
|
410
418
|
used_external_vars.add(variable)
|
|
411
419
|
for input_name, input_source in variable.inputs.items():
|
|
412
|
-
visit(
|
|
420
|
+
visit(make_qual_name(input_source, input_name))
|
|
413
421
|
visiting.remove(qual_name)
|
|
414
422
|
visited.add(qual_name)
|
|
415
423
|
ordered_variables.append(variable)
|
|
@@ -430,3 +438,71 @@ class Graph:
|
|
|
430
438
|
variable_source = variable.source
|
|
431
439
|
required_external_variables[variable_source][variable_name] = variable
|
|
432
440
|
return required_external_variables
|
|
441
|
+
|
|
442
|
+
def _build_reverse_dependencies(self) -> Dict[str, set[str]]:
|
|
443
|
+
"""
|
|
444
|
+
Utility to calculate set of qualified names of variables
|
|
445
|
+
impacted by each of the encountered inputs.
|
|
446
|
+
"""
|
|
447
|
+
reverse = defaultdict(set)
|
|
448
|
+
for source_name, source in self.registry.items():
|
|
449
|
+
for variable in source:
|
|
450
|
+
var_qual = make_qual_name(source_name, variable.name)
|
|
451
|
+
for input_name, input_source in variable.inputs.items():
|
|
452
|
+
input_qual = make_qual_name(input_source, input_name)
|
|
453
|
+
reverse[input_qual].add(var_qual)
|
|
454
|
+
return reverse
|
|
455
|
+
|
|
456
|
+
def dependents_of(self, qual_names: List[str] | Set[str] | str) -> Set[str]:
|
|
457
|
+
"""
|
|
458
|
+
Return qualified names of `Variable`s that directly or indirectly
|
|
459
|
+
depend on any of `qual_names`.
|
|
460
|
+
"""
|
|
461
|
+
if isinstance(qual_names, str):
|
|
462
|
+
qual_names = {qual_names}
|
|
463
|
+
else:
|
|
464
|
+
qual_names = set(qual_names)
|
|
465
|
+
reverse = self._build_reverse_dependencies()
|
|
466
|
+
result = set(qual_names)
|
|
467
|
+
stack = list(qual_names)
|
|
468
|
+
while stack:
|
|
469
|
+
current = stack.pop()
|
|
470
|
+
for dep in reverse.get(current, ()):
|
|
471
|
+
if dep not in result:
|
|
472
|
+
result.add(dep)
|
|
473
|
+
stack.append(dep)
|
|
474
|
+
return result
|
|
475
|
+
|
|
476
|
+
def explain(self, qual_name: str, direct: bool = True) -> str:
|
|
477
|
+
"""
|
|
478
|
+
Follow the dependencies chain to explain how the given
|
|
479
|
+
variable is derived.
|
|
480
|
+
:param qual_name: qualified name of the `Variable`.
|
|
481
|
+
:param direct: when `True` (default), begin explanation
|
|
482
|
+
with `qual_name`.
|
|
483
|
+
"""
|
|
484
|
+
derived = set()
|
|
485
|
+
derivation = []
|
|
486
|
+
|
|
487
|
+
def collect(qual_name_: str):
|
|
488
|
+
if qual_name_ in derived:
|
|
489
|
+
return
|
|
490
|
+
derived.add(qual_name_)
|
|
491
|
+
source_name, variable_name = qual_name_.split(QUAL_SEP)
|
|
492
|
+
variable_source = self.registry[source_name]
|
|
493
|
+
variable = variable_source[variable_name]
|
|
494
|
+
inputs_qual_names = []
|
|
495
|
+
for input_name, input_source in variable.inputs.items():
|
|
496
|
+
inputs_qual_names.append(make_qual_name(input_source, input_name))
|
|
497
|
+
collect(make_qual_name(input_source, input_name))
|
|
498
|
+
if isinstance(variable_source, External):
|
|
499
|
+
derivation.append(f"'{variable_name}' comes from external source '{source_name}'\n")
|
|
500
|
+
else:
|
|
501
|
+
derivation.append(
|
|
502
|
+
f"""'{qual_name_}' depends on {tuple(sorted(inputs_qual_names))} via \n\n{variable.metadata}"""
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
collect(qual_name)
|
|
506
|
+
derivation = reversed(derivation) if direct else derivation
|
|
507
|
+
derivation_txt = "\n" + "\n".join(derivation)
|
|
508
|
+
return derivation_txt
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
numbox/__init__.py,sha256=
|
|
1
|
+
numbox/__init__.py,sha256=Yyz9rKJSsP7kA7oh3l-XTxOFzEuuSR9Ly7oYXA1hfFM,22
|
|
2
2
|
numbox/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
numbox/core/configurations.py,sha256=0bCmxXL-QMwtvyIDhpXLeT-1KJMf_QpH0wLuEvYLGxQ,68
|
|
4
4
|
numbox/core/any/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -15,7 +15,7 @@ numbox/core/bindings/utils.py,sha256=OATfF4k8e5oPa9_wlHHkLQhY_DhrPNKYdeeGu9Nj5yg
|
|
|
15
15
|
numbox/core/proxy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
numbox/core/proxy/proxy.py,sha256=Wt7yzswDmeQXt0yjcTcnLi2coneowSHWXy_IFpZZJMU,3612
|
|
17
17
|
numbox/core/variable/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
-
numbox/core/variable/variable.py,sha256=
|
|
18
|
+
numbox/core/variable/variable.py,sha256=vmkecU_1_OUJov1cg0bT3fU7zxu1L7cGMBWD3_0cvQM,19621
|
|
19
19
|
numbox/core/work/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
20
|
numbox/core/work/builder.py,sha256=d0DRwJoyskp-6tYQyV1VE-v9eX99qJbQJ_FdAFrjuiE,6273
|
|
21
21
|
numbox/core/work/builder_utils.py,sha256=z8au1x10AwnzQ0_MAbQ6DnKTp3u9HeYZ1jyfkUMYjVg,1213
|
|
@@ -35,8 +35,8 @@ numbox/utils/meminfo.py,sha256=ykFi8Vt0WcHI3ztgMwvpn6NqaflDSQGL8tjI01jrzm0,1759
|
|
|
35
35
|
numbox/utils/standard.py,sha256=SPsQcyLZw21RaNCdfkIGE_QBaVnMtZjJY4F40_GGuak,347
|
|
36
36
|
numbox/utils/timer.py,sha256=5_d690Fb3L2axJBRxtoB0qe23exBosNR4qu6cno4QfY,641
|
|
37
37
|
numbox/utils/void_type.py,sha256=IkZsjNeAIShYJtvWbvERdHnl_mbF1rCRWiM3gp6II8U,404
|
|
38
|
-
numbox-0.3.
|
|
39
|
-
numbox-0.3.
|
|
40
|
-
numbox-0.3.
|
|
41
|
-
numbox-0.3.
|
|
42
|
-
numbox-0.3.
|
|
38
|
+
numbox-0.3.4.dist-info/LICENSE,sha256=ZlmjEDrZFNHpGxy_7HThWRlyBvuOMchK__qgTdiO_Uk,1446
|
|
39
|
+
numbox-0.3.4.dist-info/METADATA,sha256=E3a0zMbjM0vjbO2DVN9zaZOuW1D09A20spTbIZK2imM,2996
|
|
40
|
+
numbox-0.3.4.dist-info/WHEEL,sha256=WnJ8fYhv8N4SYVK2lLYNI6N0kVATA7b0piVUNvqIIJE,91
|
|
41
|
+
numbox-0.3.4.dist-info/top_level.txt,sha256=A67jOkfqidCSYYm6ifjN_WZyIiR1B27fjxv6nNbPvjc,7
|
|
42
|
+
numbox-0.3.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|