From 5d5299223bbc3f47ccba64778a49b565b119acbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tin=20Tvrtkovi=C4=87?= Date: Mon, 20 Nov 2023 13:34:34 +0100 Subject: [PATCH 1/2] Fix regression --- HISTORY.md | 5 +++++ src/cattrs/converters.py | 7 ++++++- tests/test_optionals.py | 14 +++++++++++++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index b772db02..affa1316 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,10 @@ # History +## 23.2.2 (UNRELEASED) + +- Fix a regression when unstructuring `Any | None`. + ([#453](https://github.com/python-attrs/cattrs/issues/453)) + ## 23.2.1 (2023-11-18) - Fix unnecessary `typing_extensions` import on Python 3.11. diff --git a/src/cattrs/converters.py b/src/cattrs/converters.py index 3ba1ecad..5e9e2b9c 100644 --- a/src/cattrs/converters.py +++ b/src/cattrs/converters.py @@ -954,7 +954,12 @@ def gen_unstructure_optional(self, cl: Type[T]) -> Callable[[T], Any]: """Generate an unstructuring hook for optional types.""" union_params = cl.__args__ other = union_params[0] if union_params[1] is NoneType else union_params[1] - handler = self._unstructure_func.dispatch(other) + + # TODO: Remove this special case when we make unstructuring Any consistent. + if other is Any: + handler = self.unstructure + else: + handler = self._unstructure_func.dispatch(other) def unstructure_optional(val, _handler=handler): return None if val is None else _handler(val) diff --git a/tests/test_optionals.py b/tests/test_optionals.py index 510fecf0..5eec5c0b 100644 --- a/tests/test_optionals.py +++ b/tests/test_optionals.py @@ -1,8 +1,10 @@ -from typing import NewType, Optional +from typing import Any, NewType, Optional import pytest from attrs import define +from cattrs import Converter + from ._compat import is_py310_plus @@ -39,3 +41,13 @@ class ModelWithFoo: "total_foo": "bar", "maybe_foo": "is it a bar?", } + + +def test_optional_any(converter: Converter): + """Unstructuring Any|None is equivalent to unstructuring as v.__class__.""" + + @define + class A: + pass + + assert converter.unstructure(A(), Optional[Any]) == {} From 8c3b8bdb6d341839cea86f03bc0b2b33e4f0f3f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tin=20Tvrtkovi=C4=87?= Date: Mon, 20 Nov 2023 13:48:51 +0100 Subject: [PATCH 2/2] Run CI on every PR --- .github/workflows/main.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8a0e89dd..ce18b777 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -5,7 +5,6 @@ on: push: branches: ["main"] pull_request: - branches: ["main"] workflow_dispatch: jobs: @@ -47,7 +46,7 @@ jobs: steps: - uses: "actions/checkout@v3" - + - uses: "actions/setup-python@v4" with: cache: "pip" @@ -76,7 +75,7 @@ jobs: with: name: "html-report" path: "htmlcov" - + - name: "Make badge" if: github.ref == 'refs/heads/main' uses: "schneegans/dynamic-badges-action@v1.4.0"