Skip to content

Commit 038a2ec

Browse files
authored
Add inverted options and strict mode (#2710)
Inverted options either swap disallow/allow or show/hide at the beginning of a flag or add (or remove) a '--no-' prefix.
1 parent 6e3789e commit 038a2ec

File tree

2 files changed

+78
-22
lines changed

2 files changed

+78
-22
lines changed

docs/source/command_line.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ flag (or its long form ``--help``)::
1616
[--warn-incomplete-stub] [--warn-redundant-casts]
1717
[--warn-no-return] [--warn-unused-ignores] [--show-error-context]
1818
[--fast-parser] [-i] [--cache-dir DIR] [--strict-optional]
19-
[--strict-optional-whitelist [GLOB [GLOB ...]]]
19+
[--strict-optional-whitelist [GLOB [GLOB ...]]] [--strict]
2020
[--junit-xml JUNIT_XML] [--pdb] [--show-traceback] [--stats]
2121
[--inferstats] [--custom-typing MODULE]
2222
[--custom-typeshed-dir DIR] [--scripts-are-modules]
@@ -371,6 +371,9 @@ Here are some more useful flags:
371371
type other than ``bool``. Instead use explicit checks like ``if x > 0`` or
372372
``while x is not None``.
373373

374+
- ``--strict`` mode enables all optional error checking flags. You can see the
375+
list of flags enabled by strict mode in the full ``mypy -h`` output.
376+
374377
For the remaining flags you can read the full ``mypy -h`` output.
375378

376379
.. note::

mypy/main.py

Lines changed: 74 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,29 @@ def __init__(self, prog: Optional[str]) -> None:
126126
super().__init__(prog=prog, max_help_position=28)
127127

128128

129+
# Define pairs of flag prefixes with inverse meaning.
130+
flag_prefix_pairs = [
131+
('allow', 'disallow'),
132+
('show', 'hide'),
133+
]
134+
flag_prefix_map = {} # type: Dict[str, str]
135+
for a, b in flag_prefix_pairs:
136+
flag_prefix_map[a] = b
137+
flag_prefix_map[b] = a
138+
139+
140+
def invert_flag_name(flag: str) -> str:
141+
split = flag[2:].split('-', 1)
142+
if len(split) == 2:
143+
prefix, rest = split
144+
if prefix in flag_prefix_map:
145+
return '--{}-{}'.format(flag_prefix_map[prefix], rest)
146+
elif prefix == 'no':
147+
return '--{}'.format(rest)
148+
149+
return '--no-{}'.format(flag[2:])
150+
151+
129152
def process_options(args: List[str],
130153
require_targets: bool = True
131154
) -> Tuple[List[BuildSource], Options]:
@@ -135,6 +158,32 @@ def process_options(args: List[str],
135158
fromfile_prefix_chars='@',
136159
formatter_class=AugmentedHelpFormatter)
137160

161+
strict_flag_names = [] # type: List[str]
162+
strict_flag_assignments = [] # type: List[Tuple[str, bool]]
163+
164+
def add_invertible_flag(flag: str,
165+
*,
166+
inverse: str = None,
167+
default: bool,
168+
dest: str = None,
169+
help: str,
170+
strict_flag: bool = False
171+
) -> None:
172+
if inverse is None:
173+
inverse = invert_flag_name(flag)
174+
arg = parser.add_argument(flag, # type: ignore # incorrect stub for add_argument
175+
action='store_false' if default else 'store_true',
176+
dest=dest,
177+
help=help + " (inverse: {})".format(inverse))
178+
dest = arg.dest
179+
arg = parser.add_argument(inverse, # type: ignore # incorrect stub for add_argument
180+
action='store_true' if default else 'store_false',
181+
dest=dest,
182+
help=argparse.SUPPRESS)
183+
if strict_flag:
184+
strict_flag_names.append(flag)
185+
strict_flag_assignments.append((dest, not default))
186+
138187
# Unless otherwise specified, arguments will be parsed directly onto an
139188
# Options object. Options that require further processing should have
140189
# their `dest` prefixed with `special-opts:`, which will cause them to be
@@ -154,37 +203,36 @@ def process_options(args: List[str],
154203
help="silently ignore imports of missing modules")
155204
parser.add_argument('--follow-imports', choices=['normal', 'silent', 'skip', 'error'],
156205
default='normal', help="how to treat imports (default normal)")
157-
parser.add_argument('--disallow-untyped-calls', action='store_true',
206+
add_invertible_flag('--disallow-untyped-calls', default=False, strict_flag=True,
158207
help="disallow calling functions without type annotations"
159208
" from functions with type annotations")
160-
parser.add_argument('--disallow-untyped-defs', action='store_true',
209+
add_invertible_flag('--disallow-untyped-defs', default=False, strict_flag=True,
161210
help="disallow defining functions without type annotations"
162211
" or with incomplete type annotations")
163-
parser.add_argument('--check-untyped-defs', action='store_true',
212+
add_invertible_flag('--check-untyped-defs', default=False, strict_flag=True,
164213
help="type check the interior of functions without type annotations")
165-
parser.add_argument('--disallow-subclassing-any', action='store_true',
214+
add_invertible_flag('--disallow-subclassing-any', default=False, strict_flag=True,
166215
help="disallow subclassing values of type 'Any' when defining classes")
167-
parser.add_argument('--warn-incomplete-stub', action='store_true',
216+
add_invertible_flag('--warn-incomplete-stub', default=False,
168217
help="warn if missing type annotation in typeshed, only relevant with"
169218
" --check-untyped-defs enabled")
170-
parser.add_argument('--warn-redundant-casts', action='store_true',
219+
add_invertible_flag('--warn-redundant-casts', default=False, strict_flag=True,
171220
help="warn about casting an expression to its inferred type")
172-
parser.add_argument('--warn-no-return', action='store_true',
221+
add_invertible_flag('--warn-no-return', default=False,
173222
help="warn about functions that end without returning")
174-
parser.add_argument('--warn-unused-ignores', action='store_true',
223+
add_invertible_flag('--warn-unused-ignores', default=False, strict_flag=True,
175224
help="warn about unneeded '# type: ignore' comments")
176-
parser.add_argument('--show-error-context', action='store_false',
225+
add_invertible_flag('--show-error-context', default=True,
177226
dest='hide_error_context',
178227
help='Precede errors with "note:" messages explaining context')
179-
parser.add_argument('--fast-parser', action='store_true',
180-
help="enable fast parser (recommended except on Windows)")
228+
add_invertible_flag('--fast-parser', default=False,
229+
help="enable fast parser (recommended)")
181230
parser.add_argument('-i', '--incremental', action='store_true',
182231
help="enable experimental module cache")
183232
parser.add_argument('--cache-dir', action='store', metavar='DIR',
184233
help="store module cache info in the given folder in incremental mode "
185234
"(defaults to '{}')".format(defaults.CACHE_DIR))
186-
parser.add_argument('--strict-optional', action='store_true',
187-
dest='strict_optional',
235+
add_invertible_flag('--strict-optional', default=False, strict_flag=True,
188236
help="enable experimental strict Optional checks")
189237
parser.add_argument('--strict-optional-whitelist', metavar='GLOB', nargs='*',
190238
help="suppress strict Optional errors in all but the provided files "
@@ -207,15 +255,17 @@ def process_options(args: List[str],
207255
parser.add_argument('--config-file',
208256
help="Configuration file, must have a [mypy] section "
209257
"(defaults to {})".format(defaults.CONFIG_FILE))
210-
parser.add_argument('--show-column-numbers', action='store_true',
211-
dest='show_column_numbers',
258+
add_invertible_flag('--show-column-numbers', default=False,
212259
help="Show column numbers in error messages")
213260
parser.add_argument('--find-occurrences', metavar='CLASS.MEMBER',
214261
dest='special-opts:find_occurrences',
215262
help="print out all usages of a class member (experimental)")
216-
parser.add_argument('--strict-boolean', action='store_true',
217-
dest='strict_boolean',
263+
add_invertible_flag('--strict-boolean', default=False, strict_flag=True,
218264
help='enable strict boolean checks in conditions')
265+
strict_help = "Strict mode. Enables the following flags: {}".format(
266+
", ".join(strict_flag_names))
267+
parser.add_argument('--strict', action='store_true', dest='special-opts:strict',
268+
help=strict_help)
219269
# hidden options
220270
# --shadow-file a.py tmp.py will typecheck tmp.py in place of a.py.
221271
# Useful for tools to make transformations to a file to get more
@@ -229,9 +279,6 @@ def process_options(args: List[str],
229279
parser.add_argument('--debug-cache', action='store_true', help=argparse.SUPPRESS)
230280
# --dump-graph will dump the contents of the graph of SCCs and exit.
231281
parser.add_argument('--dump-graph', action='store_true', help=argparse.SUPPRESS)
232-
parser.add_argument('--hide-error-context', action='store_true',
233-
dest='hide_error_context',
234-
help=argparse.SUPPRESS)
235282
# deprecated options
236283
parser.add_argument('-f', '--dirty-stubs', action='store_true',
237284
dest='special-opts:dirty_stubs',
@@ -270,7 +317,7 @@ def process_options(args: List[str],
270317
help="type-check given files or directories")
271318

272319
# Parse arguments once into a dummy namespace so we can get the
273-
# filename for the config file.
320+
# filename for the config file and know if the user requested all strict options.
274321
dummy = argparse.Namespace()
275322
parser.parse_args(args, dummy)
276323
config_file = defaults.CONFIG_FILE
@@ -284,6 +331,12 @@ def process_options(args: List[str],
284331
if config_file and os.path.exists(config_file):
285332
parse_config_file(options, config_file)
286333

334+
# Set strict flags before parsing (if strict mode enabled), so other command
335+
# line options can override.
336+
if getattr(dummy, 'special-opts:strict'):
337+
for dest, value in strict_flag_assignments:
338+
setattr(options, dest, value)
339+
287340
# Parse command line for real, using a split namespace.
288341
special_opts = argparse.Namespace()
289342
parser.parse_args(args, SplitNamespace(options, special_opts, 'special-opts:'))

0 commit comments

Comments
 (0)