Skip to content

Unstructuring regression for optional generic types #465

@rymndhng

Description

@rymndhng
  • cattrs version: 23.2.0
  • Python version: 3.10.10
  • Operating System: osx

Description

I upgraded cattr from 22.2.0 -> 23.2.0 and I noticed a difference in behaviour for unstructuring optional generic types.

I've attached a minimally reproducing script that shows the differences in output between the two versions

What I Did

See this code:

import attrs
import cattrs

from typing import Generic, Optional, TypeVar

T = TypeVar("T")

@attrs.define
class A:
    foo: str

@attrs.define
class B(Generic[T]):
    a: T

@attrs.define
class BOptional(Generic[T]):
    a: Optional[T]

@attrs.define
class BOptionalPipe(Generic[T]):
    a: T | None

a = A("bar")

b = B(a)
b_optional = BOptional(a)
b_pipe = BOptionalPipe(a)


print(cattrs.unstructure(b))
print(cattrs.unstructure(b_optional))
print(cattrs.unstructure(b_pipe))

On cattrs 22.2.0, this prints:

{'a': {'foo': 'bar'}}
{'a': {'foo': 'bar'}}
{'a': {'foo': 'bar'}}

On cattrs 23.2.0, this prints:

{'a': {'foo': 'bar'}}
{'a': A(foo='bar')}
{'a': A(foo='bar')}

I noticed that dispatching on a generic type parameter returns the identity function, whereas previously, this would dispatch to the value type. It seems like there's a missing hook for handling typevars with optional.

cattrs.register_unstructure_hook_func(
    typing_inspect.is_typevar, cattrs.unstructure
)

Metadata

Metadata

Assignees

No one assigned

    Labels

    regressionThis is an inintended change for the worse.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions