diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 0ea5981c31d1..ad7555bb2759 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -274,6 +274,22 @@ Here are some more useful flags: re-check your code without ``--strict-optional`` to ensure new type errors are not introduced. +.. _disallow-any: + +- ``--disallow-any`` disallows various types of ``Any`` in a module. + The option takes a comma-separated list of the following values: + ``unimported``, ``unannotated``. + + ``unimported`` disallows usage of types that come from unfollowed imports + (such types become aliases for ``Any``). Unfollowed imports occur either + when the imported module does not exist or when ``--follow-imports=skip`` + is set. + + ``unannotated`` disallows function definitions that are not fully + typed (i.e. that are missing an explicit type annotation for any + of the parameters or the return type). ``unannotated`` option is + interchangeable with ``--disallow-untyped-defs``. + - ``--disallow-untyped-defs`` reports an error whenever it encounters a function definition without type annotations. diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index 6fe139b1bb8c..07db822df8a1 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -147,6 +147,12 @@ overridden by the pattern sections matching the module name. - ``almost_silent`` (Boolean, deprecated) equivalent to ``follow_imports=skip``. +- ``disallow_any`` (Comma-separated list, default empty) is an option to + disallow various types of ``Any`` in a module. The flag takes a + comma-separated list of the following arguments: ``unimported``, + ``unannotated``. For explanations see the discussion for the + :ref:`--disallow-any ` option. + - ``disallow_untyped_calls`` (Boolean, default False) disallows calling functions without type annotations from functions with type annotations. diff --git a/mypy/main.py b/mypy/main.py index 422ca3ccec03..eb28b7e3bdb4 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -97,7 +97,7 @@ def type_check_only(sources: List[BuildSource], bin_dir: str, options: Options) options=options) -disallow_any_options = ['unimported'] +disallow_any_options = ['unimported', 'unannotated'] def disallow_any_argument_type(raw_options: str) -> List[str]: @@ -201,7 +201,6 @@ def process_options(args: List[str], strict_flag_names = [] # type: List[str] strict_flag_assignments = [] # type: List[Tuple[str, bool]] - disallow_any_options = ['unimported'] def add_invertible_flag(flag: str, *, @@ -428,6 +427,9 @@ def add_invertible_flag(flag: str, print("Warning: --no-fast-parser no longer has any effect. The fast parser " "is now mypy's default and only parser.") + if 'unannotated' in options.disallow_any: + options.disallow_untyped_defs = True + # Check for invalid argument combinations. if require_targets: code_methods = sum(bool(c) for c in [special_opts.modules, @@ -724,6 +726,8 @@ def parse_section(prefix: str, template: Options, except ValueError as err: print("%s: %s: %s" % (prefix, key, err), file=sys.stderr) continue + if key == 'disallow_any': + results['disallow_untyped_defs'] = v and 'unannotated' in v if key == 'silent_imports': print("%s: silent_imports has been replaced by " "ignore_missing_imports=True; follow_imports=skip" % prefix, file=sys.stderr) diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 6f8dcdd4a94b..6ee0247c9bf4 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -44,6 +44,12 @@ def f(): 1 + "str" [out] main:2: error: Function is missing a type annotation +[case testUntypedDefDisallowUnannotated] +# flags: --disallow-any=unannotated +def f(): + 1 + "str" +[out] +main:2: error: Function is missing a type annotation [case testSubclassingAny] # flags: --disallow-subclassing-any diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index 118a7c0f5452..cfc50f819a36 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -176,6 +176,76 @@ z.py:1: error: Function is missing a type annotation z.py:4: error: Call to untyped function "f" in typed context x.py:1: error: Function is missing a type annotation +[case testPerFileConfigSectionUntypedWithDisallowUnannotated] +# cmd: mypy w.py x.py y.py z.py +[file mypy.ini] +[[mypy] +disallow_any = unannotated +[[mypy-y*] +disallow_any = +[[mypy-z*] +disallow_untyped_defs = True +[[mypy-w*] +disallow_untyped_defs = False +[file x.py] +def f(a): + pass +[file y.py] +def f(a): + pass +[file z.py] +def f(a): + pass +[file w.py] +def f(a): + pass +[out] +z.py:1: error: Function is missing a type annotation +x.py:1: error: Function is missing a type annotation + +[case testPerFileConfigSectionDisallowUnannotatedWithUntyped] +# cmd: mypy x.py y.py z.py +[file mypy.ini] +[[mypy] +disallow_untyped_defs = True +[[mypy-y*] +disallow_any = +[[mypy-z*] +disallow_any = unannotated +[file x.py] +def f(a): + pass +[file y.py] +def f(a): + pass +[file z.py] +def f(a): + pass +[out] +z.py:1: error: Function is missing a type annotation +x.py:1: error: Function is missing a type annotation + +[case testPerFileConfigSectionDisallowUnannotatedNoOverride] +# cmd: mypy x.py y.py z.py +[file mypy.ini] +[[mypy] +[[mypy-x*] +disallow_untyped_defs = True +[[mypy-z*] +disallow_any = unannotated +[file x.py] +def f(a): + pass +[file y.py] +def f(a): + pass +[file z.py] +def f(a): + pass +[out] +z.py:1: error: Function is missing a type annotation +x.py:1: error: Function is missing a type annotation + [case testPerFileConfigSectionMultipleMatches] # cmd: mypy xx.py xy.py yx.py yy.py [file mypy.ini]