-
-
Notifications
You must be signed in to change notification settings - Fork 133
Implement a strategy to resolve natively Sequence[X] | Y #743
Copy link
Copy link
Open
Description
Hello,
I wonder whether you would be interested in providing in cattrs a strategy to allow the structuring of Sequence[X] | Y, under the conditions that:
- both
XandYcan be structured; Yis not alist,tuple,MutableSequenceorSequence;
I have already written the implementation for my work (my main worry is that my colleagues may not be able to maintain that hook factory long-term if I am gone):
def configure_union_with_single_sequence_dispatch(converter: cattrs.BaseConverter):
def is_union_containing_single_sequence(target_type: Any) -> bool:
# If target_type is a new-style alias type, we need to resolve on the __value__ field
if isinstance(target_type, TypeAliasType):
target_type = target_type.__value__
if get_origin(target_type) not in (UnionType, Union):
return False
type_args = set(target_type.__args__)
if len(type_args) < 2 or (len(type_args) == 2 and type(None) in type_args):
return False
# Design choice: support the case where tuple[X, Y], list[X], MutableSequence[X] or Sequence[X] appears in the Union
# We could have an issue in cases where one or more elements are behind new-style type aliases
sequence_types = (tuple, list, MutableSequence, Sequence)
sequence_type_args = [t for t in type_args if t in sequence_types or get_origin(t) in sequence_types]
return len(sequence_type_args) == 1
def make_structure_union_containing_single_sequence(target_type: Any, /) -> Callable[[Any, Any], Any]:
# If target_type is a new-style alias type, we need to resolve on the __value__ field
if isinstance(target_type, TypeAliasType):
target_type = target_type.__value__
type_args = set(target_type.__args__)
sequence_types = (tuple, list, MutableSequence, Sequence)
sequence_type_arg = next(t for t in type_args if t in sequence_types or get_origin(t) in sequence_types)
other_type_args = [t for t in type_args if t != sequence_type_arg]
spillover_type: Any = Union[tuple(other_type_args)] if len(other_type_args) > 1 else other_type_args[0]
def structure_union_containing_single_sequence(val: Any, _: Any) -> Any:
if isinstance(val, (tuple, list)):
return converter.structure(val, sequence_type_arg)
return converter.structure(val, spillover_type)
return structure_union_containing_single_sequence
converter.register_structure_hook_factory(is_union_containing_single_sequence, make_structure_union_containing_single_sequence)Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels