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

Make --no-implicit-reexport friendlier #13965

Closed
hauntsaninja opened this issue Oct 30, 2022 · 4 comments · Fixed by #16129
Closed

Make --no-implicit-reexport friendlier #13965

hauntsaninja opened this issue Oct 30, 2022 · 4 comments · Fixed by #16129
Labels

Comments

@hauntsaninja
Copy link
Collaborator

hauntsaninja commented Oct 30, 2022

Currently, users have to pay double jeopardy if they rely on an implicit reexport. First, they get an error "module does not explicitly export attribute [attr-defined]", but then the type of the thing also becomes Any.

I think it would be friendlier to have mypy just use the type of the hidden thing. This is useful in cases where you can't change the module you're importing from. In some cases, you can have a per-module implicit_reexport config (although I'd argue most users would struggle to find that solution), but in some "consenting adults" situations this isn't quite what you want.

Here's what the patch could look like for the semanal portion of this (edit: merged!). We'd also need to modify attribute access logic.

diff --git a/mypy/semanal.py b/mypy/semanal.py
index b37c9b2a5..06d648a16 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -2242,10 +2242,20 @@ class SemanticAnalyzer(
                     )
                     continue
 
-            if node and not node.module_hidden:
+            if node:
                 self.process_imported_symbol(
                     node, module_id, id, imported_id, fullname, module_public, context=imp
                 )
+                if node.module_hidden:
+                    self.report_missing_module_attribute(
+                        module_id,
+                        id,
+                        imported_id,
+                        module_public=module_public,
+                        module_hidden=not module_public,
+                        context=imp,
+                        add_unknown_imported_symbol=False,
+                    )
             elif module and not missing_submodule:
                 # Target module exists but the imported name is missing or hidden.
                 self.report_missing_module_attribute(
@@ -2333,6 +2343,7 @@ class SemanticAnalyzer(
         module_public: bool,
         module_hidden: bool,
         context: Node,
+        add_unknown_imported_symbol: bool = True,
     ) -> None:
         # Missing attribute.
         if self.is_incomplete_namespace(import_id):
@@ -2357,13 +2368,14 @@ class SemanticAnalyzer(
                     suggestion = f"; maybe {pretty_seq(matches, 'or')}?"
                     message += f"{suggestion}"
         self.fail(message, context, code=codes.ATTR_DEFINED)
-        self.add_unknown_imported_symbol(
-            imported_id,
-            context,
-            target_name=None,
-            module_public=module_public,
-            module_hidden=not module_public,
-        )
+        if add_unknown_imported_symbol:
+            self.add_unknown_imported_symbol(
+                imported_id,
+                context,
+                target_name=None,
+                module_public=module_public,
+                module_hidden=not module_public,
+            )
 
         if import_id == "typing":
             # The user probably has a missing definition in a test fixture. Let's verify.
diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test
index 5a075dd6e..9beffd2f9 100644
--- a/test-data/unit/check-flags.test
+++ b/test-data/unit/check-flags.test
@@ -1606,14 +1606,18 @@ strict_equality = false
 
 
 [case testNoImplicitReexport]
-# flags: --no-implicit-reexport
-from other_module_2 import a
+# flags: --no-implicit-reexport --show-error-codes
+from other_module_2 import a  # E: Module "other_module_2" does not explicitly export attribute "a"  [attr-defined]
+reveal_type(a)  # N: Revealed type is "builtins.int"
+
+import other_module_2
+reveal_type(other_module_2.a)  # E: "object" does not explicitly export attribute "a"  [attr-defined] \
+                               # N: Revealed type is "builtins.int"
+
 [file other_module_1.py]
 a = 5
 [file other_module_2.py]
 from other_module_1 import a
-[out]
-main:2: error: Module "other_module_2" does not explicitly export attribute "a"
 
 [case testNoImplicitReexportRespectsAll]
 # flags: --no-implicit-reexport
@erictraut
Copy link

This is the same thought process we went through in pyright. In earlier versions, all implicitly re-exported symbols from a "py.typed" library evaluated to Unknown (which is what pyright calls an implicit Any). Pyright users and library authors complained about this behavior, so we modified it to work the way that you're proposing above. The imported type is now resolved to its hidden type, but an error is emitted indicating that it is a private symbol.

@JelleZijlstra
Copy link
Member

#13933 is a good example of the perils of the current behavior. I agree with your suggestion.

hauntsaninja added a commit to hauntsaninja/mypy that referenced this issue Oct 31, 2022
Fixes some of python#13965

We also need to modify attribute access logic (this is the TODO in the
PR), which is a little trickier. But cases like python#13933 convinced me it's
worth making this change, even before I get around to figuring that out.
JukkaL pushed a commit that referenced this issue Oct 31, 2022
Fixes some of #13965, fixes #12749

We also need to modify attribute access logic (this is the TODO in the
PR), which is a little trickier. But cases like #13933 convinced me it's
worth making this change, even before I get around to figuring that out.
@emirkmo
Copy link

emirkmo commented Nov 16, 2022

Out of curiosity, is it a recent behavior that --implicit-reexport does not work on from .<file> import <attr>. It says not explictly exported even though I would low to ignore that.

@hauntsaninja
Copy link
Collaborator Author

@emirkmo please file a separate issue with details about what you're doing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants