Skip to content

Commit

Permalink
More tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Tinche committed Jun 18, 2024
1 parent 45ff543 commit d024347
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 5 deletions.
40 changes: 35 additions & 5 deletions src/cattrs/cols.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from __future__ import annotations

from sys import version_info
from typing import TYPE_CHECKING, Any, Iterable, NamedTuple, Tuple, TypeVar
from typing import TYPE_CHECKING, Any, Iterable, Literal, NamedTuple, Tuple, TypeVar

from attrs import NOTHING, Attribute

Expand Down Expand Up @@ -219,10 +219,20 @@ def _namedtuple_to_attrs(cl: type[tuple]) -> list[Attribute]:


def namedtuple_dict_structure_factory(
cl: type[tuple], converter: BaseConverter, **kwargs: AttributeOverride
cl: type[tuple],
converter: BaseConverter,
detailed_validation: bool | Literal["from_converter"] = "from_converter",
forbid_extra_keys: bool = False,
use_linecache: bool = True,
/,
**kwargs: AttributeOverride,
) -> StructureHook:
"""A hook factory for structuring namedtuples from dictionaries.
:param forbid_extra_keys: Whether the hook should raise a `ForbiddenExtraKeysError`
if unknown keys are encountered.
:param use_linecache: Whether to store the source code in the Python linecache.
.. versionadded:: 24.1.0
"""
try:
Expand All @@ -237,7 +247,13 @@ def namedtuple_dict_structure_factory(

try:
return make_dict_structure_fn_from_attrs(
_namedtuple_to_attrs(cl), cl, converter, **kwargs
_namedtuple_to_attrs(cl),
cl,
converter,
_cattrs_forbid_extra_keys=forbid_extra_keys,
_cattrs_use_detailed_validation=detailed_validation,
_cattrs_use_linecache=use_linecache,
**kwargs,
)
finally:
working_set.remove(cl)
Expand All @@ -246,10 +262,19 @@ def namedtuple_dict_structure_factory(


def namedtuple_dict_unstructure_factory(
cl: type[tuple], converter: BaseConverter, **kwargs: AttributeOverride
cl: type[tuple],
converter: BaseConverter,
omit_if_default: bool = False,
use_linecache: bool = True,
/,
**kwargs: AttributeOverride,
) -> UnstructureHook:
"""A hook factory for unstructuring namedtuples to dictionaries.
:param omit_if_default: When true, attributes equal to their default values
will be omitted in the result dictionary.
:param use_linecache: Whether to store the source code in the Python linecache.
.. versionadded:: 24.1.0
"""
try:
Expand All @@ -264,7 +289,12 @@ def namedtuple_dict_unstructure_factory(

try:
return make_dict_unstructure_fn_from_attrs(
_namedtuple_to_attrs(cl), cl, converter, **kwargs
_namedtuple_to_attrs(cl),
cl,
converter,
_cattrs_omit_if_default=omit_if_default,
_cattrs_use_linecache=use_linecache,
**kwargs,
)
finally:
working_set.remove(cl)
Expand Down
26 changes: 26 additions & 0 deletions tests/test_tuples.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@

from typing import NamedTuple, Tuple

from pytest import raises

from cattrs.cols import (
is_namedtuple,
namedtuple_dict_structure_factory,
namedtuple_dict_unstructure_factory,
)
from cattrs.converters import Converter
from cattrs.errors import ForbiddenExtraKeysError


def test_simple_hetero_tuples(genconverter: Converter):
Expand Down Expand Up @@ -81,3 +84,26 @@ class Test(NamedTuple):

# Defaults work.
assert genconverter.structure({"a": 1}, Test) == Test(1, "test")


def test_dict_nametuples_forbid_extra_keys(genconverter: Converter):
"""Forbidding extra keys works for structuring namedtuples from dicts."""

class Test(NamedTuple):
a: int

genconverter.register_structure_hook_factory(
lambda t: t is Test,
lambda t, c: namedtuple_dict_structure_factory(t, c, "from_converter", True),
)

with raises(Exception) as exc_info:
genconverter.structure({"a": 1, "b": "2"}, Test)

if genconverter.detailed_validation:
exc = exc_info.value.exceptions[0]
else:
exc = exc_info.value

assert isinstance(exc, ForbiddenExtraKeysError)
assert exc.extra_fields == {"b"}

0 comments on commit d024347

Please sign in to comment.