Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Option to include init=False globally on a converter, not per class #482

Open
MicaelJarniac opened this issue Jan 10, 2024 · 2 comments
Open
Labels
documentation Documentation needed.

Comments

@MicaelJarniac
Copy link
Contributor

MicaelJarniac commented Jan 10, 2024

  • cattrs version: v23.2.3
  • Python version: 3.11.6
  • Operating System: Ubuntu

Description

On previous cattrs versions, I used to structure and unstructure a lot of attrs classes that had many init=False fields.

Now, though, with the new versions of cattrs (v23.2.0), this behavior changed, breaking my code.

I'd like to be able to enable the old behavior back globally on a converter, instead of having to enable it for every single class, as I have many classes all over the place that would benefit from re-enabling init=False fields in conversion.

import attrs
import cattrs


@attrs.define
class Foo:
    bar: int
    baz: str = attrs.field(init=False)


foo = Foo(bar=1)
foo.baz = "hello"


converter = cattrs.Converter()
print(converter.unstructure(foo))
# before: {'bar': 1, 'baz': 'hello'}
# after: {'bar': 1}

I thought about something like this:

import cattrs
from cattrs.strategies import include_init_false

converter = cattrs.Converter()
include_init_false(converter)

# OR

converter = cattrs.Converter(include_init_false=True)
@MicaelJarniac
Copy link
Contributor Author

MicaelJarniac commented Jan 10, 2024

I think I found a workaround on the docs, but it wasn't linked in the places I quoted:

I'll try it out and see if it solves all my problems - I think it likely will.

But anyway, I think it'd be nice to link this workaround to these places:

(Also note that the first link includes a broken reference to the second one, which would also be nice to fix.)

Edit: Just noticed that the example is for the opposite case - to exclude init=False fields. But by changing it a tiny bit it does work to do what I want.

Edit: I simplified the code from the example, using newer features, and made it into a function:

import cattrs
from attrs import has
from cattrs.gen import make_dict_unstructure_fn


def include_init_false(c: cattrs.Converter) -> None:
    c.register_unstructure_hook_factory(
        has,
        lambda cl: make_dict_unstructure_fn(
            cl=cl,
            converter=c,
            _cattrs_include_init_false=True,
        ),
    )

@MicaelJarniac MicaelJarniac changed the title Include init=False globally on a converter, not per class Option to include init=False globally on a converter, not per class Jan 10, 2024
@Tinche
Copy link
Member

Tinche commented Jan 10, 2024

Howdy!

I think a converter parameter is almost certainly not the way to go, even though I've went that route before. Simply because the converter will end up a huge mess of different parameters.

Also the point of cattrs is to customize it with hooks and hook factories ;) I think your solution is pretty good.

Documentation is good though. I've been playing around with the idea of creating a migrations page in the docs. This would be a collection of recipes for dealing with breaking changes in cattrs, per version. And it would be in parallel with the changelog, just a different page. The idea being, if you're upgrading cattrs you can just look here for recipes on how to restore old behavior. I think this would be a good candidate for something like that.

@Tinche Tinche added the documentation Documentation needed. label Jan 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Documentation needed.
Projects
None yet
Development

No branches or pull requests

2 participants