Source code for codegen.config

import json
from typing import TYPE_CHECKING, Any

from pydantic import BaseModel, ConfigDict, ValidationError

from codegen import jsonnet, jsonpath
from codegen.errors import ConfigError
from codegen.plugins import load_plugins

if TYPE_CHECKING:
    from collections.abc import Mapping
    from pathlib import Path

    from codegen.plugins import JsonnetTarget
    from codegen.target import Target


[docs] class ExtensibleConfig(BaseModel): model_config = ConfigDict(extra="allow") @property def options(self) -> dict[str, Any]: return self.model_extra or {}
[docs] class CodegenConfig(BaseModel): package_prefix: str = ""
def load_config( path: Path, target: Target, stdlibs: Mapping[str, Path] | None = None, ) -> CodegenConfig: compiled_json = json.loads(_read_source(path, stdlibs or {})) for plugin in load_plugins(): for jsonnet_target in plugin.jsonnet_targets: if target.name in jsonnet_target.targets: plugin_fragment = json.loads( jsonnet.evaluate( path=plugin.jsonnet_dir() / jsonnet_target.path, stdlibs=stdlibs or {}, ) ) _insert_jsonnet_at_jsonpath( compiled_json, jsonnet_target, plugin_fragment ) try: return target.schema.model_validate(compiled_json) except ValidationError as exc: msg = f"Invalid config in {path}: {exc}" raise ConfigError(msg) from exc def _insert_jsonnet_at_jsonpath( data: dict, target: JsonnetTarget, fragment: list[Any], ) -> None: matches = jsonpath.search(target.jsonpath, data) if not matches: msg = f"{target.path}: jsonpath {target.jsonpath!r} matched nothing" raise ConfigError(msg) if not target.allows_many and len(matches) > 1: msg = ( f"{target.path}: jsonpath {target.jsonpath!r} has too many matches" ) raise ConfigError(msg) for match in matches: if not isinstance(match, list): msg = ( f"{target.path}: {target.jsonpath!r} selected a " f"{type(match).__name__}, expected a list" ) raise ConfigError(msg) match.extend(fragment) def _read_source(path: Path, stdlibs: Mapping[str, Path]) -> str: suffix = path.suffix.lower() try: if suffix == ".jsonnet": return jsonnet.evaluate(path, stdlibs) if suffix == ".json": return path.read_text() except RuntimeError as exc: msg = f"Jsonnet evaluation failed for {path}: {exc}" raise ConfigError(msg) from exc except OSError as exc: msg = f"Could not read {path}: {exc}" raise ConfigError(msg) from exc msg = f"Unsupported config format: {suffix!r} (use .json or .jsonnet)" raise ConfigError(msg)