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

fix: support generic dataclass #525

Merged

Conversation

PJCampi
Copy link
Contributor

@PJCampi PJCampi commented Apr 18, 2024

support generic dataclass

Copy link
Collaborator

@george-zubrienko george-zubrienko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@matt035343 wdyt about this? Imo this allows you to squeeze lots of problems in as there are no bounds on T, which means it will always be a string/number/bool, what's the point then?

@matt035343
Copy link
Collaborator

@george-zubrienko

@matt035343 wdyt about this? Imo this allows you to squeeze lots of problems in as there are no bounds on T, which means it will always be a string/number/bool, what's the point then?

I am unsure what the current behavior is, crash and burn? In that case anything is better.
However, I am interested in seeing some more unit tests for more complex generic types. For example:

  • When multiple generic types are accepted
  • When the generic type is a list/dict
  • etc...

@PJCampi
Copy link
Contributor Author

PJCampi commented Apr 22, 2024

The current implementation will not decode the generic dataclass. The dataclass attribute will be assigned the json dict value instead. In my unittest, nested is assigned {"value": "value"}

After my proposed change, whether the decoding works is down to whether T itself is supported by dataclass_json. Currently it works if the materialized type of T is supported (extended types, collections of them, dataclass, etc...). One can also register a global decoder for the TypeVar (f.ex. if T is bound or constrained).

It is not a perfect solution at all but the library's coverage improves a little. I can add more tests to better highlight what works and what doesn't.

@george-zubrienko
Copy link
Collaborator

It is not a perfect solution at all but the library's coverage improves a little. I can add more tests to better highlight what works and what doesn't.

Please do that - and request review again when ready :)

@george-zubrienko
Copy link
Collaborator

After my proposed change, whether the decoding works is down to whether T itself is supported by dataclass_json

Shouldn't we throw in case it is not supported?

@PJCampi
Copy link
Contributor Author

PJCampi commented Apr 24, 2024

Maybe I should describe my change in more details because the name of the PR (and the ticket I opened) is a bit misleading.

It's not about improving the decoding of type variables in a generic dataclass. That feature currently not supported by the library. The library sets the raw json node to a dataclass field of type T regarldess of its bound, constraints or annotation via __cls_getitem__. It never fails:

    @dataclass_json
    @dataclass
    class Foo(Generic[T]):
        bar: T

    @dataclass_json
    @dataclass
    class Bar:
        value: int

    assert Foo[Bar].from_json('{"bar": {"value": 1}}') == Foo({"value": 1})  # Bar is not decoded correctly  

I am trying to align the behaviour of the library when a generic dataclass is used as type annotation to the behavior I just described.

In the case where a generic dataclass (like Foo) is used as part of a type annotation, it is wrapped in an generic wrapper (_GenericAlias in python 3.11).

Consider the following example:

    @dataclass_json
    @dataclass
    class Baz(Generic[T]):
        foo: Foo[T]

On master we silently fail to decode the generic dataclass attribute foo and the json node is assigned to the attribute:

    assert Baz[int].from_json('{"foo": {"bar": 1}}') == Baz({"bar": 1})
    assert Foo[int].from_json('{"bar": 1}') == Foo(1)

After my change we extract the concrete dataclass type from the generic wrapper. The type is now decoded as a dataclass with generic type T. The decoding of foo becomes consistent with the decoding of an instance of Foo:

    assert Baz[int].from_json('{"foo": {"bar": 1}}') == Baz(Foo(1))
    assert Foo[int].from_json('{"bar": 1}') == Foo(1)

Throwing an exception instead of silently incorrectly setting a json node to an attribute of complex type would be a good idea but it's orthogonal to my change, and it would be a breaking change to the behaviour of the library.

I added more test cases now. I did not write any test around type bounds or constraints cause that's not supported by the library anyways.

@george-zubrienko
Copy link
Collaborator

I am convinced - @matt035343 any additional input or we release?

@matt035343
Copy link
Collaborator

I am convinced - @matt035343 any additional input or we release?

I think it looks good

@matt035343 matt035343 self-requested a review April 26, 2024 14:13
@george-zubrienko george-zubrienko merged commit de0d230 into lidatong:master Apr 26, 2024
9 checks passed
@george-zubrienko
Copy link
Collaborator

To be released Monday :) fyi @PJCampi - and thanks for contributions and thorough explanations!

renovate bot added a commit to ixm-one/pytest-cmake-presets that referenced this pull request Apr 29, 2024
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [dataclasses-json](https://togithub.com/lidatong/dataclasses-json)
([changelog](https://togithub.com/lidatong/dataclasses-json/releases)) |
`0.6.4` -> `0.6.5` |
[![age](https://developer.mend.io/api/mc/badges/age/pypi/dataclasses-json/0.6.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/pypi/dataclasses-json/0.6.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/pypi/dataclasses-json/0.6.4/0.6.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/dataclasses-json/0.6.4/0.6.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>lidatong/dataclasses-json (dataclasses-json)</summary>

###
[`v0.6.5`](https://togithub.com/lidatong/dataclasses-json/releases/tag/v0.6.5)

[Compare
Source](https://togithub.com/lidatong/dataclasses-json/compare/v0.6.4...v0.6.5)

#### What's Changed

- fix: apply global config globally by
[@&#8203;PJCampi](https://togithub.com/PJCampi) in
[lidatong/dataclasses-json#524
- fix: support generic dataclass by
[@&#8203;PJCampi](https://togithub.com/PJCampi) in
[lidatong/dataclasses-json#525

#### New Contributors

- [@&#8203;PJCampi](https://togithub.com/PJCampi) made their first
contribution in
[lidatong/dataclasses-json#524

**Full Changelog**:
lidatong/dataclasses-json@v0.6.4...v0.6.5

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/ixm-one/pytest-cmake-presets).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4zMjEuMiIsInVwZGF0ZWRJblZlciI6IjM3LjMyMS4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZTpkZXBlbmRlbmNpZXMiXX0=-->

Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
@PJCampi PJCampi deleted the fix-support-generic-dataclass branch May 9, 2024 18:33
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

Successfully merging this pull request may close these issues.

None yet

4 participants