HN 표시: Msgspec-config, msgspec에 대한 또 다른 구성 라이브러리
hackernews
|
|
🔬 연구
#msgspec
#python
#review
#라이브러리
#설정
#타입
원문 출처: hackernews · Genesis Park에서 요약 및 분석
요약
개발자는 Pydantic-settings의 편의성을 모방하면서도, Msgspec을 위한 새로운 설정 라이브러리인 'msgspec-config'를 공개했습니다. 이 도구는 모델 검증을 자동화할 뿐만 아니라, 포크 없이도 확장 가능한 유연성을 제공하는 것을 목표로 합니다. 특히 CLI 설정 및 API 리더와 같은 기능을 포함하고 있어 데이터 소스를 순서에 구애받지 않고 자유롭게 결합할 수 있습니다.
본문
Typed, multi-source configuration loading on top of msgspec . msgspec-config is for applications that need: - one typed model for configuration shape - multiple config inputs (files, .env , environment, CLI, custom providers) - deterministic precedence across all inputs - strict validation/coercion without writing parsing glue The core idea is simple: define one DataModel , attach ordered DataSource s, and instantiate the model. Please visit the API docs at this project's github pages site: https://maxpareschi.github.io/msgspec-config/ pip install msgspec-config uv add msgspec-config Tested on Python>=3.11 config.toml : host = "toml-host" port = 7000 [log] level = "INFO" .env : APP_PORT=7500 APP_LOG_LEVEL=DEBUG from msgspec_config import ( APISource, CliSource, DataModel, DotEnvSource, EnvironSource, JSONSource, TomlSource, datasources, entry, group, ) class LogConfig(DataModel): level: str = "WARN" file_path: str = "/var/log/app.log" @datasources( TomlSource(toml_path="config.toml"), DotEnvSource(dotenv_path=".env", env_prefix="APP"), EnvironSource(env_prefix="APP"), CliSource(), ) class AppConfig(DataModel): host: str = entry("127.0.0.1", min_length=1) port: int = entry(8080, ge=1, le=65535) debug: bool = False log: LogConfig = group(collapsed=True) cfg = AppConfig(port=9000) print(cfg.model_dump_json(indent=2)) Precedence is deterministic and intentional: defaults {"log": {"level": "ERROR"}} Generates options from model fields (including nested fields). Key behavior: autogenerate=True (default): fields are exposed automaticallyautogenerate=False : only fields explicitly opted in via metadata are exposed- nested fields become flags like --log-level - bools support both positive and negative forms: --debug /--no-debug - nested struct fields also accept JSON on the top-level flag: --log '{"level":"DEBUG"}' - explicit nested flags override keys from that JSON entry(..., cli=False) excludes a field from CLI generationentry(..., cli=True) force-includes a field whenautogenerate=False entry(..., cli_flag=..., cli_short_flag=...) overrides generated option names for that field- unknown CLI args are stored on source runtime state in __unmapped_kwargs__ , accessible also through method get_unmapped_payload() - unmatched CLI tokens (unknown flags and positionals) are stored on source runtime state in __raw_argv__ , retrievable by method get_raw_argv() - set kebab_case=False to use dotted long flags (e.g.--log.level ) - CLI accepts canonical and encoded/alias field names, and maps parsed values to encoded field names Field policy precedence: cli=False : field is excludedcli=True : field is includedcli_flag /cli_short_flag present: field is included- otherwise inclusion follows autogenerate src = CliSource(autogenerate=False, cli_args=["--server-host", "api"]) data = src.resolve(model=AppConfig) print(data) @datasources(CliSource()) class CliApp(DataModel): dev: bool = False # argv: ["prog", "--dev", "command", "test"] cfg = CliApp() print(cfg.dev) # True print(cfg.get_raw_argv()) # ["command", "test"] - performs an HTTP GET request againstapi_url - optional auth header via header_name +header_value - optional root_node to unwrap wrapped payloads (for example{"data": {...}} ) - request or parse failures raise RuntimeError with endpoint context src = APISource( api_url="https://example.com/config", header_name="Authorization", header_value="Bearer ", root_node="data", ) data = src.resolve() When built-ins are not enough, implement DataSource.load(...) . from typing import Any from msgspec_config import DataModel, DataSource, datasources class SecretsSource(DataSource): def load(self, model: type[DataModel] | None = None) -> dict[str, Any]: # Replace this with Vault/AWS/GCP/etc. return {"host": "secrets-host", "port": 8443} @datasources(SecretsSource()) class ServiceConfig(DataModel): host: str = "localhost" port: int = 8080 Rationale: sources are deep-cloned per model instantiation, so source-local mutable state does not leak across DataModel() calls. DataSource.resolve(...) is the public finalized loader (reset + finalize); custom sources should override load(...) . - Do not shadow DataModel /DataSource method names with fields; this is user responsibility and can break runtime behavior. DataModel is a msgspec.Struct configured as keyword-only and with dict-like output support. Useful methods: from_data(data) to create an instance from a Python mappingfrom_json(json_str) to create an instance from JSON bytes/stringmodel_dump() to get the model converted in Python builtinsmodel_dump_json(indent=...) for JSON outputmodel_json_schema(indent=...) for JSON Schema exportget_datasources_payload(*sources, **kwargs) to retrieve merged source payloads manuallyget_unmapped_payload() to lazily merge source runtime__unmapped_kwargs__ in source order plus unknown constructor kwargs (merged last)get_raw_argv() to read raw CLI leftovers (unknown flags/positionals after mapped CLI options are filtered out) Notes: from_data(...) andfr
Genesis Park 편집팀이 AI를 활용하여 작성한 분석입니다. 원문은 출처 링크를 통해 확인할 수 있습니다.
공유