Skip to content

Commit 2f86b1c

Browse files
authored
[stubtest] fix duplicate errors with invalid line numbers (#20417)
Closes #15023
1 parent 59257a2 commit 2f86b1c

2 files changed

Lines changed: 40 additions & 0 deletions

File tree

mypy/stubtest.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,18 @@ def _belongs_to_runtime(r: types.ModuleType, attr: str) -> bool:
424424

425425
for entry in sorted(to_check):
426426
stub_entry = stub.names[entry].node if entry in stub.names else MISSING
427+
if entry in stub.names:
428+
if xref := stub.names[entry].cross_ref:
429+
orig_module = xref.rsplit(".", 1)[0]
430+
elif isinstance(stub_entry, nodes.SymbolNode) and (name := stub_entry.fullname):
431+
orig_module = name.rsplit(".", 1)[0]
432+
else:
433+
orig_module = None
434+
435+
if orig_module and orig_module != stub.fullname and orig_module in _all_stubs:
436+
# Skip re-exported names whose defining module will be checked separately.
437+
continue
438+
427439
if isinstance(stub_entry, nodes.MypyFile):
428440
# Don't recursively check exported modules, since that leads to infinite recursion
429441
continue

mypy/test/teststubtest.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import textwrap
1111
import unittest
1212
from collections.abc import Callable, Iterator
13+
from pathlib import Path
1314
from typing import Any
1415

1516
from pytest import raises
@@ -2748,6 +2749,33 @@ def test_output(self) -> None:
27482749
)
27492750
assert output == expected
27502751

2752+
def test_reexport_reports_import_location(self) -> None:
2753+
with use_tmp_dir(TEST_MODULE_NAME) as tmp_dir:
2754+
Path("builtins.pyi").write_text(stubtest_builtins_stub)
2755+
Path("typing.pyi").write_text(stubtest_typing_stub)
2756+
Path("enum.pyi").write_text(stubtest_enum_stub)
2757+
2758+
os.makedirs("test_module", exist_ok=True)
2759+
Path("test_module/__init__.pyi").write_text("from .mod import f")
2760+
Path("test_module/__init__.py").write_text("from .mod import f")
2761+
Path("test_module/mod.pyi").write_text("def f(self) -> None: ...")
2762+
Path("test_module/mod.py").write_text("def f(self, x): pass")
2763+
2764+
output = io.StringIO()
2765+
outerr = io.StringIO()
2766+
with contextlib.redirect_stdout(output), contextlib.redirect_stderr(outerr):
2767+
test_stubs(parse_options([TEST_MODULE_NAME]), use_builtins_fixtures=True)
2768+
2769+
filtered_output = remove_color_code(
2770+
output.getvalue()
2771+
.replace(os.path.realpath(tmp_dir) + os.sep, "")
2772+
.replace(tmp_dir + os.sep, "")
2773+
)
2774+
2775+
assert filtered_output.count('stub does not have parameter "x"') == 1
2776+
assert "__init__.pyi" not in filtered_output
2777+
assert "mod.py:1" in filtered_output
2778+
27512779
def test_ignore_flags(self) -> None:
27522780
output = run_stubtest(
27532781
stub="", runtime="__all__ = ['f']\ndef f(): pass", options=["--ignore-missing-stub"]

0 commit comments

Comments
 (0)