Skip to content

Commit

Permalink
--ide flag
Browse files Browse the repository at this point in the history
  • Loading branch information
KotlinIsland committed Sep 17, 2023
1 parent 4b3a1ae commit 7adf0a3
Show file tree
Hide file tree
Showing 19 changed files with 136 additions and 27 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
### Added
- `TypeGuard` is retained in inferred types (#504)
- Type narrowing is applied from lambda execution (#504)
- `--ide` flag (#501)
### Enhancements
- `show-error-context`/`pretty` are now on by default (#501)
- Show fake column number when `--show-error-end` (#501)
### Fixes
- Don't show "X defined here" when error context is hidden (#498)
- Fix issue with reveal code in ignore message (#490)
Expand Down
4 changes: 4 additions & 0 deletions docs/source/command_line.rst
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ You can read more :ref:`here <baseline>`.
Based features
**************

.. option:: --ide

Preset options for best compatibility with an integrated developer environment.

.. option:: --default-return

See :confval:`default_return`.
Expand Down
2 changes: 1 addition & 1 deletion mypy/dmypy/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def __init__(self, prog: str) -> None:
"-V",
"--version",
action="version",
version=f"Basedmypy Daemon {__based_version__}\nBased on %(prog)s {__version__}",
version=f"Basedmypy-Daemon {__based_version__}\nBased on %(prog)s {__version__}",
help="Show program's version number and exit",
)
subparsers = parser.add_subparsers()
Expand Down
21 changes: 15 additions & 6 deletions mypy/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,12 @@
codes.NAME_DEFINED,
# Overrides have a custom link to docs
codes.OVERRIDE,
codes.REVEAL,
}

allowed_duplicates: Final = ["@overload", "Got:", "Expected:"]

BASE_RTD_URL: Final = "https://mypy.rtfd.io/en/stable/_refs.html#code"
BASE_RTD_URL: Final = "https://kotlinisland.github.io/basedmypy/_refs.html#code"

# Keep track of the original error code when the error code of a message is changed.
# This is used to give notes about out-of-date "type: ignore" comments.
Expand Down Expand Up @@ -648,8 +649,9 @@ def add_error_info(self, info: ErrorInfo) -> None:
and info.code not in HIDE_LINK_CODES
):
message = f"See {BASE_RTD_URL}-{info.code.code} for more info"
if message in self.only_once_messages:
if not self.options.ide and message in self.only_once_messages:
return
info_ = info
self.only_once_messages.add(message)
info = ErrorInfo(
import_ctx=info.import_ctx,
Expand All @@ -669,6 +671,7 @@ def add_error_info(self, info: ErrorInfo) -> None:
allow_dups=False,
priority=20,
)
info_.notes.append(info)
self._add_error_info(file, info)

def has_many_errors(self) -> bool:
Expand Down Expand Up @@ -793,6 +796,7 @@ def generate_unused_ignore_errors(self, file: str) -> None:
narrower = set(used_ignored_codes) & codes.sub_code_map[unused]
if narrower:
message += f", use narrower [{', '.join(narrower)}] instead of [{unused}] code"

# Don't use report since add_error_info will ignore the error!
info = ErrorInfo(
import_ctx=self.import_context(),
Expand Down Expand Up @@ -1185,11 +1189,16 @@ def render_messages(
)
)
src = (
file == current_file
and source_lines
and e.line > 0
and source_lines[e.line - 1].strip()
file == current_file and source_lines and e.line > 0 and source_lines[e.line - 1]
) or ""
# when there is no column, but we still want an ide to show an error
if e.column == -1 and self.options.show_error_end:
if src:
e.column = src.find(src.strip())
e.end_column = len(src)
else:
e.column = 1
src = src.strip()
if isinstance(e.message, ErrorMessage):
result.append(
(
Expand Down
34 changes: 27 additions & 7 deletions mypy/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,9 @@ def add_invertible_flag(
"You probably want to set this on a module override",
group=based_group,
)
add_invertible_flag(
"--ide", default=False, help="Best default for IDE integration.", group=based_group
)

config_group = parser.add_argument_group(
title="Config file",
Expand Down Expand Up @@ -940,8 +943,8 @@ def add_invertible_flag(
description="Adjust the amount of detail shown in error messages.",
)
add_invertible_flag(
"--show-error-context",
default=False,
"--hide-error-context",
default=True,
dest="show_error_context",
help='Precede errors with "note:" messages explaining context',
group=error_group,
Expand All @@ -966,14 +969,16 @@ def add_invertible_flag(
group=error_group,
)
add_invertible_flag(
"--show-error-code-links",
default=False,
help="Show links to error code documentation",
"--hide-error-code-links",
dest="show_error_code_links",
default=True,
help="Hide links to error code documentation",
group=error_group,
)
add_invertible_flag(
"--pretty",
default=False,
"--no-pretty",
default=True,
dest="pretty",
help="Use visually nicer output in error messages:"
" Use soft word wrap, show source code snippets,"
" and show error location markers",
Expand Down Expand Up @@ -1309,6 +1314,7 @@ def add_invertible_flag(
parser.error(f"Cannot find config file '{config_file}'")

mypy.options._based = dummy.__dict__["special-opts:strict"]
mypy.options._legacy = os.getenv("__MYPY_UNDER_TEST__") == "2"

based_enabled_codes = (
{
Expand All @@ -1335,9 +1341,23 @@ def add_invertible_flag(
def set_strict_flags() -> None:
pass

def set_ide_flags() -> None:
for dest, value in {
"show_error_context": False,
"error_summary": False,
"pretty": False,
"show_error_end": True,
}.items():
setattr(options, dest, value)

# Parse config file first, so command line can override.
parse_config_file(options, set_strict_flags, config_file, stdout, stderr)

# Set IDE flags before parsing (if IDE mode enabled), so other command
# line options can override.
if dummy.ide:
set_ide_flags()

# Override cache_dir if provided in the environment
environ_cache_dir = os.getenv("MYPY_CACHE_DIR", "")
if environ_cache_dir.strip():
Expand Down
16 changes: 13 additions & 3 deletions mypy/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ class BuildType:
) - {"debug_cache"}

_based = True
_legacy = False


def flip_if_not_based(b: bool) -> bool:
Expand All @@ -84,6 +85,14 @@ def flip_if_not_based(b: bool) -> bool:
return b if _based else not b


def flip_if_legacy(b: bool) -> bool:
"""Flips this bool if we are legacy.
Used to run tests in old mode.
"""
return not b if _legacy else b


# Features that are currently incomplete/experimental
TYPE_VAR_TUPLE: Final = "TypeVarTuple"
UNPACK: Final = "Unpack"
Expand Down Expand Up @@ -156,6 +165,7 @@ def __init__(self) -> None:
self.incomplete_is_typed = flip_if_not_based(False)
self.bare_literals = True
self.ignore_missing_py_typed = False
self.ide = False

# disallow_any options
self.disallow_any_generics = flip_if_not_based(True)
Expand Down Expand Up @@ -208,7 +218,7 @@ def __init__(self) -> None:
self.strict_optional = True

# Show "note: In function "foo":" messages.
self.show_error_context = False
self.show_error_context = flip_if_legacy(True)

# Use nicer output (when possible).
self.color_output = True
Expand Down Expand Up @@ -343,9 +353,9 @@ def __init__(self) -> None:
self.show_column_numbers: bool = flip_if_not_based(True)
self.show_error_end: bool = False
self.hide_error_codes = False
self.show_error_code_links = False
self.show_error_code_links = True
# Use soft word wrap and show trimmed source snippets with error location markers.
self.pretty = False
self.pretty = True
self.dump_graph = False
self.dump_deps = False
self.logical_deps = False
Expand Down
2 changes: 1 addition & 1 deletion mypy/stubtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ def test_module(module_name: str) -> Iterator[Error]:
runtime_desc=(
"This is most likely the fault of something very dynamic in your library. "
"It's also possible this is a bug in stubtest.\nIf in doubt, please "
"open an issue at https://github.com/python/mypy\n\n"
"open an issue at https://github.com/python/basedmypy\n\n"
+ traceback.format_exc().strip()
),
)
Expand Down
6 changes: 5 additions & 1 deletion mypy/test/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,8 +336,8 @@ def parse_options(

if flags:
flag_list: list[str] = safe(flags.group(1)).split()
flag_list = ["--no-pretty", "--hide-error-context", "--hide-error-code-links"] + flag_list
if based:
flag_list.insert(0, "--default-return")
flag_list.append("--hide-column-numbers")
flag_list.extend(["--enable-error-code", "no-untyped-usage"])
else:
Expand All @@ -357,6 +357,7 @@ def parse_options(
raise RuntimeError("Specifying targets via the flags pragma is not supported.")
if not based and "--show-error-codes" not in flag_list:
options.hide_error_codes = True
print(options.pretty)
else:
flag_list = []
options = Options()
Expand All @@ -371,6 +372,9 @@ def parse_options(
options.hide_error_codes = True
options.force_uppercase_builtins = True
options.force_union_syntax = True
options.pretty = False
options.show_error_context = False
options.show_error_code_links = False
# Allow custom python version to override testfile_pyversion.
if all(flag.split("=")[0] not in ["--python-version", "-2", "--py2"] for flag in flag_list):
options.python_version = testfile_pyversion(testcase.file)
Expand Down
6 changes: 5 additions & 1 deletion mypy/test/testcmdline.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ def test_python_cmdline(testcase: DataDrivenTestCase, step: int) -> None:
if not based:
args.append("--no-strict")
args.append("--no-default-return")
if "--pretty" not in args:
args.append("--no-pretty")
if "--show-error-code-links" not in args and "--ide" not in args:
args.append("--hide-error-code-links")
if "--error-summary" not in args:
args.append("--no-error-summary")
if "--show-error-codes" not in args and not based:
Expand All @@ -85,7 +89,7 @@ def test_python_cmdline(testcase: DataDrivenTestCase, step: int) -> None:
env.pop("COLUMNS", None)
extra_path = os.path.join(os.path.abspath(test_temp_dir), "pypath")
env["PYTHONPATH"] = PREFIX
env["__MYPY_UNDER_TEST__"] = "1"
env["__MYPY_UNDER_TEST__"] = "1" if based else "2"
if os.path.isdir(extra_path):
env["PYTHONPATH"] += os.pathsep + extra_path
cwd = os.path.join(test_temp_dir, custom_cwd or "")
Expand Down
2 changes: 1 addition & 1 deletion mypy/test/testdaemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def test_daemon(testcase: DataDrivenTestCase) -> None:
if cmd.split()[1] in ("start", "restart", "run"):
cmd = cmd.replace(
"-- ",
"-- --no-strict --no-infer-function-types --no-default-return --hide-column-numbers ",
"-- --no-strict --no-infer-function-types --no-default-return --hide-column-numbers --no-pretty --hide-error-context ",
)
sts, output = run_cmd(cmd)
output_lines = output.splitlines()
Expand Down
2 changes: 2 additions & 0 deletions mypy/test/testerrorstream.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ def test_error_stream(testcase: DataDrivenTestCase) -> None:
options = Options()
options.show_traceback = True
options.hide_error_codes = True
options.show_error_context = False
options.pretty = False

logged_messages: list[str] = []

Expand Down
4 changes: 3 additions & 1 deletion mypy/test/testpep561.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,9 @@ def test_pep561(testcase: DataDrivenTestCase) -> None:
f.write(f"{s}\n")
cmd_line.append(program)

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

Expand Down
2 changes: 2 additions & 0 deletions mypy/test/testpythoneval.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ def test_python_evaluation(testcase: DataDrivenTestCase, cache_dir: str) -> None
"--no-infer-function-types",
"--force-uppercase-builtins",
"--no-strict",
"--no-pretty",
"--hide-error-context",
]
interpreter = python3_path
mypy_cmdline.append(f"--python-version={'.'.join(map(str, PYTHON3_VERSION))}")
Expand Down
10 changes: 6 additions & 4 deletions mypy/test/teststubtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,12 @@ def run_stubtest(
f.write(stub)
with open(f"{TEST_MODULE_NAME}.py", "w") as f:
f.write(runtime)
if config_file:
with open(f"{TEST_MODULE_NAME}_config.ini", "w") as f:
f.write(config_file)
options = options + ["--mypy-config-file", f"{TEST_MODULE_NAME}_config.ini"]
if not config_file:
config_file = "[mypy]"
config_file += "\nshow_error_code_links=false\npretty=false"
with open(f"{TEST_MODULE_NAME}_config.ini", "w") as f:
f.write(config_file)
options = options + ["--mypy-config-file", f"{TEST_MODULE_NAME}_config.ini"]
output = io.StringIO()
mypy.options._based = False
with contextlib.redirect_stdout(output):
Expand Down
1 change: 1 addition & 0 deletions mypyc/test/testutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ def build_ir_for_single_file2(

mypy.options._based = False
options = Options()
options.show_error_context = False
options.default_return = False
options.show_traceback = True
options.hide_error_codes = True
Expand Down
8 changes: 8 additions & 0 deletions test-data/unit/check-based-misc.test
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,11 @@ from typing import Any

a: Any = 1 # E: Explicit "Any" is not allowed [no-any-explicit]
reveal_type(a) # type: ignore[no-any-expr] # N: Revealed type is "Any"


[case testFakeColumn]
# flags: --show-error-end
if True:
1 # type: ignore[operator]
[out]
main:3:5:3:31: error: Unused "type: ignore" comment [unused-ignore]
2 changes: 1 addition & 1 deletion test-data/unit/check-flags.test
Original file line number Diff line number Diff line change
Expand Up @@ -2209,7 +2209,7 @@ list(1) # E: No overload variant of "list" matches argument type "int" [call-o
# N: Possible overload variants: \
# N: def [T] __init__(self) -> List[T] \
# N: def [T] __init__(self, x: Iterable[T]) -> List[T] \
# N: See https://mypy.rtfd.io/en/stable/_refs.html#code-call-overload for more info
# N: See https://kotlinisland.github.io/basedmypy/_refs.html#code-call-overload for more info
list(2) # E: No overload variant of "list" matches argument type "int" [call-overload] \
# N: Possible overload variants: \
# N: def [T] __init__(self) -> List[T] \
Expand Down
26 changes: 26 additions & 0 deletions test-data/unit/cmdline-based-baseline.test
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ def foo() -> None:
[file .mypy/baseline.json]
{"files": {"main.py": [{"offset": 1, "code": "misc", "message": "test", "src": ""}]}, "format": "1.7", "targets": ["file:pkg"]}
[out]
pkg/main.py: note: In function "foo":
pkg/main.py:3:5: note: Revealed local types are:
pkg/main.py:3:5: note: a: int
Success: no issues found in 1 source file
Expand Down Expand Up @@ -575,3 +576,28 @@ def f(): ...
[out]
a.py:2:1: error: Unexpected keyword argument "a" for "f" [call-arg]
b.py:4:1: note: "f" defined here


[case testErrorLink]
# cmd: mypy a.py --show-error-code-links
[file a.py]
a
[file .mypy/baseline.json]
{
"files": {
"a.py": [
{
"code": "name-defined",
"column": 0,
"message": "Name \"a\" is not defined",
"offset": 1,
"src": "a",
"target": "a"
}
]
},
"format": "1.7",
"targets": [
"file:a.py"
]
}
Loading

0 comments on commit 7adf0a3

Please sign in to comment.