10000 Flush error messages incrementally after processing a file by msullivan · Pull Request #4396 · python/mypy · GitHub
[go: up one dir, main page]

Skip to content

Flush error messages incrementally after processing a file #4396

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

Merged
merged 15 commits into from
Jan 9, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Drop plugin part of the test, do test cleanup
  • Loading branch information
msullivan committed Jan 4, 2018
commit 844d5caab5b54f686e9807c9a826b3f367f7af45
7 changes: 2 additions & 5 deletions mypy/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ def build(sources: List[BuildSource],
bin_dir: Optional[str] = None,
saved_cache: Optional[SavedCache] = None,
flush_errors: Optional[Callable[[List[str], bool], None]] = None,
plugin: Optional[Plugin] = None,
) -> BuildResult:
"""Analyze a program.

Expand All @@ -154,10 +153,9 @@ def build(sources: List[BuildSource],
directories; if omitted, use '.' as the data directory
saved_cache: optional dict with saved cache state for dmypy (read-write!)
flush_errors: optional function to flush errors after a file is processed
plugin: optional plugin that overrides the configured one
"""
try:
return _build(sources, options, alt_lib_path, bin_dir, saved_cache, flush_errors, plugin)
return _build(sources, options, alt_lib_path, bin_dir, saved_cache, flush_errors)
except CompileError as e:
serious = not e.use_stdout
if flush_errors:
Expand All @@ -171,7 +169,6 @@ def _build(sources: List[BuildSource],
bin_dir: Optional[str] = None,
saved_cache: Optional[SavedCache] = None,
flush_errors: Optional[Callable[[List[str], bool], None]] = None,
plugin: Optional[Plugin] = None,
) -> BuildResult:
# This seems the most reasonable place to tune garbage collection.
gc.set_threshold(50000)
Expand Down Expand Up @@ -221,7 +218,7 @@ def _build(sources: List[BuildSource],
reports = Reports(data_dir, options.report_dirs)
source_set = BuildSourceSet(sources)
errors = Errors(options.show_error_context, options.show_column_numbers)
plugin = plugin or load_plugins(options, errors)
plugin = load_plugins(options, errors)

# Construct a build manager object to hold state during the build.
#
Expand Down
31 changes: 3 additions & 28 deletions mypy/test/testerrorstream.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from mypy.build import BuildSource
from mypy.errors import CompileError
from mypy.options import Options
from mypy.plugin import Plugin, ChainedPlugin, DefaultPlugin, FunctionContext
from mypy.nodes import CallExpr, StrExpr
from mypy.types import Type

Expand All @@ -34,22 +33,18 @@ def test_error_stream(testcase: DataDrivenTestCase) -> None:
logged_messages = [] # type: List[str]
real_messages = [] # type: List[str]

def flush_errors(msgs: List[str], serious: bool, is_real: bool = True) -> None:
def flush_errors(msgs: List[str], serious: bool) -> None:
if msgs:
logged_messages.append('==== Errors flushed ====')
logged_messages.extend(msgs)
if is_real:
real_messages.extend(msgs)

plugin = ChainedPlugin(options, [LoggingPlugin(options, flush_errors), DefaultPlugin(options)])
real_messages.extend(msgs)

sources = [BuildSource('main', '__main__', '\n'.join(testcase.input))]
try:
res = build.build(sources=sources,
options=options,
alt_lib_path=test_temp_dir,
flush_errors=flush_errors,
plugin=plugin)
flush_errors=flush_errors)
reported_messages = res.errors
except CompileError as e:
reported_messages = e.messages
Expand All @@ -60,23 +55,3 @@ def flush_errors(msgs: List[str], serious: bool, is_real: bool = True) -> None:
assert_string_arrays_equal(reported_messages, real_messages,
'Streamed/reported mismatch ({}, line {})'.format(
testcase.file, testcase.line))


# Use a typechecking plugin to allow test cases to emit messages
# during typechecking. This allows us to verify that error messages
# from one SCC are printed before later ones are typechecked.
class LoggingPlugin(Plugin):
def __init__(self, options: Options, log: Callable[[List[str], bool, bool], None]) -> None:
super().__init__(options)
self.log = log

def get_function_hook(self, fullname: str) -> Optional[Callable[[FunctionContext], Type]]:
if fullname == 'log.log_checking':
return self.hook
return None

def hook(self, ctx: FunctionContext) -> Type:
assert(isinstance(ctx.context, CallExpr) and len(ctx.context.args) > 0 and
isinstance(ctx.context.args[0], StrExpr))
self.log([ctx.context.args[0].value], False, False)
return ctx.default_return_type
22 changes: 2 additions & 20 deletions test-data/unit/errorstream.test
Original file line number Diff line number Diff line change
@@ -1,36 +1,18 @@
-- Test cases for incremental error streaming. Each test case consists of two
-- sections.
-- The first section contains [case NAME] followed by the input code, while
-- the second section contains [out] followed by the output from the checker.
-- Test cases for incremental error streaming.
-- Each time errors are reported, '==== Errors flushed ====' is printed.
-- The log.log_checking() function will immediately emit a message from
-- a plugin when a call to it is checked, which can be used to verify that
-- error messages are printed before doing later typechecking work.
--
-- The input file name in errors is "file".
--
-- Comments starting with "--" in this file will be ignored, except for lines
-- starting with "----" that are not ignored. The first two dashes of these
-- lines are interpreted as escapes and removed.

[case testErrorStream]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add test case with an import cycle? You can perhaps use "deferred nodes" to interleave messages from two modules (like 'error from module a', 'error from module b', 'error from module a') and then you can test that the errors are grouped by file correctly.

import b
[file log.py]
def log_checking(msg: str) -> None: ...
[file a.py]
1 + ''
[file b.py]
import a
import log
log.log_checking('Checking b') # Make sure that a has been flushed before this is checked
'' / 2
[out]
==== Errors flushed ====
a.py:1: error: Unsupported operand types for + ("int" and "str")
==== Errors flushed ====
Checking b
==== Errors flushed ====
b.py:4: error: Unsupported operand types for / ("str" and "int")
b.py:2: error: Unsupported operand types for / ("str" and "int")

[case testBlockers]
import b
Expand Down
0