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

Feature request: add a new disambiguation strategy #317

Closed
matmel opened this issue Oct 25, 2022 · 5 comments
Closed

Feature request: add a new disambiguation strategy #317

matmel opened this issue Oct 25, 2022 · 5 comments

Comments

@matmel
Copy link
Contributor

matmel commented Oct 25, 2022

Description

As mentioned in the tips for handling unions in the documentation, one easy way to resolve the union is to incorporate the type name as an additional key during the destructuring / structuring process. I would like to add a disambiguation function of this nature because the need is even more pressing when you deal with subclasses.

At the moment the union resolving strategy is based on unique fields of attrs classes that do not have defaults. It is actually quite easy in the subclassing union type context to get into the situation where one (or all) of the classes participating in the union have unique fields but with defaults. I think that the type name strategy is a good alternative.

I am wondering the following:

  • Where does the no default policy comes from? Is it due to the potential of _cattrs_omit_if_default=True argument of make_dict_unstructure_fn?
  • If and when PR Subclassing support #312 is accepted, I would like to send another PR with this type name key disambiguation strategy presented as an option before cutting a new release due to the predominance of the problem with subclasses. I already have a proof of concept in the type_disambiguation branch of my fork but I am waiting to have the subclasses API settled (re: customize method of Converter) before sending this new PR.

@Tinche thoughts?

@Tinche
Copy link
Member

Tinche commented Oct 25, 2022

I agree we should add a new disambiguation strategy based on tagged unions (the nomenclature is a little fuzzy here since there are a lot of terms being used in slightly different contexts). Like you said, the idea would be to inject a piece of data into the payload during unstructuring, and to use that piece during structuring. I think we should be very flexible in what that piece of data is, probably allowing the user to pass in a callable or a dictionary when creating the strategy, and a default class when that piece of data is not present. Could be the class name, the fully qualified class name, maybe a classvar on each of the classes, maybe a hardcoded mapping. We also need to be able to customize the key under which this piece of data is being stored. I think this would be super useful to have.

The current default strategy is very simple, since cattrs aims for simple defaults. We also can't change the default strategy for backwards compatibility reasons I think. But new strategies should be easily pluggable by the user.

The default strategy only works with fields with no defaults because it's not safe to do otherwise in a general sense.

Say you have two classes:

@define
class A:
    a: int = 0

@define
class B:
    b: int = 0

And you get a payload of {}. Both A and B can successfully be structured from this payload ;)

Keep in mind cattrs doesn't assume it's being used at both ends; we need to be able to structure unions from third parties, and third parties often omit optional keys from payloads.

We can get the tagged union support in before the next release, no worries.

@matmel
Copy link
Contributor Author

matmel commented Oct 26, 2022

Thanks for taking the time to make a detailed answer.

I think we should be very flexible in what that piece of data is

Agreed. I was starting to implement a subset of the features you mentioned but I thought I went a little overboard. I reduced the scope in my initial implementation by just giving the choice of the key name to add in the payload. One difficulty I had is how to transfer all those options down and up the stack to reach the disambiguation generator function. I am eager to see your customize method, it sounds promising with respect to those kind of issues.

We also can't change the default strategy for backwards compatibility reasons I think.

Agreed and this is what I have implemented. The alternate strategy is opt-in and not the default.

we need to be able to structure unions from third parties, and third parties often omit optional keys from payloads.

Thanks that is some context I was missing. It makes sense.

@Tinche
Copy link
Member

Tinche commented Nov 5, 2022

Hi,

I've got a PR with the tagged union strategy here: #318

There's a way to apply this to the include_subclasses strategy easily, I think.

Let me know what you think.

@matmel
Copy link
Contributor Author

matmel commented Nov 7, 2022

I did not test on my use case yet, but the documentation and the features that you propose are definitely well in line with what I would need.

@Tinche
Copy link
Member

Tinche commented Aug 17, 2023

I think this is complete?

@Tinche Tinche closed this as completed Aug 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants