Skip to content

Weird output while structuring unions with custom disambiguation function #267

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

Open
pritsh opened this issue May 5, 2022 · 1 comment
Open

Comments

@pritsh
Copy link

pritsh commented May 5, 2022

  • cattrs version: 22.1.0
  • Python version: 3.9

Description

Thanks for this amazing library !!

I am trying to write disambiguation function to handle union type based on the default value of child class but got into weird error, also want to understand the correct way to do it with cattrs.

@define(kw_only=True)
class GenType:
    pass

@define(kw_only=True)
class TypeOne(GenType):
    id:Literal["A"] = "A"


@define(kw_only=True)
class TypeTwo(GenType):
   id:Literal["B"] = "B"

UniType = Union[TypeOne, TypeTwo]

@define(kw_only=True)
class Container:
    types: List[UniType]

def discriminate_model_types(value: typing.Any, _klass: typing.Type) -> UniType:
    # Write some logic to find the exact type !!, I was wondering if there is correct pythonic version to do this with cattrs.
    for child_cls in GenType.__subclasses__():
        if value["id"] == child_cls().id:
            return cattr.structure(value, child_cls)

def run_union_logic():
    json = {"types":[{"id":"A"}]}
    cattr.register_structure_hook(UniType, discriminate_model_types)
    obj =cattr.structure(json, Container)
    print(obj)

    json = {"id": "A"}
    obj = cattr.structure(json, TypeOne)
    print(obj)

#Prints following:
#Container(types=[TypeOne()]) --> I dont know why property 'id' is not populated here. 
#TypeOne(id='A') 
@AdrianSosic
Copy link
Contributor

Hi, I just stumbled across this issue because I noticed the same problem. While I don't know what exactly causes the problem, I know at least why you get this result. The reason is because the original classes are still contained in the list returned by GenType.__subclasses__(). (Weirdly enough, the problem with your code example occurs for me only for class TypeTwo, but I experienced similar behavior in my own code).

What I get as an output is [__main__.TypeOne, __main__.TypeTwo, __main__.TypeTwo], where you can see that the class appears twice (in your case, you'll probably also see the same for TypeOne, causing your error). If you select from the list the second version of the class, your example will probably work.

However, I don't know the root cause for the problem, but it might have something to do with garbage collection!? On the attrs page, there is a hint related to slotted classes:
image
which points to this issue:
python-attrs/attrs#407

Also, it seems somehow related to the Union call because in my cases the duplicates only appeared after the call. Any idea here?

Anyway, the clean way to do it is described here: #140
Hope this helps 👍

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