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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions