Skip to content

Commit 97de77b

Browse files
authored
Improvements to blocker handling in fine-grained incremental mode, etc. (#4292)
Handle various additional cases of blocking errors. This also contains a fair amount of refactoring and minor additional fixes.
1 parent f07423c commit 97de77b

13 files changed

+486
-99
lines changed

mypy/build.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ def __init__(self, path: Optional[str], module: Optional[str],
9090
self.module = module or '__main__'
9191
self.text = text
9292

93+
def __repr__(self) -> str:
94+
return '<BuildSource path=%r module=%r has_text=%s>' % (self.path,
95+
self.module,
96+
self.text is not None)
97+
9398

9499
class BuildSourceSet:
95100
"""Efficiently test a file's membership in the set of build sources."""
@@ -633,7 +638,7 @@ def parse_file(self, id: str, path: str, source: str, ignore_errors: bool) -> My
633638
Raise CompileError if there is a parse error.
634639
"""
635640
num_errs = self.errors.num_messages()
636-
tree = parse(source, path, self.errors, options=self.options)
641+
tree = parse(source, path, id, self.errors, options=self.options)
637642
tree._fullname = id
638643
self.add_stats(files_parsed=1,
639644
modules_parsed=int(not tree.is_stub),
@@ -2070,7 +2075,11 @@ def dump_graph(graph: Graph) -> None:
20702075

20712076

20722077
def load_graph(sources: List[BuildSource], manager: BuildManager) -> Graph:
2073-
"""Given some source files, load the full dependency graph."""
2078+
"""Given some source files, load the full dependency graph.
2079+
2080+
As this may need to parse files, this can raise CompileError in case
2081+
there are syntax errors.
2082+
"""
20742083
graph = {} # type: Graph
20752084
# The deque is used to implement breadth-first traversal.
20762085
# TODO: Consider whether to go depth-first instead. This may

mypy/errors.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,13 @@ def is_blockers(self) -> bool:
331331
"""Are the any errors that are blockers?"""
332332
return any(err for err in self.error_info if err.blocker)
333333

334+
def blocker_module(self) -> Optional[str]:
335+
"""Return the module with a blocking error, or None if not possible."""
336+
for err in self.error_info:
337+
if err.blocker:
338+
return err.module
339+
return None
340+
334341
def is_errors_for_file(self, file: str) -> bool:
335342
"""Are there any errors for the given file?"""
336343
return file in self.error_files
@@ -340,7 +347,9 @@ def raise_error(self) -> None:
340347
341348
Render the messages suitable for displaying.
342349
"""
343-
raise CompileError(self.messages(), use_stdout=True)
350+
raise CompileError(self.messages(),
351+
use_stdout=True,
352+
module_with_blocker=self.blocker_module())
344353

345354
def messages(self) -> List[str]:
346355
"""Return a string list that represents the error messages.
@@ -506,11 +515,17 @@ class CompileError(Exception):
506515

507516
messages = None # type: List[str]
508517
use_stdout = False
518+
# Can be set in case there was a module with a blocking error
519+
module_with_blocker = None # type: Optional[str]
509520

510-
def __init__(self, messages: List[str], use_stdout: bool = False) -> None:
521+
def __init__(self,
522+
messages: List[str],
523+
use_stdout: bool = False,
524+
module_with_blocker: Optional[str] = None) -> None:
511525
super().__init__('\n'.join(messages))
512526
self.messages = messages
513527
self.use_stdout = use_stdout
528+
self.module_with_blocker = module_with_blocker
514529

515530

516531
class DecodeError(Exception):

mypy/fastparse.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868

6969
def parse(source: Union[str, bytes],
7070
fnam: str,
71+
module: Optional[str],
7172
errors: Optional[Errors] = None,
7273
options: Options = Options()) -> MypyFile:
7374

@@ -80,7 +81,7 @@ def parse(source: Union[str, bytes],
8081
if errors is None:
8182
errors = Errors()
8283
raise_on_error = True
83-
errors.set_file(fnam, None)
84+
errors.set_file(fnam, module)
8485
is_stub_file = fnam.endswith('.pyi')
8586
try:
8687
if is_stub_file:
@@ -97,7 +98,7 @@ def parse(source: Union[str, bytes],
9798
tree.path = fnam
9899
tree.is_stub = is_stub_file
99100
except SyntaxError as e:
100-
errors.report(e.lineno, e.offset, e.msg)
101+
errors.report(e.lineno, e.offset, e.msg, blocker=True)
101102
tree = MypyFile([], [], False, set())
102103

103104
if raise_on_error and errors.is_errors():
@@ -111,7 +112,7 @@ def parse_type_comment(type_comment: str, line: int, errors: Optional[Errors]) -
111112
typ = ast3.parse(type_comment, '<type_comment>', 'eval')
112113
except SyntaxError as e:
113114
if errors is not None:
114-
errors.report(line, e.offset, TYPE_COMMENT_SYNTAX_ERROR)
115+
errors.report(line, e.offset, TYPE_COMMENT_SYNTAX_ERROR, blocker=True)
115116
return None
116117
else:
117118
raise
@@ -158,7 +159,7 @@ def __init__(self,
158159
self.errors = errors
159160

160161
def fail(self, msg: str, line: int, column: int) -> None:
161-
self.errors.report(line, column, msg)
162+
self.errors.report(line, column, msg, blocker=True)
162163

163164
def generic_visit(self, node: ast3.AST) -> None:
164165
raise RuntimeError('AST node not implemented: ' + str(type(node)))
@@ -1013,7 +1014,7 @@ def parent(self) -> Optional[ast3.AST]:
10131014

10141015
def fail(self, msg: str, line: int, column: int) -> None:
10151016
if self.errors:
1016-
self.errors.report(line, column, msg)
1017+
self.errors.report(line, column, msg, blocker=True)
10171018

10181019
def visit_raw_str(self, s: str) -> Type:
10191020
# An escape hatch that allows the AST walker in fastparse2 to

mypy/fastparse2.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979

8080
def parse(source: Union[str, bytes],
8181
fnam: str,
82+
module: Optional[str],
8283
errors: Optional[Errors] = None,
8384
options: Options = Options()) -> MypyFile:
8485
"""Parse a source file, without doing any semantic analysis.
@@ -90,7 +91,7 @@ def parse(source: Union[str, bytes],
9091
if errors is None:
9192
errors = Errors()
9293
raise_on_error = True
93-
errors.set_file(fnam, None)
94+
errors.set_file(fnam, module)
9495
is_stub_file = fnam.endswith('.pyi')
9596
try:
9697
assert options.python_version[0] < 3 and not is_stub_file
@@ -103,7 +104,7 @@ def parse(source: Union[str, bytes],
103104
tree.path = fnam
104105
tree.is_stub = is_stub_file
105106
except SyntaxError as e:
106-
errors.report(e.lineno, e.offset, e.msg)
107+
errors.report(e.lineno, e.offset, e.msg, blocker=True)
107108
tree = MypyFile([], [], False, set())
108109

109110
if raise_on_error and errors.is_errors():
@@ -150,7 +151,7 @@ def __init__(self,
150151
self.errors = errors
151152

152153
def fail(self, msg: str, line: int, column: int) -> None:
153-
self.errors.report(line, column, msg)
154+
self.errors.report(line, column, msg, blocker=True)
154155

155156
def generic_visit(self, node: ast27.AST) -> None:
156157
raise RuntimeError('AST node not implemented: ' + str(type(node)))

mypy/parse.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
def parse(source: Union[str, bytes],
99
fnam: str,
10+
module: Optional[str],
1011
errors: Optional[Errors],
1112
options: Options) -> MypyFile:
1213
"""Parse a source file, without doing any semantic analysis.
@@ -21,11 +22,13 @@ def parse(source: Union[str, bytes],
2122
import mypy.fastparse
2223
return mypy.fastparse.parse(source,
2324
fnam=fnam,
25+
module=module,
2426
errors=errors,
2527
options=options)
2628
else:
2729
import mypy.fastparse2
2830
return mypy.fastparse2.parse(source,
2931
fnam=fnam,
32+
module=module,
3033
errors=errors,
3134
options=options)

0 commit comments

Comments
 (0)