diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index b26b6bf7b65f..a950f8a44296 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -16,7 +16,7 @@ flag (or its long form ``--help``):: [--warn-incomplete-stub] [--warn-redundant-casts] [--warn-no-return] [--warn-unused-ignores] [--show-error-context] [--fast-parser] [-i] [--cache-dir DIR] [--strict-optional] - [--strict-optional-whitelist [GLOB [GLOB ...]]] + [--strict-optional-whitelist [GLOB [GLOB ...]]] [--strict] [--junit-xml JUNIT_XML] [--pdb] [--show-traceback] [--stats] [--inferstats] [--custom-typing MODULE] [--custom-typeshed-dir DIR] [--scripts-are-modules] @@ -371,6 +371,9 @@ Here are some more useful flags: type other than ``bool``. Instead use explicit checks like ``if x > 0`` or ``while x is not None``. +- ``--strict`` mode enables all optional error checking flags. You can see the + list of flags enabled by strict mode in the full ``mypy -h`` output. + For the remaining flags you can read the full ``mypy -h`` output. .. note:: diff --git a/mypy/main.py b/mypy/main.py index d6fae34b4c9a..ea203f0f1a44 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -126,6 +126,29 @@ def __init__(self, prog: Optional[str]) -> None: super().__init__(prog=prog, max_help_position=28) +# Define pairs of flag prefixes with inverse meaning. +flag_prefix_pairs = [ + ('allow', 'disallow'), + ('show', 'hide'), +] +flag_prefix_map = {} # type: Dict[str, str] +for a, b in flag_prefix_pairs: + flag_prefix_map[a] = b + flag_prefix_map[b] = a + + +def invert_flag_name(flag: str) -> str: + split = flag[2:].split('-', 1) + if len(split) == 2: + prefix, rest = split + if prefix in flag_prefix_map: + return '--{}-{}'.format(flag_prefix_map[prefix], rest) + elif prefix == 'no': + return '--{}'.format(rest) + + return '--no-{}'.format(flag[2:]) + + def process_options(args: List[str], require_targets: bool = True ) -> Tuple[List[BuildSource], Options]: @@ -135,6 +158,32 @@ def process_options(args: List[str], fromfile_prefix_chars='@', formatter_class=AugmentedHelpFormatter) + strict_flag_names = [] # type: List[str] + strict_flag_assignments = [] # type: List[Tuple[str, bool]] + + def add_invertible_flag(flag: str, + *, + inverse: str = None, + default: bool, + dest: str = None, + help: str, + strict_flag: bool = False + ) -> None: + if inverse is None: + inverse = invert_flag_name(flag) + arg = parser.add_argument(flag, # type: ignore # incorrect stub for add_argument + action='store_false' if default else 'store_true', + dest=dest, + help=help + " (inverse: {})".format(inverse)) + dest = arg.dest + arg = parser.add_argument(inverse, # type: ignore # incorrect stub for add_argument + action='store_true' if default else 'store_false', + dest=dest, + help=argparse.SUPPRESS) + if strict_flag: + strict_flag_names.append(flag) + strict_flag_assignments.append((dest, not default)) + # Unless otherwise specified, arguments will be parsed directly onto an # Options object. Options that require further processing should have # their `dest` prefixed with `special-opts:`, which will cause them to be @@ -154,37 +203,36 @@ def process_options(args: List[str], help="silently ignore imports of missing modules") parser.add_argument('--follow-imports', choices=['normal', 'silent', 'skip', 'error'], default='normal', help="how to treat imports (default normal)") - parser.add_argument('--disallow-untyped-calls', action='store_true', + add_invertible_flag('--disallow-untyped-calls', default=False, strict_flag=True, help="disallow calling functions without type annotations" " from functions with type annotations") - parser.add_argument('--disallow-untyped-defs', action='store_true', + add_invertible_flag('--disallow-untyped-defs', default=False, strict_flag=True, help="disallow defining functions without type annotations" " or with incomplete type annotations") - parser.add_argument('--check-untyped-defs', action='store_true', + add_invertible_flag('--check-untyped-defs', default=False, strict_flag=True, help="type check the interior of functions without type annotations") - parser.add_argument('--disallow-subclassing-any', action='store_true', + add_invertible_flag('--disallow-subclassing-any', default=False, strict_flag=True, help="disallow subclassing values of type 'Any' when defining classes") - parser.add_argument('--warn-incomplete-stub', action='store_true', + add_invertible_flag('--warn-incomplete-stub', default=False, help="warn if missing type annotation in typeshed, only relevant with" " --check-untyped-defs enabled") - parser.add_argument('--warn-redundant-casts', action='store_true', + add_invertible_flag('--warn-redundant-casts', default=False, strict_flag=True, help="warn about casting an expression to its inferred type") - parser.add_argument('--warn-no-return', action='store_true', + add_invertible_flag('--warn-no-return', default=False, help="warn about functions that end without returning") - parser.add_argument('--warn-unused-ignores', action='store_true', + add_invertible_flag('--warn-unused-ignores', default=False, strict_flag=True, help="warn about unneeded '# type: ignore' comments") - parser.add_argument('--show-error-context', action='store_false', + add_invertible_flag('--show-error-context', default=True, dest='hide_error_context', help='Precede errors with "note:" messages explaining context') - parser.add_argument('--fast-parser', action='store_true', - help="enable fast parser (recommended except on Windows)") + add_invertible_flag('--fast-parser', default=False, + help="enable fast parser (recommended)") parser.add_argument('-i', '--incremental', action='store_true', help="enable experimental module cache") parser.add_argument('--cache-dir', action='store', metavar='DIR', help="store module cache info in the given folder in incremental mode " "(defaults to '{}')".format(defaults.CACHE_DIR)) - parser.add_argument('--strict-optional', action='store_true', - dest='strict_optional', + add_invertible_flag('--strict-optional', default=False, strict_flag=True, help="enable experimental strict Optional checks") parser.add_argument('--strict-optional-whitelist', metavar='GLOB', nargs='*', help="suppress strict Optional errors in all but the provided files " @@ -207,15 +255,17 @@ def process_options(args: List[str], parser.add_argument('--config-file', help="Configuration file, must have a [mypy] section " "(defaults to {})".format(defaults.CONFIG_FILE)) - parser.add_argument('--show-column-numbers', action='store_true', - dest='show_column_numbers', + add_invertible_flag('--show-column-numbers', default=False, help="Show column numbers in error messages") parser.add_argument('--find-occurrences', metavar='CLASS.MEMBER', dest='special-opts:find_occurrences', help="print out all usages of a class member (experimental)") - parser.add_argument('--strict-boolean', action='store_true', - dest='strict_boolean', + add_invertible_flag('--strict-boolean', default=False, strict_flag=True, help='enable strict boolean checks in conditions') + strict_help = "Strict mode. Enables the following flags: {}".format( + ", ".join(strict_flag_names)) + parser.add_argument('--strict', action='store_true', dest='special-opts:strict', + help=strict_help) # hidden options # --shadow-file a.py tmp.py will typecheck tmp.py in place of a.py. # Useful for tools to make transformations to a file to get more @@ -229,9 +279,6 @@ def process_options(args: List[str], parser.add_argument('--debug-cache', action='store_true', help=argparse.SUPPRESS) # --dump-graph will dump the contents of the graph of SCCs and exit. parser.add_argument('--dump-graph', action='store_true', help=argparse.SUPPRESS) - parser.add_argument('--hide-error-context', action='store_true', - dest='hide_error_context', - help=argparse.SUPPRESS) # deprecated options parser.add_argument('-f', '--dirty-stubs', action='store_true', dest='special-opts:dirty_stubs', @@ -270,7 +317,7 @@ def process_options(args: List[str], help="type-check given files or directories") # Parse arguments once into a dummy namespace so we can get the - # filename for the config file. + # filename for the config file and know if the user requested all strict options. dummy = argparse.Namespace() parser.parse_args(args, dummy) config_file = defaults.CONFIG_FILE @@ -284,6 +331,12 @@ def process_options(args: List[str], if config_file and os.path.exists(config_file): parse_config_file(options, config_file) + # Set strict flags before parsing (if strict mode enabled), so other command + # line options can override. + if getattr(dummy, 'special-opts:strict'): + for dest, value in strict_flag_assignments: + setattr(options, dest, value) + # Parse command line for real, using a split namespace. special_opts = argparse.Namespace() parser.parse_args(args, SplitNamespace(options, special_opts, 'special-opts:'))