Skip to content

Commit 4e30e89

Browse files
authored
Fix dmypy inspect for namespace packages (#16357)
Fixes #15781 The fix is to switch to already resolved paths instead of relying on `crawl_up()`. This should be more robust w.r.t. various special cases. I also tweak the tests slightly to show full file names, to have a more consistent output.
1 parent b8c748a commit 4e30e89

File tree

5 files changed

+65
-55
lines changed

5 files changed

+65
-55
lines changed

mypy/dmypy_server.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,7 @@ def initialize_fine_grained(
461461
messages = result.errors
462462
self.fine_grained_manager = FineGrainedBuildManager(result)
463463

464+
original_sources_len = len(sources)
464465
if self.following_imports():
465466
sources = find_all_sources_in_build(self.fine_grained_manager.graph, sources)
466467
self.update_sources(sources)
@@ -525,7 +526,8 @@ def initialize_fine_grained(
525526

526527
__, n_notes, __ = count_stats(messages)
527528
status = 1 if messages and n_notes < len(messages) else 0
528-
messages = self.pretty_messages(messages, len(sources), is_tty, terminal_width)
529+
# We use explicit sources length to match the logic in non-incremental mode.
530+
messages = self.pretty_messages(messages, original_sources_len, is_tty, terminal_width)
529531
return {"out": "".join(s + "\n" for s in messages), "err": "", "status": status}
530532

531533
def fine_grained_increment(

mypy/inspections.py

+5-11
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from typing import Callable
77

88
from mypy.build import State
9-
from mypy.find_sources import InvalidSourceList, SourceFinder
109
from mypy.messages import format_type
1110
from mypy.modulefinder import PYTHON_EXTENSIONS
1211
from mypy.nodes import (
@@ -206,9 +205,6 @@ def __init__(
206205
force_reload: bool = False,
207206
) -> None:
208207
self.fg_manager = fg_manager
209-
self.finder = SourceFinder(
210-
self.fg_manager.manager.fscache, self.fg_manager.manager.options
211-
)
212208
self.verbosity = verbosity
213209
self.limit = limit
214210
self.include_span = include_span
@@ -561,16 +557,14 @@ def find_module(self, file: str) -> tuple[State | None, dict[str, object]]:
561557
if not any(file.endswith(ext) for ext in PYTHON_EXTENSIONS):
562558
return None, {"error": "Source file is not a Python file"}
563559

564-
try:
565-
module, _ = self.finder.crawl_up(os.path.normpath(file))
566-
except InvalidSourceList:
567-
return None, {"error": "Invalid source file name: " + file}
568-
569-
state = self.fg_manager.graph.get(module)
560+
# We are using a bit slower but robust way to find a module by path,
561+
# to be sure that namespace packages are handled properly.
562+
abs_path = os.path.abspath(file)
563+
state = next((s for s in self.fg_manager.graph.values() if s.abspath == abs_path), None)
570564
self.module = state
571565
return (
572566
state,
573-
{"out": f"Unknown module: {module}", "err": "", "status": 1} if state is None else {},
567+
{"out": f"Unknown module: {file}", "err": "", "status": 1} if state is None else {},
574568
)
575569

576570
def run_inspection(

mypy/test/testfinegrained.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ def maybe_inspect(self, step: int, server: Server, src: str) -> list[str]:
352352
)
353353
val = res["error"] if "error" in res else res["out"] + res["err"]
354354
output.extend(val.strip().split("\n"))
355-
return normalize_messages(output)
355+
return output
356356

357357
def get_suggest(self, program_text: str, incremental_step: int) -> list[tuple[str, str]]:
358358
step_bit = "1?" if incremental_step == 1 else str(incremental_step)

test-data/unit/daemon.test

+16-2
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ $ dmypy inspect foo.pyc:1:1:2:2
379379
Source file is not a Python file
380380
== Return code: 2
381381
$ dmypy inspect bar/baz.py:1:1:2:2
382-
Unknown module: baz
382+
Unknown module: bar/baz.py
383383
== Return code: 1
384384
$ dmypy inspect foo.py:3:1:1:1
385385
"end_line" must not be before "line"
@@ -434,7 +434,7 @@ $ dmypy inspect foo.pyc:1:2
434434
Source file is not a Python file
435435
== Return code: 2
436436
$ dmypy inspect bar/baz.py:1:2
437-
Unknown module: baz
437+
Unknown module: bar/baz.py
438438
== Return code: 1
439439
$ dmypy inspect foo.py:7:5 --include-span
440440
7:5:7:5 -> "int"
@@ -571,3 +571,17 @@ class A:
571571
x: int
572572
class B:
573573
x: int
574+
575+
[case testDaemonInspectSelectCorrectFile]
576+
$ dmypy run test.py --export-types
577+
Daemon started
578+
Success: no issues found in 1 source file
579+
$ dmypy inspect demo/test.py:1:1
580+
"int"
581+
$ dmypy inspect test.py:1:1
582+
"str"
583+
[file test.py]
584+
b: str
585+
from demo.test import a
586+
[file demo/test.py]
587+
a: int

test-data/unit/fine-grained-inspect.test

+40-40
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
[case testInspectTypeBasic]
2-
# inspect2: --include-kind foo.py:10:13
3-
# inspect2: --show=type --include-kind foo.py:10:13
4-
# inspect2: --include-span -vv foo.py:12:5
5-
# inspect2: --include-span --include-kind foo.py:12:5:12:9
2+
# inspect2: --include-kind tmp/foo.py:10:13
3+
# inspect2: --show=type --include-kind tmp/foo.py:10:13
4+
# inspect2: --include-span -vv tmp/foo.py:12:5
5+
# inspect2: --include-span --include-kind tmp/foo.py:12:5:12:9
66
import foo
77
[file foo.py]
88
from typing import TypeVar, Generic
@@ -29,10 +29,10 @@ MemberExpr -> "T"
2929
CallExpr:12:5:12:9 -> "C[int]"
3030

3131
[case testInspectAttrsBasic]
32-
# inspect2: --show=attrs foo.py:6:1
33-
# inspect2: --show=attrs foo.py:7:1
34-
# inspect2: --show=attrs foo.py:10:1
35-
# inspect2: --show=attrs --include-object-attrs foo.py:10:1
32+
# inspect2: --show=attrs tmp/foo.py:6:1
33+
# inspect2: --show=attrs tmp/foo.py:7:1
34+
# inspect2: --show=attrs tmp/foo.py:10:1
35+
# inspect2: --show=attrs --include-object-attrs tmp/foo.py:10:1
3636
import foo
3737
[file foo.py]
3838
from bar import Meta
@@ -56,12 +56,12 @@ class Meta(type):
5656
{"function": ["__name__"], "object": ["__init__"]}
5757

5858
[case testInspectDefBasic]
59-
# inspect2: --show=definition foo.py:5:5
60-
# inspect2: --show=definition --include-kind foo.py:6:3
61-
# inspect2: --show=definition --include-span foo.py:7:5
62-
# inspect2: --show=definition foo.py:8:1:8:4
63-
# inspect2: --show=definition foo.py:8:6:8:8
64-
# inspect2: --show=definition foo.py:9:3
59+
# inspect2: --show=definition tmp/foo.py:5:5
60+
# inspect2: --show=definition --include-kind tmp/foo.py:6:3
61+
# inspect2: --show=definition --include-span tmp/foo.py:7:5
62+
# inspect2: --show=definition tmp/foo.py:8:1:8:4
63+
# inspect2: --show=definition tmp/foo.py:8:6:8:8
64+
# inspect2: --show=definition tmp/foo.py:9:3
6565
import foo
6666
[file foo.py]
6767
from bar import var, test, A
@@ -95,18 +95,18 @@ def foo(x: Union[int, str]) -> None:
9595
[builtins fixtures/classmethod.pyi]
9696
[out]
9797
==
98-
bar.py:4:0:meth
98+
tmp/bar.py:4:0:meth
9999
MemberExpr -> tmp/bar.py:2:5:x
100100
7:1:7:5 -> tmp/bar.py:6:9:y
101-
bar.py:9:1:test
102-
bar.py:8:1:var
103-
baz.py:3:2:foo
101+
tmp/bar.py:9:1:test
102+
tmp/bar.py:8:1:var
103+
tmp/baz.py:3:2:foo
104104

105105
[case testInspectFallbackAttributes]
106-
# inspect2: --show=attrs --include-object-attrs foo.py:5:1
107-
# inspect2: --show=attrs foo.py:8:1
108-
# inspect2: --show=attrs --include-kind foo.py:10:1
109-
# inspect2: --show=attrs --include-kind --include-object-attrs foo.py:10:1
106+
# inspect2: --show=attrs --include-object-attrs tmp/foo.py:5:1
107+
# inspect2: --show=attrs tmp/foo.py:8:1
108+
# inspect2: --show=attrs --include-kind tmp/foo.py:10:1
109+
# inspect2: --show=attrs --include-kind --include-object-attrs tmp/foo.py:10:1
110110
import foo
111111
[file foo.py]
112112
class B: ...
@@ -128,7 +128,7 @@ NameExpr -> {}
128128
NameExpr -> {"object": ["__eq__", "__init__", "__ne__"]}
129129

130130
[case testInspectTypeVarBoundAttrs]
131-
# inspect2: --show=attrs foo.py:8:13
131+
# inspect2: --show=attrs tmp/foo.py:8:13
132132
import foo
133133
[file foo.py]
134134
from typing import TypeVar
@@ -144,10 +144,10 @@ def foo(arg: T) -> T:
144144
{"C": ["x"]}
145145

146146
[case testInspectTypeVarValuesAttrs]
147-
# inspect2: --show=attrs --force-reload foo.py:13:13
148-
# inspect2: --show=attrs --force-reload --union-attrs foo.py:13:13
149-
# inspect2: --show=attrs foo.py:16:5
150-
# inspect2: --show=attrs --union-attrs foo.py:16:5
147+
# inspect2: --show=attrs --force-reload tmp/foo.py:13:13
148+
# inspect2: --show=attrs --force-reload --union-attrs tmp/foo.py:13:13
149+
# inspect2: --show=attrs tmp/foo.py:16:5
150+
# inspect2: --show=attrs --union-attrs tmp/foo.py:16:5
151151
import foo
152152
[file foo.py]
153153
from typing import TypeVar, Generic
@@ -174,8 +174,8 @@ class C(Generic[T]):
174174
{"A": ["x", "z"], "B": ["y", "z"]}
175175

176176
[case testInspectTypeVarBoundDef]
177-
# inspect2: --show=definition foo.py:9:13
178-
# inspect2: --show=definition foo.py:8:9
177+
# inspect2: --show=definition tmp/foo.py:9:13
178+
# inspect2: --show=definition tmp/foo.py:8:9
179179
import foo
180180
[file foo.py]
181181
from typing import TypeVar
@@ -189,13 +189,13 @@ def foo(arg: T) -> T:
189189
return arg
190190
[out]
191191
==
192-
foo.py:7:9:arg
193-
foo.py:4:5:x
192+
tmp/foo.py:7:9:arg
193+
tmp/foo.py:4:5:x
194194

195195
[case testInspectTypeVarValuesDef]
196-
# inspect2: --show=definition --force-reload foo.py:13:9
197-
# inspect2: --show=definition --force-reload foo.py:14:13
198-
# inspect2: --show=definition foo.py:18:7
196+
# inspect2: --show=definition --force-reload tmp/foo.py:13:9
197+
# inspect2: --show=definition --force-reload tmp/foo.py:14:13
198+
# inspect2: --show=definition tmp/foo.py:18:7
199199
import foo
200200
[file foo.py]
201201
from typing import TypeVar, Generic
@@ -218,12 +218,12 @@ class C(Generic[T]):
218218
x.z
219219
[out]
220220
==
221-
foo.py:5:5:z, tmp/foo.py:9:5:z
222-
foo.py:12:9:arg
223-
foo.py:5:5:z, tmp/foo.py:9:5:z
221+
tmp/foo.py:5:5:z, tmp/foo.py:9:5:z
222+
tmp/foo.py:12:9:arg
223+
tmp/foo.py:5:5:z, tmp/foo.py:9:5:z
224224

225225
[case testInspectModuleAttrs]
226-
# inspect2: --show=attrs foo.py:2:1
226+
# inspect2: --show=attrs tmp/foo.py:2:1
227227
import foo
228228
[file foo.py]
229229
from pack import bar
@@ -239,7 +239,7 @@ class C: ...
239239
{"<pack.bar>": ["C", "__annotations__", "__doc__", "__file__", "__name__", "__package__", "bar", "x"], "ModuleType": ["__file__", "__getattr__"]}
240240

241241
[case testInspectModuleDef]
242-
# inspect2: --show=definition --include-kind foo.py:2:1
242+
# inspect2: --show=definition --include-kind tmp/foo.py:2:1
243243
import foo
244244
[file foo.py]
245245
from pack import bar
@@ -255,7 +255,7 @@ NameExpr -> tmp/pack/bar.py:1:1:bar
255255
MemberExpr -> tmp/pack/bar.py:3:5:x
256256

257257
[case testInspectFunctionArgDef]
258-
# inspect2: --show=definition --include-span foo.py:4:13
258+
# inspect2: --show=definition --include-span tmp/foo.py:4:13
259259
# TODO: for now all arguments have line/column set to function definition.
260260
import foo
261261
[file foo.py]

0 commit comments

Comments
 (0)