Skip to content

Commit e13315d

Browse files
msullivanJukkaL
authored andcommitted
Recompute lib_path on every fine-grained incremental run (#4981)
This fixes some crashes and false positives when creating or removing __init__.py files that do not appear in the build. This just factors out the existing implementation of lib_path computation and reuses it. It is somewhat inefficient and should be improved on if we are going to call it on every fine-grained update.
1 parent bece3c4 commit e13315d

File tree

5 files changed

+61
-19
lines changed

5 files changed

+61
-19
lines changed

mypy/build.py

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -195,19 +195,11 @@ def default_flush_errors(new_messages: List[str], is_serious: bool) -> None:
195195
raise
196196

197197

198-
def _build(sources: List[BuildSource],
199-
options: Options,
200-
alt_lib_path: Optional[str],
201-
bin_dir: Optional[str],
202-
flush_errors: Callable[[List[str], bool], None],
203-
fscache: Optional[FileSystemCache],
204-
) -> BuildResult:
205-
# This seems the most reasonable place to tune garbage collection.
206-
gc.set_threshold(50000)
207-
208-
data_dir = default_data_dir(bin_dir)
209-
fscache = fscache or FileSystemCache()
210-
198+
def compute_lib_path(sources: List[BuildSource],
199+
options: Options,
200+
alt_lib_path: Optional[str],
201+
data_dir: str,
202+
fscache: FileSystemCache) -> List[str]:
211203
# Determine the default module search path.
212204
lib_path = default_lib_path(data_dir,
213205
options.python_version,
@@ -219,7 +211,9 @@ def _build(sources: List[BuildSource],
219211
# as in the source tree.
220212
root_dir = dirname(dirname(__file__))
221213
lib_path.insert(0, os.path.join(root_dir, 'test-data', 'unit', 'lib-stub'))
222-
else:
214+
# alt_lib_path is used by some tests to bypass the normal lib_path mechanics.
215+
# If we don't have one, grab directories of source files.
216+
if not alt_lib_path:
223217
for source in sources:
224218
if source.path:
225219
# Include directory of the program file in the module search path.
@@ -246,6 +240,24 @@ def _build(sources: List[BuildSource],
246240
if alt_lib_path:
247241
lib_path.insert(0, alt_lib_path)
248242

243+
return lib_path
244+
245+
246+
def _build(sources: List[BuildSource],
247+
options: Options,
248+
alt_lib_path: Optional[str],
249+
bin_dir: Optional[str],
250+
flush_errors: Callable[[List[str], bool], None],
251+
fscache: Optional[FileSystemCache],
252+
) -> BuildResult:
253+
# This seems the most reasonable place to tune garbage collection.
254+
gc.set_threshold(50000)
255+
256+
data_dir = default_data_dir(bin_dir)
257+
fscache = fscache or FileSystemCache()
258+
259+
lib_path = compute_lib_path(sources, options, alt_lib_path, data_dir, fscache)
260+
249261
reports = Reports(data_dir, options.report_dirs)
250262
source_set = BuildSourceSet(sources)
251263
errors = Errors(options.show_error_context, options.show_column_numbers)

mypy/dmypy_server.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,14 +309,19 @@ def initialize_fine_grained(self, sources: List[mypy.build.BuildSource]) -> Dict
309309

310310
def fine_grained_increment(self, sources: List[mypy.build.BuildSource]) -> Dict[str, Any]:
311311
assert self.fine_grained_manager is not None
312+
manager = self.fine_grained_manager.manager
312313

313314
t0 = time.time()
314315
self.update_sources(sources)
315316
changed, removed = self.find_changed(sources)
317+
# Update the lib_path, which can change when sources do.
318+
# TODO: This is slow.
319+
manager.lib_path = tuple(mypy.build.compute_lib_path(
320+
sources, manager.options, self.alt_lib_path, manager.data_dir, self.fscache))
316321
t1 = time.time()
317322
messages = self.fine_grained_manager.update(changed, removed)
318323
t2 = time.time()
319-
self.fine_grained_manager.manager.log(
324+
manager.log(
320325
"fine-grained increment: find_changed: {:.3f}s, update: {:.3f}s".format(
321326
t1 - t0, t2 - t1))
322327
status = 1 if messages else 0

mypy/test/testerrorstream.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ def flush_errors(msgs: List[str], serious: bool) -> None:
4040
try:
4141
build.build(sources=sources,
4242
options=options,
43-
alt_lib_path=test_temp_dir,
4443
flush_errors=flush_errors)
4544
except CompileError as e:
4645
assert e.messages == []

mypy/test/testfinegrained.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
66
See the comment at the top of test-data/unit/fine-grained.test for more
77
information.
8+
9+
N.B.: Unlike most of the other test suites, testfinegrained does not
10+
rely on an alt_lib_path for finding source files. This means that they
11+
can test interactions with the lib_path that is built implicitly based
12+
on specified sources.
813
"""
914

1015
import os
@@ -84,7 +89,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None:
8489
config = None
8590
if config:
8691
parse_config_file(options, config)
87-
server = Server(options, alt_lib_path=test_temp_dir)
92+
server = Server(options)
8893

8994
step = 1
9095
sources = self.parse_sources(main_src, step, options)
@@ -186,8 +191,7 @@ def build(self,
186191
sources: List[BuildSource]) -> List[str]:
187192
try:
188193
result = build.build(sources=sources,
189-
options=options,
190-
alt_lib_path=test_temp_dir)
194+
options=options)
191195
except CompileError as e:
192196
return e.messages
193197
return result.errors

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,28 @@ def f(x: int) -> None: pass
484484
[out]
485485
==
486486

487+
[case testDeleteSubpackageInit1]
488+
# cmd: mypy q/r/s.py
489+
# flags: --follow-imports=skip --ignore-missing-imports
490+
[file q/__init__.py]
491+
[file q/r/__init__.py]
492+
[file q/r/s.py]
493+
[delete q/__init__.py.2]
494+
[out]
495+
==
496+
497+
[case testAddSubpackageInit2]
498+
# cmd: mypy q/r/s.py
499+
# flags: --follow-imports=skip --ignore-missing-imports
500+
[file q/r/__init__.py]
501+
[file q/r/s.py]
502+
1
503+
[file q/r/s.py.2]
504+
2
505+
[file q/__init__.py.2]
506+
[out]
507+
==
508+
487509
[case testModifyTwoFilesNoError2]
488510
import a
489511
[file a.py]

0 commit comments

Comments
 (0)