-
-
Notifications
You must be signed in to change notification settings - Fork 30.6k
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
Postponed annotations break inspection of dataclasses #78957
Comments
The new postponed annotations have an unexpected interaction with dataclasses. Namely, you cannot get the type hints of any of the data classes methods. For example, I have some code that inspects the type parameters of a class's
In Python 3.6 and 3.7, this does what is expected; it prints However, if in Python 3.7, I add
I know why this is happening. The I know that the use of lambdas to implement PEP-563 was rejected for performance reasons. I could be wrong, but I think this was motivated by variable annotations because the lambda would have to be constructed each time the function body ran. I was wondering if I could motivate storing the annotations as lambdas in class bodies and function signatures, in which the environment is already being captured and is code that usually only runs once. Original mailing list discussion: https://mail.python.org/pipermail/python-dev/2018-September/155289.html |
[Adding to nosy people who were on the original email] Copying (part of) my response from the email thread: These work: But I agree that maybe doing something with dataclasses to address this would be good. Especially as the first one requires being in the same module as Foo. See this for Yury's self-described "hack-ish fix we can use" until we do something better: https://gist.github.com/1st1/37fdd3cc84cd65b9af3471b935b722df |
Actually, I think I found a better solution that doesn't require any changes to anything besides dataclasses. Currently, dataclasses uses 'exec()' function to dynamically create methods like '__init__'. The generated code for '__init__' needs to access MISSING and _HAS_DEFAULT_FACTORY constants from the dataclasses module. To do that, we compile the code with 'exec()' with globals set to a dict with {MISSING, _HAS_DEFAULT_FACTORY} keys in it. This does the trick, but '__init__.__globals__' ends up pointing to that custom dict, instead of pointing to the module's dict. The other way around is to use a closure around __init__ to inject MISSING and _HAS_DEFAULT_FACTORY values *and* to compile the code in a proper __dict__ of the module the dataclass was defined in. Please take a look at the PR. |
And FWIF I don't think we need to use lambdas for annotations to solve issues like this one. |
Ned: I'm marking this as a release blocker because I'd like to get it in 3.7.1. If I can't do that in the next 5 hours or so, I'll remove the release blocker tag. |
Unfortunately, I'm not going to be able to give this the attention it deserves before the 3.7.1 cutoff. The changes are sufficiently tricky that I want to make sure I think through the issue in the appropriate detail. Ned: I've made it not a release blocker. Please remove yourself as nosy if you don't care about this issue. I don't plan on making it a release blocker again, I just did that because it was so near the deadline. Yury: Sorry for not being able to get delve in to it sufficiently. Thanks for your work on it. I should be able to review this in the next few days, so we can get this in to 3.7.2. |
NP, Eric, take your time. I agree that the PR isn't simple and needs a very careful review. |
fwiw, agreed that this should wait for 3.7.2. |
Yury, thanks for your patch. I'll review it soon. Please note that postponed annotations only reveal a problem that we already had if anybody used a string forward reference: >>> from dataclasses import dataclass
>>> from typing import get_type_hints
>>> class C:
... pass
...
>>> @dataclass
... class D:
... c: C
...
>>> @dataclass
... class E:
... c: "C"
...
>>> get_type_hints(C.__init__)
{}
>>> get_type_hints(D.__init__)
{'c': <class '__main__.C'>, 'return': <class 'NoneType'>}
>>> get_type_hints(E.__init__)
Traceback (most recent call last):
...
NameError: name 'C' is not defined |
Yeah, makes sense. It's cool that the PR fixes string forward references as well. |
Any chance this could get into 3.7.3? |
I'm finally getting time to look at this. I'll see what I can do. |
This PR has been sitting for a while. Any chance we can bring it over the finish line? |
FWIW we discussed my patch with Eric at PyCon 2019 and I think he had no issues with it. Eric, can I merge it? Should we backport to 3.8? |
Yury: I'm okay with merging. If I recall, you were going to add a comment or two about the approach (a closure, if I remember correctly). I think we should backport to 3.8 and 3.7: it's a bug. |
Sure, I'll rebase and add a comment in a couple of days. I'll elevate the status so that I don't forget before 3.8.1 |
Ping, 3.8.1 cutoff is coming up very soon. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: