Test: fixup
This commit is contained in:
@@ -128,7 +128,7 @@ def type_to_dict(t: Any, scope: str = "", type_map: dict[TypeVar, type] = {}) ->
|
|||||||
if origin is None:
|
if origin is None:
|
||||||
# Non-generic user-defined or built-in type
|
# Non-generic user-defined or built-in type
|
||||||
# TODO: handle custom types
|
# TODO: handle custom types
|
||||||
raise JSchemaTypeError("Unhandled Type: ", origin)
|
raise JSchemaTypeError(f"{scope} Unhandled Type: ", origin)
|
||||||
|
|
||||||
elif origin is Literal:
|
elif origin is Literal:
|
||||||
# Handle Literal values for enums in JSON Schema
|
# Handle Literal values for enums in JSON Schema
|
||||||
@@ -173,7 +173,7 @@ def type_to_dict(t: Any, scope: str = "", type_map: dict[TypeVar, type] = {}) ->
|
|||||||
new_map.update(inspect_dataclass_fields(t))
|
new_map.update(inspect_dataclass_fields(t))
|
||||||
return type_to_dict(origin, scope, new_map)
|
return type_to_dict(origin, scope, new_map)
|
||||||
|
|
||||||
raise JSchemaTypeError(f"Error api type not yet supported {t!s}")
|
raise JSchemaTypeError(f"{scope} - Error api type not yet supported {t!s}")
|
||||||
|
|
||||||
elif isinstance(t, type):
|
elif isinstance(t, type):
|
||||||
if t is str:
|
if t is str:
|
||||||
@@ -188,7 +188,7 @@ def type_to_dict(t: Any, scope: str = "", type_map: dict[TypeVar, type] = {}) ->
|
|||||||
return {"type": "object"}
|
return {"type": "object"}
|
||||||
if t is Any:
|
if t is Any:
|
||||||
raise JSchemaTypeError(
|
raise JSchemaTypeError(
|
||||||
f"Usage of the Any type is not supported for API functions. In: {scope}"
|
f"{scope} - Usage of the Any type is not supported for API functions. In: {scope}"
|
||||||
)
|
)
|
||||||
if t is pathlib.Path:
|
if t is pathlib.Path:
|
||||||
return {
|
return {
|
||||||
@@ -197,13 +197,13 @@ def type_to_dict(t: Any, scope: str = "", type_map: dict[TypeVar, type] = {}) ->
|
|||||||
}
|
}
|
||||||
if t is dict:
|
if t is dict:
|
||||||
raise JSchemaTypeError(
|
raise JSchemaTypeError(
|
||||||
"Error: generic dict type not supported. Use dict[str, Any] instead"
|
f"{scope} - Generic 'dict' type not supported. Use dict[str, Any] or any more expressive type."
|
||||||
)
|
)
|
||||||
|
|
||||||
# Optional[T] gets internally transformed Union[T,NoneType]
|
# Optional[T] gets internally transformed Union[T,NoneType]
|
||||||
if t is NoneType:
|
if t is NoneType:
|
||||||
return {"type": "null"}
|
return {"type": "null"}
|
||||||
|
|
||||||
raise JSchemaTypeError(f"Error primitive type not supported {t!s}")
|
raise JSchemaTypeError(f"{scope} - Error primitive type not supported {t!s}")
|
||||||
else:
|
else:
|
||||||
raise JSchemaTypeError(f"Error type not supported {t!s}")
|
raise JSchemaTypeError(f"{scope} - Error type not supported {t!s}")
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class Machine:
|
|||||||
name: str
|
name: str
|
||||||
flake: FlakeId
|
flake: FlakeId
|
||||||
nix_options: list[str] = field(default_factory=list)
|
nix_options: list[str] = field(default_factory=list)
|
||||||
cached_deployment: None | dict = None
|
cached_deployment: None | dict[str, Any] = None
|
||||||
|
|
||||||
_eval_cache: dict[str, str] = field(default_factory=dict)
|
_eval_cache: dict[str, str] = field(default_factory=dict)
|
||||||
_build_cache: dict[str, Path] = field(default_factory=dict)
|
_build_cache: dict[str, Path] = field(default_factory=dict)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import sys
|
|||||||
from dataclasses import is_dataclass
|
from dataclasses import is_dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from clan_cli.api.util import type_to_dict
|
from clan_cli.api.util import JSchemaTypeError, type_to_dict
|
||||||
from clan_cli.errors import ClanError
|
from clan_cli.errors import ClanError
|
||||||
|
|
||||||
|
|
||||||
@@ -100,10 +100,21 @@ def load_dataclass_from_file(
|
|||||||
|
|
||||||
if dataclass_type and is_dataclass(dataclass_type):
|
if dataclass_type and is_dataclass(dataclass_type):
|
||||||
return dataclass_type
|
return dataclass_type
|
||||||
return None
|
|
||||||
|
raise ClanError(f"Could not load dataclass {class_name} from file: {file_path}")
|
||||||
|
|
||||||
|
|
||||||
def test_all_dataclasses() -> None:
|
def test_all_dataclasses() -> None:
|
||||||
|
"""
|
||||||
|
This Test ensures that all dataclasses are compatible with the API.
|
||||||
|
|
||||||
|
It will load all dataclasses from the clan_cli directory and
|
||||||
|
generate a JSON schema for each of them.
|
||||||
|
|
||||||
|
It will fail if any dataclass cannot be converted to JSON schema.
|
||||||
|
This means the dataclass in its current form is not compatible with the API.
|
||||||
|
"""
|
||||||
|
|
||||||
# Excludes:
|
# Excludes:
|
||||||
# - API includes Type Generic wrappers, that are not known in the init file.
|
# - API includes Type Generic wrappers, that are not known in the init file.
|
||||||
excludes = ["api/__init__.py"]
|
excludes = ["api/__init__.py"]
|
||||||
@@ -112,14 +123,24 @@ def test_all_dataclasses() -> None:
|
|||||||
dataclasses = find_dataclasses_in_directory(cli_path, excludes)
|
dataclasses = find_dataclasses_in_directory(cli_path, excludes)
|
||||||
|
|
||||||
for file, dataclass in dataclasses:
|
for file, dataclass in dataclasses:
|
||||||
print(f"Found dataclass {dataclass} in {file}")
|
print(f"checking dataclass {dataclass} in file: {file}")
|
||||||
# The parent directory of the clan_cli is the projects root directory
|
|
||||||
try:
|
try:
|
||||||
dclass = load_dataclass_from_file(file, dataclass, str(cli_path.parent))
|
dclass = load_dataclass_from_file(file, dataclass, str(cli_path.parent))
|
||||||
json_schema = type_to_dict(dclass, scope=f"FILE {file} {dataclass}")
|
type_to_dict(dclass)
|
||||||
except Exception as e:
|
except JSchemaTypeError as e:
|
||||||
print(f"Error loading dataclass {dataclass} from {file}: {e}")
|
print(f"Error loading dataclass {dataclass} from {file}: {e}")
|
||||||
raise ClanError(
|
raise ClanError(
|
||||||
f"Error loading dataclass {dataclass} from {file}: {e}",
|
f"""
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
Error converting dataclass 'class {dataclass}()' from {file}
|
||||||
|
|
||||||
|
Details:
|
||||||
|
{e}
|
||||||
|
|
||||||
|
Help:
|
||||||
|
- Converting public fields to PRIVATE by prefixing them with underscore ('_')
|
||||||
|
- Ensure all private fields are initialized the API wont provide initial values for them.
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
""",
|
||||||
location=__file__,
|
location=__file__,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user