infinity-data 3.1.1__tar.gz → 3.3.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.
- infinity_data-3.3.0/PKG-INFO +156 -0
- infinity_data-3.3.0/README.md +131 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/pyproject.toml +18 -1
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/emit/__init__.py +4 -1
- infinity_data-3.3.0/src/infinity_data/emit/plain_yaml.py +173 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/semantic/builder/builder.py +11 -1
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/semantic/diagnostics.py +5 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/semantic/resolver/resolver.py +6 -3
- infinity_data-3.3.0/src/infinity_data/tools/__init__.py +7 -0
- infinity_data-3.3.0/src/infinity_data/tools/cov/__init__.py +30 -0
- infinity_data-3.3.0/src/infinity_data/tools/cov/__main__.py +6 -0
- infinity_data-3.3.0/src/infinity_data/tools/cov/cli.py +120 -0
- infinity_data-3.3.0/src/infinity_data/tools/cov/convert.py +47 -0
- infinity_data-3.3.0/src/infinity_data/tools/lsp/__init__.py +31 -0
- infinity_data-3.3.0/src/infinity_data/tools/lsp/__main__.py +6 -0
- infinity_data-3.3.0/src/infinity_data/tools/lsp/language.py +1045 -0
- infinity_data-3.3.0/src/infinity_data/tools/lsp/positions.py +78 -0
- infinity_data-3.3.0/src/infinity_data/tools/lsp/protocol.py +76 -0
- infinity_data-3.3.0/src/infinity_data/tools/lsp/server.py +210 -0
- infinity_data-3.3.0/src/infinity_data.egg-info/PKG-INFO +156 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data.egg-info/SOURCES.txt +15 -1
- infinity_data-3.3.0/src/infinity_data.egg-info/entry_points.txt +3 -0
- infinity_data-3.3.0/src/infinity_data.egg-info/requires.txt +4 -0
- infinity_data-3.1.1/PKG-INFO +0 -60
- infinity_data-3.1.1/README.md +0 -38
- infinity_data-3.1.1/src/infinity_data.egg-info/PKG-INFO +0 -60
- {infinity_data-3.1.1 → infinity_data-3.3.0}/LICENSE +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/setup.cfg +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/__init__.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/emit/config.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/emit/converter.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/frontend.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/infra/__init__.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/infra/diagnostics.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/infra/file.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/infra/ll1_stream.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/infra/location.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/infra/path.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/parser/__init__.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/parser/diagnostics.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/parser/models.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/parser/parser.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/parser/token_stream.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/pipeline.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/py.typed +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/sandbox/__init__.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/sandbox/config.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/sandbox/errors.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/sandbox/mediator.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/sandbox/schema.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/semantic/__init__.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/semantic/builder/__init__.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/semantic/builder/models.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/semantic/constraints.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/semantic/executor/__init__.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/semantic/executor/executor.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/semantic/jsonpath.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/semantic/registry/__init__.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/semantic/registry/_core.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/semantic/registry/dict_constraints.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/semantic/registry/general.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/semantic/registry/logic.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/semantic/registry/types.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/semantic/resolver/__init__.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/semantic/resolver/identity.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/semantic/resolver/imports.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/semantic/resolver/models.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/semantic/std.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/tokenizer/__init__.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/tokenizer/char_stream.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/tokenizer/diagnostics.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/tokenizer/finalizer.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/tokenizer/models/__init__.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/tokenizer/models/raw_tokens.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/tokenizer/models/tokens.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data/tokenizer/tokenizer.py +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data.egg-info/dependency_links.txt +0 -0
- {infinity_data-3.1.1 → infinity_data-3.3.0}/src/infinity_data.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: infinity-data
|
|
3
|
+
Version: 3.3.0
|
|
4
|
+
Summary: InfinityData:声明式配置语言(.infd/.inft)的 Python 编译器库
|
|
5
|
+
Author: infinity_system: yin_bailiang
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/yinbailiang/infinity_data
|
|
8
|
+
Project-URL: Documentation, https://github.com/yinbailiang/infinity_data#readme
|
|
9
|
+
Keywords: config,configuration,declarative,dsl,compiler,infinity-data
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Requires-Python: >=3.12
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Provides-Extra: tool
|
|
22
|
+
Requires-Dist: pyyaml; extra == "tool"
|
|
23
|
+
Requires-Dist: tomli-w; extra == "tool"
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# InfinityData
|
|
27
|
+
|
|
28
|
+
声明式配置语言(`.infd` / `.inft`)的 Python 编译器库。
|
|
29
|
+
|
|
30
|
+
- **编译优先**:`.infd` 是源码,JSON/YAML/TOML 是构建产物
|
|
31
|
+
- **模板即约束**:模板定义自动注册为约束校验器
|
|
32
|
+
- **零信任默认**:库默认 `deny_all` 沙盒,显式开放而非事后限制
|
|
33
|
+
- **结构化错误**:稳定错误码 + 结构化参数 + 多语言渲染
|
|
34
|
+
|
|
35
|
+
## 快速使用
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
uv add infinity_data # 或 pip install infinity-data
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from infinity_data import load, safe_load, SandboxConfig, Schema
|
|
43
|
+
|
|
44
|
+
result = load('app.infd') # 默认零信任
|
|
45
|
+
if result.has_errors:
|
|
46
|
+
for d in result.diagnostics:
|
|
47
|
+
print(d.location, d.code, d.message)
|
|
48
|
+
else:
|
|
49
|
+
print(result.value) # 降维后的 dict
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 文档导航
|
|
53
|
+
|
|
54
|
+
| 文档 | 内容 |
|
|
55
|
+
|---|---|
|
|
56
|
+
| [neo_desg.md](./neo_desg.md) | 语言基础设计(语法 / 类型 / 模板 / 约束) |
|
|
57
|
+
| [extra_desg.md](./extra_desg.md) | 库 API / CLI / 生态设计 |
|
|
58
|
+
| [impl_desg.md](./impl_desg.md) | 编译器实现层的取舍与假设 |
|
|
59
|
+
|
|
60
|
+
## 官方工具
|
|
61
|
+
|
|
62
|
+
随库内置两个命令行工具(`infd-lsp` 语言服务器 + `infd-cov` 配置转换器),
|
|
63
|
+
零依赖手写 JSON-RPC / emitter,直接消费编译器的公开 API
|
|
64
|
+
(`compile_source` / `parse_source` / `RawTokenizer` / emit / semantic)。
|
|
65
|
+
工具的**可选运行依赖**(PyYAML / tomli-w)由 `tool` extra 提供,一次装齐:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
uv add 'infinity_data[tool]' # 可选:!file 的 yaml 导入 + infd-cov -f toml 输出
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 配置转换(infd-cov)
|
|
72
|
+
|
|
73
|
+
编译 `.infd` / `.inft` → 各种配置格式(YAML / JSON / TOML):
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
infd-cov app.infd # → YAML(stdout,零依赖 emitter)
|
|
77
|
+
infd-cov app.infd out.json -f json # → JSON 文件
|
|
78
|
+
infd-cov app.infd -f toml # → TOML(需 tomli-w,见 tool extra)
|
|
79
|
+
infd-cov app.infd --extract web.spec # → 点分路径提取
|
|
80
|
+
cat a.infd | infd-cov -f json # stdin → stdout
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
- `--format`:`yaml`(默认,零依赖手写 emitter)/ `json` / `toml`
|
|
84
|
+
- `--extract PATH`:点分路径(如 `web.spec.selector`);空 = 整树
|
|
85
|
+
- `--severity LEVEL`:拒绝阈值(`error`/`warning`/`info`/`debug`/`none`,默认 `error`)
|
|
86
|
+
- `#env: NAME "VALUE"` 声明的变量在当前进程缺失时自动注入虚拟值
|
|
87
|
+
- 演示文件:`examples/app.infd`(干净配置)/ `examples/demo.infd`(含故意错误)
|
|
88
|
+
|
|
89
|
+
### 语言服务器(LSP)
|
|
90
|
+
|
|
91
|
+
`infd-lsp`:零依赖手写 JSON-RPC 2.0 over stdio 语言服务器。
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
infd-lsp # 作为任意 LSP 客户端的后端
|
|
95
|
+
# 或
|
|
96
|
+
python -m infinity_data.tools.lsp
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
#### 已实现的能力
|
|
100
|
+
|
|
101
|
+
| 能力 | 说明 |
|
|
102
|
+
|---|---|
|
|
103
|
+
| 诊断 | `didOpen`/`didChange`/`didSave` → `publishDiagnostics`(精确位置 + 稳定错误码 + 中文消息;UTF-16 对齐) |
|
|
104
|
+
| 补全 | 命名空间(`$` 触发)+ 内置约束名 + 当前文件可见模板名 + 模板字段(含必填/可选)+ 语言关键字 |
|
|
105
|
+
| 悬停 | 约束描述、**编译产物预览**(字段/子字段/模板参数,jsonpath 投影)、**被实例化的模板**(模板骨架提示在前 + 实例预览在后)、**`$` 变量值**(定义点与调用点)、模板结构骨架(必填/可选 + description 元数据) |
|
|
106
|
+
| 文档大纲 | `documentSymbol`:模板定义(Class)+ 顶层字段(Property) |
|
|
107
|
+
| 跳转定义 | `definition`:模板调用 / `!from` 导入 / `$` 引用(含跨文件) |
|
|
108
|
+
| 语义令牌 | `semanticTokens`:直接用 `RawTokenizer` 分词(UTF-16 单元对齐;跨行多行字符串按行拆分) |
|
|
109
|
+
| 虚拟环境变量 | 扫描 `#env: NAME "VALUE"` 注释,进程未设置时自动注入虚拟值,避免 `env_not_set` 中止编译 |
|
|
110
|
+
|
|
111
|
+
#### 在 VS Code 中使用
|
|
112
|
+
|
|
113
|
+
在项目 `.vscode/settings.json` 里用任意支持自定义 LSP 的扩展(如 `lspConfig`)配置:
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"lspConfig.servers": [
|
|
118
|
+
{ "language": "infd", "cmd": ["infd-lsp"] },
|
|
119
|
+
{ "language": "inft", "cmd": ["infd-lsp"] }
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
打开 `.infd` / `.inft` 文件即可获得:诊断波浪线 + 语法高亮(semantic tokens)+ 补全 + 悬停 + 大纲 + 跳转定义。
|
|
125
|
+
|
|
126
|
+
#### 端到端演示
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
uv run python examples/lsp_demo.py # 自动起服务器 + 走一遍握手 + 打印结果
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
演示文件在 `examples/demo.infd`(故意含若干错误,展示诊断 / 补全 / 悬停 / 高亮)。
|
|
133
|
+
|
|
134
|
+
### 虚拟环境变量(`#env` 注释)
|
|
135
|
+
|
|
136
|
+
`.infd` / `.inft` 里可以用**特殊注释**声明环境变量名和虚拟值,工具(LSP / `infd-cov`)
|
|
137
|
+
在当前进程缺失该变量时自动注入,避免 `env_not_set` 中止编译、吞掉同文件其他诊断:
|
|
138
|
+
|
|
139
|
+
```infd
|
|
140
|
+
#env: DEEPSEEK_API_KEY "sk-virtual-key"
|
|
141
|
+
!env import DEEPSEEK_API_KEY as ds_key
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
- 语法:`#env: NAME "VALUE"` —— **变量名在前、虚拟值在后**
|
|
145
|
+
- 真实环境变量优先:进程里已设置时用真实值,只有缺失时才用注释里的虚拟值兜底
|
|
146
|
+
- 动机:`infinity_data` 沙盒在 env 未授权/未设置时会中止编译;用 `#env` 声明兜底后,
|
|
147
|
+
编辑器/CLI 始终能看到全部诊断(如约束违反、未定义模板)
|
|
148
|
+
|
|
149
|
+
## 开发
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
uv run pytest -q # 测试
|
|
153
|
+
uv run ruff check src tests # lint
|
|
154
|
+
uv run pyright # 类型检查(strict)
|
|
155
|
+
uv run python test.py # 错误报告演示
|
|
156
|
+
```
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# InfinityData
|
|
2
|
+
|
|
3
|
+
声明式配置语言(`.infd` / `.inft`)的 Python 编译器库。
|
|
4
|
+
|
|
5
|
+
- **编译优先**:`.infd` 是源码,JSON/YAML/TOML 是构建产物
|
|
6
|
+
- **模板即约束**:模板定义自动注册为约束校验器
|
|
7
|
+
- **零信任默认**:库默认 `deny_all` 沙盒,显式开放而非事后限制
|
|
8
|
+
- **结构化错误**:稳定错误码 + 结构化参数 + 多语言渲染
|
|
9
|
+
|
|
10
|
+
## 快速使用
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
uv add infinity_data # 或 pip install infinity-data
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
from infinity_data import load, safe_load, SandboxConfig, Schema
|
|
18
|
+
|
|
19
|
+
result = load('app.infd') # 默认零信任
|
|
20
|
+
if result.has_errors:
|
|
21
|
+
for d in result.diagnostics:
|
|
22
|
+
print(d.location, d.code, d.message)
|
|
23
|
+
else:
|
|
24
|
+
print(result.value) # 降维后的 dict
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 文档导航
|
|
28
|
+
|
|
29
|
+
| 文档 | 内容 |
|
|
30
|
+
|---|---|
|
|
31
|
+
| [neo_desg.md](./neo_desg.md) | 语言基础设计(语法 / 类型 / 模板 / 约束) |
|
|
32
|
+
| [extra_desg.md](./extra_desg.md) | 库 API / CLI / 生态设计 |
|
|
33
|
+
| [impl_desg.md](./impl_desg.md) | 编译器实现层的取舍与假设 |
|
|
34
|
+
|
|
35
|
+
## 官方工具
|
|
36
|
+
|
|
37
|
+
随库内置两个命令行工具(`infd-lsp` 语言服务器 + `infd-cov` 配置转换器),
|
|
38
|
+
零依赖手写 JSON-RPC / emitter,直接消费编译器的公开 API
|
|
39
|
+
(`compile_source` / `parse_source` / `RawTokenizer` / emit / semantic)。
|
|
40
|
+
工具的**可选运行依赖**(PyYAML / tomli-w)由 `tool` extra 提供,一次装齐:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
uv add 'infinity_data[tool]' # 可选:!file 的 yaml 导入 + infd-cov -f toml 输出
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 配置转换(infd-cov)
|
|
47
|
+
|
|
48
|
+
编译 `.infd` / `.inft` → 各种配置格式(YAML / JSON / TOML):
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
infd-cov app.infd # → YAML(stdout,零依赖 emitter)
|
|
52
|
+
infd-cov app.infd out.json -f json # → JSON 文件
|
|
53
|
+
infd-cov app.infd -f toml # → TOML(需 tomli-w,见 tool extra)
|
|
54
|
+
infd-cov app.infd --extract web.spec # → 点分路径提取
|
|
55
|
+
cat a.infd | infd-cov -f json # stdin → stdout
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
- `--format`:`yaml`(默认,零依赖手写 emitter)/ `json` / `toml`
|
|
59
|
+
- `--extract PATH`:点分路径(如 `web.spec.selector`);空 = 整树
|
|
60
|
+
- `--severity LEVEL`:拒绝阈值(`error`/`warning`/`info`/`debug`/`none`,默认 `error`)
|
|
61
|
+
- `#env: NAME "VALUE"` 声明的变量在当前进程缺失时自动注入虚拟值
|
|
62
|
+
- 演示文件:`examples/app.infd`(干净配置)/ `examples/demo.infd`(含故意错误)
|
|
63
|
+
|
|
64
|
+
### 语言服务器(LSP)
|
|
65
|
+
|
|
66
|
+
`infd-lsp`:零依赖手写 JSON-RPC 2.0 over stdio 语言服务器。
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
infd-lsp # 作为任意 LSP 客户端的后端
|
|
70
|
+
# 或
|
|
71
|
+
python -m infinity_data.tools.lsp
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
#### 已实现的能力
|
|
75
|
+
|
|
76
|
+
| 能力 | 说明 |
|
|
77
|
+
|---|---|
|
|
78
|
+
| 诊断 | `didOpen`/`didChange`/`didSave` → `publishDiagnostics`(精确位置 + 稳定错误码 + 中文消息;UTF-16 对齐) |
|
|
79
|
+
| 补全 | 命名空间(`$` 触发)+ 内置约束名 + 当前文件可见模板名 + 模板字段(含必填/可选)+ 语言关键字 |
|
|
80
|
+
| 悬停 | 约束描述、**编译产物预览**(字段/子字段/模板参数,jsonpath 投影)、**被实例化的模板**(模板骨架提示在前 + 实例预览在后)、**`$` 变量值**(定义点与调用点)、模板结构骨架(必填/可选 + description 元数据) |
|
|
81
|
+
| 文档大纲 | `documentSymbol`:模板定义(Class)+ 顶层字段(Property) |
|
|
82
|
+
| 跳转定义 | `definition`:模板调用 / `!from` 导入 / `$` 引用(含跨文件) |
|
|
83
|
+
| 语义令牌 | `semanticTokens`:直接用 `RawTokenizer` 分词(UTF-16 单元对齐;跨行多行字符串按行拆分) |
|
|
84
|
+
| 虚拟环境变量 | 扫描 `#env: NAME "VALUE"` 注释,进程未设置时自动注入虚拟值,避免 `env_not_set` 中止编译 |
|
|
85
|
+
|
|
86
|
+
#### 在 VS Code 中使用
|
|
87
|
+
|
|
88
|
+
在项目 `.vscode/settings.json` 里用任意支持自定义 LSP 的扩展(如 `lspConfig`)配置:
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"lspConfig.servers": [
|
|
93
|
+
{ "language": "infd", "cmd": ["infd-lsp"] },
|
|
94
|
+
{ "language": "inft", "cmd": ["infd-lsp"] }
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
打开 `.infd` / `.inft` 文件即可获得:诊断波浪线 + 语法高亮(semantic tokens)+ 补全 + 悬停 + 大纲 + 跳转定义。
|
|
100
|
+
|
|
101
|
+
#### 端到端演示
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
uv run python examples/lsp_demo.py # 自动起服务器 + 走一遍握手 + 打印结果
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
演示文件在 `examples/demo.infd`(故意含若干错误,展示诊断 / 补全 / 悬停 / 高亮)。
|
|
108
|
+
|
|
109
|
+
### 虚拟环境变量(`#env` 注释)
|
|
110
|
+
|
|
111
|
+
`.infd` / `.inft` 里可以用**特殊注释**声明环境变量名和虚拟值,工具(LSP / `infd-cov`)
|
|
112
|
+
在当前进程缺失该变量时自动注入,避免 `env_not_set` 中止编译、吞掉同文件其他诊断:
|
|
113
|
+
|
|
114
|
+
```infd
|
|
115
|
+
#env: DEEPSEEK_API_KEY "sk-virtual-key"
|
|
116
|
+
!env import DEEPSEEK_API_KEY as ds_key
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
- 语法:`#env: NAME "VALUE"` —— **变量名在前、虚拟值在后**
|
|
120
|
+
- 真实环境变量优先:进程里已设置时用真实值,只有缺失时才用注释里的虚拟值兜底
|
|
121
|
+
- 动机:`infinity_data` 沙盒在 env 未授权/未设置时会中止编译;用 `#env` 声明兜底后,
|
|
122
|
+
编辑器/CLI 始终能看到全部诊断(如约束违反、未定义模板)
|
|
123
|
+
|
|
124
|
+
## 开发
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
uv run pytest -q # 测试
|
|
128
|
+
uv run ruff check src tests # lint
|
|
129
|
+
uv run pyright # 类型检查(strict)
|
|
130
|
+
uv run python test.py # 错误报告演示
|
|
131
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "infinity-data"
|
|
3
|
-
version = "3.
|
|
3
|
+
version = "3.3.0"
|
|
4
4
|
description = "InfinityData:声明式配置语言(.infd/.inft)的 Python 编译器库"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -28,16 +28,33 @@ classifiers = [
|
|
|
28
28
|
requires-python = ">=3.12"
|
|
29
29
|
dependencies = []
|
|
30
30
|
|
|
31
|
+
[project.scripts]
|
|
32
|
+
infd-lsp = "infinity_data.tools.lsp.__main__:main"
|
|
33
|
+
infd-cov = "infinity_data.tools.cov.__main__:main"
|
|
34
|
+
|
|
35
|
+
[project.optional-dependencies]
|
|
36
|
+
# 工具 extra:infd-lsp / infd-cov 的可选运行依赖(uv add 'infinity_data[tool]')
|
|
37
|
+
tool = [
|
|
38
|
+
"pyyaml", # !file 的 yaml/yml 格式导入(LSP / 编译器)
|
|
39
|
+
"tomli-w", # infd-cov -f toml 输出
|
|
40
|
+
]
|
|
41
|
+
|
|
31
42
|
[project.urls]
|
|
32
43
|
Repository = "https://github.com/yinbailiang/infinity_data"
|
|
33
44
|
Documentation = "https://github.com/yinbailiang/infinity_data#readme"
|
|
34
45
|
|
|
35
46
|
[dependency-groups]
|
|
47
|
+
# 工具组:cli/lsp 工具的可选运行依赖(测试/开发组保证安装,测试可依赖它们存在)
|
|
48
|
+
tool = [
|
|
49
|
+
"tomli-w", # infd-cov -f toml 输出
|
|
50
|
+
"pyyaml", # !file 的 yaml/yml 格式导入(LSP / 编译器)
|
|
51
|
+
]
|
|
36
52
|
# 测试组:仅运行测试所需(CI test job 只装这组,更快)
|
|
37
53
|
test = [
|
|
38
54
|
"pyyaml",
|
|
39
55
|
"pytest>=8.0",
|
|
40
56
|
"pytest-cov>=5.0",
|
|
57
|
+
{ include-group = "tool" },
|
|
41
58
|
]
|
|
42
59
|
# 开发组:lint/类型/钩子 + 测试组(本地开发与 CI pre-commit job 装这组)
|
|
43
60
|
dev = [
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
- **本层(emit,有损投影)** → :func:`to_json` / :func:`to_yaml` / :func:`to_toml`,
|
|
8
8
|
按 :class:`EmitConfig` 控制 Decimal 编码与 noexist 标记;:func:`project_output`
|
|
9
9
|
为纯 Python 值的轻量投影;:func:`restore_python` 反向**尝试还原**加载的原始
|
|
10
|
-
dict 为 :data:`~infinity_data.semantic.std.StdPythonValue`
|
|
10
|
+
dict 为 :data:`~infinity_data.semantic.std.StdPythonValue`;:func:`yaml_dump`
|
|
11
|
+
为零依赖 YAML block emitter(覆盖配置常用值,不依赖 PyYAML)
|
|
11
12
|
|
|
12
13
|
**emit 不应被流水线内部使用**——内部表示一律走 semantic 的忠实转换。
|
|
13
14
|
"""
|
|
@@ -22,6 +23,7 @@ from infinity_data.emit.converter import (
|
|
|
22
23
|
to_toml,
|
|
23
24
|
to_yaml,
|
|
24
25
|
)
|
|
26
|
+
from infinity_data.emit.plain_yaml import yaml_dump
|
|
25
27
|
|
|
26
28
|
__all__ = [
|
|
27
29
|
'EmitConfig',
|
|
@@ -32,4 +34,5 @@ __all__ = [
|
|
|
32
34
|
'to_json',
|
|
33
35
|
'to_toml',
|
|
34
36
|
'to_yaml',
|
|
37
|
+
'yaml_dump',
|
|
35
38
|
]
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""零依赖 YAML emitter(block style,覆盖配置常用值)。
|
|
2
|
+
|
|
3
|
+
与 :func:`infinity_data.emit.converter.to_yaml`(基于 PyYAML)的关系:
|
|
4
|
+
本模块是**零依赖**轻量实现,供 ``infd-cov`` 默认 YAML 输出使用;PyYAML 未安装
|
|
5
|
+
时也可用。只覆盖配置场景常见值(dict / list / 标量 / Decimal / 多行字符串),
|
|
6
|
+
不做完整 YAML 规范实现。
|
|
7
|
+
|
|
8
|
+
- ``yaml_dump(obj)``:任意 Python 值 → YAML block 文本
|
|
9
|
+
- 值域与 :data:`~infinity_data.semantic.std.StdPythonValue` 对齐:Decimal(infd
|
|
10
|
+
浮点字面量)经 ``str()`` 输出;``null`` → ``null``;多行字符串 → ``|-``
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from collections.abc import Mapping
|
|
16
|
+
from decimal import Decimal
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
__all__ = ['yaml_dump']
|
|
20
|
+
|
|
21
|
+
# 可序列化值(reduce 后的配置值;Decimal 为 infd 浮点字面量)。
|
|
22
|
+
# PEP 695 递归别名:容器值也是 YamlValue,isinstance 收窄后无 Unknown。
|
|
23
|
+
type YamlValue = Mapping[str, YamlValue] | list[YamlValue] | str | int | float | bool | Decimal | None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def yaml_dump(obj: Any) -> str:
|
|
27
|
+
"""任意 Python 值 → YAML block 文本。"""
|
|
28
|
+
out: list[str] = []
|
|
29
|
+
_dump(obj, out, 0)
|
|
30
|
+
return '\n'.join(out)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _dump(obj: YamlValue, out: list[str], indent: int) -> None:
|
|
34
|
+
pad = ' ' * indent
|
|
35
|
+
if isinstance(obj, Mapping):
|
|
36
|
+
if not obj:
|
|
37
|
+
out.append(f'{pad}{{}}')
|
|
38
|
+
return
|
|
39
|
+
for k, v in obj.items():
|
|
40
|
+
key = _plain(str(k))
|
|
41
|
+
if isinstance(v, Mapping):
|
|
42
|
+
if not v:
|
|
43
|
+
out.append(f'{pad}{key}: {{}}')
|
|
44
|
+
else:
|
|
45
|
+
out.append(f'{pad}{key}:')
|
|
46
|
+
_dump(v, out, indent + 1)
|
|
47
|
+
elif isinstance(v, list):
|
|
48
|
+
if not v:
|
|
49
|
+
out.append(f'{pad}{key}: []')
|
|
50
|
+
else:
|
|
51
|
+
out.append(f'{pad}{key}:')
|
|
52
|
+
_dump(v, out, indent + 1)
|
|
53
|
+
else:
|
|
54
|
+
if isinstance(v, str) and '\n' in v:
|
|
55
|
+
out.append(f'{pad}{key}: |-')
|
|
56
|
+
out.extend(_block_scalar_lines(v, indent))
|
|
57
|
+
else:
|
|
58
|
+
out.append(f'{pad}{key}: {_scalar(v)}')
|
|
59
|
+
elif isinstance(obj, list):
|
|
60
|
+
if not obj:
|
|
61
|
+
out.append(f'{pad}[]')
|
|
62
|
+
return
|
|
63
|
+
for v in obj:
|
|
64
|
+
if isinstance(v, Mapping):
|
|
65
|
+
if not v:
|
|
66
|
+
out.append(f'{pad}- {{}}')
|
|
67
|
+
continue
|
|
68
|
+
first = True
|
|
69
|
+
for k, vv in v.items():
|
|
70
|
+
key = _plain(str(k))
|
|
71
|
+
prefix = f'{pad}- ' if first else f'{pad} '
|
|
72
|
+
first = False
|
|
73
|
+
if isinstance(vv, Mapping):
|
|
74
|
+
if not vv:
|
|
75
|
+
out.append(f'{prefix}{key}: {{}}')
|
|
76
|
+
else:
|
|
77
|
+
out.append(f'{prefix}{key}:')
|
|
78
|
+
_dump(vv, out, indent + 2)
|
|
79
|
+
elif isinstance(vv, list):
|
|
80
|
+
if not vv:
|
|
81
|
+
out.append(f'{prefix}{key}: []')
|
|
82
|
+
else:
|
|
83
|
+
out.append(f'{prefix}{key}:')
|
|
84
|
+
_dump(vv, out, indent + 2)
|
|
85
|
+
else:
|
|
86
|
+
if isinstance(vv, str) and '\n' in vv:
|
|
87
|
+
out.append(f'{prefix}{key}: |-')
|
|
88
|
+
out.extend(_block_scalar_lines(vv, indent + 1))
|
|
89
|
+
else:
|
|
90
|
+
out.append(f'{prefix}{key}: {_scalar(vv)}')
|
|
91
|
+
elif isinstance(v, list):
|
|
92
|
+
out.append(f'{pad}-')
|
|
93
|
+
_dump(v, out, indent + 1)
|
|
94
|
+
else:
|
|
95
|
+
if isinstance(v, str) and '\n' in v:
|
|
96
|
+
out.append(f'{pad}- |-')
|
|
97
|
+
out.extend(_block_scalar_lines(v, indent))
|
|
98
|
+
else:
|
|
99
|
+
out.append(f'{pad}- {_scalar(v)}')
|
|
100
|
+
else:
|
|
101
|
+
out.append(f'{pad}{_scalar(obj)}')
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _block_scalar_lines(s: str, indent: int) -> list[str]:
|
|
105
|
+
"""多行字符串 → YAML 块标量内容行(调用处负责输出 ``key: |-`` 头行)。
|
|
106
|
+
|
|
107
|
+
内容行缩进 = 2 * (indent + 1)(比 key 多一级);空行保持完全空白,
|
|
108
|
+
避免因缩进不足提前结束块;尾部空行去除(配合 ``|-`` 无末尾换行)。
|
|
109
|
+
"""
|
|
110
|
+
pad = ' ' * (indent + 1)
|
|
111
|
+
lines = s.split('\n')
|
|
112
|
+
while lines and lines[-1] == '':
|
|
113
|
+
lines.pop()
|
|
114
|
+
return [f'{pad}{line}' if line else '' for line in lines]
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _scalar(v: Any) -> str:
|
|
118
|
+
"""标量 → YAML 文本。"""
|
|
119
|
+
if v is None:
|
|
120
|
+
return 'null'
|
|
121
|
+
if isinstance(v, bool):
|
|
122
|
+
return 'true' if v else 'false'
|
|
123
|
+
if isinstance(v, (int, float, Decimal)):
|
|
124
|
+
# nan / ±inf:YAML 接受 .nan/.inf,str() 输出的 nan/inf 也常见可读
|
|
125
|
+
return str(v)
|
|
126
|
+
return _plain(str(v))
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _plain(s: str) -> str:
|
|
130
|
+
"""普通字符串;含 YAML 特殊字符/歧义时加单引号。"""
|
|
131
|
+
if s == '':
|
|
132
|
+
return "''"
|
|
133
|
+
# YAML 1.1 歧义字面量 → 引号
|
|
134
|
+
if s in (
|
|
135
|
+
'null',
|
|
136
|
+
'Null',
|
|
137
|
+
'NULL',
|
|
138
|
+
'true',
|
|
139
|
+
'True',
|
|
140
|
+
'TRUE',
|
|
141
|
+
'false',
|
|
142
|
+
'False',
|
|
143
|
+
'FALSE',
|
|
144
|
+
'yes',
|
|
145
|
+
'Yes',
|
|
146
|
+
'YES',
|
|
147
|
+
'no',
|
|
148
|
+
'No',
|
|
149
|
+
'NO',
|
|
150
|
+
'on',
|
|
151
|
+
'On',
|
|
152
|
+
'ON',
|
|
153
|
+
'off',
|
|
154
|
+
'Off',
|
|
155
|
+
'OFF',
|
|
156
|
+
'~',
|
|
157
|
+
'.nan',
|
|
158
|
+
'.inf',
|
|
159
|
+
'-.inf',
|
|
160
|
+
'+.inf',
|
|
161
|
+
):
|
|
162
|
+
return f"'{s}'"
|
|
163
|
+
# 首字符/内嵌特殊 → 引号
|
|
164
|
+
if s[0] in '-?:,[]{}#&*!|>\'"%@`' or s[0] in ' \t':
|
|
165
|
+
return _quote(s)
|
|
166
|
+
if ': ' in s or ' #' in s or '\n' in s or '\t' in s or s.rstrip() != s:
|
|
167
|
+
return _quote(s)
|
|
168
|
+
return s
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _quote(s: str) -> str:
|
|
172
|
+
"""单引号引用('' 转义单引号)。"""
|
|
173
|
+
return "'" + s.replace("'", "''") + "'"
|
|
@@ -541,6 +541,8 @@ class AstBuilder:
|
|
|
541
541
|
|
|
542
542
|
type_cast 为显式 as bool/int/float/str 转换;source 为 ``$name`` 表达式在源码中的位置。
|
|
543
543
|
本文件空间查不到 → ``dollar.undefined`` 警告取 null(不中断编译)。
|
|
544
|
+
非字符串结构化值(list / dict)应用 as → ``dollar.convert_unsupported``
|
|
545
|
+
错误 + 原样返回(容错继续编译,§1.8)。
|
|
544
546
|
"""
|
|
545
547
|
namespace = scope.namespaces if scope is not None else {}
|
|
546
548
|
if name not in namespace:
|
|
@@ -552,7 +554,15 @@ class AstBuilder:
|
|
|
552
554
|
if type_cast is None:
|
|
553
555
|
return raw
|
|
554
556
|
if isinstance(raw, (StdArray, StdObject)):
|
|
555
|
-
|
|
557
|
+
# 结构化值(list / dict)不支持 as 转换:写了 as 就必须转换、不静默忽略——
|
|
558
|
+
# 报错 + 原样返回容错(继续编译)
|
|
559
|
+
self._err(
|
|
560
|
+
'dollar.convert_unsupported',
|
|
561
|
+
{'name': name, 'kind': 'list' if isinstance(raw, StdArray) else 'dict', 'type': type_cast},
|
|
562
|
+
source,
|
|
563
|
+
path,
|
|
564
|
+
)
|
|
565
|
+
return raw
|
|
556
566
|
# 空字面量:as 转换**保持传播**(null → null、noexist → noexist),
|
|
557
567
|
# 不做任何转换、不产生警告——三态可空语义不因显式 as 而丢失
|
|
558
568
|
# (此时 raw 已收窄为 StdLiteral)
|
|
@@ -135,6 +135,11 @@ register_diagnostic_define(
|
|
|
135
135
|
'{path_prefix}无法将 ${name}={raw!r} 转为 {type}',
|
|
136
136
|
en='{path_prefix}cannot convert ${name}={raw!r} to {type}',
|
|
137
137
|
),
|
|
138
|
+
diagnostic_define(
|
|
139
|
+
'dollar.convert_unsupported',
|
|
140
|
+
'{path_prefix}结构化值 ${name}({kind})不支持 as {type} 转换,已原样保留',
|
|
141
|
+
en='{path_prefix}structured value ${name} ({kind}) does not support as {type}; kept as-is',
|
|
142
|
+
),
|
|
138
143
|
diagnostic_define('inft.not_allowed', '{path_prefix}.inft 文件只允许模板定义,发现其他语句'),
|
|
139
144
|
diagnostic_define('error.generic', '{path_prefix}{message}'),
|
|
140
145
|
# ═══════════════════════════════════════════════════════════
|
|
@@ -192,6 +192,7 @@ class TemplateGraphResolver:
|
|
|
192
192
|
"""把 ``!from`` 的导入项映射进目标 scope;冲突一律 ERROR。
|
|
193
193
|
|
|
194
194
|
- 导入文件中不存在该模板 → ERROR
|
|
195
|
+
- 可见名(含 ``as`` 别名)与注册约束同名 → ERROR(模板即约束,内置约束不可被遮蔽)
|
|
195
196
|
- 可见名与文件内定义同名 → ERROR(与文件内定义冲突)
|
|
196
197
|
- 可见名已存在(重复导入)→ ERROR,保留先到者(拒绝隐式覆盖)
|
|
197
198
|
"""
|
|
@@ -203,6 +204,8 @@ class TemplateGraphResolver:
|
|
|
203
204
|
)
|
|
204
205
|
continue
|
|
205
206
|
visible = item.alias or item.name
|
|
207
|
+
if self._check_template_name_conflict(visible, item.source):
|
|
208
|
+
continue
|
|
206
209
|
if visible in scope.visible:
|
|
207
210
|
if visible in local_names:
|
|
208
211
|
self._collector.add(
|
|
@@ -295,9 +298,9 @@ class TemplateGraphResolver:
|
|
|
295
298
|
imported_doc = self._parse_document(file)
|
|
296
299
|
|
|
297
300
|
# 1) 本地模板:先注册(循环导入时依赖文件的本地名部分已可见)
|
|
298
|
-
#
|
|
299
|
-
#
|
|
300
|
-
#
|
|
301
|
+
# 身份初始按来源文件路径区分(收集阶段,避免同名互相覆盖);最终由
|
|
302
|
+
# _remap_content_identities 重算为依赖闭包组合哈希(§2.5)——内容 + 依赖
|
|
303
|
+
# 相同 → 同身份;依赖不同 → 依赖 identity 进 hash → 自然区分
|
|
301
304
|
scope: Scope = Scope()
|
|
302
305
|
local_names: set[str] = set()
|
|
303
306
|
for s in imported_doc.statements:
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""InfinityData 官方命令行工具包:配置转换(cov)+ 语言服务器(lsp)。
|
|
2
|
+
|
|
3
|
+
- :mod:`infinity_data.tools.cov`:``infd-cov`` 配置转换器(.infd/.inft → YAML/JSON/TOML)
|
|
4
|
+
- :mod:`infinity_data.tools.lsp`:``infd-lsp`` 语言服务器(LSP over stdio)
|
|
5
|
+
|
|
6
|
+
每个子包都带 ``__main__.py``,可用 ``python -m infinity_data.tools.<tool>`` 启动。
|
|
7
|
+
"""
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""infd-cov:编译 .infd/.inft → 各种配置格式(YAML / JSON / TOML)。
|
|
2
|
+
|
|
3
|
+
用法::
|
|
4
|
+
|
|
5
|
+
infd-cov app.infd # → YAML(stdout,零依赖 emitter)
|
|
6
|
+
infd-cov app.infd out.json -f json # → JSON 文件
|
|
7
|
+
infd-cov app.infd -f toml # → TOML(需 tomli-w)
|
|
8
|
+
infd-cov app.infd --extract web.spec # → 点分路径提取
|
|
9
|
+
cat a.infd | infd-cov -f json # stdin → stdout
|
|
10
|
+
|
|
11
|
+
行为:
|
|
12
|
+
- 沙盒 ``SandboxConfig.full_access()``:放行全部导入(含跨目录 ``!from`` 模板导入)
|
|
13
|
+
- ``#env: NAME "VALUE"`` 声明的变量在当前进程缺失时自动注入虚拟值
|
|
14
|
+
- ``--severity LEVEL``:拒绝阈值(error/warning/info/debug/none,默认 error)。
|
|
15
|
+
达到该级别(含更高)的诊断 → 全部报告到 stderr + 退出 1(不输出);
|
|
16
|
+
none = 从不拒绝
|
|
17
|
+
- ``--extract PATH``:点分路径(如 ``web.spec.selector``);空 = 整树;
|
|
18
|
+
路径不存在 → stderr + 退出 1
|
|
19
|
+
- noexist 字段(键不出现)已在 reduce 阶段过滤
|
|
20
|
+
|
|
21
|
+
模块划分(与 lsp 相同的包结构):
|
|
22
|
+
- :mod:`~infinity_data.tools.cov.cli`:入口与主流程(argparse / 编译 / 阈值)
|
|
23
|
+
- :mod:`~infinity_data.tools.cov.convert`:格式渲染(YAML/JSON/TOML)+ 路径提取
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from infinity_data.tools.cov.cli import main, virtual_env
|
|
27
|
+
|
|
28
|
+
__version__ = '1.0.0'
|
|
29
|
+
|
|
30
|
+
__all__ = ['main', 'virtual_env', '__version__']
|