feat(classgen): add support for unknown types
This commit is contained in:
@@ -30,6 +30,7 @@ Note: This module assumes the presence of other modules and classes such as `Cla
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import dataclasses
|
import dataclasses
|
||||||
|
import inspect
|
||||||
from dataclasses import dataclass, fields, is_dataclass
|
from dataclasses import dataclass, fields, is_dataclass
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -261,6 +262,10 @@ def construct_value(
|
|||||||
|
|
||||||
return t(field_value) # type: ignore
|
return t(field_value) # type: ignore
|
||||||
|
|
||||||
|
if inspect.isclass(t) and t.__name__ == "Unknown":
|
||||||
|
# Return the field value as is
|
||||||
|
return field_value
|
||||||
|
|
||||||
msg = f"Unhandled field type {t} with value {field_value}"
|
msg = f"Unhandled field type {t} with value {field_value}"
|
||||||
raise ClanError(msg)
|
raise ClanError(msg)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import copy
|
import copy
|
||||||
import dataclasses
|
import dataclasses
|
||||||
|
import inspect
|
||||||
import pathlib
|
import pathlib
|
||||||
from dataclasses import MISSING
|
from dataclasses import MISSING
|
||||||
from enum import EnumType
|
from enum import EnumType
|
||||||
@@ -110,6 +111,12 @@ def type_to_dict(
|
|||||||
if t is None:
|
if t is None:
|
||||||
return {"type": "null"}
|
return {"type": "null"}
|
||||||
|
|
||||||
|
if inspect.isclass(t) and t.__name__ == "Unknown":
|
||||||
|
# Empty should represent unknown
|
||||||
|
# We don't know anything about this type
|
||||||
|
# Nor about the nested fields, if there are any
|
||||||
|
return {}
|
||||||
|
|
||||||
if dataclasses.is_dataclass(t):
|
if dataclasses.is_dataclass(t):
|
||||||
fields = dataclasses.fields(t)
|
fields = dataclasses.fields(t)
|
||||||
properties = {}
|
properties = {}
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
# ruff: noqa: RUF001
|
# ruff: noqa: RUF001
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import sys
|
import sys
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Error(Exception):
|
class Error(Exception):
|
||||||
pass
|
pass
|
||||||
@@ -130,6 +133,12 @@ def field_def_from_default_value(
|
|||||||
finalize_field: Callable[..., tuple[str, str]],
|
finalize_field: Callable[..., tuple[str, str]],
|
||||||
) -> tuple[str, str] | None:
|
) -> tuple[str, str] | None:
|
||||||
# default_value = prop_info.get("default")
|
# default_value = prop_info.get("default")
|
||||||
|
if "Unknown" in field_types:
|
||||||
|
# Unknown type, doesnt matter what the default value is
|
||||||
|
# type Unknown | a -> Unknown
|
||||||
|
return finalize_field(
|
||||||
|
field_types=field_types,
|
||||||
|
)
|
||||||
if default_value is None:
|
if default_value is None:
|
||||||
return finalize_field(
|
return finalize_field(
|
||||||
field_types=field_types | {"None"},
|
field_types=field_types | {"None"},
|
||||||
@@ -237,6 +246,9 @@ def generate_dataclass(
|
|||||||
if not prop_type and not union_variants and not enum_variants:
|
if not prop_type and not union_variants and not enum_variants:
|
||||||
msg = f"Type not found for property {prop} {prop_info}"
|
msg = f"Type not found for property {prop} {prop_info}"
|
||||||
raise Error(msg)
|
raise Error(msg)
|
||||||
|
msg = f"Type not found for property {prop} {prop_info}.\nConverting to unknown type.\n"
|
||||||
|
logger.warning(msg)
|
||||||
|
prop_type = "Unknown"
|
||||||
|
|
||||||
if union_variants:
|
if union_variants:
|
||||||
field_types = map_json_type(union_variants)
|
field_types = map_json_type(union_variants)
|
||||||
@@ -283,6 +295,8 @@ def generate_dataclass(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
known_classes.add(nested_class_name)
|
known_classes.add(nested_class_name)
|
||||||
|
elif prop_type == "Unknown":
|
||||||
|
field_types = {"Unknown"}
|
||||||
else:
|
else:
|
||||||
field_types = map_json_type(
|
field_types = map_json_type(
|
||||||
prop_type,
|
prop_type,
|
||||||
@@ -388,6 +402,12 @@ def run_gen(args: argparse.Namespace) -> None:
|
|||||||
# ruff: noqa: F401
|
# ruff: noqa: F401
|
||||||
# fmt: off
|
# fmt: off
|
||||||
from typing import Any, Literal, NotRequired, TypedDict\n
|
from typing import Any, Literal, NotRequired, TypedDict\n
|
||||||
|
|
||||||
|
# Mimic "unknown".
|
||||||
|
# 'Any' is unsafe because it allows any operations
|
||||||
|
# This forces the user to use type-narrowing or casting in the code
|
||||||
|
class Unknown:
|
||||||
|
pass
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
f.write(dataclass_code)
|
f.write(dataclass_code)
|
||||||
|
|||||||
Reference in New Issue
Block a user