python-attrs / python-attrs/cattrs

Regression of unstructuring typing.Any based on runtime types

Open
#320 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
1.1k
Forks
159
Avg merge
12h 21m
Merged PRs (30d)
6

Description

  • 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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the reported behavior with the Container and Thing examples using both BaseConverter and GenConverter, comparing the 1.1.2 and current outputs. Start at the converters' unstructure handling for statically typed Any fields; done means attrs instances in Any and List[Any] fields are converted consistently with the intended behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.