diff --git a/mypy/build.py b/mypy/build.py index 443f10bd1d61..1577fbbe27e1 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -301,7 +301,7 @@ def default_lib_path(data_dir: str, ]) # NOTE: dependencies + suppressed == all reachable imports; # suppressed contains those reachable imports that were prevented by -# --silent-imports or simply not found. +# silent mode or simply not found. # Priorities used for imports. (Here, top-level includes inside a class.) @@ -481,7 +481,7 @@ def module_not_found(self, path: str, line: int, id: str) -> None: else: self.errors.report(line, 0, "Cannot find module named '{}'".format(id)) self.errors.report(line, 0, '(Perhaps setting MYPYPATH ' - 'or using the "--silent-imports" flag would help)', + 'or using the "--ignore-missing-imports" flag would help)', severity='note', only_once=True) def report_file(self, file: MypyFile, type_map: Dict[Expression, Type]) -> None: @@ -1101,6 +1101,9 @@ class State: # Options, specialized for this file options = None # type: Options + # Whether to ignore all errors + ignore_all = False + def __init__(self, id: Optional[str], path: Optional[str], @@ -1137,16 +1140,25 @@ def __init__(self, file_id = '__builtin__' path = find_module(file_id, manager.lib_path) if path: - # In silent mode, don't import .py files, except from stubs. - if (self.options.silent_imports and - path.endswith('.py') and (caller_state or ancestor_for)): - # (Never silence builtins, even if it's a .py file; - # this can happen in tests!) - if (id != 'builtins' and - not ((caller_state and - caller_state.tree and - caller_state.tree.is_stub))): - if self.options.almost_silent: + # For non-stubs, look at options.follow_imports: + # - normal (default) -> fully analyze + # - silent -> analyze but silence errors + # - skip -> don't analyze, make the type Any + follow_imports = self.options.follow_imports + if (follow_imports != 'normal' + and path.endswith('.py') # Stubs are always normal + and id != 'builtins' # Builtins is always normal + and not (caller_state and + caller_state.tree and + caller_state.tree.is_stub)): + if follow_imports == 'silent': + # Still import it, but silence non-blocker errors. + manager.log("Silencing %s (%s)" % (path, id)) + self.ignore_all = True + else: + # In 'error' mode, produce special error messages. + manager.log("Skipping %s (%s)" % (path, id)) + if follow_imports == 'error': if ancestor_for: self.skipping_ancestor(id, path, ancestor_for) else: @@ -1159,9 +1171,7 @@ def __init__(self, # misspelled module name, missing stub, module not in # search path or the module has not been installed. if caller_state: - suppress_message = (self.options.silent_imports - and not self.options.almost_silent) - if not suppress_message: + if not self.options.ignore_missing_imports: save_import_context = manager.errors.import_context() manager.errors.set_import_context(caller_state.import_context) manager.module_not_found(caller_state.xpath, caller_line, id) @@ -1207,11 +1217,10 @@ def skipping_ancestor(self, id: str, path: str, ancestor_for: 'State') -> None: manager = self.manager manager.errors.set_import_context([]) manager.errors.set_file(ancestor_for.xpath) - manager.errors.report(-1, -1, "Ancestor package '%s' silently ignored" % (id,), + manager.errors.report(-1, -1, "Ancestor package '%s' ignored" % (id,), severity='note', only_once=True) - manager.errors.report(-1, -1, "(Using --silent-imports, submodule passed on command line)", - severity='note', only_once=True) - manager.errors.report(-1, -1, "(This note brought to you by --almost-silent)", + manager.errors.report(-1, -1, + "(Using --follow-imports=error, submodule passed on command line)", severity='note', only_once=True) def skipping_module(self, id: str, path: str) -> None: @@ -1222,12 +1231,10 @@ def skipping_module(self, id: str, path: str) -> None: manager.errors.set_file(self.caller_state.xpath) line = self.caller_line manager.errors.report(line, 0, - "Import of '%s' silently ignored" % (id,), + "Import of '%s' ignored" % (id,), severity='note') manager.errors.report(line, 0, - "(Using --silent-imports, module not passed on command line)", - severity='note', only_once=True) - manager.errors.report(line, 0, "(This note courtesy of --almost-silent)", + "(Using --follow-imports=error, module not passed on command line)", severity='note', only_once=True) manager.errors.set_import_context(save_import_context) @@ -1244,7 +1251,7 @@ def is_fresh(self) -> bool: """Return whether the cache data for this file is fresh.""" # NOTE: self.dependencies may differ from # self.meta.dependencies when a dependency is dropped due to - # suppression by --silent-imports. However when a suppressed + # suppression by silent mode. However when a suppressed # dependency is added back we find out later in the process. return (self.meta is not None and self.is_interface_fresh() @@ -1303,24 +1310,25 @@ def calculate_mros(self) -> None: fixup_module_pass_two(self.tree, self.manager.modules) def fix_suppressed_dependencies(self, graph: Graph) -> None: - """Corrects whether dependencies are considered stale or not when using silent_imports. + """Corrects whether dependencies are considered stale in silent mode. - This method is a hack to correct imports in silent_imports + incremental mode. + This method is a hack to correct imports in silent mode + incremental mode. In particular, the problem is that when running mypy with a cold cache, the `parse_file(...)` function is called *at the start* of the `load_graph(...)` function. Note that load_graph will mark some dependencies as suppressed if they weren't specified - on the command line in silent_imports mode. + on the command line in silent mode. However, if the interface for a module is changed, parse_file will be called within `process_stale_scc` -- *after* load_graph is finished, wiping out the changes load_graph previously made. This method is meant to be run after parse_file finishes in process_stale_scc and will - recompute what modules should be considered suppressed in silent_import mode. + recompute what modules should be considered suppressed in silent mode. """ # TODO: See if it's possible to move this check directly into parse_file in some way. # TODO: Find a way to write a test case for this fix. - silent_mode = self.options.silent_imports or self.options.almost_silent + silent_mode = (self.options.ignore_missing_imports or + self.options.follow_imports == 'skip') if not silent_mode: return @@ -1360,7 +1368,8 @@ def parse_file(self) -> None: except (UnicodeDecodeError, DecodeError) as decodeerr: raise CompileError([ "mypy: can't decode file '{}': {}".format(self.path, str(decodeerr))]) - self.tree = manager.parse_file(self.id, self.xpath, source, self.options.ignore_errors) + self.tree = manager.parse_file(self.id, self.xpath, source, + self.ignore_all or self.options.ignore_errors) modules[self.id] = self.tree @@ -1413,7 +1422,7 @@ def parse_file(self) -> None: # NOTE: What to do about race conditions (like editing the # file while mypy runs)? A previous version of this code # explicitly checked for this, but ran afoul of other reasons - # for differences (e.g. --silent-imports). + # for differences (e.g. silent mode). self.dependencies = dependencies self.suppressed = suppressed self.priorities = priorities diff --git a/mypy/errors.py b/mypy/errors.py index 90640978a919..702031d54727 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -204,7 +204,7 @@ def report(self, line: int, column: int, message: str, blocker: bool = False, def add_error_info(self, info: ErrorInfo) -> None: (file, line) = info.origin - if not info.blocker: + if not info.blocker: # Blockers cannot be ignored if file in self.ignored_lines and line in self.ignored_lines[file]: # Annotation requests us to ignore all errors on this line. self.used_ignored_lines[file].add(line) diff --git a/mypy/main.py b/mypy/main.py index 8812f943936a..c14faa54a590 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -148,10 +148,10 @@ def process_options(args: List[str], "(defaults to sys.platform).") parser.add_argument('-2', '--py2', dest='python_version', action='store_const', const=defaults.PYTHON2_VERSION, help="use Python 2 mode") - parser.add_argument('-s', '--silent-imports', action='store_true', - help="don't follow imports to .py files") - parser.add_argument('--almost-silent', action='store_true', - help="like --silent-imports but reports the imports as errors") + parser.add_argument('--ignore-missing-imports', action='store_true', + 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', help="disallow calling functions without type annotations" " from functions with type annotations") @@ -175,7 +175,7 @@ def process_options(args: List[str], dest='hide_error_context', help="Hide context notes before errors") parser.add_argument('--fast-parser', action='store_true', - help="enable experimental fast parser") + help="enable fast parser (recommended except on Windows)") parser.add_argument('-i', '--incremental', action='store_true', help="enable experimental module cache") parser.add_argument('--cache-dir', action='store', metavar='DIR', @@ -223,14 +223,18 @@ def process_options(args: List[str], # is easier to debug). parser.add_argument('--debug-cache', action='store_true', help=argparse.SUPPRESS) # deprecated options - parser.add_argument('--silent', action='store_true', dest='special-opts:silent', - help=argparse.SUPPRESS) parser.add_argument('-f', '--dirty-stubs', action='store_true', dest='special-opts:dirty_stubs', help=argparse.SUPPRESS) parser.add_argument('--use-python-path', action='store_true', dest='special-opts:use_python_path', help=argparse.SUPPRESS) + parser.add_argument('-s', '--silent-imports', action='store_true', + dest='special-opts:silent_imports', + help=argparse.SUPPRESS) + parser.add_argument('--almost-silent', action='store_true', + dest='special-opts:almost_silent', + help=argparse.SUPPRESS) report_group = parser.add_argument_group( title='report generation', @@ -259,7 +263,11 @@ def process_options(args: List[str], # filename for the config file. dummy = argparse.Namespace() parser.parse_args(args, dummy) - config_file = dummy.config_file or defaults.CONFIG_FILE + config_file = defaults.CONFIG_FILE + if dummy.config_file: + config_file = dummy.config_file + if not os.path.exists(config_file): + parser.error("Cannot file config file '%s'" % config_file) # Parse config file first, so command line can override. options = Options() @@ -278,11 +286,18 @@ def process_options(args: List[str], "See https://github.com/python/mypy/issues/1411 for more discussion." ) - # warn about deprecated options - if special_opts.silent: - print("Warning: --silent is deprecated; use --silent-imports", - file=sys.stderr) - options.silent_imports = True + # Process deprecated options + if special_opts.almost_silent: + print("Warning: --almost-silent has been replaced by " + "--follow=imports=errors", file=sys.stderr) + if options.follow_imports == 'normal': + options.follow_imports = 'errors' + elif special_opts.silent_imports: + print("Warning: --silent-imports has been replaced by " + "--ignore-missing-imports --follow=imports=skip", file=sys.stderr) + options.ignore_missing_imports = True + if options.follow_imports == 'normal': + options.follow_imports = 'skip' if special_opts.dirty_stubs: print("Warning: -f/--dirty-stubs is deprecated and no longer necessary. Mypy no longer " "checks the git status of stubs.", @@ -456,6 +471,9 @@ def get_init_file(dir: str) -> Optional[str]: 'custom_typeshed_dir': str, 'mypy_path': lambda s: [p.strip() for p in re.split('[,:]', s)], 'junit_xml': str, + # These two are for backwards compatibility + 'silent_imports': bool, + 'almost_silent': bool, } @@ -472,14 +490,13 @@ def parse_config_file(options: Options, filename: str) -> None: return if 'mypy' not in parser: print("%s: No [mypy] section in config file" % filename, file=sys.stderr) - return - - section = parser['mypy'] - prefix = '%s: [%s]' % (filename, 'mypy') - updates, report_dirs = parse_section(prefix, options, section) - for k, v in updates.items(): - setattr(options, k, v) - options.report_dirs.update(report_dirs) + else: + section = parser['mypy'] + prefix = '%s: [%s]' % (filename, 'mypy') + updates, report_dirs = parse_section(prefix, options, section) + for k, v in updates.items(): + setattr(options, k, v) + options.report_dirs.update(report_dirs) for name, section in parser.items(): if name.startswith('mypy-'): @@ -509,7 +526,7 @@ def parse_section(prefix: str, template: Options, Returns a dict of option values encountered, and a dict of report directories. """ - results = {} + results = {} # type: Dict[str, object] report_dirs = {} # type: Dict[str, str] for key in section: key = key.replace('-', '_') @@ -542,6 +559,20 @@ def parse_section(prefix: str, template: Options, except ValueError as err: print("%s: %s: %s" % (prefix, key, err), file=sys.stderr) continue + if key == 'silent_imports': + print("%s: silent_imports has been replaced by " + "ignore_missing_imports=True; follow_imports=skip" % prefix, file=sys.stderr) + if v: + if 'ignore_missing_imports' not in results: + results['ignore_missing_imports'] = True + if 'follow_imports' not in results: + results['follow_imports'] = 'skip' + if key == 'almost_silent': + print("%s: almost_silent has been replaced by " + "follow_imports=error" % prefix, file=sys.stderr) + if v: + if 'follow_imports' not in results: + results['follow_imports'] = 'error' results[key] = v return results, report_dirs diff --git a/mypy/options.py b/mypy/options.py index a561adcf24be..68daf873cc7d 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -17,8 +17,8 @@ class Options: """Options collected from flags.""" PER_MODULE_OPTIONS = { - "silent_imports", - "almost_silent", + "ignore_missing_imports", + "follow_imports", "disallow_untyped_calls", "disallow_untyped_defs", "check_untyped_defs", @@ -40,8 +40,8 @@ def __init__(self) -> None: self.custom_typeshed_dir = None # type: Optional[str] self.mypy_path = [] # type: List[str] self.report_dirs = {} # type: Dict[str, str] - self.silent_imports = False - self.almost_silent = False + self.ignore_missing_imports = False + self.follow_imports = 'normal' # normal|silent|skip|error # Disallow calling untyped functions from typed ones self.disallow_untyped_calls = False diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index 885570197568..1ac6b88992b8 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -125,14 +125,7 @@ def run_case_once(self, testcase: DataDrivenTestCase, incremental=0) -> None: original_program_text = '\n'.join(testcase.input) module_data = self.parse_module(original_program_text, incremental) - options = self.parse_options(original_program_text, testcase) - options.use_builtins_fixtures = True - options.show_traceback = True - if 'optional' in testcase.file: - options.strict_optional = True - if incremental: - options.incremental = True if incremental == 1: # In run 1, copy program text to program file. for module_name, program_path, program_text in module_data: @@ -141,10 +134,10 @@ def run_case_once(self, testcase: DataDrivenTestCase, incremental=0) -> None: f.write(program_text) break elif incremental == 2: - # In run 2, copy *.py.next files to *.py files. + # In run 2, copy *.next files to * files. for dn, dirs, files in os.walk(os.curdir): for file in files: - if file.endswith('.py.next'): + if file.endswith('.next'): full = os.path.join(dn, file) target = full[:-5] shutil.copy(full, target) @@ -155,6 +148,15 @@ def run_case_once(self, testcase: DataDrivenTestCase, incremental=0) -> None: new_time = os.stat(target).st_mtime + 1 os.utime(target, times=(new_time, new_time)) + # Parse options after moving files (in case mypy.ini is being moved). + options = self.parse_options(original_program_text, testcase) + options.use_builtins_fixtures = True + options.show_traceback = True + if 'optional' in testcase.file: + options.strict_optional = True + if incremental: + options.incremental = True + sources = [] for module_name, program_path, program_text in module_data: # Always set to none so we're forced to reread the module in incremental mode @@ -188,7 +190,7 @@ def run_case_once(self, testcase: DataDrivenTestCase, incremental=0) -> None: assert_string_arrays_equal(output, a, msg.format(testcase.file, testcase.line)) if incremental and res: - if not options.silent_imports and testcase.output is None: + if options.follow_imports == 'normal' and testcase.output is None: self.verify_cache(module_data, a, res.manager) if incremental == 2: self.check_module_equivalence( diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 4bd422d91ce2..ada05a73ac68 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -49,7 +49,7 @@ class Foo(ActualClass, FakeClass): pass # E: Class cannot subclass 'FakeClass' [out] [case testSubclassingAnySilentImports] -# flags: --disallow-subclassing-any --silent-imports +# flags: --disallow-subclassing-any --follow-imports=skip # cmd: mypy -m main [file main.py] @@ -63,7 +63,7 @@ class BaseClass: pass tmp/main.py:2: error: Class cannot subclass 'BaseClass' (has type 'Any') [case testSubclassingAnySilentImports2] -# flags: --disallow-subclassing-any --silent-imports +# flags: --disallow-subclassing-any --follow-imports=skip # cmd: mypy -m main [file main.py] @@ -180,3 +180,51 @@ import b tmp/a.py:1: note: In module imported here, main:2: note: ... from here: tmp/b.py:1: error: Unsupported operand types for + ("int" and "str") + +[case testFollowImportsNormal] +# flags: --follow-imports=normal +from mod import x +x + "" +[file mod.py] +1 + "" +x = 0 +[out] +tmp/mod.py:1: error: Unsupported operand types for + ("int" and "str") +main:3: error: Unsupported operand types for + ("int" and "str") + +[case testFollowImportsSilent] +# flags: --follow-imports=silent +from mod import x +x + "" # E: Unsupported operand types for + ("int" and "str") +[file mod.py] +1 + "" +x = 0 + +[case testFollowImportsSkip] +# flags: --follow-imports=skip +from mod import x +x + "" +[file mod.py] +this deliberate syntax error will not be reported +[out] + +[case testFollowImportsError] +# flags: --follow-imports=error +from mod import x +x + "" +[file mod.py] +deliberate syntax error +[out] +main:2: note: Import of 'mod' ignored +main:2: note: (Using --follow-imports=error, module not passed on command line) + +[case testIgnoreMissingImportsFalse] +from mod import x +[out] +main:1: error: Cannot find module named 'mod' +main:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) + +[case testIgnoreMissingImportsTrue] +# flags: --ignore-missing-imports +from mod import x +[out] diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 3930ce0d8f98..67e0aae9b95b 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -982,7 +982,7 @@ import a.b tmp/b.py:4: error: Name 'a' already defined [case testIncrementalSilentImportsAndImportsInClass] -# flags: --silent-imports +# flags: --ignore-missing-imports class MyObject(object): from bar import FooBar [stale] @@ -1010,7 +1010,7 @@ tmp/m.py:4: error: Argument 1 to "bar" has incompatible type "int"; expected "st [case testIncrementalUnsilencingModule] # cmd: mypy -m main package.subpackage.mod2 # cmd2: mypy -m main package.subpackage.mod1 -# flags: --silent-imports +# flags: --follow-imports=skip [file main.py] from package.subpackage.mod1 import Class @@ -1047,7 +1047,7 @@ import foo # type: ignore [case testIncrementalWithSilentImportsAndIgnore] # cmd: mypy -m main b # cmd2: mypy -m main c c.submodule -# flags: --silent-imports +# flags: --follow-imports=skip [file main.py] import a # type: ignore @@ -1195,7 +1195,7 @@ tmp/m1.py:3: error: Argument 1 to "accepts_int" has incompatible type "str"; exp [case testIncrementalSilentImportsWithBlatantError] # cmd: mypy -m main -# flags: --silent-imports +# flags: --follow-imports=skip [file main.py] from evil import Hello @@ -1216,7 +1216,7 @@ tmp/main.py:2: error: Revealed type is 'Any' [case testIncrementalImportIsNewlySilenced] # cmd: mypy -m main foo # cmd2: mypy -m main -# flags: --silent-imports +# flags: --follow-imports=skip [file main.py] from foo import bar @@ -1235,7 +1235,7 @@ bar = 3 [case testIncrementalSilencedModuleNoLongerCausesError] # cmd: mypy -m main evil # cmd2: mypy -m main -# flags: --silent-imports +# flags: --follow-imports=skip [file main.py] from evil import bar @@ -1377,7 +1377,7 @@ tmp/mod1.py:3: error: Revealed type is 'builtins.str' [case testIncrementalSilentImportsWithInnerImports] # cmd: mypy -m main foo -# flags: --silent-imports +# flags: --ignore-missing-imports [file main.py] from foo import MyClass @@ -1402,7 +1402,7 @@ tmp/main.py:3: error: Revealed type is 'Any' [case testIncrementalSilentImportsWithInnerImportsAndNewFile] # cmd: mypy -m main foo # cmd2: mypy -m main foo unrelated -# flags: --silent-imports +# flags: --follow-imports=skip [file main.py] from foo import MyClass @@ -1509,7 +1509,7 @@ tmp/foo.py:3: error: Argument 1 to "accept_int" has incompatible type "str"; exp [case testIncrementalPartialSubmoduleUpdate] # cmd: mypy -m a # cmd2: mypy -m a a.c -# flags: --silent-imports +# flags: --follow-imports=skip [file a/__init__.py] from .b import B @@ -1587,3 +1587,56 @@ class Z(X, Y): pass main:8: error: Definition of "attr" in base class "X" is incompatible with definition in base class "Y" [out2] main:8: error: Definition of "attr" in base class "X" is incompatible with definition in base class "Y" + +[case testIncrementalFollowImportsSilent] +# flags: --follow-imports=silent +import a +[file a.py] +x = 0 +[file a.py.next] +x = 0 +x + '' + +[case testIncrementalFollowImportsSkip] +# flags: --follow-imports=skip +import a +reveal_type(a.x) +[file a.py] +/ +[file a.py.next] +// +[out] +main:3: error: Revealed type is 'Any' +[out2] +main:3: error: Revealed type is 'Any' + +[case testIncrementalFollowImportsError] +# flags: --follow-imports=error +import a +[file a.py] +/ +[file a.py.next] +// +[out1] +main:2: note: Import of 'a' ignored +main:2: note: (Using --follow-imports=error, module not passed on command line) +[out2] +main:2: note: Import of 'a' ignored +main:2: note: (Using --follow-imports=error, module not passed on command line) + +[case testIncrementalFollowImportsVariable] +# flags: --config-file tmp/mypy.ini +import a +reveal_type(a.x) +[file a.py] +x = 0 +[file mypy.ini] +[[mypy] +follow_imports = normal +[file mypy.ini.next] +[[mypy] +follow_imports = skip +[out1] +main:3: error: Revealed type is 'builtins.int' +[out2] +main:3: error: Revealed type is 'Any' diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index 7a8851557511..19db7eb91ab2 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -199,7 +199,7 @@ import nonexistent None + '' [out] main:1: error: Cannot find module named 'nonexistent' -main:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) main:2: error: Unsupported left operand type for + (None) [case testTypeCheckWithUnknownModule2] @@ -211,7 +211,7 @@ m.x = '' x = 1 [out] main:1: error: Cannot find module named 'nonexistent' -main:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) main:2: error: Unsupported left operand type for + (None) main:4: error: Incompatible types in assignment (expression has type "str", variable has type "int") @@ -224,7 +224,7 @@ m.x = '' x = 1 [out] main:1: error: Cannot find module named 'nonexistent' -main:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) main:2: error: Unsupported left operand type for + (None) main:4: error: Incompatible types in assignment (expression has type "str", variable has type "int") @@ -233,7 +233,7 @@ import nonexistent, another None + '' [out] main:1: error: Cannot find module named 'nonexistent' -main:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) main:1: error: Cannot find module named 'another' main:2: error: Unsupported left operand type for + (None) @@ -242,7 +242,7 @@ import nonexistent as x None + '' [out] main:1: error: Cannot find module named 'nonexistent' -main:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) main:2: error: Unsupported left operand type for + (None) [case testTypeCheckWithUnknownModuleUsingFromImport] @@ -250,7 +250,7 @@ from nonexistent import x None + '' [out] main:1: error: Cannot find module named 'nonexistent' -main:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) main:2: error: Unsupported left operand type for + (None) [case testTypeCheckWithUnknownModuleUsingImportStar] @@ -258,7 +258,7 @@ from nonexistent import * None + '' [out] main:1: error: Cannot find module named 'nonexistent' -main:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) main:2: error: Unsupported left operand type for + (None) [case testAccessingUnknownModule] @@ -267,7 +267,7 @@ xyz.foo() xyz() [out] main:1: error: Cannot find module named 'xyz' -main:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) [case testAccessingUnknownModule2] import xyz, bar @@ -275,7 +275,7 @@ xyz.foo() bar() [out] main:1: error: Cannot find module named 'xyz' -main:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) main:1: error: Cannot find module named 'bar' [case testAccessingUnknownModule3] @@ -284,7 +284,7 @@ xyz.foo() z() [out] main:1: error: Cannot find module named 'xyz' -main:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) main:2: error: Name 'xyz' is not defined [case testAccessingNameImportedFromUnknownModule] @@ -293,14 +293,14 @@ y.foo() z() [out] main:1: error: Cannot find module named 'xyz' -main:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) [case testAccessingNameImportedFromUnknownModule2] from xyz import * y [out] main:1: error: Cannot find module named 'xyz' -main:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) main:2: error: Name 'y' is not defined [case testAccessingNameImportedFromUnknownModule3] @@ -309,7 +309,7 @@ y z [out] main:1: error: Cannot find module named 'xyz' -main:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) main:2: error: Name 'y' is not defined [case testUnknownModuleRedefinition] @@ -317,7 +317,7 @@ import xab def xab(): pass [out] main:1: error: Cannot find module named 'xab' -main:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) [case testAccessingUnknownModuleFromOtherModule] import x @@ -328,7 +328,7 @@ import nonexistent [builtins fixtures/module.pyi] [out] tmp/x.py:1: error: Cannot find module named 'nonexistent' -tmp/x.py:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +tmp/x.py:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) main:3: error: "module" has no attribute "z" [case testUnknownModuleImportedWithinFunction] @@ -338,7 +338,7 @@ def foobar(): pass foobar('') [out] main:2: error: Cannot find module named 'foobar' -main:2: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:2: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) main:4: error: Too many arguments for "foobar" [case testUnknownModuleImportedWithinFunction2] @@ -348,7 +348,7 @@ def x(): pass x('') [out] main:2: error: Cannot find module named 'foobar' -main:2: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:2: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) main:4: error: Too many arguments for "x" [case testRelativeImports] @@ -931,30 +931,28 @@ tmp/bar.py:1: error: Module 'foo' has no attribute 'B' [case testImportSuppressedWhileAlmostSilent] # cmd: mypy -m main -# flags: --silent-imports --almost-silent +# flags: --follow-imports=error [file main.py] import mod [file mod.py] [builtins fixtures/module.pyi] [out] -tmp/main.py:1: note: Import of 'mod' silently ignored -tmp/main.py:1: note: (Using --silent-imports, module not passed on command line) -tmp/main.py:1: note: (This note courtesy of --almost-silent) +tmp/main.py:1: note: Import of 'mod' ignored +tmp/main.py:1: note: (Using --follow-imports=error, module not passed on command line) [case testAncestorSuppressedWhileAlmostSilent] # cmd: mypy -m foo.bar -# flags: --silent-imports --almost-silent +# flags: --follow-imports=error [file foo/bar.py] [file foo/__init__.py] [builtins fixtures/module.pyi] [out] -tmp/foo/bar.py: note: Ancestor package 'foo' silently ignored -tmp/foo/bar.py: note: (Using --silent-imports, submodule passed on command line) -tmp/foo/bar.py: note: (This note brought to you by --almost-silent) +tmp/foo/bar.py: note: Ancestor package 'foo' ignored +tmp/foo/bar.py: note: (Using --follow-imports=error, submodule passed on command line) [case testStubImportNonStubWhileSilent] # cmd: mypy -m main -# flags: --silent-imports +# flags: --follow-imports=skip [file main.py] from stub import x # Permitted from other import y # Disallowed diff --git a/test-data/unit/check-python2.test b/test-data/unit/check-python2.test index b17d889f892c..99a55f0c36cd 100644 --- a/test-data/unit/check-python2.test +++ b/test-data/unit/check-python2.test @@ -144,7 +144,7 @@ import asyncio import Bastion [out] main:1: error: Cannot find module named 'asyncio' -main:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) main:2: error: No library stub file for standard library module 'Bastion' main:2: note: (Stub files are from https://github.com/python/typeshed) diff --git a/test-data/unit/check-semanal-error.test b/test-data/unit/check-semanal-error.test index b1953154e1b7..0a078290a1a7 100644 --- a/test-data/unit/check-semanal-error.test +++ b/test-data/unit/check-semanal-error.test @@ -19,7 +19,7 @@ m.x = m.y 1() # E [out] main:1: error: Cannot find module named 'm' -main:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) main:4: error: "int" not callable [case testMissingModuleImport2] @@ -29,7 +29,7 @@ x.a = x.b 1() # E [out] main:1: error: Cannot find module named 'm' -main:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) main:4: error: "int" not callable [case testMissingModuleImport3] @@ -38,7 +38,7 @@ x # E 1() # E [out] main:1: error: Cannot find module named 'm' -main:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) main:2: error: Name 'x' is not defined main:3: error: "int" not callable diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index da0044467710..f54c5af29baa 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -78,7 +78,7 @@ else: [builtins fixtures/bool.pyi] [out] main:6: error: Cannot find module named 'pow123' -main:6: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:6: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) [case testMypyConditional] import typing @@ -97,7 +97,7 @@ else: import xyz753 [out] main:3: error: Cannot find module named 'pow123' -main:3: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:3: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) [case testTypeCheckingConditionalFromImport] from typing import TYPE_CHECKING @@ -107,7 +107,7 @@ else: import xyz753 [out] main:3: error: Cannot find module named 'pow123' -main:3: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:3: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) [case testNegatedTypeCheckingConditional] import typing @@ -118,7 +118,7 @@ else: [builtins fixtures/bool.pyi] [out] main:5: error: Cannot find module named 'xyz753' -main:5: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:5: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) [case testUndefinedTypeCheckingConditional] if not TYPE_CHECKING: # E @@ -129,7 +129,7 @@ else: [out] main:1: error: Name 'TYPE_CHECKING' is not defined main:4: error: Cannot find module named 'xyz753' -main:4: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:4: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) [case testConditionalClassDefPY3] def f(): pass diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index 46a9848ef16b..9a172856e0d2 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -229,9 +229,9 @@ mypy.ini: [mypy]: Unrecognized report type: bad_report # cmd: mypy -c pass [file mypy.ini] [[mypy] -silent_imports = nah +ignore_missing_imports = nah [out] -mypy.ini: [mypy]: silent_imports: Not a boolean: nah +mypy.ini: [mypy]: ignore_missing_imports: Not a boolean: nah [case testConfigErrorNotPerFile] # cmd: mypy -c pass @@ -319,7 +319,7 @@ baz(bar(foo(42))) baz(bar(foo('oof'))) [out] file.py:1: error: Cannot find module named 'no_stubs' -file.py:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +file.py:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) file.py:6: error: Argument 1 to "foo" has incompatible type "str"; expected "int" [case testIgnoreErrorsConfig] @@ -334,3 +334,146 @@ ignore_errors = True "" + 0 [out] y.py:1: error: Unsupported operand types for + ("str" and "int") + +[case testConfigFollowImportsNormal] +# cmd: mypy main.py +[file main.py] +from a import x +x + 0 +x + '' # E +import a +a.x + 0 +a.x + '' # E +a.y # E +a + 0 # E +[file mypy.ini] +[[mypy] +follow_imports = normal +[file a.py] +x = 0 +x += '' # Error reported here +[out] +a.py:2: error: Unsupported operand types for + ("int" and "str") +main.py:3: error: Unsupported operand types for + ("int" and "str") +main.py:6: error: Unsupported operand types for + ("int" and "str") +main.py:7: error: "module" has no attribute "y" +main.py:8: error: Unsupported operand types for + ("module" and "int") + +[case testConfigFollowImportsSilent] +# cmd: mypy main.py +[file main.py] +from a import x +x + '' +import a +a.x + '' +a.y +a + 0 +[file mypy.ini] +[[mypy] +follow_imports = silent +[file a.py] +x = 0 +x += '' # No error reported +[out] +main.py:2: error: Unsupported operand types for + ("int" and "str") +main.py:4: error: Unsupported operand types for + ("int" and "str") +main.py:5: error: "module" has no attribute "y" +main.py:6: error: Unsupported operand types for + ("module" and "int") + +[case testConfigFollowImportsSkip] +# cmd: mypy main.py +[file main.py] +from a import x +reveal_type(x) # Expect Any +import a +reveal_type(a.x) # Expect Any +[file mypy.ini] +[[mypy] +follow_imports = skip +[file a.py] +/ # No error reported +[out] +main.py:2: error: Revealed type is 'Any' +main.py:4: error: Revealed type is 'Any' + +[case testConfigFollowImportsError] +# cmd: mypy main.py +[file main.py] +from a import x +reveal_type(x) # Expect Any +import a # Error reported here +reveal_type(a.x) # Expect Any +[file mypy.ini] +[[mypy] +follow_imports = error +[file a.py] +/ # No error reported +[out] +main.py:1: note: Import of 'a' ignored +main.py:1: note: (Using --follow-imports=error, module not passed on command line) +main.py:2: error: Revealed type is 'Any' +main.py:4: error: Revealed type is 'Any' + +[case testConfigFollowImportsSelective] +# cmd: mypy main.py +[file mypy.ini] +[[mypy] +[[mypy-normal] +follow_imports = normal +[[mypy-silent] +follow_imports = silent +[[mypy-skip] +follow_imports = skip +[[mypy-error] +follow_imports = error +[file main.py] +import normal +import silent +import skip +import error +reveal_type(normal.x) +reveal_type(silent.x) +reveal_type(skip) +reveal_type(error) +[file normal.py] +x = 0 +x += '' +[file silent.py] +x = 0 +x += '' +[file skip.py] +bla bla +[file error.py] +bla bla +[out] +main.py:4: note: Import of 'error' ignored +main.py:4: note: (Using --follow-imports=error, module not passed on command line) +normal.py:2: error: Unsupported operand types for + ("int" and "str") +main.py:5: error: Revealed type is 'builtins.int' +main.py:6: error: Revealed type is 'builtins.int' +main.py:7: error: Revealed type is 'Any' +main.py:8: error: Revealed type is 'Any' + +[case testConfigSilentMissingImportsOff] +# cmd: mypy main.py +[file main.py] +import missing # Expect error here +reveal_type(missing.x) # Expect Any +[file mypy.ini] +[[mypy] +ignore_missing_imports = False +[out] +main.py:1: error: Cannot find module named 'missing' +main.py:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) +main.py:2: error: Revealed type is 'Any' + +[case testConfigSilentMissingImportsOn] +# cmd: mypy main.py +[file main.py] +import missing # No error here +reveal_type(missing.x) # Expect Any +[file mypy.ini] +[[mypy] +ignore_missing_imports = True +[out] +main.py:2: error: Revealed type is 'Any' diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index c114fdf1a66e..cc222d9799b7 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -222,21 +222,21 @@ import typing import m [out] main:2: error: Cannot find module named 'm' -main:2: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:2: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) [case testMissingModule2] import typing from m import x [out] main:2: error: Cannot find module named 'm' -main:2: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:2: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) [case testMissingModule3] import typing from m import * [out] main:2: error: Cannot find module named 'm' -main:2: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:2: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) [case testMissingModuleRelativeImport] import typing @@ -245,7 +245,7 @@ import m from .x import y [out] tmp/m/__init__.py:1: error: Cannot find module named 'm.x' -tmp/m/__init__.py:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +tmp/m/__init__.py:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) [case testMissingModuleRelativeImport2] import typing @@ -255,7 +255,7 @@ import m.a from .x import y [out] tmp/m/a.py:1: error: Cannot find module named 'm.x' -tmp/m/a.py:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +tmp/m/a.py:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) [case testModuleNotImported] import typing @@ -304,7 +304,7 @@ import typing import m.n [out] main:2: error: Cannot find module named 'm' -main:2: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:2: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) main:2: error: Cannot find module named 'm.n' [case testMissingPackage] @@ -313,7 +313,7 @@ from m.n import x from a.b import * [out] main:2: error: Cannot find module named 'm.n' -main:2: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:2: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) main:3: error: Cannot find module named 'a.b' [case testErrorInImportedModule] @@ -343,7 +343,7 @@ m.n.x x = 1 [out] main:2: error: Cannot find module named 'm' -main:2: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help) +main:2: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) main:2: error: Cannot find module named 'm.n' [case testBreakOutsideLoop]