diff --git a/HISTORY.rst b/HISTORY.rst index 93de9e65..4c00d1aa 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -21,6 +21,7 @@ History * Expose all error classes in the `cattr.errors` namespace. Note that it is deprecated, just use `cattrs.errors`. (`#252 `_) * ``cattrs.Converter`` and ``cattrs.BaseConverter`` can now copy themselves using the ``copy`` method. (`#284 `_) +* Fix generating structuring functions for types with quotes in the name. (`#291 `_ `#277 `_) 22.1.0 (2022-04-03) ------------------- diff --git a/src/cattrs/gen.py b/src/cattrs/gen.py index 95cc02ed..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__} @ 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()}}") diff --git a/tests/test_gen_dict.py b/tests/test_gen_dict.py index c152b49b..6f9d5cff 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 Dict, Type import pytest from attr import Factory, define, field @@ -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 @@ -287,3 +287,20 @@ class A: assert structured.a == 1 assert structured.c == 1 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 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 Union[Literal["a", 2, 3], Literal[4]], lambda v, _: v + ) + assert converter.structure( + {2: "a"}, Dict[Union[Literal["a", 2, 3], Literal[4]], str] + ) == {2: "a"}