Skip to content

Regression of unstructuring typing.Any based on runtime types #320

Open
@xaviergmail

Description

@xaviergmail
  • cattrs version: 22.2.0
  • Python version: 3.9
  • Operating System: macOS / Linux

Description

There is a regression in unstructuring of statically typed Any field in versions published after 1.1.2. It returns the value as-is, regardless of whether the runtime type is an attrs class.

What I Did

Using the following code:

try:
    from attrs import define
    try:
        from cattrs.converters import BaseConverter, GenConverter
    except ImportError:
        from cattrs.converters import Converter as BaseConverter
        from cattrs.converters import GenConverter
except ImportError:
    from attr import define
    from cattr.converters import Converter as BaseConverter, GenConverter


bc = BaseConverter()
gc = GenConverter()


@define
class Container:
    many: ty.List[ty.Any]
    one: ty.Any


@define
class Thing:
    field: int


c = Container(many=[Thing(field=1)], one=Thing(field=2))

print("BaseConverter", bc.unstructure(c))
print("GenConverter", gc.unstructure(c))

I ran the following:

cattrs 1.1.2

List[Any] and Any get unstructured fine.

❯ pip install cattrs==1.1.2 --force-reinstall &> /dev/null && pip show cattrs | grep Version && python thing.py
Version: 1.1.2
BaseConverter {'many': [{'field': 1}], 'one': {'field': 2}}
GenConverter {'many': [{'field': 1}], 'one': {'field': 2}}

cattrs 1.2.0

List[Any] gets unstructured but not Any

❯ pip install cattrs==1.2.0 --force-reinstall &> /dev/null && pip show cattrs | grep Version && python thing.py
Version: 1.2.0
BaseConverter {'many': [{'field': 1}], 'one': Thing(field=2)}
GenConverter {'many': [{'field': 1}], 'one': Thing(field=2)}

cattrs 1.4.0 onwards

GenConverter no longer unstructures Any fields
BaseConverter retained the inconsistent 1.2.0 behavior

❯ pip install cattrs==1.4.0 --force-reinstall &> /dev/null && pip show cattrs | grep Version && python thing.py
Version: 1.4.0
BaseConverter {'many': [{'field': 1}], 'one': Thing(field=2)}
GenConverter {'many': [Thing(field=1)], 'one': Thing(field=2)}

The behavior then stays consistent from 1.4.0 onward. We are still slowly updating our application code to use our internal libraries which are now using cattrs 22.2.0, which is how I came across this. We have a very specific use case where using generics would be impractical; we're just now moving from 3.7 to 3.9 and therefore can't use 3.11's variadic generics and defining a handful of TypeVars would be unsightly.

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