diff --git a/crd_typed/__init__.py b/crd_typed/__init__.py index ebb74b5..332c640 100644 --- a/crd_typed/__init__.py +++ b/crd_typed/__init__.py @@ -1,4 +1,8 @@ -from typing import Any, Dict +from typing import Any, Callable, Dict, List + +__all__ = ["CustomResource", "CustomResourceDict"] + +_U = TypeVar("_U") class CustomResource(Dict[Any, Any]): @@ -6,3 +10,36 @@ class CustomResource(Dict[Any, Any]): def __class_getitem__(cls: Any, item: Any) -> Any: return dict + + +def CustomResourceDict( + name: str, definition_path: str, property_path: List[str], group: str, version: str, kind: str +) -> Callable[[_U], _U]: + """ + CustomResourceDict creates TypedDict type representing Custom Resource + + For example: + from crd_typed import CustomResourceDict + + MyResource = CustomResourceDict( + "MyResource" + definition_path = "/path/to/crd.yaml", + property_path = ["spec", "some_property"] + group = "myapi.io", + version = "v1", + kind = "MyResource" + ) + """ + + def new_type(x: _U): + return x + + new_type.__name__ = str(name) + new_type.__supertype__ = Dict[Any, Any] + new_type._definition_path = definition_path # type: ignore + new_type._property_path = property_path # type: ignore + new_type._group = group # type: ignore + new_type._version = version # type: ignore + new_type._kind = kind # type: ignore + + return new_type diff --git a/crd_typed/plugin.py b/crd_typed/plugin.py index ed7b8b2..ca3fcd2 100644 --- a/crd_typed/plugin.py +++ b/crd_typed/plugin.py @@ -2,8 +2,8 @@ import yaml from jsonschema_typed.plugin import APIv4, TypeMaker -from mypy.nodes import MypyFile -from mypy.plugin import AnalyzeTypeContext, Plugin +from mypy.nodes import GDEF, MypyFile, SymbolTableNode +from mypy.plugin import AnalyzeTypeContext, DynamicClassDefContext, Plugin from mypy.types import AnyType, RawExpressionType, Type, TypeOfAny, UnboundType @@ -12,6 +12,11 @@ class CRDPlugin(Plugin): CustomResource = "crd_typed.CustomResource" + def get_dynamic_class_hook(self, fullname: str) -> Optional[Callable[[DynamicClassDefContext], None]]: + if fullname == self.CustomResource: + return custom_resource_dict_callback + return None + def get_type_analyze_hook(self, fullname: str) -> Optional[Callable[[AnalyzeTypeContext], Type]]: if fullname == self.CustomResource: return custom_resource_callback @@ -23,6 +28,18 @@ def get_additional_deps(self, file: MypyFile) -> List[Tuple[int, str, int]]: return [(10, "mypy_extensions", -1)] +def custom_resource_dict_callback(ctx: DynamicClassDefContext) -> None: + pass + + # api = APIv4() + + # create our own AnalyzeTypeContext from DynamicClassDefContext and pass to TypeMaker + + # use resulting type to create TypeInfo.typeddict_type and pass it to table + + # ctx.api.add_symbol_table_node(ctx.name, SymbolTableNode(GDEF, info)) + + def custom_resource_callback(ctx: AnalyzeTypeContext) -> Type: if not ctx.type.args: return ctx.type