pyrefly
pyrefly copied to clipboard
Make None generalise to Optional[T]
Describe the Bug
In most cases if we see None we should treat that as None | ?, perhaps just in an assignment
CC @stroxler
Sandbox Link
No response
(Only applicable for extension issues) IDE Information
No response
https://pyrefly.org/sandbox/?code=MQAg6gpgNgxg9gWwiALnVALZAFAngJwgDMpcQBlAQwDsATAIzgA8AaTASwGcQuRKQADpQDmEAFCgA7lkIhccAK4gYNEJPzsUENpwyVZNWiCgR91PowUpUuAe2rCe5vCgxxqAOgkSQACQgGBsqKAiYgRKYoCoTccEQc3JyGjEwAXD4AtCAA4hDWhJRQGSjsSDYCyDBYMADW9o4REAyUtZkgAKqcyIQAbqZQAPoothAAFACUqOj2nBUw1vYR+IRGwxWcbRBMFRpI1NaSmhiYyACSACIAouGR0RDcrpQHyJwKAgJw+NajEB7CHnwrHB4AhQnltCBhHBitDaMR7Jp2O42G4+vg2HkYOM2mANFo+HQQPgFM5cK53ME4SBaOxCPNSE45Ip8CB6Pg4JIuvgxGIiOyEOV6jxQZ9rAAqHlw+JaTgoUZMAC8ADl3BBxukQJqiRA+oUhiN5eMgA
When we have an unannotated param with default=None, we infer Any, but I think typeshed would like us to infer Any | None
https://github.com/python/typeshed/issues/14029
@yangdanny97 - agreed, but I actually think that is a separate issue (but also worth fixing). For any default=Y we should infer Any | TYPE[Y].
@ndmitchell just wanna make sure i'm on the right track here...
Seems like we already support this for simple assignments to variables:
https://pyrefly.org/sandbox/?code=GYexC4AICMwG0gXkgFQE4FcCmAoAHkpAIw4CWwkoEOktkByAciAHa5pYBuWAhnAPoAXAJ4AHLAAo8AShxA
Jon's feedback seemed related to inserting None into containers, which I don't think could be solved by searching for a common parent class since it would be object in this case.
I see that override checks for class vars are a problem when the parent class sets it as None
https://pyrefly.org/sandbox/?code=MYGwhgzhAEBiD28BcAoa7oA9oF5oDl4A7AUxRVEhgCEwAnACgXgEpUMtdoBGFIA
Mypy complains like we do, but Pyright allows this pattern even though it's not sound (below is Pyright's reveal_type behavior)
class Foo:
x = None
class Bar(Foo):
x = 1
reveal_type(Bar.x) # int
reveal_type(Foo.x) # None
def test(x: type[Foo]):
reveal_type(x.x) # None
Here's a non-inheritance example:
class C:
x = None
def __init__(self, x: int | None):
if x:
self.x = x
c = C(None)
reveal_type(c.x)
This issue has someone assigned, but has not had recent activity for more than 2 weeks.
If you are still working on this issue, please add a comment so everyone knows. Otherwise, please unassign yourself and allow someone else to take over.
Thank you for your contributions!
I think there's 2 pieces to this:
- make un-annotated fields inherit annotations from the parent class (I'm not sure which one they should inherit if there's multiple inheritance, but perhaps any of them will suffice). this should always happen
- if a field is un-annotated and doesn't inherit any annotations we can infer
Any. this should be gated behind a flag.
edit: we may already have no. 1 implemented
This issue has someone assigned, but has not had recent activity for more than 2 weeks.
If you are still working on this issue, please add a comment so everyone knows. Otherwise, please unassign yourself and allow someone else to take over.
Thank you for your contributions!