Skip to content

Support module-level strict config flag #12174

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions mypy/config_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ def check_follow_imports(choice: str) -> str:
})


def parse_config_file(options: Options, set_strict_flags: Callable[[], None],
def parse_config_file(options: Options,
strict_flag_assignments: Sequence[Tuple[str, object]],
filename: Optional[str],
stdout: Optional[TextIO] = None,
stderr: Optional[TextIO] = None) -> None:
Expand Down Expand Up @@ -205,6 +206,9 @@ def parse_config_file(options: Options, set_strict_flags: Callable[[], None],
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("%s: No [mypy] section in config file" % file_read, file=stderr)
Expand All @@ -217,11 +221,16 @@ def parse_config_file(options: Options, set_strict_flags: Callable[[], None],
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("%sPer-module sections should not specify reports (%s)" %
(prefix, ', '.join(s + '_report' for s in sorted(report_dirs))),
Expand Down Expand Up @@ -336,7 +345,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
Expand Down Expand Up @@ -416,7 +425,7 @@ def parse_section(prefix: str, template: Options,
continue
if key == 'strict':
if v:
set_strict_flags()
set_strict_flags(results)
continue
if key == 'silent_imports':
print("%ssilent_imports has been replaced by "
Expand Down Expand Up @@ -523,7 +532,7 @@ def parse_mypy_comments(
stderr = StringIO()
strict_found = False

def set_strict_flags() -> None:
def set_strict_flags(ignored: Any) -> None:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer having reasonable name/type even if ignored.

nonlocal strict_found
strict_found = True

Expand Down
9 changes: 3 additions & 6 deletions mypy/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -911,17 +911,14 @@ def add_invertible_flag(flag: str,

options = Options()

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)
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'): # noqa
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', '')
Expand Down
16 changes: 13 additions & 3 deletions mypy/stubtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1204,11 +1204,21 @@ def test_stubs(args: argparse.Namespace, use_builtins_fixtures: bool = False) ->
options.custom_typeshed_dir = args.custom_typeshed_dir
options.config_file = args.mypy_config_file
options.use_builtins_fixtures = use_builtins_fixtures
options.per_module_options = {}

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)
strict_flags = ['warn_unused_configs', 'warn_unused_ignores']

strict_flag_assignments = [(dest, True) for dest in strict_flags]
parse_config_file(options, strict_flag_assignments, options.config_file,
sys.stdout, sys.stderr)

for dest in strict_flags:
if getattr(options, dest, False):
print('note: {} = True [global]'.format(dest))
for glob, updates in options.per_module_options.items():
if updates.get(dest):
print('note: {} = True [{}]'.format(dest, glob))

try:
modules = build_stubs(modules, options, find_submodules=not args.check_typeshed)
Expand Down
2 changes: 1 addition & 1 deletion mypy/test/testfinegrained.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ def get_options(self,

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
Expand Down
19 changes: 19 additions & 0 deletions mypy/test/teststubtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1047,3 +1047,22 @@ def test_config_file(self) -> None:
)
output = run_stubtest(stub=stub, runtime=runtime, options=[], config_file=config_file)
assert output == ""

def test_config_file_strict(self) -> None:
config_file = (
"[mypy]\n"
"strict = True\n"
)
output = run_stubtest(stub="", runtime="", options=[], config_file=config_file)
assert output == (
"note: warn_unused_configs = True [global]\n"
"note: warn_unused_ignores = True [global]\n"
)
config_file = (
"[mypy-test_module.*]\n"
"strict = True\n"
)
output = run_stubtest(stub="", runtime="", options=[], config_file=config_file)
assert output == (
"note: warn_unused_ignores = True [test_module.*]\n"
)