Skip to content

Commit 9d56820

Browse files
authored
Add enable_incomplete_feature validation to stubtest (#17635)
Closes #17634 Refs #17628 Refs #17629
1 parent fc5e1ff commit 9d56820

File tree

4 files changed

+32
-8
lines changed

4 files changed

+32
-8
lines changed

mypy/main.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from mypy.find_sources import InvalidSourceList, create_source_list
2424
from mypy.fscache import FileSystemCache
2525
from mypy.modulefinder import BuildSource, FindModuleCache, SearchPaths, get_search_dirs, mypy_path
26-
from mypy.options import COMPLETE_FEATURES, INCOMPLETE_FEATURES, BuildType, Options
26+
from mypy.options import INCOMPLETE_FEATURES, BuildType, Options
2727
from mypy.split_namespace import SplitNamespace
2828
from mypy.version import __version__
2929

@@ -1336,13 +1336,7 @@ def set_strict_flags() -> None:
13361336
validate_package_allow_list(options.untyped_calls_exclude)
13371337

13381338
options.process_error_codes(error_callback=parser.error)
1339-
1340-
# Validate incomplete features.
1341-
for feature in options.enable_incomplete_feature:
1342-
if feature not in INCOMPLETE_FEATURES | COMPLETE_FEATURES:
1343-
parser.error(f"Unknown incomplete feature: {feature}")
1344-
if feature in COMPLETE_FEATURES:
1345-
print(f"Warning: {feature} is already enabled by default")
1339+
options.process_incomplete_features(error_callback=parser.error, warning_callback=print)
13461340

13471341
# Compute absolute path for custom typeshed (if present).
13481342
if options.custom_typeshed_dir is not None:

mypy/options.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,16 @@ def process_error_codes(self, *, error_callback: Callable[[str], Any]) -> None:
433433
# Enabling an error code always overrides disabling
434434
self.disabled_error_codes -= self.enabled_error_codes
435435

436+
def process_incomplete_features(
437+
self, *, error_callback: Callable[[str], Any], warning_callback: Callable[[str], Any]
438+
) -> None:
439+
# Validate incomplete features.
440+
for feature in self.enable_incomplete_feature:
441+
if feature not in INCOMPLETE_FEATURES | COMPLETE_FEATURES:
442+
error_callback(f"Unknown incomplete feature: {feature}")
443+
if feature in COMPLETE_FEATURES:
444+
warning_callback(f"Warning: {feature} is already enabled by default")
445+
436446
def apply_changes(self, changes: dict[str, object]) -> Options:
437447
# Note: effects of this method *must* be idempotent.
438448
new_options = Options()

mypy/stubtest.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1945,7 +1945,13 @@ def error_callback(msg: str) -> typing.NoReturn:
19451945
print(_style("error:", color="red", bold=True), msg)
19461946
sys.exit(1)
19471947

1948+
def warning_callback(msg: str) -> None:
1949+
print(_style("warning:", color="yellow", bold=True), msg)
1950+
19481951
options.process_error_codes(error_callback=error_callback)
1952+
options.process_incomplete_features(
1953+
error_callback=error_callback, warning_callback=warning_callback
1954+
)
19491955

19501956
try:
19511957
modules = build_stubs(modules, options, find_submodules=not args.check_typeshed)

mypy/test/teststubtest.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2490,6 +2490,20 @@ def test_config_file_error_codes(self) -> None:
24902490
output = run_stubtest(stub=stub, runtime=runtime, options=[], config_file=config_file)
24912491
assert output == "Success: no issues found in 1 module\n"
24922492

2493+
def test_config_file_wrong_incomplete_feature(self) -> None:
2494+
runtime = "x = 1\n"
2495+
stub = "x: int\n"
2496+
config_file = "[mypy]\nenable_incomplete_feature = Unpack\n"
2497+
output = run_stubtest(stub=stub, runtime=runtime, options=[], config_file=config_file)
2498+
assert output == (
2499+
"warning: Warning: Unpack is already enabled by default\n"
2500+
"Success: no issues found in 1 module\n"
2501+
)
2502+
2503+
config_file = "[mypy]\nenable_incomplete_feature = not-a-valid-name\n"
2504+
with self.assertRaises(SystemExit):
2505+
run_stubtest(stub=stub, runtime=runtime, options=[], config_file=config_file)
2506+
24932507
def test_no_modules(self) -> None:
24942508
output = io.StringIO()
24952509
with contextlib.redirect_stdout(output):

0 commit comments

Comments
 (0)