clan-app: WebExecutor now mirrors jsonschema api types generically

This commit is contained in:
Qubasa
2024-07-16 16:34:59 +02:00
parent be4f90eca1
commit ccece77680
5 changed files with 78 additions and 55 deletions

View File

@@ -1,7 +1,7 @@
from collections.abc import Callable
from dataclasses import dataclass
from functools import wraps
from inspect import Parameter, signature
from inspect import Parameter, Signature, signature
from typing import Annotated, Any, Generic, Literal, TypeVar, get_type_hints
from clan_cli.errors import ClanError
@@ -20,12 +20,14 @@ class ApiError:
@dataclass
class SuccessDataClass(Generic[ResponseDataType]):
op_key: str
status: Annotated[Literal["success"], "The status of the response."]
data: ResponseDataType
@dataclass
class ErrorDataClass:
op_key: str
status: Literal["error"]
errors: list[ApiError]
@@ -39,7 +41,7 @@ def update_wrapper_signature(wrapper: Callable, wrapped: Callable) -> None:
# Add 'op_key' parameter
op_key_param = Parameter(
"op_key", Parameter.KEYWORD_ONLY, default=None, annotation=str | None
"op_key", Parameter.KEYWORD_ONLY, default=None, annotation=str
)
params.append(op_key_param)
@@ -50,26 +52,28 @@ def update_wrapper_signature(wrapper: Callable, wrapped: Callable) -> None:
class MethodRegistry:
def __init__(self) -> None:
self._orig_annotations: dict[str, dict[str, Any]] = {}
self._orig_signature: dict[str, Signature] = {}
self._registry: dict[str, Callable[..., Any]] = {}
@property
def annotations(self) -> dict[str, dict[str, Any]]:
return self._orig_annotations
def orig_signatures(self) -> dict[str, Signature]:
return self._orig_signature
@property
def signatures(self) -> dict[str, Signature]:
return {name: signature(fn) for name, fn in self.functions.items()}
@property
def functions(self) -> dict[str, Callable[..., Any]]:
return self._registry
def reset(self) -> None:
self._orig_annotations.clear()
self._orig_signature.clear()
self._registry.clear()
def register_abstract(self, fn: Callable[..., T]) -> Callable[..., T]:
@wraps(fn)
def wrapper(
*args: Any, op_key: str | None = None, **kwargs: Any
) -> ApiResponse[T]:
def wrapper(*args: Any, op_key: str, **kwargs: Any) -> ApiResponse[T]:
raise NotImplementedError(
f"""{fn.__name__} - The platform didn't implement this function.
@@ -96,20 +100,19 @@ API.register(open_file)
def register(self, fn: Callable[..., T]) -> Callable[..., T]:
if fn.__name__ in self._registry:
raise ValueError(f"Function {fn.__name__} already registered")
if fn.__name__ in self._orig_annotations:
if fn.__name__ in self._orig_signature:
raise ValueError(f"Function {fn.__name__} already registered")
# make copy of original function
self._orig_annotations[fn.__name__] = fn.__annotations__.copy()
self._orig_signature[fn.__name__] = signature(fn)
@wraps(fn)
def wrapper(
*args: Any, op_key: str | None = None, **kwargs: Any
) -> ApiResponse[T]:
def wrapper(*args: Any, op_key: str, **kwargs: Any) -> ApiResponse[T]:
try:
data: T = fn(*args, **kwargs)
return SuccessDataClass(status="success", data=data)
return SuccessDataClass(status="success", data=data, op_key=op_key)
except ClanError as e:
return ErrorDataClass(
op_key=op_key,
status="error",
errors=[
ApiError(
@@ -127,7 +130,7 @@ API.register(open_file)
wrapper.__annotations__["return"] = ApiResponse[orig_return_type] # type: ignore
# Add additional argument for the operation key
wrapper.__annotations__["op_key"] = str | None # type: ignore
wrapper.__annotations__["op_key"] = str # type: ignore
update_wrapper_signature(wrapper, fn)