From f6bf2201a56cece3d1eaf44dab4d90dfbecb8578 Mon Sep 17 00:00:00 2001 From: Xavier Bergeron Date: Mon, 1 Aug 2022 19:22:17 -0700 Subject: [PATCH 1/6] Fix: GenConverter syntax errors with certain types --- src/cattrs/gen.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cattrs/gen.py b/src/cattrs/gen.py index 95cc02ed..76ad648e 100644 --- a/src/cattrs/gen.py +++ b/src/cattrs/gen.py @@ -340,7 +340,7 @@ def make_dict_structure_fn( lines.append(f"{i}except Exception as e:") i = f"{i} " lines.append( - f"{i}e.__note__ = 'Structuring class {cl.__qualname__} @ attribute {an}'" + f"{i}e.__note__ = 'Structuring class ' + {cl.__qualname__!r} + ' @ attribute', {an}" ) lines.append(f"{i}errors.append(e)") @@ -352,7 +352,7 @@ def make_dict_structure_fn( ] post_lines.append( - f" if errors: raise __c_cve('While structuring {cl.__name__}', errors, __cl)" + f" if errors: raise __c_cve('While structuring ' + {cl.__name__!r}, errors, __cl)" ) instantiation_lines = ( [" try:"] @@ -360,7 +360,7 @@ def make_dict_structure_fn( + [f" {line}" for line in invocation_lines] + [" )"] + [ - f" except Exception as exc: raise __c_cve('While structuring {cl.__name__}', [exc], __cl)" + f" except Exception as exc: raise __c_cve('While structuring ' + {cl.__name__!r}, [exc], __cl)" ] ) else: @@ -717,7 +717,7 @@ def make_mapping_structure_fn( lines.append(" errors.append(e)") lines.append(" if errors:") lines.append( - f" raise IterableValidationError('While structuring {cl!r}', errors, __cattr_mapping_cl)" + f" raise IterableValidationError('While structuring ' + {repr(cl)!r}, errors, __cattr_mapping_cl)" ) else: lines.append(f" res = {{{k_s}: {v_s} for k, v in mapping.items()}}") From 33fd2c1bf713d7268d032ae13fd1edd457f7368e Mon Sep 17 00:00:00 2001 From: Xavier Bergeron Date: Tue, 16 Aug 2022 09:28:40 -0700 Subject: [PATCH 2/6] Fix syntax error in generated code --- src/cattrs/gen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cattrs/gen.py b/src/cattrs/gen.py index 76ad648e..5a3ed22f 100644 --- a/src/cattrs/gen.py +++ b/src/cattrs/gen.py @@ -340,7 +340,7 @@ def make_dict_structure_fn( lines.append(f"{i}except Exception as e:") i = f"{i} " lines.append( - f"{i}e.__note__ = 'Structuring class ' + {cl.__qualname__!r} + ' @ attribute', {an}" + f"{i}e.__note__ = 'Structuring class ' + {cl.__qualname__!r} + ' @ attribute {an}'" ) lines.append(f"{i}errors.append(e)") From c07188b80068a2e41e0af9843bd509b677048952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tin=20Tvrtkovi=C4=87?= Date: Sat, 17 Sep 2022 19:04:51 +0200 Subject: [PATCH 3/6] Add test and snippet --- HISTORY.rst | 1 + tests/test_gen_dict.py | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index 8b876de0..d45612a0 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -19,6 +19,7 @@ History * Fix `Converter.register_structure_hook_factory` and `cattrs.gen.make_dict_unstructure_fn` type annotations. (`#281 `_) * Expose all error classes in the `cattr.errors` namespace. Note that it is deprecated, just use `cattrs.errors`. (`#252 `_) +* Fix generating structuring functions for types with quotes in the name. (`#291 `_ `#277 `_) 22.1.0 (2022-04-03) ------------------- diff --git a/tests/test_gen_dict.py b/tests/test_gen_dict.py index c152b49b..d0753edd 100644 --- a/tests/test_gen_dict.py +++ b/tests/test_gen_dict.py @@ -1,5 +1,5 @@ """Tests for generated dict functions.""" -from typing import Type +from typing import Annotated, Dict, Literal, Type import pytest from attr import Factory, define, field @@ -287,3 +287,18 @@ class A: assert structured.a == 1 assert structured.c == 1 assert not hasattr(structured, "b") + + +def test_type_names_with_quotes(): + """Types with quote characters in their reprs should work.""" + + converter = Converter() + + assert converter.structure({1: 1}, Dict[Annotated[int, "'"], int]) == {1: 1} + + converter.register_structure_hook_func( + lambda t: t is Literal["a", 2, 3] | Literal[4], lambda v, _: v + ) + assert converter.structure( + {2: "a"}, Dict[Literal["a", 2, 3] | Literal[4], str] + ) == {2: "a"} From 59fac487e8be822c948bfb3e11d589b5161f29af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tin=20Tvrtkovi=C4=87?= Date: Sat, 17 Sep 2022 19:11:09 +0200 Subject: [PATCH 4/6] Conditionally run test --- tests/test_gen_dict.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_gen_dict.py b/tests/test_gen_dict.py index d0753edd..fd8aa667 100644 --- a/tests/test_gen_dict.py +++ b/tests/test_gen_dict.py @@ -8,7 +8,7 @@ from hypothesis.strategies import data, just, one_of, sampled_from from cattrs import BaseConverter, Converter -from cattrs._compat import adapted_fields, fields +from cattrs._compat import adapted_fields, fields, is_py39_plus from cattrs.errors import ClassValidationError, ForbiddenExtraKeysError from cattrs.gen import make_dict_structure_fn, make_dict_unstructure_fn, override @@ -289,6 +289,7 @@ class A: assert not hasattr(structured, "b") +@pytest.mark.skipif(not is_py39_plus, reason="literals and annotated are 3.9+") def test_type_names_with_quotes(): """Types with quote characters in their reprs should work.""" From 5e90679967be9f10a01cc0e57972aae323bd30cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tin=20Tvrtkovi=C4=87?= Date: Sat, 17 Sep 2022 19:13:29 +0200 Subject: [PATCH 5/6] Local import --- tests/test_gen_dict.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_gen_dict.py b/tests/test_gen_dict.py index fd8aa667..630d6260 100644 --- a/tests/test_gen_dict.py +++ b/tests/test_gen_dict.py @@ -1,5 +1,5 @@ """Tests for generated dict functions.""" -from typing import Annotated, Dict, Literal, Type +from typing import Dict, Type import pytest from attr import Factory, define, field @@ -292,6 +292,7 @@ class A: @pytest.mark.skipif(not is_py39_plus, reason="literals and annotated are 3.9+") def test_type_names_with_quotes(): """Types with quote characters in their reprs should work.""" + from typing import Annotated, Literal converter = Converter() From 9d3db6d8c141917b6b271447f476336f0cebd378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tin=20Tvrtkovi=C4=87?= Date: Sat, 17 Sep 2022 19:35:07 +0200 Subject: [PATCH 6/6] Switch to old union syntax --- tests/test_gen_dict.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_gen_dict.py b/tests/test_gen_dict.py index 630d6260..6f9d5cff 100644 --- a/tests/test_gen_dict.py +++ b/tests/test_gen_dict.py @@ -292,15 +292,15 @@ class A: @pytest.mark.skipif(not is_py39_plus, reason="literals and annotated are 3.9+") def test_type_names_with_quotes(): """Types with quote characters in their reprs should work.""" - from typing import Annotated, Literal + from typing import Annotated, Literal, Union converter = Converter() assert converter.structure({1: 1}, Dict[Annotated[int, "'"], int]) == {1: 1} converter.register_structure_hook_func( - lambda t: t is Literal["a", 2, 3] | Literal[4], lambda v, _: v + lambda t: t is Union[Literal["a", 2, 3], Literal[4]], lambda v, _: v ) assert converter.structure( - {2: "a"}, Dict[Literal["a", 2, 3] | Literal[4], str] + {2: "a"}, Dict[Union[Literal["a", 2, 3], Literal[4]], str] ) == {2: "a"}