From 9c598eeaccb191dec5519f86a5a0809649d873f3 Mon Sep 17 00:00:00 2001 From: Thomas <34217413+thommann@users.noreply.github.com> Date: Tue, 8 Jul 2025 16:41:09 +0200 Subject: [PATCH] fix(jambo): Update legacy typing to built in types (#9) * Migrate from `typing_extensions` to `typing` for supported Python versions, and update code to use modern type hinting with unions and annotations. * Update type hinting to use lowercase generics for Python 3.9+ compatibility. --- jambo/parser/_type_parser.py | 4 +-- jambo/parser/allof_type_parser.py | 2 +- jambo/parser/anyof_type_parser.py | 11 ++++-- jambo/parser/array_type_parser.py | 2 +- jambo/parser/boolean_type_parser.py | 2 +- jambo/parser/const_type_parser.py | 2 +- jambo/parser/enum_type_parser.py | 2 +- jambo/parser/float_type_parser.py | 2 +- jambo/parser/int_type_parser.py | 2 +- jambo/parser/null_type_parser.py | 2 +- jambo/parser/object_type_parser.py | 2 +- jambo/parser/oneof_type_parser.py | 11 +++--- jambo/parser/ref_type_parser.py | 6 ++-- jambo/parser/string_type_parser.py | 2 +- jambo/types/json_schema_type.py | 50 +++++++++++++------------- jambo/types/type_parser_options.py | 2 +- tests/parser/test_anyof_type_parser.py | 6 ++-- tests/parser/test_array_type_parser.py | 2 +- tests/parser/test_const_type_parser.py | 2 +- 19 files changed, 63 insertions(+), 51 deletions(-) diff --git a/jambo/parser/_type_parser.py b/jambo/parser/_type_parser.py index 9350c25..76d2614 100644 --- a/jambo/parser/_type_parser.py +++ b/jambo/parser/_type_parser.py @@ -1,7 +1,7 @@ from jambo.types.type_parser_options import TypeParserOptions from pydantic import Field, TypeAdapter -from typing_extensions import Annotated, Any, Generic, Self, TypeVar, Unpack +from typing import Annotated, Any, Generic, Self, TypeVar, Unpack from abc import ABC, abstractmethod @@ -47,7 +47,7 @@ class GenericTypeParser(ABC, Generic[T]): if not self._validate_default(parsed_type, parsed_properties): raise ValueError( - f"Default value {properties.get('default')} is not valid for type {parsed_type.__name__}" + f"Default value {properties.get('default')} is not valid for type {parsed_type}" ) return parsed_type, parsed_properties diff --git a/jambo/parser/allof_type_parser.py b/jambo/parser/allof_type_parser.py index 709365e..f6fa7c9 100644 --- a/jambo/parser/allof_type_parser.py +++ b/jambo/parser/allof_type_parser.py @@ -1,7 +1,7 @@ from jambo.parser._type_parser import GenericTypeParser from jambo.types.type_parser_options import TypeParserOptions -from typing_extensions import Any, Unpack +from typing import Any, Unpack class AllOfTypeParser(GenericTypeParser): diff --git a/jambo/parser/anyof_type_parser.py b/jambo/parser/anyof_type_parser.py index 819867b..6dae037 100644 --- a/jambo/parser/anyof_type_parser.py +++ b/jambo/parser/anyof_type_parser.py @@ -2,11 +2,14 @@ from jambo.parser._type_parser import GenericTypeParser from jambo.types.type_parser_options import TypeParserOptions from pydantic import Field -from typing_extensions import Annotated, Union, Unpack +from typing import Annotated, Unpack +from types import UnionType +from functools import reduce +from operator import or_ class AnyOfTypeParser(GenericTypeParser): - mapped_type = Union + mapped_type = UnionType json_schema_type = "anyOf" @@ -41,4 +44,6 @@ class AnyOfTypeParser(GenericTypeParser): for t, v in sub_types ] - return Union[(*field_types,)], mapped_properties + union_type = reduce(or_, field_types) + + return union_type, mapped_properties diff --git a/jambo/parser/array_type_parser.py b/jambo/parser/array_type_parser.py index 52e3bcf..0113f24 100644 --- a/jambo/parser/array_type_parser.py +++ b/jambo/parser/array_type_parser.py @@ -1,7 +1,7 @@ from jambo.parser._type_parser import GenericTypeParser from jambo.types.type_parser_options import TypeParserOptions -from typing_extensions import Iterable, TypeVar, Unpack +from typing import Iterable, TypeVar, Unpack import copy diff --git a/jambo/parser/boolean_type_parser.py b/jambo/parser/boolean_type_parser.py index ecb703a..7c66d1a 100644 --- a/jambo/parser/boolean_type_parser.py +++ b/jambo/parser/boolean_type_parser.py @@ -1,7 +1,7 @@ from jambo.parser._type_parser import GenericTypeParser from jambo.types.type_parser_options import TypeParserOptions -from typing_extensions import Unpack +from typing import Unpack class BooleanTypeParser(GenericTypeParser): diff --git a/jambo/parser/const_type_parser.py b/jambo/parser/const_type_parser.py index 4865df0..e0840f2 100644 --- a/jambo/parser/const_type_parser.py +++ b/jambo/parser/const_type_parser.py @@ -3,7 +3,7 @@ from jambo.types.json_schema_type import JSONSchemaNativeTypes from jambo.types.type_parser_options import TypeParserOptions from pydantic import AfterValidator -from typing_extensions import Annotated, Any, Literal, Unpack +from typing import Annotated, Any, Literal, Unpack class ConstTypeParser(GenericTypeParser): diff --git a/jambo/parser/enum_type_parser.py b/jambo/parser/enum_type_parser.py index 5ea9e67..cec110b 100644 --- a/jambo/parser/enum_type_parser.py +++ b/jambo/parser/enum_type_parser.py @@ -2,7 +2,7 @@ from jambo.parser._type_parser import GenericTypeParser from jambo.types.json_schema_type import JSONSchemaNativeTypes from jambo.types.type_parser_options import TypeParserOptions -from typing_extensions import Unpack +from typing import Unpack from enum import Enum diff --git a/jambo/parser/float_type_parser.py b/jambo/parser/float_type_parser.py index f4655c3..a7f0d2a 100644 --- a/jambo/parser/float_type_parser.py +++ b/jambo/parser/float_type_parser.py @@ -1,7 +1,7 @@ from jambo.parser._type_parser import GenericTypeParser from jambo.types.type_parser_options import TypeParserOptions -from typing_extensions import Unpack +from typing import Unpack class FloatTypeParser(GenericTypeParser): diff --git a/jambo/parser/int_type_parser.py b/jambo/parser/int_type_parser.py index 161465b..96932ec 100644 --- a/jambo/parser/int_type_parser.py +++ b/jambo/parser/int_type_parser.py @@ -1,7 +1,7 @@ from jambo.parser._type_parser import GenericTypeParser from jambo.types.type_parser_options import TypeParserOptions -from typing_extensions import Unpack +from typing import Unpack class IntTypeParser(GenericTypeParser): diff --git a/jambo/parser/null_type_parser.py b/jambo/parser/null_type_parser.py index 35036ef..b660f25 100644 --- a/jambo/parser/null_type_parser.py +++ b/jambo/parser/null_type_parser.py @@ -1,7 +1,7 @@ from jambo.parser._type_parser import GenericTypeParser from jambo.types.type_parser_options import TypeParserOptions -from typing_extensions import Unpack +from typing import Unpack class NullTypeParser(GenericTypeParser): diff --git a/jambo/parser/object_type_parser.py b/jambo/parser/object_type_parser.py index 8deb5ac..d17a8f0 100644 --- a/jambo/parser/object_type_parser.py +++ b/jambo/parser/object_type_parser.py @@ -2,7 +2,7 @@ from jambo.parser._type_parser import GenericTypeParser from jambo.types.type_parser_options import TypeParserOptions from pydantic import BaseModel, ConfigDict, Field, create_model -from typing_extensions import Any, Unpack +from typing import Any, Unpack class ObjectTypeParser(GenericTypeParser): diff --git a/jambo/parser/oneof_type_parser.py b/jambo/parser/oneof_type_parser.py index 79146b9..fb396e0 100644 --- a/jambo/parser/oneof_type_parser.py +++ b/jambo/parser/oneof_type_parser.py @@ -1,12 +1,15 @@ +from types import UnionType + from jambo.parser._type_parser import GenericTypeParser from jambo.types.type_parser_options import TypeParserOptions from pydantic import Field, BeforeValidator, TypeAdapter, ValidationError -from typing_extensions import Annotated, Union, Unpack, Any - +from typing import Annotated, Unpack, Any +from functools import reduce +from operator import or_ class OneOfTypeParser(GenericTypeParser): - mapped_type = Union + mapped_type = UnionType json_schema_type = "oneOf" @@ -36,7 +39,7 @@ class OneOfTypeParser(GenericTypeParser): for t, v in sub_types ] - union_type = Union[(*field_types,)] + union_type = reduce(or_, field_types) discriminator = properties.get("discriminator") if discriminator and isinstance(discriminator, dict): diff --git a/jambo/parser/ref_type_parser.py b/jambo/parser/ref_type_parser.py index 57abeac..d3cf49e 100644 --- a/jambo/parser/ref_type_parser.py +++ b/jambo/parser/ref_type_parser.py @@ -1,10 +1,12 @@ +from __future__ import annotations + from jambo.parser import GenericTypeParser from jambo.types.type_parser_options import TypeParserOptions -from typing_extensions import Any, ForwardRef, Literal, TypeVar, Union, Unpack +from typing import Any, ForwardRef, Literal, TypeVar, Unpack -RefType = TypeVar("RefType", bound=Union[type, ForwardRef]) +RefType = TypeVar("RefType", bound=type | ForwardRef) RefStrategy = Literal["forward_ref", "def_ref"] diff --git a/jambo/parser/string_type_parser.py b/jambo/parser/string_type_parser.py index 04f1e98..3d7b2b9 100644 --- a/jambo/parser/string_type_parser.py +++ b/jambo/parser/string_type_parser.py @@ -2,7 +2,7 @@ from jambo.parser._type_parser import GenericTypeParser from jambo.types.type_parser_options import TypeParserOptions from pydantic import EmailStr, HttpUrl, IPvAnyAddress, FilePath -from typing_extensions import Unpack +from typing import Unpack from datetime import date, datetime, time diff --git a/jambo/types/json_schema_type.py b/jambo/types/json_schema_type.py index 6f61837..a30612c 100644 --- a/jambo/types/json_schema_type.py +++ b/jambo/types/json_schema_type.py @@ -1,4 +1,6 @@ -from typing_extensions import Dict, List, Literal, TypedDict, Union +from __future__ import annotations + +from typing import Literal, TypedDict from types import NoneType @@ -19,7 +21,7 @@ JSONSchemaNativeTypes: tuple[type, ...] = ( ) -JSONType = Union[str, int, float, bool, None, Dict[str, "JSONType"], List["JSONType"]] +JSONType = str | int | float | bool | None | dict[str, "JSONType"] | list["JSONType"] class JSONSchema(TypedDict, total=False): @@ -27,23 +29,23 @@ class JSONSchema(TypedDict, total=False): title: str description: str default: JSONType - examples: List[JSONType] + examples: list[JSONType] # Type definitions - type: Union[JSONSchemaType, List[JSONSchemaType]] + type: JSONSchemaType | list[JSONSchemaType] # Object-specific keywords - properties: Dict[str, "JSONSchema"] - required: List[str] - additionalProperties: Union[bool, "JSONSchema"] + properties: dict[str, "JSONSchema"] + required: list[str] + additionalProperties: bool | "JSONSchema" minProperties: int maxProperties: int - patternProperties: Dict[str, "JSONSchema"] - dependencies: Dict[str, Union[List[str], "JSONSchema"]] + patternProperties: dict[str, "JSONSchema"] + dependencies: dict[str, list[str] | "JSONSchema"] # Array-specific keywords - items: Union["JSONSchema", List["JSONSchema"]] - additionalItems: Union[bool, "JSONSchema"] + items: "JSONSchema" | list["JSONSchema"] + additionalItems: bool | "JSONSchema" minItems: int maxItems: int uniqueItems: bool @@ -62,7 +64,7 @@ class JSONSchema(TypedDict, total=False): multipleOf: float # Enum and const - enum: List[JSONType] + enum: list[JSONType] const: JSONType # Conditionals @@ -71,23 +73,23 @@ class JSONSchema(TypedDict, total=False): else_: "JSONSchema" # 'else' is also a reserved word # Combination keywords - allOf: List["JSONSchema"] - anyOf: List["JSONSchema"] - oneOf: List["JSONSchema"] + allOf: list["JSONSchema"] + anyOf: list["JSONSchema"] + oneOf: list["JSONSchema"] not_: "JSONSchema" # 'not' is a reserved word # Fix forward references -JSONSchema.__annotations__["properties"] = Dict[str, JSONSchema] -JSONSchema.__annotations__["items"] = Union[JSONSchema, List[JSONSchema]] -JSONSchema.__annotations__["additionalItems"] = Union[bool, JSONSchema] -JSONSchema.__annotations__["additionalProperties"] = Union[bool, JSONSchema] -JSONSchema.__annotations__["patternProperties"] = Dict[str, JSONSchema] -JSONSchema.__annotations__["dependencies"] = Dict[str, Union[List[str], JSONSchema]] +JSONSchema.__annotations__["properties"] = dict[str, JSONSchema] +JSONSchema.__annotations__["items"] = JSONSchema | list[JSONSchema] +JSONSchema.__annotations__["additionalItems"] = bool | JSONSchema +JSONSchema.__annotations__["additionalProperties"] = bool | JSONSchema +JSONSchema.__annotations__["patternProperties"] = dict[str, JSONSchema] +JSONSchema.__annotations__["dependencies"] = dict[str, list[str] | JSONSchema] JSONSchema.__annotations__["if_"] = JSONSchema JSONSchema.__annotations__["then"] = JSONSchema JSONSchema.__annotations__["else_"] = JSONSchema -JSONSchema.__annotations__["allOf"] = List[JSONSchema] -JSONSchema.__annotations__["anyOf"] = List[JSONSchema] -JSONSchema.__annotations__["oneOf"] = List[JSONSchema] +JSONSchema.__annotations__["allOf"] = list[JSONSchema] +JSONSchema.__annotations__["anyOf"] = list[JSONSchema] +JSONSchema.__annotations__["oneOf"] = list[JSONSchema] JSONSchema.__annotations__["not_"] = JSONSchema diff --git a/jambo/types/type_parser_options.py b/jambo/types/type_parser_options.py index 4f7d8e0..b882598 100644 --- a/jambo/types/type_parser_options.py +++ b/jambo/types/type_parser_options.py @@ -1,6 +1,6 @@ from jambo.types.json_schema_type import JSONSchema -from typing_extensions import TypedDict +from typing import TypedDict class TypeParserOptions(TypedDict): diff --git a/tests/parser/test_anyof_type_parser.py b/tests/parser/test_anyof_type_parser.py index de5cb9b..72ff6c3 100644 --- a/tests/parser/test_anyof_type_parser.py +++ b/tests/parser/test_anyof_type_parser.py @@ -1,6 +1,6 @@ from jambo.parser.anyof_type_parser import AnyOfTypeParser -from typing_extensions import Annotated, Union, get_args, get_origin +from typing import Annotated, get_args, get_origin from unittest import TestCase @@ -42,7 +42,7 @@ class TestAnyOfTypeParser(TestCase): ) # check union type has string and int - self.assertEqual(get_origin(type_parsing), Union) + self.assertEqual(get_origin(type_parsing), type(str | int)) type_1, type_2 = get_args(type_parsing) @@ -67,7 +67,7 @@ class TestAnyOfTypeParser(TestCase): ) # check union type has string and int - self.assertEqual(get_origin(type_parsing), Union) + self.assertEqual(get_origin(type_parsing), type(str | int)) type_1, type_2 = get_args(type_parsing) diff --git a/tests/parser/test_array_type_parser.py b/tests/parser/test_array_type_parser.py index ee09987..135b31c 100644 --- a/tests/parser/test_array_type_parser.py +++ b/tests/parser/test_array_type_parser.py @@ -1,6 +1,6 @@ from jambo.parser import ArrayTypeParser -from typing_extensions import get_args +from typing import get_args from unittest import TestCase diff --git a/tests/parser/test_const_type_parser.py b/tests/parser/test_const_type_parser.py index 8f3661c..31783f6 100644 --- a/tests/parser/test_const_type_parser.py +++ b/tests/parser/test_const_type_parser.py @@ -1,6 +1,6 @@ from jambo.parser import ConstTypeParser -from typing_extensions import Annotated, Literal, get_args, get_origin +from typing import Annotated, Literal, get_args, get_origin from unittest import TestCase