Skip to content

Commit

Permalink
Show error codes by default (#13542)
Browse files Browse the repository at this point in the history
Part of the effort to nudge users into using specific type ignores. See
#13541

This PR enables `show-error-codes` by default. Without knowing the code,
users will always choose to use a bare `type: ignore` instead.
  • Loading branch information
cdce8p authored Sep 27, 2022
1 parent 00d547f commit da43c91
Show file tree
Hide file tree
Showing 27 changed files with 68 additions and 39 deletions.
4 changes: 2 additions & 2 deletions docs/source/command_line.rst
Original file line number Diff line number Diff line change
Expand Up @@ -691,9 +691,9 @@ in error messages.
``file:line:column:end_line:end_column``. This option implies
``--show-column-numbers``.

.. option:: --show-error-codes
.. option:: --hide-error-codes

This flag will add an error code ``[<code>]`` to error messages. The error
This flag will hide the error code ``[<code>]`` from error messages. By default, the error
code is shown after each error message::

prog.py:1: error: "str" has no attribute "trim" [attr-defined]
Expand Down
4 changes: 2 additions & 2 deletions docs/source/config_file.rst
Original file line number Diff line number Diff line change
Expand Up @@ -717,12 +717,12 @@ These options may only be set in the global section (``[mypy]``).

Shows column numbers in error messages.

.. confval:: show_error_codes
.. confval:: hide_error_codes

:type: boolean
:default: False

Shows error codes in error messages. See :ref:`error-codes` for more information.
Hides error codes in error messages. See :ref:`error-codes` for more information.

.. confval:: pretty

Expand Down
6 changes: 3 additions & 3 deletions docs/source/error_codes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ Error codes may change in future mypy releases.
Displaying error codes
----------------------

Error codes are not displayed by default. Use :option:`--show-error-codes <mypy --show-error-codes>`
or config ``show_error_codes = True`` to display error codes. Error codes are shown inside square brackets:
Error codes are displayed by default. Use :option:`--hide-error-codes <mypy --hide-error-codes>`
or config ``hide_error_codes = True`` to hide error codes. Error codes are shown inside square brackets:

.. code-block:: text
$ mypy --show-error-codes prog.py
$ mypy prog.py
prog.py:1: error: "str" has no attribute "trim" [attr-defined]
It's also possible to require error codes for ``type: ignore`` comments.
Expand Down
3 changes: 1 addition & 2 deletions docs/source/type_inference_and_annotations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,7 @@ short explanation of the bug. To do that, use this format:
app.run(8000) # type: ignore # `run()` in v2.0 accepts an `int`, as a port
Mypy displays an error code for each error if you use
:option:`--show-error-codes <mypy --show-error-codes>`:
By default, mypy displays an error code for each error:

.. code-block:: text
Expand Down
2 changes: 1 addition & 1 deletion mypy/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ def _build(
errors = Errors(
options.show_error_context,
options.show_column_numbers,
options.show_error_codes,
options.hide_error_codes,
options.pretty,
options.show_error_end,
lambda path: read_py_file(path, cached_read),
Expand Down
3 changes: 3 additions & 0 deletions mypy/config_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,9 @@ def parse_section(
elif key.startswith("disallow") and hasattr(template, key[3:]):
options_key = key[3:]
invert = True
elif key.startswith("show_") and hasattr(template, "hide_" + key[5:]):
options_key = "hide_" + key[5:]
invert = True
elif key == "strict":
pass # Special handling below
else:
Expand Down
2 changes: 1 addition & 1 deletion mypy/dmypy_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ def __init__(self, options: Options, status_file: str, timeout: int | None = Non

# Since the object is created in the parent process we can check
# the output terminal options here.
self.formatter = FancyFormatter(sys.stdout, sys.stderr, options.show_error_codes)
self.formatter = FancyFormatter(sys.stdout, sys.stderr, options.hide_error_codes)

def _response_metadata(self) -> dict[str, str]:
py_version = f"{self.options.python_version[0]}_{self.options.python_version[1]}"
Expand Down
6 changes: 3 additions & 3 deletions mypy/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ def __init__(
self,
show_error_context: bool = False,
show_column_numbers: bool = False,
show_error_codes: bool = False,
hide_error_codes: bool = False,
pretty: bool = False,
show_error_end: bool = False,
read_source: Callable[[str], list[str] | None] | None = None,
Expand All @@ -267,7 +267,7 @@ def __init__(
) -> None:
self.show_error_context = show_error_context
self.show_column_numbers = show_column_numbers
self.show_error_codes = show_error_codes
self.hide_error_codes = hide_error_codes
self.show_absolute_path = show_absolute_path
self.pretty = pretty
self.show_error_end = show_error_end
Expand Down Expand Up @@ -782,7 +782,7 @@ def format_messages(
s = f"{srcloc}: {severity}: {message}"
else:
s = message
if self.show_error_codes and code and severity != "note":
if not self.hide_error_codes and code and severity != "note":
# If note has an error code, it is related to a previous error. Avoid
# displaying duplicate error codes.
s = f"{s} [{code.code}]"
Expand Down
6 changes: 3 additions & 3 deletions mypy/fastparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,11 +258,11 @@ def parse(
on failure. Otherwise, use the errors object to report parse errors.
"""
raise_on_error = False
if errors is None:
errors = Errors()
raise_on_error = True
if options is None:
options = Options()
if errors is None:
errors = Errors(hide_error_codes=options.hide_error_codes)
raise_on_error = True
errors.set_file(fnam, module, options=options)
is_stub_file = fnam.endswith(".pyi")
if is_stub_file:
Expand Down
8 changes: 4 additions & 4 deletions mypy/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def main(
if clean_exit:
options.fast_exit = False

formatter = util.FancyFormatter(stdout, stderr, options.show_error_codes)
formatter = util.FancyFormatter(stdout, stderr, options.hide_error_codes)

if options.install_types and (stdout is not sys.stdout or stderr is not sys.stderr):
# Since --install-types performs user input, we want regular stdout and stderr.
Expand Down Expand Up @@ -151,7 +151,7 @@ def run_build(
stdout: TextIO,
stderr: TextIO,
) -> tuple[build.BuildResult | None, list[str], bool]:
formatter = util.FancyFormatter(stdout, stderr, options.show_error_codes)
formatter = util.FancyFormatter(stdout, stderr, options.hide_error_codes)

messages = []

Expand Down Expand Up @@ -871,9 +871,9 @@ def add_invertible_flag(
group=error_group,
)
add_invertible_flag(
"--show-error-codes",
"--hide-error-codes",
default=False,
help="Show error codes in error messages",
help="Hide error codes in error messages",
group=error_group,
)
add_invertible_flag(
Expand Down
2 changes: 1 addition & 1 deletion mypy/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ def __init__(self) -> None:
self.shadow_file: list[list[str]] | None = None
self.show_column_numbers: bool = False
self.show_error_end: bool = False
self.show_error_codes = False
self.hide_error_codes = False
# Use soft word wrap and show trimmed source snippets with error location markers.
self.pretty = False
self.dump_graph = False
Expand Down
3 changes: 3 additions & 0 deletions mypy/test/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,12 +369,15 @@ def parse_options(
if targets:
# TODO: support specifying targets via the flags pragma
raise RuntimeError("Specifying targets via the flags pragma is not supported.")
if "--show-error-codes" not in flag_list:
options.hide_error_codes = True
else:
flag_list = []
options = Options()
# TODO: Enable strict optional in test cases by default (requires *many* test case changes)
options.strict_optional = False
options.error_summary = False
options.hide_error_codes = True

# Allow custom python version to override testfile_pyversion.
if all(flag.split("=")[0] not in ["--python-version", "-2", "--py2"] for flag in flag_list):
Expand Down
2 changes: 1 addition & 1 deletion mypy/test/testcheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def run_case_once(
if "columns" in testcase.file:
options.show_column_numbers = True
if "errorcodes" in testcase.file:
options.show_error_codes = True
options.hide_error_codes = False

if incremental_step and options.incremental:
# Don't overwrite # flags: --no-incremental in incremental test cases
Expand Down
2 changes: 2 additions & 0 deletions mypy/test/testcmdline.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ def test_python_cmdline(testcase: DataDrivenTestCase, step: int) -> None:
args.append("--show-traceback")
if "--error-summary" not in args:
args.append("--no-error-summary")
if "--show-error-codes" not in args:
args.append("--hide-error-codes")
# Type check the program.
fixed = [python3_path, "-m", "mypy"]
env = os.environ.copy()
Expand Down
2 changes: 2 additions & 0 deletions mypy/test/testdaemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ def parse_script(input: list[str]) -> list[list[str]]:


def run_cmd(input: str) -> tuple[int, str]:
if input[1:].startswith("mypy run --") and "--show-error-codes" not in input:
input += " --hide-error-codes"
if input.startswith("dmypy "):
input = sys.executable + " -m mypy." + input
if input.startswith("mypy "):
Expand Down
1 change: 1 addition & 0 deletions mypy/test/testerrorstream.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def test_error_stream(testcase: DataDrivenTestCase) -> None:
"""
options = Options()
options.show_traceback = True
options.hide_error_codes = True

logged_messages: list[str] = []

Expand Down
1 change: 1 addition & 0 deletions mypy/test/testparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def test_parser(testcase: DataDrivenTestCase) -> None:
The argument contains the description of the test case.
"""
options = Options()
options.hide_error_codes = True

if testcase.file.endswith("python310.test"):
options.python_version = (3, 10)
Expand Down
2 changes: 1 addition & 1 deletion mypy/test/testpep561.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def test_pep561(testcase: DataDrivenTestCase) -> None:
f.write(f"{s}\n")
cmd_line.append(program)

cmd_line.extend(["--no-error-summary"])
cmd_line.extend(["--no-error-summary", "--hide-error-codes"])
if python_executable != sys.executable:
cmd_line.append(f"--python-executable={python_executable}")

Expand Down
1 change: 1 addition & 0 deletions mypy/test/testpythoneval.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def test_python_evaluation(testcase: DataDrivenTestCase, cache_dir: str) -> None
"--no-strict-optional",
"--no-silence-site-packages",
"--no-error-summary",
"--hide-error-codes",
]
interpreter = python3_path
mypy_cmdline.append(f"--python-version={'.'.join(map(str, PYTHON3_VERSION))}")
Expand Down
4 changes: 2 additions & 2 deletions mypy/test/teststubtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1559,13 +1559,13 @@ def test_mypy_build(self) -> None:
output = run_stubtest(stub="+", runtime="", options=[])
assert remove_color_code(output) == (
"error: not checking stubs due to failed mypy compile:\n{}.pyi:1: "
"error: invalid syntax\n".format(TEST_MODULE_NAME)
"error: invalid syntax [syntax]\n".format(TEST_MODULE_NAME)
)

output = run_stubtest(stub="def f(): ...\ndef f(): ...", runtime="", options=[])
assert remove_color_code(output) == (
"error: not checking stubs due to mypy build errors:\n{}.pyi:2: "
'error: Name "f" already defined on line 1\n'.format(TEST_MODULE_NAME)
'error: Name "f" already defined on line 1 [no-redef]\n'.format(TEST_MODULE_NAME)
)

def test_missing_stubs(self) -> None:
Expand Down
6 changes: 3 additions & 3 deletions mypy/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -522,8 +522,8 @@ class FancyFormatter:
This currently only works on Linux and Mac.
"""

def __init__(self, f_out: IO[str], f_err: IO[str], show_error_codes: bool) -> None:
self.show_error_codes = show_error_codes
def __init__(self, f_out: IO[str], f_err: IO[str], hide_error_codes: bool) -> None:
self.hide_error_codes = hide_error_codes
# Check if we are in a human-facing terminal on a supported platform.
if sys.platform not in ("linux", "darwin", "win32", "emscripten"):
self.dummy_term = True
Expand Down Expand Up @@ -690,7 +690,7 @@ def colorize(self, error: str) -> str:
"""Colorize an output line by highlighting the status and error code."""
if ": error:" in error:
loc, msg = error.split("error:", maxsplit=1)
if not self.show_error_codes:
if self.hide_error_codes:
return (
loc + self.style("error:", "red", bold=True) + self.highlight_quote_groups(msg)
)
Expand Down
1 change: 0 additions & 1 deletion mypy_self_check.ini
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ warn_no_return = True
strict_optional = True
disallow_any_unimported = True
show_traceback = True
show_error_codes = True
pretty = True
always_false = MYPYC
plugins = misc/proper_plugin.py
Expand Down
2 changes: 1 addition & 1 deletion mypyc/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class Errors:
def __init__(self) -> None:
self.num_errors = 0
self.num_warnings = 0
self._errors = mypy.errors.Errors()
self._errors = mypy.errors.Errors(hide_error_codes=True)

def error(self, msg: str, path: str, line: int) -> None:
self._errors.report(line, None, msg, severity="error", file=path)
Expand Down
1 change: 1 addition & 0 deletions mypyc/test/testutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ def build_ir_for_single_file2(
compiler_options = compiler_options or CompilerOptions(capi_version=(3, 5))
options = Options()
options.show_traceback = True
options.hide_error_codes = True
options.use_builtins_fixtures = True
options.strict_optional = True
options.python_version = (3, 6)
Expand Down
9 changes: 9 additions & 0 deletions test-data/unit/check-errorcodes.test
Original file line number Diff line number Diff line change
Expand Up @@ -922,3 +922,12 @@ def f(d: D, s: str) -> None:
[case testRecommendErrorCode]
# type: ignore[whatever] # E: type ignore with error code is not supported for modules; use `# mypy: disable-error-code=...` [syntax]
1 + "asdf"

[case testShowErrorCodesInConfig]
# flags: --config-file tmp/mypy.ini
# Test 'show_error_codes = True' in config doesn't raise an exception
var: int = "" # E: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment]

[file mypy.ini]
\[mypy]
show_error_codes = True
18 changes: 13 additions & 5 deletions test-data/unit/check-flags.test
Original file line number Diff line number Diff line change
Expand Up @@ -2023,12 +2023,12 @@ x = 'should be fine'
x.trim()

[case testDisableDifferentErrorCode]
# flags: --disable-error-code name-defined --show-error-code
# flags: --disable-error-code name-defined --show-error-codes
x = 'should not be fine'
x.trim() # E: "str" has no attribute "trim" [attr-defined]

[case testDisableMultipleErrorCode]
# flags: --disable-error-code attr-defined --disable-error-code return-value --show-error-code
# flags: --disable-error-code attr-defined --disable-error-code return-value --show-error-codes
x = 'should be fine'
x.trim()

Expand All @@ -2038,12 +2038,12 @@ def bad_return_type() -> str:
bad_return_type('no args taken!') # E: Too many arguments for "bad_return_type" [call-arg]

[case testEnableErrorCode]
# flags: --disable-error-code attr-defined --enable-error-code attr-defined --show-error-code
# flags: --disable-error-code attr-defined --enable-error-code attr-defined --show-error-codes
x = 'should be fine'
x.trim() # E: "str" has no attribute "trim" [attr-defined]

[case testEnableDifferentErrorCode]
# flags: --disable-error-code attr-defined --enable-error-code name-defined --show-error-code
# flags: --disable-error-code attr-defined --enable-error-code name-defined --show-error-codes
x = 'should not be fine'
x.trim()
y.trim() # E: Name "y" is not defined [name-defined]
Expand All @@ -2054,7 +2054,7 @@ y.trim() # E: Name "y" is not defined [name-defined]
--disable-error-code return-value \
--disable-error-code call-arg \
--enable-error-code attr-defined \
--enable-error-code return-value --show-error-code
--enable-error-code return-value --show-error-codes
x = 'should be fine'
x.trim() # E: "str" has no attribute "trim" [attr-defined]

Expand Down Expand Up @@ -2109,3 +2109,11 @@ enable_error_code = ignore-without-code, truthy-bool

\[mypy-tests.*]
disable_error_code = ignore-without-code

[case testShowErrorCodes]
# flags: --show-error-codes
x: int = "" # E: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment]

[case testHideErrorCodes]
# flags: --hide-error-codes
x: int = "" # E: Incompatible types in assignment (expression has type "str", variable has type "int")
6 changes: 3 additions & 3 deletions test-data/unit/daemon.test
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ Daemon started
$ dmypy check foo.py bar.py
$ dmypy recheck
$ dmypy recheck --update foo.py --remove bar.py sir_not_appearing_in_this_film.py
foo.py:1: error: Import of "bar" ignored
foo.py:1: error: Import of "bar" ignored [misc]
foo.py:1: note: (Using --follow-imports=error, module not passed on command line)
== Return code: 1
$ dmypy recheck --update bar.py
Expand Down Expand Up @@ -277,7 +277,7 @@ $ dmypy suggest foo.foo
(str) -> int
$ {python} -c "import shutil; shutil.copy('foo2.py', 'foo.py')"
$ dmypy check foo.py bar.py
bar.py:3: error: Incompatible types in assignment (expression has type "int", variable has type "str")
bar.py:3: error: Incompatible types in assignment (expression has type "int", variable has type "str") [assignment]
== Return code: 1
[file foo.py]
def foo(arg):
Expand All @@ -304,7 +304,7 @@ $ dmypy inspect foo:1:2:3:4
Command "inspect" is only valid after a "check" command (that produces no parse errors)
== Return code: 2
$ dmypy check foo.py --export-types
foo.py:3: error: Incompatible types in assignment (expression has type "str", variable has type "int")
foo.py:3: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment]
== Return code: 1
$ dmypy inspect foo:1
Format should be file:line:column[:end_line:end_column]
Expand Down

0 comments on commit da43c91

Please sign in to comment.