From 2684385499691aab7c95250544f62b9adf8a95be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Fri, 20 Dec 2019 23:52:21 +0200 Subject: [PATCH 1/2] Allow strict in config, explicitly disallow inline --- mypy/config_parser.py | 26 +++++++++++++++++-------- mypy/main.py | 12 ++++++++---- mypy/test/testfinegrained.py | 2 +- test-data/unit/check-flags.test | 16 +++++++++++++++ test-data/unit/check-inline-config.test | 5 +++++ 5 files changed, 48 insertions(+), 13 deletions(-) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index 6a94757f58ed..b436d098c5e4 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -6,7 +6,7 @@ import re import sys -from typing import Any, Dict, List, Mapping, Optional, Tuple, TextIO +from typing import Any, Callable, Dict, List, Mapping, Optional, Tuple, TextIO from typing_extensions import Final from mypy import defaults @@ -88,7 +88,8 @@ def split_and_match_files(paths: str) -> List[str]: } # type: Final -def parse_config_file(options: Options, filename: Optional[str], +def parse_config_file(options: Options, set_strict_flags: Callable[[], None], + filename: Optional[str], stdout: Optional[TextIO] = None, stderr: Optional[TextIO] = None) -> None: """Parse a config file into an Options object. @@ -127,7 +128,7 @@ def parse_config_file(options: Options, filename: Optional[str], else: section = parser['mypy'] prefix = '%s: [%s]: ' % (file_read, 'mypy') - updates, report_dirs = parse_section(prefix, options, section, stderr) + updates, report_dirs = parse_section(prefix, options, set_strict_flags, section, stderr) for k, v in updates.items(): setattr(options, k, v) options.report_dirs.update(report_dirs) @@ -135,7 +136,8 @@ def parse_config_file(options: Options, filename: Optional[str], for name, section in parser.items(): if name.startswith('mypy-'): prefix = '%s: [%s]: ' % (file_read, name) - updates, report_dirs = parse_section(prefix, options, section, stderr) + updates, report_dirs = parse_section( + prefix, options, set_strict_flags, section, stderr) if report_dirs: print("%sPer-module sections should not specify reports (%s)" % (prefix, ', '.join(s + '_report' for s in sorted(report_dirs))), @@ -163,6 +165,7 @@ def parse_config_file(options: Options, filename: Optional[str], def parse_section(prefix: str, template: Options, + set_strict_flags: Callable[[], None], section: Mapping[str, str], stderr: TextIO = sys.stderr ) -> Tuple[Dict[str, object], Dict[str, str]]: @@ -205,9 +208,7 @@ def parse_section(prefix: str, template: Options, options_key = key[3:] invert = True elif key == 'strict': - print("%sStrict mode is not supported in configuration files: specify " - "individual flags instead (see 'mypy -h' for the list of flags enabled " - "in strict mode)" % prefix, file=stderr) + set_strict_flags() else: print("%sUnrecognized option: %s = %s" % (prefix, key, section[key]), file=stderr) @@ -330,10 +331,19 @@ def parse_mypy_comments( errors.extend((lineno, x) for x in parse_errors) stderr = StringIO() - new_sections, reports = parse_section('', template, parser['dummy'], stderr=stderr) + strict_found = False + + def set_strict_flags() -> None: + nonlocal strict_found + strict_found = True + + new_sections, reports = parse_section( + '', template, set_strict_flags, parser['dummy'], stderr=stderr) errors.extend((lineno, x) for x in stderr.getvalue().strip().split('\n') if x) if reports: errors.append((lineno, "Reports not supported in inline configuration")) + if strict_found: + errors.append((lineno, "Setting 'strict' not supported in inline configuration")) sections.update(new_sections) return sections, errors diff --git a/mypy/main.py b/mypy/main.py index 4b8d9c5f7b0d..c08aab020dff 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -806,15 +806,19 @@ def add_invertible_flag(flag: str, if config_file and not os.path.exists(config_file): parser.error("Cannot find config file '%s'" % config_file) - # Parse config file first, so command line can override. options = Options() - parse_config_file(options, config_file, stdout, stderr) + + def set_strict_flags() -> None: + for dest, value in strict_flag_assignments: + 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 strict flags before parsing (if strict mode enabled), so other command # line options can override. if getattr(dummy, 'special-opts:strict'): # noqa - for dest, value in strict_flag_assignments: - setattr(options, dest, value) + set_strict_flags() # Override cache_dir if provided in the environment environ_cache_dir = os.getenv('MYPY_CACHE_DIR', '') diff --git a/mypy/test/testfinegrained.py b/mypy/test/testfinegrained.py index 8939e5ff9fa2..9c50d96712ab 100644 --- a/mypy/test/testfinegrained.py +++ b/mypy/test/testfinegrained.py @@ -193,7 +193,7 @@ def get_options(self, for name, _ in testcase.files: if 'mypy.ini' in name: - parse_config_file(options, name) + parse_config_file(options, lambda: None, name) break return options diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index cf6d810d7357..a2c36c0ca0cb 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -1149,6 +1149,22 @@ def f(c: A) -> None: # E: Missing type parameters for generic type "A" pass [out] +[case testStrictInConfigAnyGeneric] +# flags: --config-file tmp/mypy.ini +from typing import TypeVar, Generic + +T = TypeVar('T') + +class A(Generic[T]): + pass + +def f(c: A) -> None: # E: Missing type parameters for generic type "A" + pass +[file mypy.ini] +\[mypy] +strict = True +[out] + [case testStrictAndStrictEquality] # flags: --strict x = 0 diff --git a/test-data/unit/check-inline-config.test b/test-data/unit/check-inline-config.test index 4cf82b03e671..b8540cf938e0 100644 --- a/test-data/unit/check-inline-config.test +++ b/test-data/unit/check-inline-config.test @@ -157,3 +157,8 @@ main:4: error: Unterminated quote in configuration comment # mypy: skip-file [out] main:1: error: Unrecognized option: skip_file = True + +[case testInlineStrict] +# mypy: strict +[out] +main:1: error: Setting 'strict' not supported in inline configuration From c32dfe52cc1e01d12fb10e74eda083effa0412a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Sun, 19 Jan 2020 12:33:53 +0200 Subject: [PATCH 2/2] Improve strict not supported in inline config error message --- mypy/config_parser.py | 6 +++++- test-data/unit/check-inline-config.test | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index b436d098c5e4..14dfedbd12a7 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -343,7 +343,11 @@ def set_strict_flags() -> None: if reports: errors.append((lineno, "Reports not supported in inline configuration")) if strict_found: - errors.append((lineno, "Setting 'strict' not supported in inline configuration")) + errors.append((lineno, + "Setting 'strict' not supported in inline configuration: specify it in " + "a configuration file instead, or set individual inline flags " + "(see 'mypy -h' for the list of flags enabled in strict mode)")) + sections.update(new_sections) return sections, errors diff --git a/test-data/unit/check-inline-config.test b/test-data/unit/check-inline-config.test index b8540cf938e0..9bcff53cb523 100644 --- a/test-data/unit/check-inline-config.test +++ b/test-data/unit/check-inline-config.test @@ -161,4 +161,4 @@ main:1: error: Unrecognized option: skip_file = True [case testInlineStrict] # mypy: strict [out] -main:1: error: Setting 'strict' not supported in inline configuration +main:1: error: Setting 'strict' not supported in inline configuration: specify it in a configuration file instead, or set individual inline flags (see 'mypy -h' for the list of flags enabled in strict mode)