From 835bb364edd04f52170a83cca35a4e645e29011a Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 4 Aug 2024 12:35:48 +0300 Subject: [PATCH 1/2] Add `enable_incomplete_feature` validation to `stubtest` --- mypy/main.py | 9 ++------- mypy/options.py | 10 ++++++++++ mypy/stubtest.py | 6 ++++++ mypy/test/teststubtest.py | 14 ++++++++++++++ 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index 49a395b478b39..86dabfc515b91 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -23,7 +23,7 @@ from mypy.find_sources import InvalidSourceList, create_source_list from mypy.fscache import FileSystemCache from mypy.modulefinder import BuildSource, FindModuleCache, SearchPaths, get_search_dirs, mypy_path -from mypy.options import COMPLETE_FEATURES, INCOMPLETE_FEATURES, BuildType, Options +from mypy.options import INCOMPLETE_FEATURES, BuildType, Options from mypy.split_namespace import SplitNamespace from mypy.version import __version__ @@ -1337,12 +1337,7 @@ def set_strict_flags() -> None: options.process_error_codes(error_callback=parser.error) - # Validate incomplete features. - for feature in options.enable_incomplete_feature: - if feature not in INCOMPLETE_FEATURES | COMPLETE_FEATURES: - parser.error(f"Unknown incomplete feature: {feature}") - if feature in COMPLETE_FEATURES: - print(f"Warning: {feature} is already enabled by default") + options.validate_incomplete_features(error_callback=parser.error, warning_callback=print) # Compute absolute path for custom typeshed (if present). if options.custom_typeshed_dir is not None: diff --git a/mypy/options.py b/mypy/options.py index 5ab397e0e1562..6670f06fcc38f 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -433,6 +433,16 @@ def process_error_codes(self, *, error_callback: Callable[[str], Any]) -> None: # Enabling an error code always overrides disabling self.disabled_error_codes -= self.enabled_error_codes + def validate_incomplete_features( + self, *, error_callback: Callable[[str], Any], warning_callback: Callable[[str], Any] + ) -> None: + # Validate incomplete features. + for feature in self.enable_incomplete_feature: + if feature not in INCOMPLETE_FEATURES | COMPLETE_FEATURES: + error_callback(f"Unknown incomplete feature: {feature}") + if feature in COMPLETE_FEATURES: + warning_callback(f"Warning: {feature} is already enabled by default") + def apply_changes(self, changes: dict[str, object]) -> Options: # Note: effects of this method *must* be idempotent. new_options = Options() diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 6299f21e48e9f..6306af5443c32 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1945,7 +1945,13 @@ def error_callback(msg: str) -> typing.NoReturn: print(_style("error:", color="red", bold=True), msg) sys.exit(1) + def warning_callback(msg: str) -> None: + print(_style("warning:", color="yellow", bold=True), msg) + options.process_error_codes(error_callback=error_callback) + options.validate_incomplete_features( + error_callback=error_callback, warning_callback=warning_callback + ) try: modules = build_stubs(modules, options, find_submodules=not args.check_typeshed) diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 1cc6c38e6e854..c10c683ffdac3 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -2490,6 +2490,20 @@ def test_config_file_error_codes(self) -> None: output = run_stubtest(stub=stub, runtime=runtime, options=[], config_file=config_file) assert output == "Success: no issues found in 1 module\n" + def test_config_file_wrong_incomplete_feature(self) -> None: + runtime = "x = 1\n" + stub = "x: int\n" + config_file = "[mypy]\nenable_incomplete_feature = Unpack\n" + output = run_stubtest(stub=stub, runtime=runtime, options=[], config_file=config_file) + assert output == ( + "warning: Warning: Unpack is already enabled by default\n" + "Success: no issues found in 1 module\n" + ) + + config_file = "[mypy]\nenable_incomplete_feature = not-a-valid-name\n" + with self.assertRaises(SystemExit): + run_stubtest(stub=stub, runtime=runtime, options=[], config_file=config_file) + def test_no_modules(self) -> None: output = io.StringIO() with contextlib.redirect_stdout(output): From 599a5553dc180cce05f7c5b0bd816dfa580b4f35 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 4 Aug 2024 12:51:03 +0300 Subject: [PATCH 2/2] Use the same naming --- mypy/main.py | 3 +-- mypy/options.py | 2 +- mypy/stubtest.py | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index 86dabfc515b91..f177bb1c2062c 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -1336,8 +1336,7 @@ def set_strict_flags() -> None: validate_package_allow_list(options.untyped_calls_exclude) options.process_error_codes(error_callback=parser.error) - - options.validate_incomplete_features(error_callback=parser.error, warning_callback=print) + options.process_incomplete_features(error_callback=parser.error, warning_callback=print) # Compute absolute path for custom typeshed (if present). if options.custom_typeshed_dir is not None: diff --git a/mypy/options.py b/mypy/options.py index 6670f06fcc38f..5e64d5e40035b 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -433,7 +433,7 @@ def process_error_codes(self, *, error_callback: Callable[[str], Any]) -> None: # Enabling an error code always overrides disabling self.disabled_error_codes -= self.enabled_error_codes - def validate_incomplete_features( + def process_incomplete_features( self, *, error_callback: Callable[[str], Any], warning_callback: Callable[[str], Any] ) -> None: # Validate incomplete features. diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 6306af5443c32..ca17ccfe2f5bf 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1949,7 +1949,7 @@ def warning_callback(msg: str) -> None: print(_style("warning:", color="yellow", bold=True), msg) options.process_error_codes(error_callback=error_callback) - options.validate_incomplete_features( + options.process_incomplete_features( error_callback=error_callback, warning_callback=warning_callback )