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

Expand imports in classes #11344

Closed
wants to merge 8 commits into from
Closed

Conversation

A5rocks
Copy link
Contributor

@A5rocks A5rocks commented Oct 16, 2021

Description

(each on their own line since I don't know what GitHub does for syntax for these :P)
Fixes #11045, external reference huggingface/transformers#13390
Fixes #10488
Fixes #7045
Fixes #7806
Fixes #11641
Fixes #11351
Fixes #10488

(Sadly this does not fix the crash in #8891)

This PR makes from module import function in a class assign that function to the class.

Explicitly out of scope (right now) is import name in a class making ClassThing.name.func_in_name work.

Test Plan

I verified these changes by running the reproduction steps of the issues listed as fixed, and put one of them in the unit tests.

@github-actions

This comment has been minimized.

@A5rocks
Copy link
Contributor Author

A5rocks commented Oct 16, 2021

Aha, I forgot about imports in methods. Whoops.

@A5rocks A5rocks changed the title Expand imports Expand imports in classes Oct 16, 2021
Copy link
Collaborator

@97littleleaf11 97littleleaf11 left a comment

Choose a reason for hiding this comment

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

lgtm!

@A5rocks
Copy link
Contributor Author

A5rocks commented Nov 10, 2021

Hello! I pushed some changes that should have resolved the comments! (I actually did this 24 days ago, but didn't realize it was probably best to leave a comment...)

Copy link
Collaborator

@JukkaL JukkaL left a comment

Choose a reason for hiding this comment

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

Thanks for the PR! This fixes a fairly nasty issue, but I think that we either need a somewhat more involved fix, or we can fall back to an even simpler workaround where we just reject this but don't crash.

if imported_id not in self.type.names.keys() and isinstance(defn, FuncDef):
if not defn.is_decorated and not defn.is_overload:
defn.info = self.type
self.add_symbol(defn.name, defn, imp)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Unfortunately this doesn't seem right/enough. We modify the function as defined in the original module, but it should only be treated as a method in this class, not elsewhere. I wonder what would happen if import the same function into multiple classes. For completeness, we should probably do this for Decorator and OverloadedFuncDef as well.

Implementing this in a full and correct way seems somewhat complicated. We could perhaps take a copy of the original FuncDef (or other node) and only modify the copy.

Alternatively, you could detect this and generate a blocking error, and not add anything to the symbol table. This would be the easiest fix. The error message can contain a note that suggests doing something like name = mod.func instead.

class Foo:
from a import func

reveal_type(Foo().func)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Test what happens if we import two functions with the same name (from different modules)?

Also test what happens if we import a function over an existing name.

Test multiple classes that import the same function.

Test calling the original function via module and instance.

@hauntsaninja
Copy link
Collaborator

hauntsaninja commented Jan 19, 2022

I was running into this issue at work and foolishly missed this excellent PR. I ended up with something very similar to this, here's my patch:

diff --git a/mypy/semanal.py b/mypy/semanal.py
index 22d6b2e57..fdbf6c10d 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -4756,7 +4756,16 @@ class SemanticAnalyzer(NodeVisitor[None],
                             module_hidden: bool) -> None:
         """Add an alias to an existing symbol through import."""
         assert not module_hidden or not module_public
-        symbol = SymbolTableNode(node.kind, node.node,
+
+        node_node = node.node
+        if self.is_class_scope() and isinstance(node_node, FuncBase):
+            # Imports inside class scope do not produce methods.
+            # We construct a Var so as to treat them more like assignments.
+            node_node = Var(node_node.name, node_node.type)
+            assert self.type is not None
+            node_node.info = self.type
+
+        symbol = SymbolTableNode(node.kind, node_node,
                                  module_public=module_public,
                                  module_hidden=module_hidden)
         self.add_symbol_table_node(name, symbol, context)

It differs from this PR in that:

  • we don't bind self, which I think is accurate to the runtime
  • we don't modify the original definition
  • it implicitly handles import forms other than ImportFrom
  • it implicitly handles other FuncBases (such as OverloadedFuncDef)

Curious if @A5rocks @JukkaL this looks workable to you?

Edit: I was wrong about self binding. We do bind self if we import a Python function, but I tested with a C import.

@hauntsaninja
Copy link
Collaborator

Just went through issues and updated the PR body with things I think are duplicate reports of this crash. It's quite a lot!

@A5rocks
Copy link
Contributor Author

A5rocks commented Jan 19, 2022

Looks quite good to me (as long as it works); I haven't worked much on this PR as I ran into the mutable problem and couldn't figure it out.

I can push more of my test cases in a bit.

Edit: I must have lost that stash :S

@A5rocks
Copy link
Contributor Author

A5rocks commented Feb 15, 2022

Superseded by #12023

@A5rocks A5rocks closed this Feb 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment