From ef9eacba3d03271e5522f938cf0c2df849d354c8 Mon Sep 17 00:00:00 2001 From: Devon Stewart Date: Mon, 17 Mar 2025 22:41:22 -0700 Subject: [PATCH 1/8] Avoiding mixing type rendering and unstructured codegen --- src/replit_river/codegen/client.py | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/replit_river/codegen/client.py b/src/replit_river/codegen/client.py index 440e6634..873601c5 100644 --- a/src/replit_river/codegen/client.py +++ b/src/replit_river/codegen/client.py @@ -643,7 +643,16 @@ def extract_props(tpe: RiverType) -> list[dict[str, RiverType]]: """ ) current_chunks.append( - f" kind: {render_type_expr(type_name)} | None{value}" + f" kind: { + render_type_expr( + UnionTypeExpr( + [ + type_name, + NoneTypeExpr(), + ] + ) + ) + }{value}" ) else: value = "" @@ -666,7 +675,11 @@ def extract_props(tpe: RiverType) -> list[dict[str, RiverType]]: reindent( " ", f"""\ - {name}: NotRequired[{render_type_expr(type_name)}] | None + {name}: NotRequired[{ + render_type_expr( + UnionTypeExpr([type_name, NoneTypeExpr()]) + ) + }] """, ) ) @@ -675,7 +688,16 @@ def extract_props(tpe: RiverType) -> list[dict[str, RiverType]]: reindent( " ", f"""\ - {name}: {render_type_expr(type_name)} | None = None + {name}: { + render_type_expr( + UnionTypeExpr( + [ + type_name, + NoneTypeExpr(), + ] + ) + ) + } = None """, ) ) From 6609ad71a22116ca60556e67d16a690cabf5c70c Mon Sep 17 00:00:00 2001 From: Devon Stewart Date: Tue, 18 Mar 2025 10:56:00 -0700 Subject: [PATCH 2/8] Revert "We need to explicitly import grpc.aio if we want to use it without errors" This reverts commit 4e6128e5686d2e627655e33a7ca2a0e9958573bb. --- src/replit_river/rpc.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/replit_river/rpc.py b/src/replit_river/rpc.py index 1a3cebba..8415143a 100644 --- a/src/replit_river/rpc.py +++ b/src/replit_river/rpc.py @@ -18,7 +18,6 @@ ) import grpc -import grpc.aio from aiochannel import Channel, ChannelClosed from opentelemetry.propagators.textmap import Setter from pydantic import BaseModel, ConfigDict, Field From b37a9bb3b452a0a3d7568d777b9964f1a604b9c4 Mon Sep 17 00:00:00 2001 From: Devon Stewart Date: Tue, 18 Mar 2025 11:44:11 -0700 Subject: [PATCH 3/8] Break out common test baseline --- .../snapshot/codegen_snapshot_fixtures.py | 40 +++++++++++++++++++ tests/codegen/snapshot/test_enum.py | 26 ++---------- tests/codegen/stream/test_stream.py | 24 ++--------- 3 files changed, 47 insertions(+), 43 deletions(-) create mode 100644 tests/codegen/snapshot/codegen_snapshot_fixtures.py diff --git a/tests/codegen/snapshot/codegen_snapshot_fixtures.py b/tests/codegen/snapshot/codegen_snapshot_fixtures.py new file mode 100644 index 00000000..1a343fc8 --- /dev/null +++ b/tests/codegen/snapshot/codegen_snapshot_fixtures.py @@ -0,0 +1,40 @@ +from io import StringIO +from pathlib import Path +from typing import Callable, TextIO + +from pytest_snapshot.plugin import Snapshot + +from replit_river.codegen.client import schema_to_river_client_codegen + + +class UnclosableStringIO(StringIO): + def close(self) -> None: + pass + + +def validate_codegen( + *, + snapshot: Snapshot, + read_schema: Callable[[], TextIO], + target_path: str, + client_name: str, +) -> None: + snapshot.snapshot_dir = "tests/codegen/snapshot/snapshots" + files: dict[Path, UnclosableStringIO] = {} + + def file_opener(path: Path) -> TextIO: + buffer = UnclosableStringIO() + assert path not in files, "Codegen attempted to write to the same file twice!" + files[path] = buffer + return buffer + + schema_to_river_client_codegen( + read_schema=read_schema, + target_path=target_path, + client_name=client_name, + file_opener=file_opener, + typed_dict_inputs=True, + ) + for path, file in files.items(): + file.seek(0) + snapshot.assert_match(file.read(), Path(snapshot.snapshot_dir, path)) diff --git a/tests/codegen/snapshot/test_enum.py b/tests/codegen/snapshot/test_enum.py index e3ccfc0d..b24fbc20 100644 --- a/tests/codegen/snapshot/test_enum.py +++ b/tests/codegen/snapshot/test_enum.py @@ -1,10 +1,8 @@ from io import StringIO -from pathlib import Path -from typing import TextIO from pytest_snapshot.plugin import Snapshot -from replit_river.codegen.client import schema_to_river_client_codegen +from tests.codegen.snapshot.codegen_snapshot_fixtures import validate_codegen test_unknown_enum_schema = """ { @@ -152,28 +150,10 @@ """ -class UnclosableStringIO(StringIO): - def close(self) -> None: - pass - - def test_unknown_enum(snapshot: Snapshot) -> None: - snapshot.snapshot_dir = "tests/codegen/snapshot/snapshots" - files: dict[Path, UnclosableStringIO] = {} - - def file_opener(path: Path) -> TextIO: - buffer = UnclosableStringIO() - assert path not in files, "Codegen attempted to write to the same file twice!" - files[path] = buffer - return buffer - - schema_to_river_client_codegen( + validate_codegen( + snapshot=snapshot, read_schema=lambda: StringIO(test_unknown_enum_schema), target_path="test_unknown_enum", client_name="foo", - file_opener=file_opener, - typed_dict_inputs=True, ) - for path, file in files.items(): - file.seek(0) - snapshot.assert_match(file.read(), Path(snapshot.snapshot_dir, path)) diff --git a/tests/codegen/stream/test_stream.py b/tests/codegen/stream/test_stream.py index c03940b4..2529fd1a 100644 --- a/tests/codegen/stream/test_stream.py +++ b/tests/codegen/stream/test_stream.py @@ -1,39 +1,23 @@ import importlib -from pathlib import Path -from typing import AsyncIterable, TextIO +from typing import AsyncIterable import pytest from pytest_snapshot.plugin import Snapshot from replit_river.client import Client -from replit_river.codegen.client import schema_to_river_client_codegen -from tests.codegen.snapshot.test_enum import UnclosableStringIO +from tests.codegen.snapshot.codegen_snapshot_fixtures import validate_codegen from tests.common_handlers import basic_stream @pytest.mark.parametrize("handlers", [{**basic_stream}]) async def test_basic_stream(snapshot: Snapshot, client: Client) -> None: - snapshot.snapshot_dir = "tests/codegen/snapshot/snapshots" - files: dict[Path, UnclosableStringIO] = {} - - def file_opener(path: Path) -> TextIO: - buffer = UnclosableStringIO() - assert path not in files, "Codegen attempted to write to the same file twice!" - files[path] = buffer - return buffer - - schema_to_river_client_codegen( + validate_codegen( + snapshot=snapshot, read_schema=lambda: open("tests/codegen/stream/schema.json"), target_path="test_basic_stream", client_name="StreamClient", - file_opener=file_opener, - typed_dict_inputs=True, ) - for path, file in files.items(): - file.seek(0) - snapshot.assert_match(file.read(), Path(snapshot.snapshot_dir, path)) - import tests.codegen.snapshot.snapshots.test_basic_stream importlib.reload(tests.codegen.snapshot.snapshots.test_basic_stream) From 707b45646c10d2017b1195ee2cbac95f110ce7fc Mon Sep 17 00:00:00 2001 From: Devon Stewart Date: Tue, 18 Mar 2025 11:52:23 -0700 Subject: [PATCH 4/8] Adding test_pathological --- tests/codegen/snapshot/test_pathological.py | 12 + tests/codegen/types/schema.json | 320 ++++++++++++++++++++ 2 files changed, 332 insertions(+) create mode 100644 tests/codegen/snapshot/test_pathological.py create mode 100644 tests/codegen/types/schema.json diff --git a/tests/codegen/snapshot/test_pathological.py b/tests/codegen/snapshot/test_pathological.py new file mode 100644 index 00000000..68775c8f --- /dev/null +++ b/tests/codegen/snapshot/test_pathological.py @@ -0,0 +1,12 @@ +from pytest_snapshot.plugin import Snapshot + +from tests.codegen.snapshot.codegen_snapshot_fixtures import validate_codegen + + +async def test_pathological_types(snapshot: Snapshot) -> None: + validate_codegen( + snapshot=snapshot, + read_schema=lambda: open("tests/codegen/types/schema.json"), + target_path="test_pathological_types", + client_name="PathologicalClient", + ) diff --git a/tests/codegen/types/schema.json b/tests/codegen/types/schema.json new file mode 100644 index 00000000..93383fc4 --- /dev/null +++ b/tests/codegen/types/schema.json @@ -0,0 +1,320 @@ +{ + "services": { + "test_service": { + "procedures": { + "pathological_method": { + "input": { + "type": "object", + "properties": { + "string": { + "type": "string" + }, + "req_string": { + "type": "string" + }, + "Uint8Array": { + "type": "Uint8Array" + }, + "req_Uint8Array": { + "type": "Uint8Array" + }, + "number": { + "type": "number" + }, + "req_number": { + "type": "number" + }, + "integer": { + "type": "integer" + }, + "req_integer": { + "type": "integer" + }, + "boolean": { + "type": "boolean" + }, + "req_boolean": { + "type": "boolean" + }, + "null": { + "type": "null" + }, + "req_null": { + "type": "null" + }, + "undefined": { + "type": "undefined" + }, + "req_undefined": { + "type": "undefined" + }, + "Date": { + "type": "Date" + }, + "req_Date": { + "type": "Date" + }, + "arr_string": { + "type": "array", + "items": { + "type": "string" + } + }, + "req_arr_string": { + "type": "array", + "items": { + "type": "string" + } + }, + "arr_Uint8Array": { + "type": "array", + "items": { + "type": "Uint8Array" + } + }, + "req_arr_Uint8Array": { + "type": "array", + "items": { + "type": "Uint8Array" + } + }, + "arr_number": { + "type": "array", + "items": { + "type": "number" + } + }, + "req_arr_number": { + "type": "array", + "items": { + "type": "number" + } + }, + "arr_integer": { + "type": "array", + "items": { + "type": "integer" + } + }, + "req_arr_integer": { + "type": "array", + "items": { + "type": "integer" + } + }, + "arr_boolean": { + "type": "array", + "items": { + "type": "boolean" + } + }, + "req_arr_boolean": { + "type": "array", + "items": { + "type": "boolean" + } + }, + "arr_null": { + "type": "array", + "items": { + "type": "null" + } + }, + "req_arr_null": { + "type": "array", + "items": { + "type": "null" + } + }, + "arr_undefined": { + "type": "array", + "items": { + "type": "undefined" + } + }, + "req_arr_undefined": { + "type": "array", + "items": { + "type": "undefined" + } + }, + "arr_Date": { + "type": "array", + "items": { + "type": "Date" + } + }, + "req_arr_Date": { + "type": "array", + "items": { + "type": "Date" + } + }, + "obj_string": { + "type": "object", + "properties": { + "prop_string": { + "type": "string" + } + } + }, + "req_obj_string": { + "type": "object", + "properties": { + "prop_string": { + "type": "string" + } + } + }, + "obj_Uint8Array": { + "type": "object", + "properties": { + "prop_Uint8Array": { + "type": "Uint8Array" + } + } + }, + "req_obj_Uint8Array": { + "type": "object", + "properties": { + "prop_Uint8Array": { + "type": "Uint8Array" + } + } + }, + "obj_number": { + "type": "object", + "properties": { + "prop_number": { + "type": "number" + } + } + }, + "req_obj_number": { + "type": "object", + "properties": { + "prop_number": { + "type": "number" + } + } + }, + "obj_integer": { + "type": "object", + "properties": { + "prop_integer": { + "type": "integer" + } + } + }, + "req_obj_integer": { + "type": "object", + "properties": { + "prop_integer": { + "type": "integer" + } + } + }, + "obj_boolean": { + "type": "object", + "properties": { + "prop_boolean": { + "type": "boolean" + } + } + }, + "req_obj_boolean": { + "type": "object", + "properties": { + "prop_boolean": { + "type": "boolean" + } + } + }, + "obj_null": { + "type": "object", + "properties": { + "prop_null": { + "type": "null" + } + } + }, + "req_obj_null": { + "type": "object", + "properties": { + "prop_null": { + "type": "null" + } + } + }, + "obj_undefined": { + "type": "object", + "properties": { + "prop_undefined": { + "type": "undefined" + } + } + }, + "req_obj_undefined": { + "type": "object", + "properties": { + "prop_undefined": { + "type": "undefined" + } + } + }, + "obj_Date": { + "type": "object", + "properties": { + "prop_Date": { + "type": "Date" + } + } + }, + "req_obj_Date": { + "type": "object", + "properties": { + "prop_Date": { + "type": "Date" + } + } + } + }, + "required": [ + "req_string", + "req_Uint8Array", + "req_number", + "req_integer", + "req_boolean", + "req_null", + "req_undefined", + "req_Date", + "req_arr_string", + "req_arr_Uint8Array", + "req_arr_number", + "req_arr_integer", + "req_arr_boolean", + "req_arr_null", + "req_arr_undefined", + "req_arr_Date", + "req_obj_string", + "req_obj_Uint8Array", + "req_obj_number", + "req_obj_integer", + "req_obj_boolean", + "req_obj_null", + "req_obj_undefined", + "req_obj_Date" + ] + }, + "output": { + "type": "boolean" + }, + "errors": { + "not": {} + }, + "type": "rpc" + } + } + } + } +} From 64389e73888664604aa42beaa9a2cd33cfd67cbb Mon Sep 17 00:00:00 2001 From: Devon Stewart Date: Tue, 18 Mar 2025 12:20:45 -0700 Subject: [PATCH 5/8] Add comparison to type exprs --- src/replit_river/codegen/typing.py | 44 ++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/replit_river/codegen/typing.py b/src/replit_river/codegen/typing.py index 3489945c..9a89b024 100644 --- a/src/replit_river/codegen/typing.py +++ b/src/replit_river/codegen/typing.py @@ -16,12 +16,24 @@ class TypeName: def __str__(self) -> str: raise Exception("Complex type must be put through render_type_expr!") + def __eq__(self, other: object) -> bool: + return isinstance(other, TypeName) and other.value == self.value + + def __lt__(self, other: object) -> bool: + return hash(self) < hash(other) + @dataclass(frozen=True) class NoneTypeExpr: def __str__(self) -> str: raise Exception("Complex type must be put through render_type_expr!") + def __eq__(self, other: object) -> bool: + return isinstance(other, NoneTypeExpr) + + def __lt__(self, other: object) -> bool: + return hash(self) < hash(other) + @dataclass(frozen=True) class DictTypeExpr: @@ -30,6 +42,12 @@ class DictTypeExpr: def __str__(self) -> str: raise Exception("Complex type must be put through render_type_expr!") + def __eq__(self, other: object) -> bool: + return isinstance(other, DictTypeExpr) and other.nested == self.nested + + def __lt__(self, other: object) -> bool: + return hash(self) < hash(other) + @dataclass(frozen=True) class ListTypeExpr: @@ -38,6 +56,12 @@ class ListTypeExpr: def __str__(self) -> str: raise Exception("Complex type must be put through render_type_expr!") + def __eq__(self, other: object) -> bool: + return isinstance(other, ListTypeExpr) and other.nested == self.nested + + def __lt__(self, other: object) -> bool: + return hash(self) < hash(other) + @dataclass(frozen=True) class LiteralTypeExpr: @@ -46,6 +70,12 @@ class LiteralTypeExpr: def __str__(self) -> str: raise Exception("Complex type must be put through render_type_expr!") + def __eq__(self, other: object) -> bool: + return isinstance(other, LiteralTypeExpr) and other.nested == self.nested + + def __lt__(self, other: object) -> bool: + return hash(self) < hash(other) + @dataclass(frozen=True) class UnionTypeExpr: @@ -54,6 +84,14 @@ class UnionTypeExpr: def __str__(self) -> str: raise Exception("Complex type must be put through render_type_expr!") + def __eq__(self, other: object) -> bool: + return isinstance(other, UnionTypeExpr) and set(other.nested) == set( + self.nested + ) + + def __lt__(self, other: object) -> bool: + return hash(self) < hash(other) + @dataclass(frozen=True) class OpenUnionTypeExpr: @@ -62,6 +100,12 @@ class OpenUnionTypeExpr: def __str__(self) -> str: raise Exception("Complex type must be put through render_type_expr!") + def __eq__(self, other: object) -> bool: + return isinstance(other, OpenUnionTypeExpr) and other.union == self.union + + def __lt__(self, other: object) -> bool: + return hash(self) < hash(other) + TypeExpression = ( TypeName From 7e8eb9a084ac69f338191e8423bb312f1d10a91d Mon Sep 17 00:00:00 2001 From: Devon Stewart Date: Tue, 18 Mar 2025 12:50:46 -0700 Subject: [PATCH 6/8] Hackily work around None | None --- src/replit_river/codegen/typing.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/replit_river/codegen/typing.py b/src/replit_river/codegen/typing.py index 9a89b024..0d78005b 100644 --- a/src/replit_river/codegen/typing.py +++ b/src/replit_river/codegen/typing.py @@ -161,6 +161,13 @@ def render_type_expr(value: TypeExpression) -> str: literals.append(tpe) else: _other.append(tpe) + + without_none: list[TypeExpression] = [ + x for x in _other if not isinstance(x, NoneTypeExpr) + ] + has_none = len(_other) > len(without_none) + _other = without_none + retval: str = " | ".join(render_type_expr(x) for x in _other) if literals: _rendered: str = ", ".join(repr(x.nested) for x in literals) @@ -168,6 +175,11 @@ def render_type_expr(value: TypeExpression) -> str: retval = f"Literal[{_rendered}] | {retval}" else: retval = f"Literal[{_rendered}]" + if has_none: + if retval: + retval = f"{retval} | None" + else: + retval = "None" return retval case OpenUnionTypeExpr(inner): return ( From fb1189da8ef3eb0bb5eaced116052f2a9f52ef6a Mon Sep 17 00:00:00 2001 From: Devon Stewart Date: Tue, 18 Mar 2025 12:54:35 -0700 Subject: [PATCH 7/8] Help figure out what was wrong --- src/replit_river/codegen/client.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/replit_river/codegen/client.py b/src/replit_river/codegen/client.py index 873601c5..8d2791b9 100644 --- a/src/replit_river/codegen/client.py +++ b/src/replit_river/codegen/client.py @@ -1268,6 +1268,8 @@ def schema_to_river_client_codegen( stdout=subprocess.PIPE, ) stdout, _ = popen.communicate(contents.encode()) + if popen.returncode != 0: + f.write(contents) f.write(stdout.decode("utf-8")) except: f.write(contents) From bcb25f8cce3398358470aa16409fb1ac1673ebdd Mon Sep 17 00:00:00 2001 From: Devon Stewart Date: Tue, 18 Mar 2025 12:55:10 -0700 Subject: [PATCH 8/8] Generating types --- .../test_pathological_types/__init__.py | 13 + .../test_service/__init__.py | 58 +++ .../test_service/pathological_method.py | 479 ++++++++++++++++++ 3 files changed, 550 insertions(+) create mode 100644 tests/codegen/snapshot/snapshots/test_pathological_types/__init__.py create mode 100644 tests/codegen/snapshot/snapshots/test_pathological_types/test_service/__init__.py create mode 100644 tests/codegen/snapshot/snapshots/test_pathological_types/test_service/pathological_method.py diff --git a/tests/codegen/snapshot/snapshots/test_pathological_types/__init__.py b/tests/codegen/snapshot/snapshots/test_pathological_types/__init__.py new file mode 100644 index 00000000..640c7426 --- /dev/null +++ b/tests/codegen/snapshot/snapshots/test_pathological_types/__init__.py @@ -0,0 +1,13 @@ +# Code generated by river.codegen. DO NOT EDIT. +from pydantic import BaseModel +from typing import Literal + +import replit_river as river + + +from .test_service import Test_ServiceService + + +class PathologicalClient: + def __init__(self, client: river.Client[Literal[None]]): + self.test_service = Test_ServiceService(client) diff --git a/tests/codegen/snapshot/snapshots/test_pathological_types/test_service/__init__.py b/tests/codegen/snapshot/snapshots/test_pathological_types/test_service/__init__.py new file mode 100644 index 00000000..bc60d38a --- /dev/null +++ b/tests/codegen/snapshot/snapshots/test_pathological_types/test_service/__init__.py @@ -0,0 +1,58 @@ +# Code generated by river.codegen. DO NOT EDIT. +from collections.abc import AsyncIterable, AsyncIterator +from typing import Any +import datetime + +from pydantic import TypeAdapter + +from replit_river.error_schema import RiverError, RiverErrorTypeAdapter +import replit_river as river + + +from .pathological_method import ( + Pathological_MethodInput, + Pathological_MethodInputTypeAdapter, + encode_Pathological_MethodInput, + encode_Pathological_MethodInputObj_Boolean, + encode_Pathological_MethodInputObj_Date, + encode_Pathological_MethodInputObj_Integer, + encode_Pathological_MethodInputObj_Null, + encode_Pathological_MethodInputObj_Number, + encode_Pathological_MethodInputObj_String, + encode_Pathological_MethodInputObj_Uint8Array, + encode_Pathological_MethodInputObj_Undefined, + encode_Pathological_MethodInputReq_Obj_Boolean, + encode_Pathological_MethodInputReq_Obj_Date, + encode_Pathological_MethodInputReq_Obj_Integer, + encode_Pathological_MethodInputReq_Obj_Null, + encode_Pathological_MethodInputReq_Obj_Number, + encode_Pathological_MethodInputReq_Obj_String, + encode_Pathological_MethodInputReq_Obj_Uint8Array, + encode_Pathological_MethodInputReq_Obj_Undefined, +) + +boolTypeAdapter: TypeAdapter[Any] = TypeAdapter(bool) + + +class Test_ServiceService: + def __init__(self, client: river.Client[Any]): + self.client = client + + async def pathological_method( + self, + input: Pathological_MethodInput, + timeout: datetime.timedelta, + ) -> bool: + return await self.client.send_rpc( + "test_service", + "pathological_method", + input, + encode_Pathological_MethodInput, + lambda x: boolTypeAdapter.validate_python( + x # type: ignore[arg-type] + ), + lambda x: RiverErrorTypeAdapter.validate_python( + x # type: ignore[arg-type] + ), + timeout, + ) diff --git a/tests/codegen/snapshot/snapshots/test_pathological_types/test_service/pathological_method.py b/tests/codegen/snapshot/snapshots/test_pathological_types/test_service/pathological_method.py new file mode 100644 index 00000000..f2bb120f --- /dev/null +++ b/tests/codegen/snapshot/snapshots/test_pathological_types/test_service/pathological_method.py @@ -0,0 +1,479 @@ +# ruff: noqa +# Code generated by river.codegen. DO NOT EDIT. +from collections.abc import AsyncIterable, AsyncIterator +import datetime +from typing import ( + Any, + Literal, + Mapping, + NotRequired, + TypedDict, +) +from typing_extensions import Annotated + +from pydantic import BaseModel, Field, TypeAdapter, WrapValidator +from replit_river.error_schema import RiverError +from replit_river.client import RiverUnknownValue, translate_unknown_value + +import replit_river as river + + +def encode_Pathological_MethodInputObj_Date( + x: "Pathological_MethodInputObj_Date", +) -> Any: + return { + k: v + for (k, v) in ( + { + "prop_Date": x.get("prop_Date"), + } + ).items() + if v is not None + } + + +class Pathological_MethodInputObj_Date(TypedDict): + prop_Date: NotRequired[datetime.datetime | None] + + +def encode_Pathological_MethodInputObj_Uint8Array( + x: "Pathological_MethodInputObj_Uint8Array", +) -> Any: + return { + k: v + for (k, v) in ( + { + "prop_Uint8Array": x.get("prop_Uint8Array"), + } + ).items() + if v is not None + } + + +class Pathological_MethodInputObj_Uint8Array(TypedDict): + prop_Uint8Array: NotRequired[bytes | None] + + +def encode_Pathological_MethodInputObj_Boolean( + x: "Pathological_MethodInputObj_Boolean", +) -> Any: + return { + k: v + for (k, v) in ( + { + "prop_boolean": x.get("prop_boolean"), + } + ).items() + if v is not None + } + + +class Pathological_MethodInputObj_Boolean(TypedDict): + prop_boolean: NotRequired[bool | None] + + +def encode_Pathological_MethodInputObj_Integer( + x: "Pathological_MethodInputObj_Integer", +) -> Any: + return { + k: v + for (k, v) in ( + { + "prop_integer": x.get("prop_integer"), + } + ).items() + if v is not None + } + + +class Pathological_MethodInputObj_Integer(TypedDict): + prop_integer: NotRequired[int | None] + + +def encode_Pathological_MethodInputObj_Null( + x: "Pathological_MethodInputObj_Null", +) -> Any: + return { + k: v + for (k, v) in ( + { + "prop_null": x.get("prop_null"), + } + ).items() + if v is not None + } + + +class Pathological_MethodInputObj_Null(TypedDict): + prop_null: NotRequired[None] + + +def encode_Pathological_MethodInputObj_Number( + x: "Pathological_MethodInputObj_Number", +) -> Any: + return { + k: v + for (k, v) in ( + { + "prop_number": x.get("prop_number"), + } + ).items() + if v is not None + } + + +class Pathological_MethodInputObj_Number(TypedDict): + prop_number: NotRequired[float | None] + + +def encode_Pathological_MethodInputObj_String( + x: "Pathological_MethodInputObj_String", +) -> Any: + return { + k: v + for (k, v) in ( + { + "prop_string": x.get("prop_string"), + } + ).items() + if v is not None + } + + +class Pathological_MethodInputObj_String(TypedDict): + prop_string: NotRequired[str | None] + + +def encode_Pathological_MethodInputObj_Undefined( + x: "Pathological_MethodInputObj_Undefined", +) -> Any: + return { + k: v + for (k, v) in ( + { + "prop_undefined": x.get("prop_undefined"), + } + ).items() + if v is not None + } + + +class Pathological_MethodInputObj_Undefined(TypedDict): + prop_undefined: NotRequired[None] + + +def encode_Pathological_MethodInputReq_Obj_Date( + x: "Pathological_MethodInputReq_Obj_Date", +) -> Any: + return { + k: v + for (k, v) in ( + { + "prop_Date": x.get("prop_Date"), + } + ).items() + if v is not None + } + + +class Pathological_MethodInputReq_Obj_Date(TypedDict): + prop_Date: NotRequired[datetime.datetime | None] + + +def encode_Pathological_MethodInputReq_Obj_Uint8Array( + x: "Pathological_MethodInputReq_Obj_Uint8Array", +) -> Any: + return { + k: v + for (k, v) in ( + { + "prop_Uint8Array": x.get("prop_Uint8Array"), + } + ).items() + if v is not None + } + + +class Pathological_MethodInputReq_Obj_Uint8Array(TypedDict): + prop_Uint8Array: NotRequired[bytes | None] + + +def encode_Pathological_MethodInputReq_Obj_Boolean( + x: "Pathological_MethodInputReq_Obj_Boolean", +) -> Any: + return { + k: v + for (k, v) in ( + { + "prop_boolean": x.get("prop_boolean"), + } + ).items() + if v is not None + } + + +class Pathological_MethodInputReq_Obj_Boolean(TypedDict): + prop_boolean: NotRequired[bool | None] + + +def encode_Pathological_MethodInputReq_Obj_Integer( + x: "Pathological_MethodInputReq_Obj_Integer", +) -> Any: + return { + k: v + for (k, v) in ( + { + "prop_integer": x.get("prop_integer"), + } + ).items() + if v is not None + } + + +class Pathological_MethodInputReq_Obj_Integer(TypedDict): + prop_integer: NotRequired[int | None] + + +def encode_Pathological_MethodInputReq_Obj_Null( + x: "Pathological_MethodInputReq_Obj_Null", +) -> Any: + return { + k: v + for (k, v) in ( + { + "prop_null": x.get("prop_null"), + } + ).items() + if v is not None + } + + +class Pathological_MethodInputReq_Obj_Null(TypedDict): + prop_null: NotRequired[None] + + +def encode_Pathological_MethodInputReq_Obj_Number( + x: "Pathological_MethodInputReq_Obj_Number", +) -> Any: + return { + k: v + for (k, v) in ( + { + "prop_number": x.get("prop_number"), + } + ).items() + if v is not None + } + + +class Pathological_MethodInputReq_Obj_Number(TypedDict): + prop_number: NotRequired[float | None] + + +def encode_Pathological_MethodInputReq_Obj_String( + x: "Pathological_MethodInputReq_Obj_String", +) -> Any: + return { + k: v + for (k, v) in ( + { + "prop_string": x.get("prop_string"), + } + ).items() + if v is not None + } + + +class Pathological_MethodInputReq_Obj_String(TypedDict): + prop_string: NotRequired[str | None] + + +def encode_Pathological_MethodInputReq_Obj_Undefined( + x: "Pathological_MethodInputReq_Obj_Undefined", +) -> Any: + return { + k: v + for (k, v) in ( + { + "prop_undefined": x.get("prop_undefined"), + } + ).items() + if v is not None + } + + +class Pathological_MethodInputReq_Obj_Undefined(TypedDict): + prop_undefined: NotRequired[None] + + +def encode_Pathological_MethodInput( + x: "Pathological_MethodInput", +) -> Any: + return { + k: v + for (k, v) in ( + { + "Date": x.get("Date"), + "Uint8Array": x.get("Uint8Array"), + "arr_Date": x.get("arr_Date"), + "arr_Uint8Array": x.get("arr_Uint8Array"), + "arr_boolean": x.get("arr_boolean"), + "arr_integer": x.get("arr_integer"), + "arr_null": x.get("arr_null"), + "arr_number": x.get("arr_number"), + "arr_string": x.get("arr_string"), + "arr_undefined": x.get("arr_undefined"), + "boolean": x.get("boolean"), + "integer": x.get("integer"), + "null": x.get("null"), + "number": x.get("number"), + "obj_Date": encode_Pathological_MethodInputObj_Date(x["obj_Date"]) + if "obj_Date" in x and x["obj_Date"] is not None + else None, + "obj_Uint8Array": encode_Pathological_MethodInputObj_Uint8Array( + x["obj_Uint8Array"] + ) + if "obj_Uint8Array" in x and x["obj_Uint8Array"] is not None + else None, + "obj_boolean": encode_Pathological_MethodInputObj_Boolean( + x["obj_boolean"] + ) + if "obj_boolean" in x and x["obj_boolean"] is not None + else None, + "obj_integer": encode_Pathological_MethodInputObj_Integer( + x["obj_integer"] + ) + if "obj_integer" in x and x["obj_integer"] is not None + else None, + "obj_null": encode_Pathological_MethodInputObj_Null(x["obj_null"]) + if "obj_null" in x and x["obj_null"] is not None + else None, + "obj_number": encode_Pathological_MethodInputObj_Number(x["obj_number"]) + if "obj_number" in x and x["obj_number"] is not None + else None, + "obj_string": encode_Pathological_MethodInputObj_String(x["obj_string"]) + if "obj_string" in x and x["obj_string"] is not None + else None, + "obj_undefined": encode_Pathological_MethodInputObj_Undefined( + x["obj_undefined"] + ) + if "obj_undefined" in x and x["obj_undefined"] is not None + else None, + "req_Date": x.get("req_Date"), + "req_Uint8Array": x.get("req_Uint8Array"), + "req_arr_Date": x.get("req_arr_Date"), + "req_arr_Uint8Array": x.get("req_arr_Uint8Array"), + "req_arr_boolean": x.get("req_arr_boolean"), + "req_arr_integer": x.get("req_arr_integer"), + "req_arr_null": x.get("req_arr_null"), + "req_arr_number": x.get("req_arr_number"), + "req_arr_string": x.get("req_arr_string"), + "req_arr_undefined": x.get("req_arr_undefined"), + "req_boolean": x.get("req_boolean"), + "req_integer": x.get("req_integer"), + "req_null": x.get("req_null"), + "req_number": x.get("req_number"), + "req_obj_Date": encode_Pathological_MethodInputReq_Obj_Date( + x["req_obj_Date"] + ) + if "req_obj_Date" in x and x["req_obj_Date"] is not None + else None, + "req_obj_Uint8Array": encode_Pathological_MethodInputReq_Obj_Uint8Array( + x["req_obj_Uint8Array"] + ) + if "req_obj_Uint8Array" in x and x["req_obj_Uint8Array"] is not None + else None, + "req_obj_boolean": encode_Pathological_MethodInputReq_Obj_Boolean( + x["req_obj_boolean"] + ) + if "req_obj_boolean" in x and x["req_obj_boolean"] is not None + else None, + "req_obj_integer": encode_Pathological_MethodInputReq_Obj_Integer( + x["req_obj_integer"] + ) + if "req_obj_integer" in x and x["req_obj_integer"] is not None + else None, + "req_obj_null": encode_Pathological_MethodInputReq_Obj_Null( + x["req_obj_null"] + ) + if "req_obj_null" in x and x["req_obj_null"] is not None + else None, + "req_obj_number": encode_Pathological_MethodInputReq_Obj_Number( + x["req_obj_number"] + ) + if "req_obj_number" in x and x["req_obj_number"] is not None + else None, + "req_obj_string": encode_Pathological_MethodInputReq_Obj_String( + x["req_obj_string"] + ) + if "req_obj_string" in x and x["req_obj_string"] is not None + else None, + "req_obj_undefined": encode_Pathological_MethodInputReq_Obj_Undefined( + x["req_obj_undefined"] + ) + if "req_obj_undefined" in x and x["req_obj_undefined"] is not None + else None, + "req_string": x.get("req_string"), + "req_undefined": x.get("req_undefined"), + "string": x.get("string"), + "undefined": x.get("undefined"), + } + ).items() + if v is not None + } + + +class Pathological_MethodInput(TypedDict): + Date: NotRequired[datetime.datetime | None] + Uint8Array: NotRequired[bytes | None] + arr_Date: NotRequired[list[datetime.datetime] | None] + arr_Uint8Array: NotRequired[list[bytes] | None] + arr_boolean: NotRequired[list[bool] | None] + arr_integer: NotRequired[list[int] | None] + arr_null: NotRequired[list[None] | None] + arr_number: NotRequired[list[float] | None] + arr_string: NotRequired[list[str] | None] + arr_undefined: NotRequired[list[None] | None] + boolean: NotRequired[bool | None] + integer: NotRequired[int | None] + null: NotRequired[None] + number: NotRequired[float | None] + obj_Date: NotRequired[Pathological_MethodInputObj_Date | None] + obj_Uint8Array: NotRequired[Pathological_MethodInputObj_Uint8Array | None] + obj_boolean: NotRequired[Pathological_MethodInputObj_Boolean | None] + obj_integer: NotRequired[Pathological_MethodInputObj_Integer | None] + obj_null: NotRequired[Pathological_MethodInputObj_Null | None] + obj_number: NotRequired[Pathological_MethodInputObj_Number | None] + obj_string: NotRequired[Pathological_MethodInputObj_String | None] + obj_undefined: NotRequired[Pathological_MethodInputObj_Undefined | None] + req_Date: datetime.datetime + req_Uint8Array: bytes + req_arr_Date: list[datetime.datetime] + req_arr_Uint8Array: list[bytes] + req_arr_boolean: list[bool] + req_arr_integer: list[int] + req_arr_null: list[None] + req_arr_number: list[float] + req_arr_string: list[str] + req_arr_undefined: list[None] + req_boolean: bool + req_integer: int + req_null: None + req_number: float + req_obj_Date: Pathological_MethodInputReq_Obj_Date + req_obj_Uint8Array: Pathological_MethodInputReq_Obj_Uint8Array + req_obj_boolean: Pathological_MethodInputReq_Obj_Boolean + req_obj_integer: Pathological_MethodInputReq_Obj_Integer + req_obj_null: Pathological_MethodInputReq_Obj_Null + req_obj_number: Pathological_MethodInputReq_Obj_Number + req_obj_string: Pathological_MethodInputReq_Obj_String + req_obj_undefined: Pathological_MethodInputReq_Obj_Undefined + req_string: str + req_undefined: None + string: NotRequired[str | None] + undefined: NotRequired[None] + + +Pathological_MethodInputTypeAdapter: TypeAdapter[Any] = TypeAdapter( + Pathological_MethodInput +)