Skip to content

Respect type annotation for containers #129

@Dr-ZeeD

Description

@Dr-ZeeD
  • cattrs version: 1.3.0
  • Python version: Python 3.9.1
  • Operating System: macOS 11.2.1

Description

If I see it correctly, support for generics exist. However, containers can also be generic and in combination with Protocols we could do neat things.

In a way, this leads me to a question: Why do unstructure hooks not also take an optional cls argument with the annotated type?

What I Did

from collections.abc import MutableSequence
from typing import Any, Protocol

from attr import define
from cattr import GenConverter

converter = GenConverter()


class A(Protocol):
    a: int


@define
class B:
    a: int
    b: int


def is_A(cls: type) -> bool:
    return cls is A


def default_unstructure_with_type(data: type[A]) -> dict[Any, Any]:
    return {"_type": type(data).__qualname__} | converter.unstructure(data)


converter.register_unstructure_hook_func(is_A, default_unstructure_with_type)
# using a registry we might also supply a structure function that acts on "_type"


if __name__ == "__main__":
    b = [B(1, 2), B(3, 4)]

    print(converter.unstructure(b[0]))
    # -> {'a': 1, 'b': 2}
    # OK

    print(converter.unstructure(b[0], unstructure_as=A))
    # -> {'_type': 'B', 'a': 1, 'b': 2}
    # OK

    print(converter.unstructure(b))
    # -> [{'a': 1, 'b': 2}, {'a': 3, 'b': 4}]
    # OK

    print(converter.unstructure(b, unstructure_as=MutableSequence[A]))
    # -> [{'a': 1, 'b': 2}, {'a': 3, 'b': 4}]
    # Not OK
    # Expected: [{'_type': 'B', 'a': 1, 'b': 2}, {'_type': 'B', 'a': 3, 'b': 4}]

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions