diff --git a/mypy/config_parser.py b/mypy/config_parser.py index a6bf021000c1..c44a4cda24ba 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -231,7 +231,7 @@ def split_commas(value: str) -> list[str]: def parse_config_file( options: Options, - set_strict_flags: Callable[[], None], + strict_flag_assignments: Sequence[tuple[str, object]], filename: str | None, stdout: TextIO | None = None, stderr: TextIO | None = None, @@ -284,6 +284,9 @@ def parse_config_file( os.environ["MYPY_CONFIG_FILE_DIR"] = os.path.dirname(os.path.abspath(config_file)) + def set_strict_flags(updates: dict[str, object]) -> None: + updates.update(strict_flag_assignments) + if "mypy" not in parser: if filename or file_read not in defaults.SHARED_CONFIG_FILES: print(f"{file_read}: No [mypy] section in config file", file=stderr) @@ -297,11 +300,16 @@ def parse_config_file( setattr(options, k, v) options.report_dirs.update(report_dirs) + def set_strict_flags_section(updates: dict[str, object]) -> None: + for dest, value in strict_flag_assignments: + if dest in PER_MODULE_OPTIONS: + updates[dest] = value + for name, section in parser.items(): if name.startswith("mypy-"): prefix = get_prefix(file_read, name) updates, report_dirs = parse_section( - prefix, options, set_strict_flags, section, config_types, stderr + prefix, options, set_strict_flags_section, section, config_types, stderr ) if report_dirs: print( @@ -440,7 +448,7 @@ def destructure_overrides(toml_data: dict[str, Any]) -> dict[str, Any]: def parse_section( prefix: str, template: Options, - set_strict_flags: Callable[[], None], + set_strict_flags: Callable[[dict[str, object]], None], section: Mapping[str, Any], config_types: dict[str, Any], stderr: TextIO = sys.stderr, @@ -535,7 +543,7 @@ def parse_section( continue if key == "strict": if v: - set_strict_flags() + set_strict_flags(results) continue results[options_key] = v @@ -631,7 +639,7 @@ def parse_mypy_comments( stderr = StringIO() strict_found = False - def set_strict_flags() -> None: + def set_strict_flags(updates: dict[str, object]) -> None: nonlocal strict_found strict_found = True diff --git a/mypy/main.py b/mypy/main.py index ae3e7d75be7f..7616bbc4468d 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -1265,21 +1265,15 @@ def add_invertible_flag( parser.error(f"Cannot find config file '{config_file}'") options = Options() - strict_option_set = False - - def set_strict_flags() -> None: - nonlocal strict_option_set - strict_option_set = True - 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) + parse_config_file(options, strict_flag_assignments, 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"): - set_strict_flags() + for dest, value in strict_flag_assignments: + setattr(options, dest, value) # Override cache_dir if provided in the environment environ_cache_dir = os.getenv("MYPY_CACHE_DIR", "") @@ -1400,9 +1394,6 @@ def set_strict_flags() -> None: " new type inference algorithm is already enabled by default" ) - if options.strict_concatenate and not strict_option_set: - print("Warning: --strict-concatenate is deprecated; use --extra-checks instead") - # Set target. if special_opts.modules + special_opts.packages: options.build_type = BuildType.MODULE diff --git a/mypy/options.py b/mypy/options.py index d315d297e023..e869781d9537 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -212,7 +212,7 @@ def __init__(self) -> None: # This makes 1 == '1', 1 in ['1'], and 1 is '1' errors. self.strict_equality = False - # Deprecated, use extra_checks instead. + # Make arguments prepended via Concatenate be truly positional-only. self.strict_concatenate = False # Enable additional checks that are technically correct but impractical. diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 36cd0a213d4d..b91466e6a9f5 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1940,15 +1940,12 @@ def test_stubs(args: _Arguments, use_builtins_fixtures: bool = False) -> int: options.abs_custom_typeshed_dir = os.path.abspath(options.custom_typeshed_dir) options.config_file = args.mypy_config_file options.use_builtins_fixtures = use_builtins_fixtures + options.per_module_options = {} options.show_traceback = args.show_traceback options.pdb = args.pdb if options.config_file: - - def set_strict_flags() -> None: # not needed yet - return - - parse_config_file(options, set_strict_flags, options.config_file, sys.stdout, sys.stderr) + parse_config_file(options, [], options.config_file, sys.stdout, sys.stderr) def error_callback(msg: str) -> typing.NoReturn: print(_style("error:", color="red", bold=True), msg) diff --git a/mypy/test/testfinegrained.py b/mypy/test/testfinegrained.py index 800ba2dff087..0f86e7c20dae 100644 --- a/mypy/test/testfinegrained.py +++ b/mypy/test/testfinegrained.py @@ -162,7 +162,7 @@ def get_options(self, source: str, testcase: DataDrivenTestCase, build_cache: bo for name, _ in testcase.files: if "mypy.ini" in name or "pyproject.toml" in name: - parse_config_file(options, lambda: None, name) + parse_config_file(options, [], name) break return options diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index c6419923ebc6..6219082171df 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -2125,6 +2125,31 @@ disallow_subclassing_any = true module = 'm' disallow_subclassing_any = false +[case testStrictPerModule] +# flags: --config-file tmp/mypy.ini + +import strictmodule +import loosemodule +a = 0 # type: ignore + +[file strictmodule.py] +def foo(): # E: Function is missing a return type annotation \ + # N: Use "-> None" if function does not return a value + 1 + "asdf" # E: Unsupported operand types for + ("int" and "str") + +a = 0 # type: ignore # E: Unused "type: ignore" comment + +[file loosemodule.py] +def foo(): + 1 + "asdf" + +a = 0 # type: ignore + +[file mypy.ini] +\[mypy] +strict = False +\[mypy-strictmodule] +strict = True [case testNoImplicitOptionalPerModule] # flags: --config-file tmp/mypy.ini