feat(classgen): convert only certain attributes
This commit is contained in:
@@ -31,6 +31,6 @@ Service = dict[str, Any]
|
||||
class Inventory(TypedDict):
|
||||
machines: NotRequired[dict[str, Machine]]
|
||||
meta: NotRequired[Meta]
|
||||
modules: NotRequired[dict[str, int | bool | None | str | dict[str, Any] | float | list[Any]]]
|
||||
modules: NotRequired[dict[str, Any]]
|
||||
services: NotRequired[dict[str, Service]]
|
||||
tags: NotRequired[dict[str, list[str]]]
|
||||
tags: NotRequired[dict[str, Any]]
|
||||
|
||||
@@ -5,4 +5,4 @@ set -euo pipefail
|
||||
jsonSchema=$(nix build .#schemas.inventory-schema-abstract --print-out-paths)/schema.json
|
||||
SCRIPT_DIR=$(dirname "$0")
|
||||
cd "$SCRIPT_DIR"
|
||||
nix run .#classgen -- "$jsonSchema" "../../../clan-cli/clan_cli/inventory/classes.py" --stop-at "Service"
|
||||
nix run .#classgen -- "$jsonSchema" "../../../clan-cli/clan_cli/inventory/classes.py"
|
||||
|
||||
@@ -60,7 +60,7 @@ let
|
||||
ln -sf ${nixpkgs'} $out/clan_cli/nixpkgs
|
||||
cp -r ${../../templates} $out/clan_cli/templates
|
||||
|
||||
${classgen}/bin/classgen ${inventory-schema-abstract}/schema.json $out/clan_cli/inventory/classes.py --stop-at "Service"
|
||||
${classgen}/bin/classgen ${inventory-schema-abstract}/schema.json $out/clan_cli/inventory/classes.py
|
||||
'';
|
||||
|
||||
# Create a custom nixpkgs for use within the project
|
||||
|
||||
@@ -169,7 +169,7 @@
|
||||
];
|
||||
|
||||
installPhase = ''
|
||||
${self'.packages.classgen}/bin/classgen ${self'.legacyPackages.schemas.inventory-schema-abstract}/schema.json ./clan_cli/inventory/classes.py --stop-at "Service"
|
||||
${self'.packages.classgen}/bin/classgen ${self'.legacyPackages.schemas.inventory-schema-abstract}/schema.json ./clan_cli/inventory/classes.py
|
||||
|
||||
python docs.py reference
|
||||
mkdir -p $out
|
||||
@@ -188,7 +188,7 @@
|
||||
];
|
||||
|
||||
installPhase = ''
|
||||
${self'.packages.classgen}/bin/classgen ${self'.legacyPackages.schemas.inventory-schema-abstract}/schema.json ./clan_cli/inventory/classes.py --stop-at "Service"
|
||||
${self'.packages.classgen}/bin/classgen ${self'.legacyPackages.schemas.inventory-schema-abstract}/schema.json ./clan_cli/inventory/classes.py
|
||||
mkdir -p $out
|
||||
# Retrieve python API Typescript types
|
||||
python api.py > $out/API.json
|
||||
@@ -214,7 +214,7 @@
|
||||
classFile = "classes.py";
|
||||
};
|
||||
installPhase = ''
|
||||
${self'.packages.classgen}/bin/classgen ${self'.legacyPackages.schemas.inventory-schema-abstract}/schema.json b_classes.py --stop-at "Service"
|
||||
${self'.packages.classgen}/bin/classgen ${self'.legacyPackages.schemas.inventory-schema-abstract}/schema.json b_classes.py
|
||||
file1=$classFile
|
||||
file2=b_classes.py
|
||||
|
||||
|
||||
@@ -46,6 +46,6 @@ mkShell {
|
||||
|
||||
# Generate classes.py from inventory schema
|
||||
# This file is in .gitignore
|
||||
${self'.packages.classgen}/bin/classgen ${self'.legacyPackages.schemas.inventory-schema-abstract}/schema.json $PKG_ROOT/clan_cli/inventory/classes.py --stop-at "Service"
|
||||
${self'.packages.classgen}/bin/classgen ${self'.legacyPackages.schemas.inventory-schema-abstract}/schema.json $PKG_ROOT/clan_cli/inventory/classes.py
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -54,7 +54,11 @@ def map_json_type(
|
||||
|
||||
known_classes = set()
|
||||
root_class = "Inventory"
|
||||
stop_at = None
|
||||
# TODO: make this configurable
|
||||
# For now this only includes static top-level attributes of the inventory.
|
||||
attrs = ["machines", "meta", "services"]
|
||||
|
||||
static: dict[str, str] = {"Service": "dict[str, Any]"}
|
||||
|
||||
|
||||
def field_def_from_default_type(
|
||||
@@ -193,19 +197,32 @@ def get_field_def(
|
||||
|
||||
|
||||
# Recursive function to generate dataclasses from JSON schema
|
||||
def generate_dataclass(schema: dict[str, Any], class_name: str = root_class) -> str:
|
||||
def generate_dataclass(
|
||||
schema: dict[str, Any],
|
||||
attr_path: list[str],
|
||||
class_name: str = root_class,
|
||||
) -> str:
|
||||
properties = schema.get("properties", {})
|
||||
|
||||
required_fields = []
|
||||
fields_with_default = []
|
||||
nested_classes: list[str] = []
|
||||
if stop_at and class_name == stop_at:
|
||||
# Skip generating classes below the stop_at property
|
||||
return f"{class_name} = dict[str, Any]"
|
||||
|
||||
# if We are at the top level, and the attribute name is in shallow
|
||||
# return f"{class_name} = dict[str, Any]"
|
||||
if class_name in static:
|
||||
return f"{class_name} = {static[class_name]}"
|
||||
|
||||
for prop, prop_info in properties.items():
|
||||
# If we are at the top level, and the attribute name is not explicitly included we only do shallow
|
||||
field_name = prop.replace("-", "_")
|
||||
|
||||
if len(attr_path) == 0 and prop not in attrs:
|
||||
field_def = f"{field_name}: NotRequired[dict[str, Any]]"
|
||||
fields_with_default.append(field_def)
|
||||
# breakpoint()
|
||||
continue
|
||||
|
||||
prop_type = prop_info.get("type", None)
|
||||
union_variants = prop_info.get("oneOf", [])
|
||||
enum_variants = prop_info.get("enum", [])
|
||||
@@ -243,7 +260,9 @@ def generate_dataclass(schema: dict[str, Any], class_name: str = root_class) ->
|
||||
|
||||
if nested_class_name not in known_classes:
|
||||
nested_classes.append(
|
||||
generate_dataclass(inner_type, nested_class_name)
|
||||
generate_dataclass(
|
||||
inner_type, [*attr_path, prop], nested_class_name
|
||||
)
|
||||
)
|
||||
known_classes.add(nested_class_name)
|
||||
|
||||
@@ -259,7 +278,9 @@ def generate_dataclass(schema: dict[str, Any], class_name: str = root_class) ->
|
||||
field_types = {nested_class_name}
|
||||
if nested_class_name not in known_classes:
|
||||
nested_classes.append(
|
||||
generate_dataclass(prop_info, nested_class_name)
|
||||
generate_dataclass(
|
||||
prop_info, [*attr_path, prop], nested_class_name
|
||||
)
|
||||
)
|
||||
known_classes.add(nested_class_name)
|
||||
else:
|
||||
@@ -324,6 +345,8 @@ def generate_dataclass(schema: dict[str, Any], class_name: str = root_class) ->
|
||||
)
|
||||
required_fields.append(field_def)
|
||||
|
||||
# breakpoint()
|
||||
|
||||
fields_str = "\n ".join(required_fields + fields_with_default)
|
||||
nested_classes_str = "\n\n".join(nested_classes)
|
||||
|
||||
@@ -338,14 +361,11 @@ def generate_dataclass(schema: dict[str, Any], class_name: str = root_class) ->
|
||||
|
||||
def run_gen(args: argparse.Namespace) -> None:
|
||||
print(f"Converting {args.input} to {args.output}")
|
||||
if args.stop_at:
|
||||
global stop_at
|
||||
stop_at = args.stop_at
|
||||
|
||||
dataclass_code = ""
|
||||
with args.input.open() as f:
|
||||
schema = json.load(f)
|
||||
dataclass_code = generate_dataclass(schema)
|
||||
dataclass_code = generate_dataclass(schema, [])
|
||||
|
||||
with args.output.open("w") as f:
|
||||
f.write(
|
||||
|
||||
Reference in New Issue
Block a user