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 crashes in class scoped imports #12023

Merged
merged 33 commits into from
Feb 17, 2022

Conversation

hauntsaninja
Copy link
Collaborator

@hauntsaninja hauntsaninja commented Jan 20, 2022

Builds on #11344 and addresses feedback provided in that PR.

Fixes #11045, fixes huggingface/transformers#13390
Fixes #10488
Fixes #7045
Fixes #7806
Fixes #11641
Fixes #11351
Fixes #10488

Co-authored-by: @A5rocks

@hauntsaninja hauntsaninja changed the title Fix class scoped imports Fix crashes in class scoped imports Jan 20, 2022
@github-actions

This comment has been minimized.

@hauntsaninja
Copy link
Collaborator Author

hauntsaninja commented Jan 20, 2022

Does anyone know what mypyc wants from us?

  File "mypy/semanal.py", line 4805, in add_imported_symbol
TypeError: mypy.nodes.Var object expected; got mypy.nodes.FuncDef

where L4805 is:

symbol_node = Var(symbol_node.name, symbol_node.type)

and symbol_node is originally defined as:

symbol_node: Optional[SymbolNode] = node.node

(and type checking proper is happy)

mypy/semanal.py Outdated Show resolved Hide resolved
@hauntsaninja
Copy link
Collaborator Author

@JelleZijlstra thanks for the feedback! I fixed the self binding behaviour and changed things so that we get line numbers for the "already defined" errors

@github-actions

This comment has been minimized.

1 similar comment
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

2 similar comments
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

1 similar comment
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

class Foo:
import mod

reveal_type(Foo.mod.foo) # N: Revealed type is "builtins.int"
Copy link
Member

Choose a reason for hiding this comment

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

I think reveal_type(Foo.mod) will also be helpful here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Okay, can add!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's revealed as builtins.object, not types.ModuleType, but pretty sure that's just because of the stub fixtures

@@ -131,9 +131,9 @@ def f() -> None: pass
[case testImportWithinClassBody2]
import typing
class C:
from m import f
from m import f # E: Method must have at least one argument
Copy link
Member

Choose a reason for hiding this comment

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

This seems to be placed on the wrong line.

Copy link
Collaborator Author

@hauntsaninja hauntsaninja Feb 16, 2022

Choose a reason for hiding this comment

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

This is actually on the correct line. In this kind of situation, C.f is capable of working like a method would. However, because this f takes no arguments, if you try to do C().f() you'd get a TypeError at runtime. See also: #7045 (comment)

I agree that for the code in this test, where there is no attempt to use C.f as a method, the error is a little surprising. But I think the code in this test is not particularly idiomatic. Maybe we could make it only error on the caller side if they try to call it as a method, but implementing that is not easy. So I'd like to keep that out of scope for this PR, which fixes crashes and whose behaviour is technically correct.

Copy link
Member

Choose a reason for hiding this comment

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

Oh, I see! 👍


symbol_node: Optional[SymbolNode] = node.node

if self.is_class_scope():
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we can somehow create a new function here to make the inference better? Right now - this is sooo complex, but I understand the problem you have with mypyc 🙂

Copy link
Collaborator Author

@hauntsaninja hauntsaninja Feb 16, 2022

Choose a reason for hiding this comment

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

mypy is actually able to narrow the types just fine. If you remove f, everything type checks perfectly. It's only mypyc that's the issue :'( I've tried a lot of variants, if you have a suggestion that you think is better and that mypyc will understand, I'm happy to try!

@@ -2722,7 +2722,7 @@ import m

[file m.py]
class C:
from mm import f
from mm import f # E: Method must have at least one argument
Copy link
Member

Choose a reason for hiding this comment

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

The same here 🤔

@github-actions
Copy link
Contributor

According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉


[case testClassScopeImportModuleStar]
class Foo:
from mod import *
Copy link
Member

Choose a reason for hiding this comment

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

This is actually a syntax error, you can only do import * in the global scope (at least in Python 3).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Huh, TIL. Tried on Python 2 and it works but gives me a SyntaxWarning. Thanks Python 3!

Copy link
Member

Choose a reason for hiding this comment

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

I think tests should tell us about syntax errors. Why do they pass? 🤔

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Most syntax errors happen at parse time, but some, like this one, do not. The ones that happen later often cause problems for mypy, e.g. this: #11499 or from __future__ import braces

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Another example is nonlocal x at module level

@JelleZijlstra JelleZijlstra merged commit 1de5e55 into python:master Feb 17, 2022
@hauntsaninja hauntsaninja deleted the class-scope-import branch February 17, 2022 03:13
hauntsaninja pushed a commit to hauntsaninja/mypy that referenced this pull request Feb 17, 2022
More context in python#12197

This essentially walks back changes in python#12023 that apply to
function-like things that are imported in class-scope. We just issue a
(non-blocking) error and mark the type as Any. Inference for variables
still works fine; I'm yet to think of any problems that could cause.

A full fix seems quite difficult, but this is better than both a crash
and a blocking error.
@hauntsaninja
Copy link
Collaborator Author

Walking back some of the changes in this PR in #12199 , see #12197

JelleZijlstra pushed a commit that referenced this pull request Feb 17, 2022
Fixes #12197

This essentially walks back changes in #12023 that apply to
function-like things that are imported in class-scope. We just issue a
(non-blocking) error and mark the type as Any. Inference for variables
still works fine; I'm yet to think of any problems that could cause.

A full fix seems quite difficult, but this is better than both a crash
and a blocking error.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment