-
Notifications
You must be signed in to change notification settings - Fork 2
Fix #66: dataclass from Generic now serializes #68
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
rhaps0dy
wants to merge
6
commits into
NiklasRosenstein:develop
Choose a base branch
from
rhaps0dy:deserialize-generic
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 3 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
71b136f
Fix #66: dataclass from Generic now serializes
rhaps0dy 17b2b6a
slap run fmt
rhaps0dy 2bc2ed0
Ignore type errors that we introduced on purpose
rhaps0dy a26b8f3
UnboundGeneric -> UnboundTypeVar
rhaps0dy c6be3b0
thorough UnboundGeneric -> UnboundTypeVar
rhaps0dy 0d84ae9
Add test for deserialization direction
rhaps0dy File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
[[entries]] | ||
id = "693674ea-b2b2-4733-bce6-4d5bae59b164" | ||
type = "fix" | ||
description = "Fix #66: dataclasses inheriting from uninstantiated Generic did not get all their fields serialized" | ||
author = "@rhaps0dy" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not 100% sure atm. whether the order plays a role here, i.e. if maybe the two unrolls should be the other way around. 🤔 Will dig a bit when I get the time, unless you want to hash out what it means.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, quickly: this comes from typeapi ClassTypeHint
.bases
, which usesget_type_hint_original_bases
. That on purpose accesses__orig_bases__
, which seems to be a CPython attribute of classes which is not very well documented.According to this PEP,
__orig_bases__
contains the instantiated generics for a particular type (e.g.A[int]
), as opposed to just the bare types (e.g.A
). The former is useful because it gives more information to Databind about how to de/serialize a particular field. However, subclasses overrides methods, and it's not clear that that's going to be in the correct order.We can get the correct method resolution order by calling
.mro()
on the class that we're dealing with. Given the test case with inheritanceclass A(Generic[T]) -> class B(A[int]) -> class C(B)
, callingC.mro()
would give us(C, B, A)
which would correctly tell where fields come from. But, we'd be losing type information.So, my recommendation is:
Class.mro()
before this entire loop to get the correct iteration order__orig_bases__
to get the instantiated-Generic types. IfTypeHint(t).type
matches anything in the MRO list, we use the instantiated-generic type instead of the original bare type.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I were to commit a solution like this, would you merge it?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @rhaps0dy , I'm sorry for the late response.
Thanks for explaining your train of thought so thoroughly. I'm following you, but I think that if this should, it should probably be on the
typeapi
level instead. Thatis because recursing throughClassTypeHint.bases
should allow you to reconstruct the MRO while at the same type getting access to the typed bases. (Just as you can reconstruct the MRO manually by recursing throughtype.__bases__
, only that you don't have the additional type information).The behaviour we're seeing seems to stem from the fact that
__orig_bases__
does not get set on a subclass that inherits from a generic without parameterizing it.This is a relatively old issue (so the requirement to always add
Generic[T]
into the mix is no longer present), but it hints at the fact that inheriting from a generic should be done with parameterization (even if that is with the same or another type variable). It seems there is no clear semantic assigned to inheriting from a generic without parameterizing it.python/typing#85
This leads me to consider that this is not necessarily a bug in Databind or Typeapi but in your code. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW I think the need to add
# type: ignore[type-arg]
is a pretty good hint that the way the inheritance is defined is actually invalid in Python's (aka. Mypy's) type system, and thus I don't think we should support the case.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NiklasRosenstein/python-typeapi#40