dataioc 0.1.0__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.
@@ -0,0 +1,10 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.py[cod]
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ .coverage
7
+ htmlcov/
8
+ dist/
9
+ site/
10
+ *.egg-info/
dataioc-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 dyuu7
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
dataioc-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,119 @@
1
+ Metadata-Version: 2.4
2
+ Name: dataioc
3
+ Version: 0.1.0
4
+ Summary: Declarative data dependency graphs for Python, resolved on demand with provider overrides.
5
+ Project-URL: Documentation, https://dyuu7.github.io/dataioc/
6
+ Project-URL: Issues, https://github.com/dyuu7/dataioc/issues
7
+ Project-URL: Repository, https://github.com/dyuu7/dataioc
8
+ Author: yanang007, dyuu7
9
+ Maintainer: dyuu7
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: IoC,data dependencies,dependency graph,dependency injection,lazy evaluation,providers
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.9
25
+ Requires-Dist: typing-extensions>=4.10.0; python_version < '3.11'
26
+ Provides-Extra: numpy
27
+ Requires-Dist: numpy<3,>=1.26; extra == 'numpy'
28
+ Description-Content-Type: text/markdown
29
+
30
+ # dataioc
31
+
32
+ **Declarative data dependency graphs for Python.**
33
+
34
+ Each `DataDescriptor` names a quantity and defines how it is derived from its direct dependencies. `DataIoC` composes these local rules into a graph, then resolves and caches only the subgraph required by the result you request. Bind a provider to any quantity to replace that part of the graph without changing downstream calculations.
35
+
36
+ [English](https://github.com/dyuu7/dataioc/blob/main/README.md) | [简体中文](https://github.com/dyuu7/dataioc/blob/main/README.zh-CN.md) | [Documentation](https://dyuu7.github.io/dataioc/) | [PyPI](https://pypi.org/project/dataioc/)
37
+
38
+ [![CI](https://github.com/dyuu7/dataioc/actions/workflows/ci.yml/badge.svg)](https://github.com/dyuu7/dataioc/actions/workflows/ci.yml)
39
+
40
+ ## Example
41
+
42
+ The raw readings `10`, `20`, and `60` represent measurements of `1`, `2`, and `6`.
43
+
44
+ <img src="https://raw.githubusercontent.com/dyuu7/dataioc/main/docs/assets/data-flow.png" width="720" alt="By default, RawReadings are converted into Measurements. When a provider binds recorded data to Measurements, that default derivation is replaced while Statistics and Report remain unchanged." />
45
+
46
+ ```python
47
+ from dataioc import DataDescriptor, DataIoC
48
+
49
+
50
+ class RawReadings(DataDescriptor):
51
+ pass
52
+
53
+
54
+ class Measurements(DataDescriptor):
55
+ def __build__(self, data):
56
+ return tuple(value / 10 for value in data[RawReadings])
57
+
58
+
59
+ class Statistics(DataDescriptor):
60
+ def __build__(self, data):
61
+ values = data[Measurements]
62
+ return sum(values) / len(values), max(values)
63
+
64
+
65
+ class Report(DataDescriptor):
66
+ def __build__(self, data):
67
+ mean, peak = data[Statistics]
68
+ return f"mean={mean:g}, peak={peak:g}"
69
+
70
+
71
+ data = DataIoC().add(RawReadings, (10, 20, 60))
72
+ assert data[Report] == "mean=3, peak=6"
73
+ assert data[Measurements] is data[Measurements]
74
+ ```
75
+
76
+ `data[Report]` is the only request the caller has to make. The container follows the dependencies and caches each value it builds.
77
+
78
+ To use recorded measurements instead, bind a provider for `Measurements`:
79
+
80
+ ```python
81
+ recorded = DataIoC().add_provider(Measurements, lambda _: (1.0, 2.0, 6.0))
82
+ assert recorded[Report] == "mean=3, peak=6"
83
+ ```
84
+
85
+ `RawReadings` is no longer needed; `Statistics` and `Report` stay as they are.
86
+
87
+ ## When it helps
88
+
89
+ If both the inputs and the calculation path are fixed, ordinary function calls are simpler. Use `dataioc` when the relationships stay stable but a value may come from live measurements, recorded data, a simulation, or an estimate.
90
+
91
+ `dataioc` grew out of [deinterf](https://github.com/dyuu7/deinterf). In its [direction-cosine example](https://github.com/dyuu7/deinterf/blob/main/examples/replace_direction_cosine_source_tmi.py), the same compensation terms work whether direction cosines are derived from magnetic-vector measurements or supplied by an INS estimate. [dvmss](https://github.com/dyuu7/dvmss) applies the pattern to simulation: supply the inputs, request `Tmi`, and let the container resolve the intermediate quantities.
92
+
93
+ ## Scope
94
+
95
+ `dataioc` resolves data dependencies synchronously in the current process; it is not a workflow scheduler. Use a fresh container for each dataset or provider configuration. See [Core concepts](https://dyuu7.github.io/dataioc/concepts/) for caching, diagnostics, and other runtime limits.
96
+
97
+ ## Install
98
+
99
+ ```bash
100
+ python -m pip install dataioc
101
+ python -m pip install "dataioc[numpy]"
102
+ ```
103
+
104
+ ## Documentation
105
+
106
+ - [Quickstart](https://dyuu7.github.io/dataioc/quickstart/): build a result from local dependency rules.
107
+ - [Core concepts](https://dyuu7.github.io/dataioc/concepts/): understand descriptors, builders, caching, and failure behavior.
108
+ - [Providers](https://dyuu7.github.io/dataioc/providers/): bind a quantity to another source or derivation.
109
+ - [Indexed data](https://dyuu7.github.io/dataioc/indexed-data/): reuse one model across related data groups.
110
+ - [NumPy](https://dyuu7.github.io/dataioc/numpy/): use array subclasses.
111
+ - [API](https://dyuu7.github.io/dataioc/api/): look up interfaces.
112
+
113
+ ## Contributors
114
+
115
+ [yanang007](https://github.com/yanang007) wrote the original container. [dyuu7](https://github.com/dyuu7) shaped the design, extracted it into `dataioc`, and maintains the project.
116
+
117
+ [![Contributors](https://contrib.rocks/image?repo=dyuu7/dataioc)](https://github.com/dyuu7/dataioc/graphs/contributors)
118
+
119
+ Licensed under the [MIT License](https://github.com/dyuu7/dataioc/blob/main/LICENSE).
@@ -0,0 +1,90 @@
1
+ # dataioc
2
+
3
+ **Declarative data dependency graphs for Python.**
4
+
5
+ Each `DataDescriptor` names a quantity and defines how it is derived from its direct dependencies. `DataIoC` composes these local rules into a graph, then resolves and caches only the subgraph required by the result you request. Bind a provider to any quantity to replace that part of the graph without changing downstream calculations.
6
+
7
+ [English](https://github.com/dyuu7/dataioc/blob/main/README.md) | [简体中文](https://github.com/dyuu7/dataioc/blob/main/README.zh-CN.md) | [Documentation](https://dyuu7.github.io/dataioc/) | [PyPI](https://pypi.org/project/dataioc/)
8
+
9
+ [![CI](https://github.com/dyuu7/dataioc/actions/workflows/ci.yml/badge.svg)](https://github.com/dyuu7/dataioc/actions/workflows/ci.yml)
10
+
11
+ ## Example
12
+
13
+ The raw readings `10`, `20`, and `60` represent measurements of `1`, `2`, and `6`.
14
+
15
+ <img src="https://raw.githubusercontent.com/dyuu7/dataioc/main/docs/assets/data-flow.png" width="720" alt="By default, RawReadings are converted into Measurements. When a provider binds recorded data to Measurements, that default derivation is replaced while Statistics and Report remain unchanged." />
16
+
17
+ ```python
18
+ from dataioc import DataDescriptor, DataIoC
19
+
20
+
21
+ class RawReadings(DataDescriptor):
22
+ pass
23
+
24
+
25
+ class Measurements(DataDescriptor):
26
+ def __build__(self, data):
27
+ return tuple(value / 10 for value in data[RawReadings])
28
+
29
+
30
+ class Statistics(DataDescriptor):
31
+ def __build__(self, data):
32
+ values = data[Measurements]
33
+ return sum(values) / len(values), max(values)
34
+
35
+
36
+ class Report(DataDescriptor):
37
+ def __build__(self, data):
38
+ mean, peak = data[Statistics]
39
+ return f"mean={mean:g}, peak={peak:g}"
40
+
41
+
42
+ data = DataIoC().add(RawReadings, (10, 20, 60))
43
+ assert data[Report] == "mean=3, peak=6"
44
+ assert data[Measurements] is data[Measurements]
45
+ ```
46
+
47
+ `data[Report]` is the only request the caller has to make. The container follows the dependencies and caches each value it builds.
48
+
49
+ To use recorded measurements instead, bind a provider for `Measurements`:
50
+
51
+ ```python
52
+ recorded = DataIoC().add_provider(Measurements, lambda _: (1.0, 2.0, 6.0))
53
+ assert recorded[Report] == "mean=3, peak=6"
54
+ ```
55
+
56
+ `RawReadings` is no longer needed; `Statistics` and `Report` stay as they are.
57
+
58
+ ## When it helps
59
+
60
+ If both the inputs and the calculation path are fixed, ordinary function calls are simpler. Use `dataioc` when the relationships stay stable but a value may come from live measurements, recorded data, a simulation, or an estimate.
61
+
62
+ `dataioc` grew out of [deinterf](https://github.com/dyuu7/deinterf). In its [direction-cosine example](https://github.com/dyuu7/deinterf/blob/main/examples/replace_direction_cosine_source_tmi.py), the same compensation terms work whether direction cosines are derived from magnetic-vector measurements or supplied by an INS estimate. [dvmss](https://github.com/dyuu7/dvmss) applies the pattern to simulation: supply the inputs, request `Tmi`, and let the container resolve the intermediate quantities.
63
+
64
+ ## Scope
65
+
66
+ `dataioc` resolves data dependencies synchronously in the current process; it is not a workflow scheduler. Use a fresh container for each dataset or provider configuration. See [Core concepts](https://dyuu7.github.io/dataioc/concepts/) for caching, diagnostics, and other runtime limits.
67
+
68
+ ## Install
69
+
70
+ ```bash
71
+ python -m pip install dataioc
72
+ python -m pip install "dataioc[numpy]"
73
+ ```
74
+
75
+ ## Documentation
76
+
77
+ - [Quickstart](https://dyuu7.github.io/dataioc/quickstart/): build a result from local dependency rules.
78
+ - [Core concepts](https://dyuu7.github.io/dataioc/concepts/): understand descriptors, builders, caching, and failure behavior.
79
+ - [Providers](https://dyuu7.github.io/dataioc/providers/): bind a quantity to another source or derivation.
80
+ - [Indexed data](https://dyuu7.github.io/dataioc/indexed-data/): reuse one model across related data groups.
81
+ - [NumPy](https://dyuu7.github.io/dataioc/numpy/): use array subclasses.
82
+ - [API](https://dyuu7.github.io/dataioc/api/): look up interfaces.
83
+
84
+ ## Contributors
85
+
86
+ [yanang007](https://github.com/yanang007) wrote the original container. [dyuu7](https://github.com/dyuu7) shaped the design, extracted it into `dataioc`, and maintains the project.
87
+
88
+ [![Contributors](https://contrib.rocks/image?repo=dyuu7/dataioc)](https://github.com/dyuu7/dataioc/graphs/contributors)
89
+
90
+ Licensed under the [MIT License](https://github.com/dyuu7/dataioc/blob/main/LICENSE).
@@ -0,0 +1,90 @@
1
+ # dataioc
2
+
3
+ **面向 Python 的声明式数据依赖图。**
4
+
5
+ 每个 `DataDescriptor` 代表一个数据量,并定义它如何由直接依赖推导。`DataIoC` 将这些局部规则组成计算图,只计算和缓存当前结果所需的子图。给任一数据量绑定 provider,就能替换图中的这一步,下游计算无需改动。
6
+
7
+ [English](https://github.com/dyuu7/dataioc/blob/main/README.md) | [简体中文](https://github.com/dyuu7/dataioc/blob/main/README.zh-CN.md) | [文档](https://dyuu7.github.io/dataioc/zh/) | [PyPI](https://pypi.org/project/dataioc/)
8
+
9
+ [![CI](https://github.com/dyuu7/dataioc/actions/workflows/ci.yml/badge.svg)](https://github.com/dyuu7/dataioc/actions/workflows/ci.yml)
10
+
11
+ ## 示例
12
+
13
+ 原始读数 `10`、`20`、`60` 分别表示测量值 `1`、`2`、`6`。
14
+
15
+ <img src="https://raw.githubusercontent.com/dyuu7/dataioc/main/docs/assets/data-flow.png" width="720" alt="默认情况下,RawReadings 转换为 Measurements;当 provider 将历史数据绑定到 Measurements 时,这条默认推导被替换,而 Statistics 和 Report 保持不变。" />
16
+
17
+ ```python
18
+ from dataioc import DataDescriptor, DataIoC
19
+
20
+
21
+ class RawReadings(DataDescriptor):
22
+ pass
23
+
24
+
25
+ class Measurements(DataDescriptor):
26
+ def __build__(self, data):
27
+ return tuple(value / 10 for value in data[RawReadings])
28
+
29
+
30
+ class Statistics(DataDescriptor):
31
+ def __build__(self, data):
32
+ values = data[Measurements]
33
+ return sum(values) / len(values), max(values)
34
+
35
+
36
+ class Report(DataDescriptor):
37
+ def __build__(self, data):
38
+ mean, peak = data[Statistics]
39
+ return f"mean={mean:g}, peak={peak:g}"
40
+
41
+
42
+ data = DataIoC().add(RawReadings, (10, 20, 60))
43
+ assert data[Report] == "mean=3, peak=6"
44
+ assert data[Measurements] is data[Measurements]
45
+ ```
46
+
47
+ 调用方只需请求 `data[Report]`,容器会沿依赖关系完成其余计算,并缓存得到的值。
48
+
49
+ 如果这次要使用历史测量值,只需给 `Measurements` 注册另一个 provider:
50
+
51
+ ```python
52
+ recorded = DataIoC().add_provider(Measurements, lambda _: (1.0, 2.0, 6.0))
53
+ assert recorded[Report] == "mean=3, peak=6"
54
+ ```
55
+
56
+ `RawReadings` 此时不再需要,`Statistics` 和 `Report` 都不用改。
57
+
58
+ ## 适用场景
59
+
60
+ 输入和计算过程都固定时,直接调用函数更简单。`dataioc` 适合关系稳定、数据来源会变的模型,例如同一个量在不同场景下来自实测、历史记录、仿真或估计。
61
+
62
+ `dataioc` 最初来自 [deinterf](https://github.com/dyuu7/deinterf):[方向余弦既可以根据磁矢量测量得到,也可以直接采用惯导估计](https://github.com/dyuu7/deinterf/blob/main/examples/replace_direction_cosine_source_tmi.py),后面的补偿计算不用跟着改。[dvmss](https://github.com/dyuu7/dvmss) 也沿用这种组织方式:应用给出输入并请求 `Tmi`,中间量由容器补齐。
63
+
64
+ ## 范围
65
+
66
+ `dataioc` 只负责在当前进程中同步解析数据依赖,不是工作流调度器。每组数据或 provider 配置应使用一个新容器。缓存、诊断和其他运行限制见[核心概念](https://dyuu7.github.io/dataioc/zh/concepts/)。
67
+
68
+ ## 安装
69
+
70
+ ```bash
71
+ python -m pip install dataioc
72
+ python -m pip install "dataioc[numpy]"
73
+ ```
74
+
75
+ ## 文档
76
+
77
+ - [快速开始](https://dyuu7.github.io/dataioc/zh/quickstart/):从局部依赖规则构建结果。
78
+ - [核心概念](https://dyuu7.github.io/dataioc/zh/concepts/):了解描述符、builder、缓存和失败行为。
79
+ - [Provider](https://dyuu7.github.io/dataioc/zh/providers/):将一个量绑定到另一种来源或推导方式。
80
+ - [索引数据](https://dyuu7.github.io/dataioc/zh/indexed-data/):让同一模型复用于多组相关数据。
81
+ - [NumPy](https://dyuu7.github.io/dataioc/zh/numpy/):使用数组子类。
82
+ - [API](https://dyuu7.github.io/dataioc/zh/api/):查询接口。
83
+
84
+ ## 贡献者
85
+
86
+ [yanang007](https://github.com/yanang007) 编写了最初的容器实现。[dyuu7](https://github.com/dyuu7) 提出了这套设想,将其工程化以及抽取为 `dataioc`,并负责维护。
87
+
88
+ [![贡献者](https://contrib.rocks/image?repo=dyuu7/dataioc)](https://github.com/dyuu7/dataioc/graphs/contributors)
89
+
90
+ 代码采用 [MIT License](https://github.com/dyuu7/dataioc/blob/main/LICENSE)。
@@ -0,0 +1,38 @@
1
+ # API reference
2
+
3
+ ## Core
4
+
5
+ ::: dataioc.DataIoC
6
+ options:
7
+ members:
8
+ - with_data
9
+ - add
10
+ - add_provider
11
+ - __getitem__
12
+ - __setitem__
13
+ - find_builder
14
+ - logger
15
+ show_root_heading: true
16
+ show_source: false
17
+
18
+ ::: dataioc.DataDescriptor
19
+ options:
20
+ show_root_heading: true
21
+ show_source: false
22
+
23
+ ::: dataioc.IndexedData
24
+ options:
25
+ show_root_heading: true
26
+ show_source: false
27
+
28
+ ::: dataioc.UniqueData
29
+ options:
30
+ show_root_heading: true
31
+ show_source: false
32
+
33
+ ## NumPy
34
+
35
+ ::: dataioc.DataNDArray
36
+ options:
37
+ show_root_heading: true
38
+ show_source: false
@@ -0,0 +1,38 @@
1
+ # API 参考
2
+
3
+ ## 核心
4
+
5
+ ::: dataioc.DataIoC
6
+ options:
7
+ members:
8
+ - with_data
9
+ - add
10
+ - add_provider
11
+ - __getitem__
12
+ - __setitem__
13
+ - find_builder
14
+ - logger
15
+ show_root_heading: true
16
+ show_source: false
17
+
18
+ ::: dataioc.DataDescriptor
19
+ options:
20
+ show_root_heading: true
21
+ show_source: false
22
+
23
+ ::: dataioc.IndexedData
24
+ options:
25
+ show_root_heading: true
26
+ show_source: false
27
+
28
+ ::: dataioc.UniqueData
29
+ options:
30
+ show_root_heading: true
31
+ show_source: false
32
+
33
+ ## NumPy
34
+
35
+ ::: dataioc.DataNDArray
36
+ options:
37
+ show_root_heading: true
38
+ show_source: false
@@ -0,0 +1,34 @@
1
+ ---
2
+ config:
3
+ theme: base
4
+ themeVariables:
5
+ fontFamily: monospace
6
+ fontSize: 24px
7
+ primaryColor: '#ffffff'
8
+ primaryTextColor: '#253039'
9
+ primaryBorderColor: '#7d8992'
10
+ lineColor: '#53616a'
11
+ background: '#ffffff'
12
+ clusterBkg: '#ffffff'
13
+ clusterBorder: '#aeb8bf'
14
+ flowchart:
15
+ htmlLabels: false
16
+ curve: linear
17
+ nodeSpacing: 24
18
+ rankSpacing: 32
19
+ padding: 12
20
+ ---
21
+ flowchart TB
22
+ subgraph Default["Without a provider"]
23
+ direction LR
24
+ RawReadingsA[RawReadings] --> MeasurementsA[Measurements] --> StatisticsA[Statistics] --> ReportA[Report]
25
+ end
26
+ subgraph Replaced["With a provider bound to Measurements"]
27
+ direction LR
28
+ RecordedData[Recorded data] -->|"replaces default derivation"| MeasurementsB[Measurements] --> StatisticsB[Statistics] --> ReportB[Report]
29
+ end
30
+ classDef derived fill:#e1f3ed,stroke:#278668,stroke-width:2px
31
+ classDef source fill:#ffffff,stroke:#a26624,stroke-width:2px,stroke-dasharray:5 4
32
+ class MeasurementsA,MeasurementsB derived
33
+ class RecordedData source
34
+ linkStyle 3 stroke:#a26624,stroke-width:2px,color:#a26624
Binary file
@@ -0,0 +1,94 @@
1
+ # Core concepts
2
+
3
+ `dataioc` separates three concerns: what a quantity means, how one implementation derives it, and which implementation a particular run uses. This is the data-oriented form of inversion of control.
4
+
5
+ ## A quantity is a stable key
6
+
7
+ `DataDescriptor` identifies a value in the model. Its name can represent a domain concept such as raw readings, usable measurements, statistics, or a report.
8
+
9
+ ```python
10
+ from dataioc import DataDescriptor, DataIoC
11
+
12
+
13
+ class RawReadings(DataDescriptor[tuple[int, ...]]):
14
+ pass
15
+
16
+
17
+ class Measurements(DataDescriptor[tuple[float, ...]]):
18
+ def __build__(self, container: DataIoC) -> tuple[float, ...]:
19
+ return tuple(value / 10 for value in container[RawReadings])
20
+
21
+
22
+ class Statistics(DataDescriptor[tuple[float, float]]):
23
+ def __build__(self, container: DataIoC) -> tuple[float, float]:
24
+ values = container[Measurements]
25
+ return sum(values) / len(values), max(values)
26
+
27
+
28
+ class Report(DataDescriptor[str]):
29
+ def __build__(self, container: DataIoC) -> str:
30
+ mean, peak = container[Statistics]
31
+ return f"mean={mean:g}, peak={peak:g}"
32
+ ```
33
+
34
+ A descriptor is the container key, not the stored value itself. It may carry hashable parameters so that one descriptor class can identify related values. Do not mutate a descriptor after registration, because its parameters participate in equality and hashing.
35
+
36
+ ## A builder is a local rule
37
+
38
+ A builder says how to obtain one quantity from its direct dependencies. In the example, `Report` knows about `Statistics`, but not about `Measurements` or `RawReadings`. Each lower layer owns the next relationship.
39
+
40
+ A builder can be:
41
+
42
+ - a `__build__` method on a descriptor or provider object;
43
+ - a callable accepting one `DataIoC` argument;
44
+ - a class with a suitable `__build__` method.
45
+
46
+ Dependencies are requested with `container[Target]`. These requests are ordinary Python, so a builder can use conditions, loops, libraries, or existing domain objects.
47
+
48
+ ## Requests form the graph
49
+
50
+ ```python
51
+ container = DataIoC().add(RawReadings, (10, 20, 60))
52
+ assert container[Report] == "mean=3, peak=6"
53
+ ```
54
+
55
+ Requesting `Report` forms and resolves `Report -> Statistics -> Measurements -> RawReadings`. The graph is implicit in the local rules and is expanded only as far as the requested result requires. Callers do not maintain a separate list of steps or execution order.
56
+
57
+ This is an executable dependency model, not a workflow scheduler: evaluation is synchronous and local to one process.
58
+
59
+ ## Register values and implementations
60
+
61
+ | Operation | Meaning |
62
+ | --- | --- |
63
+ | `with_data(*values)` | Register existing instances by type; indexed instances keep their descriptors |
64
+ | `add(Target)` | Register the target's own lazy builder |
65
+ | `add(Target, value)` | Register an existing value other than `None` |
66
+ | `container[Target] = value` | Set a value directly, including an explicit `None` |
67
+ | `add_provider(Target, provider)` | Bind the target to another builder |
68
+
69
+ By default, the container discovers a target's own builder on first access. Strict mode requires every requested key to have data or a registered builder:
70
+
71
+ ```python
72
+ strict = DataIoC(allow_implicit_registering=False)
73
+ strict.add(Report).add(Statistics).add(Measurements)
74
+ strict.add(RawReadings, (10, 20, 60))
75
+ assert strict[Report] == "mean=3, peak=6"
76
+ ```
77
+
78
+ `add(Target, None)` selects builder registration because `None` is the method's default argument. Use item assignment to store `None` as a value.
79
+
80
+ See [Providers](providers.md) for choosing another implementation at container assembly time.
81
+
82
+ ## Container lifetime and cache
83
+
84
+ Each successfully built key is cached, including a value of `None`. Repeated requests within one container therefore share the same result. Different indexed keys have separate cache entries; ordinary types and `UniqueData` are shared across IDs.
85
+
86
+ A container represents one resolved dataset and provider configuration. Replacing a source or provider does not invalidate values already built from it. Use a fresh container when the inputs or bindings should produce fresh results.
87
+
88
+ ## Failure and diagnostics
89
+
90
+ Failed builds are not cached; dependencies that completed successfully remain cached. A failed top-level build prints its dependency tree and re-raises the original exception. After supplying missing data or fixing the builder, the target can be requested again.
91
+
92
+ Successful access trees are normally cleared. Set `record_all=True` to retain them in `container.logger`; see [Provider diagnostics](providers.md#diagnostics).
93
+
94
+ The container does not provide thread safety, asynchronous construction, automatic dependent invalidation, or cycle detection.
@@ -0,0 +1,94 @@
1
+ # 核心概念
2
+
3
+ `dataioc` 将三件事分开:一个量表示什么、某种实现如何推导它,以及本次运行选择哪种实现。这是控制反转在数据计算中的体现。
4
+
5
+ ## 数据量是稳定的键
6
+
7
+ `DataDescriptor` 标识模型中的一个值。它的名称可以对应原始读数、可用的测量值、统计量或报告等领域概念。
8
+
9
+ ```python
10
+ from dataioc import DataDescriptor, DataIoC
11
+
12
+
13
+ class RawReadings(DataDescriptor[tuple[int, ...]]):
14
+ pass
15
+
16
+
17
+ class Measurements(DataDescriptor[tuple[float, ...]]):
18
+ def __build__(self, container: DataIoC) -> tuple[float, ...]:
19
+ return tuple(value / 10 for value in container[RawReadings])
20
+
21
+
22
+ class Statistics(DataDescriptor[tuple[float, float]]):
23
+ def __build__(self, container: DataIoC) -> tuple[float, float]:
24
+ values = container[Measurements]
25
+ return sum(values) / len(values), max(values)
26
+
27
+
28
+ class Report(DataDescriptor[str]):
29
+ def __build__(self, container: DataIoC) -> str:
30
+ mean, peak = container[Statistics]
31
+ return f"mean={mean:g}, peak={peak:g}"
32
+ ```
33
+
34
+ 描述符是容器使用的键,不是存储的值本身。它可以携带可哈希参数,让同一个描述符类标识一组相关数据。注册后不要修改描述符,因为它的参数会参与相等比较和哈希计算。
35
+
36
+ ## Builder 是局部规则
37
+
38
+ Builder 只说明如何从直接依赖得到一个量。在上面的例子中,`Report` 知道自己需要 `Statistics`,但不知道 `Measurements` 或 `RawReadings`。下一层关系分别由更下层的量负责。
39
+
40
+ Builder 可以是:
41
+
42
+ - 描述符或 provider 对象上的 `__build__` 方法;
43
+ - 接收一个 `DataIoC` 参数的 callable;
44
+ - 带有合适 `__build__` 方法的类。
45
+
46
+ Builder 通过 `container[Target]` 请求依赖。这些请求仍然是普通 Python 代码,因此可以使用条件、循环、第三方库和已有领域对象。
47
+
48
+ ## 请求形成计算图
49
+
50
+ ```python
51
+ container = DataIoC().add(RawReadings, (10, 20, 60))
52
+ assert container[Report] == "mean=3, peak=6"
53
+ ```
54
+
55
+ 请求 `Report` 时,容器形成并解析 `Report -> Statistics -> Measurements -> RawReadings`。计算图隐含在各个局部规则中,并且只展开到当前结果所需要的范围。调用方无需另外维护步骤列表或执行顺序。
56
+
57
+ 这是一套可执行的依赖模型,而不是工作流调度器:求值是同步的,并且只发生在当前进程内。
58
+
59
+ ## 注册值和实现
60
+
61
+ | 操作 | 含义 |
62
+ | --- | --- |
63
+ | `with_data(*values)` | 按类型注册已有实例;带索引的实例保留其描述符 |
64
+ | `add(Target)` | 注册目标自身的 lazy builder |
65
+ | `add(Target, value)` | 注册一个非 `None` 的已有值 |
66
+ | `container[Target] = value` | 直接设置值,包括显式的 `None` |
67
+ | `add_provider(Target, provider)` | 将目标绑定到另一个 builder |
68
+
69
+ 默认情况下,容器会在首次访问时发现目标自身的 builder。严格模式要求每个被请求的键已经提供数据或注册 builder:
70
+
71
+ ```python
72
+ strict = DataIoC(allow_implicit_registering=False)
73
+ strict.add(Report).add(Statistics).add(Measurements)
74
+ strict.add(RawReadings, (10, 20, 60))
75
+ assert strict[Report] == "mean=3, peak=6"
76
+ ```
77
+
78
+ `add(Target, None)` 会选择注册 builder,因为 `None` 是该方法的默认参数。需要把 `None` 存为值时,使用下标赋值。
79
+
80
+ 如何在组装容器时选择另一种实现,详见 [Provider](providers.zh.md)。
81
+
82
+ ## 容器生命周期与缓存
83
+
84
+ 每个成功构建的键都会被缓存,包括值为 `None` 的情况。因此,同一个容器中的重复请求会共享结果。不同索引键有独立的缓存条目;普通类型和 `UniqueData` 则在各个 ID 之间共享。
85
+
86
+ 一个容器代表一组已经确定的数据与 provider 配置。替换来源或 provider 不会使已经构建的值失效。输入或绑定变化后需要重新计算时,使用新的容器。
87
+
88
+ ## 失败和诊断
89
+
90
+ 失败的构建不会被缓存,已经成功构建的依赖会保留。顶层构建失败时,容器打印依赖树,然后重新抛出原始异常。补充缺失数据或修正 builder 后,可以再次请求目标。
91
+
92
+ 成功的访问树默认会清除。设置 `record_all=True` 可以将它们保留在 `container.logger` 中,详见 [Provider 诊断](providers.zh.md)。
93
+
94
+ 容器不提供线程安全、异步构建、自动依赖失效或循环依赖检测。