From d9a379146e2b3b8edd0ac89e8b90b5fe3a984d10 Mon Sep 17 00:00:00 2001 From: Tin Tvrtkovic Date: Thu, 5 Sep 2024 17:55:19 +0200 Subject: [PATCH 1/5] Fix converter type hints --- src/cattrs/converters.py | 92 ++++++++++++++++++++++++++++++---------- 1 file changed, 69 insertions(+), 23 deletions(-) diff --git a/src/cattrs/converters.py b/src/cattrs/converters.py index 8d283b30..df8c744d 100644 --- a/src/cattrs/converters.py +++ b/src/cattrs/converters.py @@ -9,7 +9,7 @@ from inspect import Signature from inspect import signature as inspect_signature from pathlib import Path -from typing import Any, Callable, Iterable, Optional, Tuple, TypeVar, overload +from typing import Any, Callable, Iterable, Optional, Tuple, TypeVar, Union, overload from attrs import Attribute, resolve_types from attrs import has as attrs_has @@ -102,17 +102,41 @@ ) # The Extended factory also takes a converter. -ExtendedUnstructureHookFactory = TypeVar( - "ExtendedUnstructureHookFactory", - bound=Callable[[TargetType, "BaseConverter"], UnstructureHook], +ExtendedUnstructureHookFactory: TypeAlias = Callable[[TargetType, T], UnstructureHook] + +# This typevar for the BaseConverter. +AnyUnstructureHookFactoryBase = TypeVar( + "AnyUnstructureHookFactoryBase", + bound=Union[ + HookFactory[UnstructureHook], ExtendedUnstructureHookFactory[BaseConverter] + ], +) + +# This typevar for the Converter. +AnyUnstructureHookFactory = TypeVar( + "AnyUnstructureHookFactory", + bound=Union[ + HookFactory[UnstructureHook], ExtendedUnstructureHookFactory[Converter] + ], ) StructureHookFactory = TypeVar("StructureHookFactory", bound=HookFactory[StructureHook]) # The Extended factory also takes a converter. -ExtendedStructureHookFactory = TypeVar( - "ExtendedStructureHookFactory", - bound=Callable[[TargetType, "BaseConverter"], StructureHook], +ExtendedStructureHookFactory: TypeAlias = Callable[[TargetType, T], StructureHook] + +# This typevar for the BaseConverter. +AnyStructureHookFactoryBase = TypeVar( + "AnyStructureHookFactoryBase", + bound=Union[ + HookFactory[StructureHook], ExtendedStructureHookFactory[BaseConverter] + ], +) + +# This typevar for the Converter. +AnyStructureHookFactory = TypeVar( + "AnyStructureHookFactory", + bound=Union[HookFactory[StructureHook], ExtendedStructureHookFactory[Converter]], ) @@ -341,12 +365,7 @@ def register_unstructure_hook_func( @overload def register_unstructure_hook_factory( self, predicate: Predicate - ) -> Callable[[UnstructureHookFactory], UnstructureHookFactory]: ... - - @overload - def register_unstructure_hook_factory( - self, predicate: Predicate - ) -> Callable[[ExtendedUnstructureHookFactory], ExtendedUnstructureHookFactory]: ... + ) -> Callable[[AnyUnstructureHookFactoryBase], AnyUnstructureHookFactoryBase]: ... @overload def register_unstructure_hook_factory( @@ -355,8 +374,10 @@ def register_unstructure_hook_factory( @overload def register_unstructure_hook_factory( - self, predicate: Predicate, factory: ExtendedUnstructureHookFactory - ) -> ExtendedUnstructureHookFactory: ... + self, + predicate: Predicate, + factory: ExtendedUnstructureHookFactory[BaseConverter], + ) -> ExtendedUnstructureHookFactory[BaseConverter]: ... def register_unstructure_hook_factory(self, predicate, factory=None): """ @@ -478,12 +499,7 @@ def register_structure_hook_func( @overload def register_structure_hook_factory( self, predicate: Predicate - ) -> Callable[[StructureHookFactory, StructureHookFactory]]: ... - - @overload - def register_structure_hook_factory( - self, predicate: Predicate - ) -> Callable[[ExtendedStructureHookFactory, ExtendedStructureHookFactory]]: ... + ) -> Callable[[AnyStructureHookFactoryBase], AnyStructureHookFactoryBase]: ... @overload def register_structure_hook_factory( @@ -492,8 +508,8 @@ def register_structure_hook_factory( @overload def register_structure_hook_factory( - self, predicate: Predicate, factory: ExtendedStructureHookFactory - ) -> ExtendedStructureHookFactory: ... + self, predicate: Predicate, factory: ExtendedStructureHookFactory[BaseConverter] + ) -> ExtendedStructureHookFactory[BaseConverter]: ... def register_structure_hook_factory(self, predicate, factory=None): """ @@ -1159,6 +1175,36 @@ def __init__( self._struct_copy_skip = self._structure_func.get_num_fns() self._unstruct_copy_skip = self._unstructure_func.get_num_fns() + @overload + def register_unstructure_hook_factory( + self, predicate: Predicate + ) -> Callable[[AnyUnstructureHookFactory], AnyUnstructureHookFactory]: ... + + @overload + def register_unstructure_hook_factory( + self, predicate: Predicate, factory: UnstructureHookFactory + ) -> UnstructureHookFactory: ... + + @overload + def register_unstructure_hook_factory( + self, predicate: Predicate, factory: ExtendedUnstructureHookFactory[Converter] + ) -> ExtendedUnstructureHookFactory[Converter]: ... + + @overload + def register_structure_hook_factory( + self, predicate: Predicate + ) -> Callable[[AnyStructureHookFactory], AnyStructureHookFactory]: ... + + @overload + def register_structure_hook_factory( + self, predicate: Predicate, factory: StructureHookFactory + ) -> StructureHookFactory: ... + + @overload + def register_structure_hook_factory( + self, predicate: Predicate, factory: ExtendedStructureHookFactory[Converter] + ) -> ExtendedStructureHookFactory[Converter]: ... + def get_structure_newtype(self, type: type[T]) -> Callable[[Any, Any], T]: base = get_newtype_base(type) handler = self.get_structure_hook(base) From a659714074a271b81156cc026b1f3b91947619d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tin=20Tvrtkovi=C4=87?= Date: Sat, 7 Sep 2024 17:26:23 +0200 Subject: [PATCH 2/5] Stringify type hints, changelog --- HISTORY.md | 5 +++++ src/cattrs/converters.py | 14 ++++---------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 78e261df..eed8c2b2 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -9,6 +9,11 @@ The third number is for emergencies when we need to start branches for older rel Our backwards-compatibility policy can be found [here](https://github.com/python-attrs/cattrs/blob/main/.github/SECURITY.md). +## 24.1.1 (UNRELEASED) + +- Fix `BaseConverter.register_structure_hook_factory` and `BaseConverter.register_unstructure_hook_factory` type hints. + ([#578](https://github.com/python-attrs/cattrs/issues/578) [#579](https://github.com/python-attrs/cattrs/pull/579)) + ## 24.1.0 (2024-08-28) - **Potentially breaking**: Unstructuring hooks for `typing.Any` are consistent now: values are unstructured using their runtime type. diff --git a/src/cattrs/converters.py b/src/cattrs/converters.py index df8c744d..ce3172af 100644 --- a/src/cattrs/converters.py +++ b/src/cattrs/converters.py @@ -107,17 +107,13 @@ # This typevar for the BaseConverter. AnyUnstructureHookFactoryBase = TypeVar( "AnyUnstructureHookFactoryBase", - bound=Union[ - HookFactory[UnstructureHook], ExtendedUnstructureHookFactory[BaseConverter] - ], + bound="HookFactory[UnstructureHook] | ExtendedUnstructureHookFactory[BaseConverter]", ) # This typevar for the Converter. AnyUnstructureHookFactory = TypeVar( "AnyUnstructureHookFactory", - bound=Union[ - HookFactory[UnstructureHook], ExtendedUnstructureHookFactory[Converter] - ], + bound="HookFactory[UnstructureHook] | ExtendedUnstructureHookFactory[Converter]", ) StructureHookFactory = TypeVar("StructureHookFactory", bound=HookFactory[StructureHook]) @@ -128,15 +124,13 @@ # This typevar for the BaseConverter. AnyStructureHookFactoryBase = TypeVar( "AnyStructureHookFactoryBase", - bound=Union[ - HookFactory[StructureHook], ExtendedStructureHookFactory[BaseConverter] - ], + bound="HookFactory[StructureHook] | ExtendedStructureHookFactory[BaseConverter]", ) # This typevar for the Converter. AnyStructureHookFactory = TypeVar( "AnyStructureHookFactory", - bound=Union[HookFactory[StructureHook], ExtendedStructureHookFactory[Converter]], + bound="HookFactory[StructureHook] | ExtendedStructureHookFactory[Converter]", ) From 24e04586eea638693340c980cd931e81b59b21ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tin=20Tvrtkovi=C4=87?= Date: Sat, 7 Sep 2024 17:33:34 +0200 Subject: [PATCH 3/5] Fix methods --- src/cattrs/converters.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/cattrs/converters.py b/src/cattrs/converters.py index ce3172af..9cc08a5c 100644 --- a/src/cattrs/converters.py +++ b/src/cattrs/converters.py @@ -1184,6 +1184,10 @@ def register_unstructure_hook_factory( self, predicate: Predicate, factory: ExtendedUnstructureHookFactory[Converter] ) -> ExtendedUnstructureHookFactory[Converter]: ... + def register_unstructure_hook_factory(self, predicate, factory=None): + # This dummy wrapper is required due to how `@overload` works. + return super().register_unstructure_hook_factory(predicate, factory) + @overload def register_structure_hook_factory( self, predicate: Predicate @@ -1199,6 +1203,10 @@ def register_structure_hook_factory( self, predicate: Predicate, factory: ExtendedStructureHookFactory[Converter] ) -> ExtendedStructureHookFactory[Converter]: ... + def register_structure_hook_factory(self, predicate, factory=None): + # This dummy wrapper is required due to how `@overload` works. + return super().register_structure_hook_factory(predicate, factory) + def get_structure_newtype(self, type: type[T]) -> Callable[[Any, Any], T]: base = get_newtype_base(type) handler = self.get_structure_hook(base) From c0e633d34051f71a49d959fab46836838b9665f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tin=20Tvrtkovi=C4=87?= Date: Wed, 11 Sep 2024 13:36:40 +0200 Subject: [PATCH 4/5] Remove unused import --- HISTORY.md | 2 +- README.md | 1 - src/cattrs/converters.py | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index eed8c2b2..23682343 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -11,7 +11,7 @@ Our backwards-compatibility policy can be found [here](https://github.com/python ## 24.1.1 (UNRELEASED) -- Fix `BaseConverter.register_structure_hook_factory` and `BaseConverter.register_unstructure_hook_factory` type hints. +- Fix {meth}`BaseConverter.register_structure_hook_factory` and {meth}`BaseConverter.register_unstructure_hook_factory` type hints. ([#578](https://github.com/python-attrs/cattrs/issues/578) [#579](https://github.com/python-attrs/cattrs/pull/579)) ## 24.1.0 (2024-08-28) diff --git a/README.md b/README.md index acec772e..cb30014e 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,6 @@ _cattrs_ works best with [_attrs_](https://www.attrs.org/) classes, and [datacla C(a=1, b=['x', 'y']) >>> unstructure(instance) {'a': 1, 'b': ['x', 'y']} - ``` diff --git a/src/cattrs/converters.py b/src/cattrs/converters.py index 9cc08a5c..2759ee7a 100644 --- a/src/cattrs/converters.py +++ b/src/cattrs/converters.py @@ -9,7 +9,7 @@ from inspect import Signature from inspect import signature as inspect_signature from pathlib import Path -from typing import Any, Callable, Iterable, Optional, Tuple, TypeVar, Union, overload +from typing import Any, Callable, Iterable, Optional, Tuple, TypeVar, overload from attrs import Attribute, resolve_types from attrs import has as attrs_has From 8e5e57c19526d8ff8864f4fbfbba93f4ccf753d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tin=20Tvrtkovi=C4=87?= Date: Wed, 11 Sep 2024 13:55:21 +0200 Subject: [PATCH 5/5] Lurve actions/upload-artifact --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6b7179e3..de772e78 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -38,6 +38,7 @@ jobs: name: coverage-data-${{ matrix.python-version }} path: .coverage.* if-no-files-found: ignore + include-hidden-files: true coverage: name: "Combine & check coverage."