Skip to content

Commit

Permalink
Introduce SimpleStructureHook
Browse files Browse the repository at this point in the history
  • Loading branch information
Tinche committed Nov 7, 2024
1 parent ea30af6 commit 4284c33
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 23 deletions.
5 changes: 3 additions & 2 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ Our backwards-compatibility policy can be found [here](https://github.com/python
- Some `defaultdicts` are now [supported by default](https://catt.rs/en/latest/defaulthooks.html#defaultdicts), and
{func}`cattrs.cols.is_defaultdict`{func} and `cattrs.cols.defaultdict_structure_factory` are exposed through {mod}`cattrs.cols`.
([#519](https://github.com/python-attrs/cattrs/issues/519) [#588](https://github.com/python-attrs/cattrs/pull/588))
- Replace `cattrs.gen.MappingStructureFn` with `cattrs.SimpleStructureHook[In, T]`.
- Python 3.13 is now supported.
([#543](https://github.com/python-attrs/cattrs/pull/543) [#547](https://github.com/python-attrs/cattrs/issues/547))
- Python 3.8 is no longer supported, as it is end-of-life. Use previous versions on this Python version.
([#591](https://github.com/python-attrs/cattrs/pull/591))
- Change type of Converter.__init__.unstruct_collection_overrides from Callable to Mapping[type, UnstructureHook]
([#594](https://github.com/python-attrs/cattrs/pull/594).
- Change type of `Converter.__init__.unstruct_collection_overrides` from `Callable` to `Mapping[type, UnstructureHook]`
([#594](https://github.com/python-attrs/cattrs/pull/594)).

## 24.1.2 (2024-09-22)

Expand Down
2 changes: 1 addition & 1 deletion docs/defaulthooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ This also means `defaultdicts` without key and value annotations (bare `defaultd
... default_factory=lambda: 1
... )

>>> hook({"key": 1}, defaultdict[str, int])
>>> hook({"key": 1})
defaultdict(<function <lambda> at ...>, {'key': 1})
```

Expand Down
28 changes: 15 additions & 13 deletions src/cattrs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,34 @@
StructureHandlerNotFoundError,
)
from .gen import override
from .types import SimpleStructureHook
from .v import transform_error

__all__ = [
"structure",
"unstructure",
"get_structure_hook",
"get_unstructure_hook",
"register_structure_hook_func",
"register_structure_hook",
"register_unstructure_hook_func",
"register_unstructure_hook",
"structure_attrs_fromdict",
"structure_attrs_fromtuple",
"global_converter",
"BaseConverter",
"Converter",
"AttributeValidationNote",
"BaseConverter",
"BaseValidationError",
"ClassValidationError",
"Converter",
"ForbiddenExtraKeysError",
"GenConverter",
"get_structure_hook",
"get_unstructure_hook",
"global_converter",
"IterableValidationError",
"IterableValidationNote",
"override",
"register_structure_hook_func",
"register_structure_hook",
"register_unstructure_hook_func",
"register_unstructure_hook",
"SimpleStructureHook",
"structure_attrs_fromdict",
"structure_attrs_fromtuple",
"structure",
"StructureHandlerNotFoundError",
"transform_error",
"unstructure",
"UnstructureStrategy",
]

Expand Down
11 changes: 8 additions & 3 deletions src/cattrs/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,14 @@
DictStructureFn,
HeteroTupleUnstructureFn,
IterableUnstructureFn,
MappingStructureFn,
MappingUnstructureFn,
make_dict_structure_fn,
make_dict_unstructure_fn,
make_hetero_tuple_unstructure_fn,
)
from .gen.typeddicts import make_dict_structure_fn as make_typeddict_dict_struct_fn
from .gen.typeddicts import make_dict_unstructure_fn as make_typeddict_dict_unstruct_fn
from .types import SimpleStructureHook

__all__ = ["UnstructureStrategy", "BaseConverter", "Converter", "GenConverter"]

Expand Down Expand Up @@ -137,6 +137,7 @@

UnstructureHookT = TypeVar("UnstructureHookT", bound=UnstructureHook)
StructureHookT = TypeVar("StructureHookT", bound=StructureHook)
CounterT = TypeVar("CounterT", bound=Counter)


class UnstructureStrategy(Enum):
Expand Down Expand Up @@ -1342,7 +1343,9 @@ def gen_unstructure_mapping(
self._unstructure_func.register_cls_list([(cl, h)], direct=True)
return h

def gen_structure_counter(self, cl: Any) -> MappingStructureFn[T]:
def gen_structure_counter(
self, cl: type[CounterT]
) -> SimpleStructureHook[Mapping[Any, Any], CounterT]:
h = mapping_structure_factory(
cl,
self,
Expand All @@ -1353,7 +1356,9 @@ def gen_structure_counter(self, cl: Any) -> MappingStructureFn[T]:
self._structure_func.register_cls_list([(cl, h)], direct=True)
return h

def gen_structure_mapping(self, cl: Any) -> MappingStructureFn[T]:
def gen_structure_mapping(
self, cl: Any
) -> SimpleStructureHook[Mapping[Any, Any], Any]:
structure_to = get_origin(cl) or cl
if structure_to in (
MutableMapping,
Expand Down
8 changes: 4 additions & 4 deletions src/cattrs/gen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
StructureHandlerNotFoundError,
)
from ..fns import identity
from ..types import SimpleStructureHook
from ._consts import AttributeOverride, already_generating, neutral
from ._generics import generate_mapping
from ._lc import generate_unique_filename
Expand Down Expand Up @@ -892,8 +893,6 @@ def mapping_unstructure_factory(

make_mapping_unstructure_fn: Final = mapping_unstructure_factory

MappingStructureFn = Callable[[Mapping[Any, Any], Any], T]


# This factory is here for backwards compatibility and circular imports.
def mapping_structure_factory(
Expand All @@ -903,7 +902,7 @@ def mapping_structure_factory(
key_type=NOTHING,
val_type=NOTHING,
detailed_validation: bool | Literal["from_converter"] = "from_converter",
) -> MappingStructureFn[T]:
) -> SimpleStructureHook[Mapping[Any, Any], T]:
"""Generate a specialized structure function for a mapping."""
fn_name = "structure_mapping"

Expand Down Expand Up @@ -1010,7 +1009,8 @@ def mapping_structure_factory(
for k, v in internal_arg_parts.items():
globs[k] = v

def_line = f"def {fn_name}(mapping, _{internal_arg_line}):"
globs["cl"] = cl
def_line = f"def {fn_name}(mapping, cl=cl{internal_arg_line}):"
total_lines = [def_line, *lines, " return res"]
script = "\n".join(total_lines)

Expand Down
12 changes: 12 additions & 0 deletions src/cattrs/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from typing import Protocol, TypeVar

__all__ = ["SimpleStructureHook"]

In = TypeVar("In")
T = TypeVar("T")


class SimpleStructureHook(Protocol[In, T]):
"""A structure hook with an optional (ignored) second argument."""

def __call__(self, _: In, /, cl=...) -> T: ...

0 comments on commit 4284c33

Please sign in to comment.