Python API¶
import devconfig_gen 不会启动任何服务,也不会写文件;只有显式传入
output_dir 时才会产生文件输出。Web UI / 向导入口是惰性导入的,导入包本身
不会加载 http.server 或 webbrowser。
包级导出¶
from devconfig_gen import (
# 数据契约
ConfigProvider, Diagnostic, GeneratedArtifact, GenerationRequest,
GenerationResult, ProviderField, ProviderStep, WebUIWidgets,
# 引擎
build_request, generate, generate_from_file, generate_pipeline,
validate_request, diagnose_request, describe_provider,
# 格式
FormatError, coerce_scalar, deep_merge, dumps, dump_data, dump_file,
load_data, load_file, loads,
# 注册表与校验
ProviderRegistry, default_registry, ValidationError,
# 惰性 UI 入口
run_interactive_wizard, run_web_ui,
)
run_interactive_wizard 与 run_web_ui 通过 PEP 562 惰性加载,首次访问时才
导入对应模块。
引擎函数¶
所有入口最终都会调用 engine.generate,因此 CLI、Python API、向导和
Web 工作台共享完全相同的校验、生成与持久化路径。
generate¶
generate(provider, request, registry=None, output_dir=None) -> GenerationResult
- 查找 Provider;
- 调用
provider.validate(request),若有错误则以"; "拼接后抛出ValueError; - 调用
provider.generate(request); - 若提供
output_dir,把产物写入该目录。
from devconfig_gen import GenerationRequest, generate
result = generate(
"custom",
GenerationRequest(
context={"document": {"app": {"name": "web", "port": 8080}}},
options={"format": "yaml"},
),
)
artifact = result.artifacts[0]
print(artifact.name, artifact.media_type) # custom.yaml application/yaml
build_request¶
build_request(*, context=None, input_path=None, options=None,
input_format=None, overrides=None) -> GenerationRequest
context为内存中的初始上下文(映射时作为合并基底);input_path可以是单个路径、路径字符串或路径序列;每个文件加载后与当前 上下文深度合并,顺序从左到右;- 若某次加载的结果不是映射,它会整体替换当前上下文;
overrides为点路径字典,最后应用;若上下文不是dict则重置为{}。
generate_pipeline¶
generate_pipeline(provider, *, context=None, input_path=None, options=None,
output_dir=None, output_format=None, input_format=None,
registry=None, overrides=None) -> GenerationResult
output_format 会写入 options["format"]。这是 CLI generate 使用的入口。
generate_from_file¶
generate_from_file(provider, input_path, *, output_dir=None,
output_format=None, options=None, registry=None)
generate_pipeline 的单文件便捷封装。
validate_request / diagnose_request¶
validate_request(provider, *, context=None, input_path=None, options=None,
input_format=None, overrides=None, registry=None) -> Sequence[str]
diagnose_request(provider, *, context=None, input_path=None, options=None,
input_format=None, overrides=None, registry=None) -> Sequence[Diagnostic]
validate_request 返回渲染后的错误消息字符串;diagnose_request 优先调用
Provider 的 diagnose,否则把 validate 的消息包装成 field="" 的
Diagnostic。两者都只构建请求并校验,不生成、不写文件。
from devconfig_gen import diagnose_request
for d in diagnose_request("env", context={}):
print(d.field, "->", d.message, f"({d.severity})")
# variables -> variables must not be empty (error)
describe_provider¶
describe_provider(provider, registry=None) -> Sequence[ProviderStep]
优先调用 Provider 的 describe_schema(),否则读取 steps 属性,都没有时
返回空元组。
数据契约(models)¶
| 类型 | 字段 | 说明 |
|---|---|---|
GenerationRequest |
context、options |
不可变;映射按只读处理 |
Diagnostic |
field、message、severity |
severity 为 "error"/"warning";str() 返回 message;as_dict() 输出 JSON 就绪字典 |
GeneratedArtifact |
name、content、media_type |
content 可为数据结构或预渲染字符串;默认 application/json |
GenerationResult |
provider、artifacts、diagnostics |
Provider 生成结果 |
ProviderField |
name、type、required、default、description、choices、minimum、maximum、title、i18n |
title 为空时 as_dict() 回退为 name;i18n 为空时省略 |
ProviderStep |
id、title、description、fields、i18n |
步骤元数据 |
ConfigProvider |
name、generate、validate |
结构化协议;diagnose、describe_schema/steps、web_ui_widgets 可选 |
WebUIWidgets |
web_ui_widgets() |
可选、纯文档性协议:把 field_type 映射到 JavaScript 工厂源码;永不强制 |
ProviderField.type 的约定取值(决定客户端如何渲染):
string、integer、boolean、mapping/dict、document、tree。
web_ui_widgets() 可为 Web 工作台注册额外的类型;未声明的类型回退到
string,未实现该方法的 Provider 行为不变。详见
Provider 参考与开发。
格式 API(formats)¶
from devconfig_gen import formats
data = formats.loads('{"a": 1}') # 自动检测 JSON/YAML
formats.loads("a: 1", "yaml")
formats.load_file("config.yaml")
formats.load_data("a: 1") # 文本或路径
text = formats.dumps(data, "json") # JSON:2 空格缩进 + 末尾换行
formats.dump_file(data, "out/config.yaml")
formats.detect_format(path="a.json") # "json"
formats.resolve_format(None, name="a.yml") # "yaml"
formats.media_type_for("yaml") # "application/yaml"
formats.format_from_media_type("application/yaml") # "yaml"
formats.deep_merge({"a": {"b": 1}}, {"a": {"c": 2}})
formats.coerce_scalar("9090") # 9090
关键语义:
dumps(data, fmt):JSON 使用indent=2, sort_keys=False, ensure_ascii=False并追加换行;YAML 在安装 PyYAML 时用safe_dump(保留插入顺序、允许 Unicode),否则用内置序列化器;loads(text, fmt=None):fmt省略时先看内容特征({/[开头视为 JSON, 否则 YAML);load_file:显式格式 > 扩展名 > 内容检测;load_data:os.PathLike一律当文件;字符串不含换行且指向存在的文件时 当路径,否则当文档文本;coerce_scalar:仅字符串参与推断,空字符串保持原样,非 JSON 文本保持 原样(例如"1.2.0"、"production");deep_merge:映射递归合并,标量/列表整体替换,不修改入参;FormatError表示解析/序列化失败,YamlError是其子类。
注册表(registry)¶
from devconfig_gen import ProviderRegistry
from devconfig_gen.providers import CustomProvider, EnvProvider, JsonProvider
registry = ProviderRegistry((CustomProvider(), JsonProvider(), EnvProvider()))
registry.names() # ('custom', 'env', 'json')
registry.get("custom")
registry.register(MyProvider())
- 名称必须为非空、已经小写的字符串(
str(provider.name).strip().lower()必须与provider.name完全一致),否则ValueError; - 重复注册同名 Provider 抛出
ValueError; get会去除首尾空白并转小写;未知名称抛出ValueError,错误信息包含 可用名称列表;default_registry已注册custom、json、env。
校验 API(validation)¶
from devconfig_gen import Diagnostic, ValidationError, validation
errors = []
value = validation.expect_integer(99999, "app.port", errors, minimum=1, maximum=65535)
# value is None; errors[0] == Diagnostic("app.port", "port must be between 1 and 65535, got 99999")
可用辅助函数:expect_mapping、expect_string、expect_integer、
expect_enum、expect_string_mapping,以及 is_mapping、is_integer。
每个 expect_* 把 Diagnostic 追加到传入的列表并返回规范化后的值(失败
返回 None),从而一次收集全部问题。消息使用字段路径的叶子名,field
保留完整点路径。ValidationError 接受 Diagnostic 或字符串序列,errors
属性为消息元组,str() 以 "; " 连接。
自定义 Provider 示例¶
from devconfig_gen import (
Diagnostic, GeneratedArtifact, GenerationRequest, GenerationResult,
ProviderField, ProviderRegistry, ProviderStep, ValidationError, generate,
)
class GreetingProvider:
name = "greeting"
steps = (
ProviderStep(
id="input",
title="Greeting input",
fields=(
ProviderField("who", type="string", required=True, title="Who"),
),
),
)
def describe_schema(self):
return self.steps
def diagnose(self, request):
if not request.context.get("who"):
return (Diagnostic("who", "missing required field: 'who'"),)
return ()
def validate(self, request):
return tuple(item.message for item in self.diagnose(request))
def generate(self, request):
diagnostics = self.diagnose(request)
if diagnostics:
raise ValidationError(diagnostics)
return GenerationResult(
provider=self.name,
artifacts=(
GeneratedArtifact(
name="greeting.json",
content={"message": f"hello {request.context['who']}"},
media_type="application/json",
),
),
)
registry = ProviderRegistry((GreetingProvider(),))
result = generate(
"greeting",
GenerationRequest(context={"who": "world"}),
registry=registry,
)
更多 Provider 约定与内置实现细节见 Provider 参考与开发。