コード例 #1
0
ファイル: testsemanal.py プロジェクト: yang/mypy-hack
 def run_test(self, testcase):
     """Perform a test case."""
     try:
         # Build test case input.
         src = '\n'.join(testcase.input)
         result = build.build(target=build.SEMANTIC_ANALYSIS,
                              sources=[BuildSource('main', None, src)],
                              flags=[build.TEST_BUILTINS],
                              alt_lib_path=test_temp_dir)
         # The output is the symbol table converted into a string.
         a = []
         for f in sorted(result.files.keys()):
             if f not in ('builtins', 'typing', 'abc'):
                 a.append('{}:'.format(f))
                 for s in str(result.files[f].names).split('\n'):
                     a.append('  ' + s)
     except CompileError as e:
         a = e.messages
     assert_string_arrays_equal(
         testcase.output, a,
         'Invalid semantic analyzer output ({}, line {})'.format(
             testcase.file, testcase.line))
コード例 #2
0
ファイル: testsemanal.py プロジェクト: CapstoneProj5/LMNFlask
def test_semanal(testcase: DataDrivenTestCase) -> None:
    """Perform a semantic analysis test case.

    The testcase argument contains a description of the test case
    (inputs and output).
    """

    try:
        src = '\n'.join(testcase.input)
        options = get_semanal_options()
        options.python_version = testfile_pyversion(testcase.file)
        result = build.build(sources=[BuildSource('main', None, src)],
                             options=options,
                             alt_lib_path=test_temp_dir)
        a = result.errors
        if a:
            raise CompileError(a)
        # Include string representations of the source files in the actual
        # output.
        for fnam in sorted(result.files.keys()):
            f = result.files[fnam]
            # Omit the builtins module and files with a special marker in the
            # path.
            # TODO the test is not reliable
            if (not f.path.endswith(
                (os.sep + 'builtins.pyi', 'typing.pyi', 'mypy_extensions.pyi',
                 'abc.pyi', 'collections.pyi'))
                    and not os.path.basename(f.path).startswith('_')
                    and not os.path.splitext(os.path.basename(
                        f.path))[0].endswith('_')):
                a += str(f).split('\n')
    except CompileError as e:
        a = e.messages
    a = normalize_error_messages(a)
    assert_string_arrays_equal(
        testcase.output, a,
        'Invalid semantic analyzer output ({}, line {})'.format(
            testcase.file, testcase.line))
コード例 #3
0
    def parse_sources(self, program_text: str,
                      incremental_step: int,
                      options: Options) -> List[BuildSource]:
        """Return target BuildSources for a test case.

        Normally, the unit tests will check all files included in the test
        case. This differs from how testcheck works by default, as dmypy
        doesn't currently support following imports.

        You can override this behavior and instruct the tests to check
        multiple modules by using a comment like this in the test case
        input:

          # cmd: main a.py

        You can also use `# cmdN:` to have a different cmd for incremental
        step N (2, 3, ...).

        """
        m = re.search('# cmd: mypy ([a-zA-Z0-9_./ ]+)$', program_text, flags=re.MULTILINE)
        regex = '# cmd{}: mypy ([a-zA-Z0-9_./ ]+)$'.format(incremental_step)
        alt_m = re.search(regex, program_text, flags=re.MULTILINE)
        if alt_m is not None:
            # Optionally return a different command if in a later step
            # of incremental mode, otherwise default to reusing the
            # original cmd.
            m = alt_m

        if m:
            # The test case wants to use a non-default set of files.
            paths = [os.path.join(test_temp_dir, path) for path in m.group(1).strip().split()]
            return create_source_list(paths, options)
        else:
            base = BuildSource(os.path.join(test_temp_dir, 'main'), '__main__', None)
            # Use expand_dir instead of create_source_list to avoid complaints
            # when there aren't any .py files in an increment
            return [base] + create_source_list([test_temp_dir], options,
                                               allow_empty_dir=True)
コード例 #4
0
ファイル: testtransform.py プロジェクト: wlmgithub/mypy
def test_transform(testcase: DataDrivenTestCase) -> None:
    """Perform an identity transform test case."""

    try:
        src = '\n'.join(testcase.input)
        options = Options()
        options.use_builtins_fixtures = True
        options.semantic_analysis_only = True
        options.show_traceback = True
        options.python_version = testfile_pyversion(testcase.file)
        result = build.build(sources=[BuildSource('main', None, src)],
                             options=options,
                             alt_lib_path=test_temp_dir)
        a = result.errors
        if a:
            raise CompileError(a)
        # Include string representations of the source files in the actual
        # output.
        for fnam in sorted(result.files.keys()):
            f = result.files[fnam]

            # Omit the builtins module and files with a special marker in the
            # path.
            # TODO the test is not reliable
            if (not f.path.endswith(
                (os.sep + 'builtins.pyi', 'typing.pyi', 'abc.pyi'))
                    and not os.path.basename(f.path).startswith('_')
                    and not os.path.splitext(os.path.basename(
                        f.path))[0].endswith('_')):
                t = TestTransformVisitor()
                f = t.mypyfile(f)
                a += str(f).split('\n')
    except CompileError as e:
        a = e.messages
    assert_string_arrays_equal(
        testcase.output, a,
        'Invalid semantic analyzer output ({}, line {})'.format(
            testcase.file, testcase.line))
コード例 #5
0
ファイル: testsemanal.py プロジェクト: umkay/mypy
 def run_test(self, testcase: DataDrivenTestCase) -> None:
     """Perform a test case."""
     try:
         # Build test case input.
         src = '\n'.join(testcase.input)
         result = build.build(sources=[BuildSource('main', None, src)],
                              options=get_semanal_options(),
                              alt_lib_path=test_temp_dir)
         # The output is the symbol table converted into a string.
         a = result.errors
         if a:
             raise CompileError(a)
         for f in sorted(result.files.keys()):
             if f not in ('builtins', 'typing', 'abc'):
                 a.append('{}:'.format(f))
                 for s in str(result.files[f].names).split('\n'):
                     a.append('  ' + s)
     except CompileError as e:
         a = e.messages
     assert_string_arrays_equal(
         testcase.output, a,
         'Invalid semantic analyzer output ({}, line {})'.format(
             testcase.file, testcase.line))
コード例 #6
0
ファイル: testcheck.py プロジェクト: icoxfog417/mypy
    def run_test(self, testcase):
        a = []
        pyversion = testcase_pyversion(testcase.file, testcase.name)
        program_text = '\n'.join(testcase.input)
        module_name, program_name, program_text = self.parse_options(
            program_text)
        source = BuildSource(program_name, module_name, program_text)
        try:
            build.build(target=build.TYPE_CHECK,
                        sources=[source],
                        pyversion=pyversion,
                        flags=[build.TEST_BUILTINS],
                        alt_lib_path=test_temp_dir)
        except CompileError as e:
            a = normalize_error_messages(e.messages)

        if testcase.output != a and mypy.myunit.UPDATE_TESTCASES:
            update_testcase_output(testcase, a, mypy.myunit.APPEND_TESTCASES)

        assert_string_arrays_equal(
            testcase.output, a,
            'Invalid type checker output ({}, line {})'.format(
                testcase.file, testcase.line))
コード例 #7
0
def load_module(
    module: str
) -> Generator[ChainMap[nodes.Expression, types.Type], None, None]:
    if not module.startswith("steam"):  # typing_extensions.TypeAlias
        yield ChainMap()
        return

    try:
        yield cache[module]
        return
    except KeyError:
        pass

    mod = importlib.import_module(module)

    path = ROOT / f"{module.replace('.', '/')}.py"
    original = path.read_text()

    path.write_text(
        f"{original}\n\n_: int = '1'"
    )  # needed to make sure .types works, TODO make an issue about this?

    try:
        result = build([BuildSource(mod.__file__, mod.__name__)],
                       options,
                       stdout=stdout,
                       stderr=stderr)
        types = result.types

        for file in result.files.values():
            cache[file.fullname] = ChainMap(
                {func.node: func.type
                 for func in file.names.values()}, types)

        yield cache[module]
    finally:
        path.write_text(original)
コード例 #8
0
ファイル: testcheck.py プロジェクト: lslaoang/mypy
    def run_case_once(self, testcase: DataDrivenTestCase, incremental_step: int = 0) -> None:
        find_module_clear_caches()
        original_program_text = '\n'.join(testcase.input)
        module_data = self.parse_module(original_program_text, incremental_step)

        if incremental_step:
            if incremental_step == 1:
                # In run 1, copy program text to program file.
                for module_name, program_path, program_text in module_data:
                    if module_name == '__main__':
                        with open(program_path, 'w') as f:
                            f.write(program_text)
                        break
            elif incremental_step > 1:
                # In runs 2+, copy *.[num] files to * files.
                for dn, dirs, files in os.walk(os.curdir):
                    for file in files:
                        if file.endswith('.' + str(incremental_step)):
                            full = os.path.join(dn, file)
                            target = full[:-2]
                            # Use retries to work around potential flakiness on Windows (AppVeyor).
                            retry_on_error(lambda: shutil.copy(full, target))

                            # In some systems, mtime has a resolution of 1 second which can cause
                            # annoying-to-debug issues when a file has the same size after a
                            # change. We manually set the mtime to circumvent this.
                            new_time = os.stat(target).st_mtime + 1
                            os.utime(target, times=(new_time, new_time))
                # Delete files scheduled to be deleted in [delete <path>.num] sections.
                for path in testcase.deleted_paths.get(incremental_step, set()):
                    # Use retries to work around potential flakiness on Windows (AppVeyor).
                    retry_on_error(lambda: os.remove(path))

        # Parse options after moving files (in case mypy.ini is being moved).
        options = parse_options(original_program_text, testcase, incremental_step)
        options.use_builtins_fixtures = True
        options.show_traceback = True
        if 'optional' in testcase.file:
            options.strict_optional = True
        if incremental_step:
            options.incremental = True
        else:
            options.cache_dir = os.devnull  # Don't waste time writing cache

        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
            sources.append(BuildSource(program_path, module_name,
                                       None if incremental_step else program_text))

        res = None
        try:
            res = build.build(sources=sources,
                              options=options,
                              alt_lib_path=test_temp_dir)
            a = res.errors
        except CompileError as e:
            a = e.messages
        a = normalize_error_messages(a)

        # Make sure error messages match
        if incremental_step == 0:
            # Not incremental
            msg = 'Unexpected type checker output ({}, line {})'
            output = testcase.output
        elif incremental_step == 1:
            msg = 'Unexpected type checker output in incremental, run 1 ({}, line {})'
            output = testcase.output
        elif incremental_step > 1:
            msg = ('Unexpected type checker output in incremental, run {}'.format(
                incremental_step) + ' ({}, line {})')
            output = testcase.output2.get(incremental_step, [])
        else:
            raise AssertionError()

        if output != a and self.update_data:
            update_testcase_output(testcase, a)
        assert_string_arrays_equal(output, a, msg.format(testcase.file, testcase.line))

        if incremental_step and res:
            if options.follow_imports == 'normal' and testcase.output is None:
                self.verify_cache(module_data, a, res.manager)
            if incremental_step > 1:
                suffix = '' if incremental_step == 2 else str(incremental_step - 1)
                self.check_module_equivalence(
                    'rechecked' + suffix,
                    testcase.expected_rechecked_modules.get(incremental_step - 1),
                    res.manager.rechecked_modules)
                self.check_module_equivalence(
                    'stale' + suffix,
                    testcase.expected_stale_modules.get(incremental_step - 1),
                    res.manager.stale_modules)
コード例 #9
0
    def run_case_once(self,
                      testcase: DataDrivenTestCase,
                      incremental=0) -> None:
        find_module_clear_caches()
        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:
                    if module_name == '__main__':
                        with open(program_path, 'w') as f:
                            f.write(program_text)
                        break
            elif incremental == 2:
                # In run 2, copy *.py.next files to *.py files.
                for dn, dirs, files in os.walk(os.curdir):
                    for file in files:
                        if file.endswith('.py.next'):
                            full = os.path.join(dn, file)
                            target = full[:-5]
                            shutil.copy(full, target)

                            # In some systems, mtime has a resolution of 1 second which can cause
                            # annoying-to-debug issues when a file has the same size after a
                            # change. We manually set the mtime to circumvent this.
                            new_time = os.stat(target).st_mtime + 1
                            os.utime(target, times=(new_time, new_time))

        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
            sources.append(
                BuildSource(program_path, module_name,
                            None if incremental else program_text))
        res = None
        try:
            res = build.build(sources=sources,
                              options=options,
                              alt_lib_path=test_temp_dir)
            a = res.errors
        except CompileError as e:
            a = e.messages
        a = normalize_error_messages(a)

        # Make sure error messages match
        if incremental == 0:
            msg = 'Invalid type checker output ({}, line {})'
            output = testcase.output
        elif incremental == 1:
            msg = 'Invalid type checker output in incremental, run 1 ({}, line {})'
            output = testcase.output
        elif incremental == 2:
            msg = 'Invalid type checker output in incremental, run 2 ({}, line {})'
            output = testcase.output2
        else:
            raise AssertionError()

        if output != a and self.update_data:
            update_testcase_output(testcase, a)
        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:
                self.verify_cache(module_data, a, res.manager)
            if incremental == 2:
                self.check_module_equivalence(
                    'rechecked', testcase.expected_rechecked_modules,
                    res.manager.rechecked_modules)
                self.check_module_equivalence('stale',
                                              testcase.expected_stale_modules,
                                              res.manager.stale_modules)
コード例 #10
0
ファイル: main.py プロジェクト: tony/mypy
def process_options() -> Tuple[List[BuildSource], Options]:
    """Process command line arguments.

    Return (mypy program path (or None),
            module to run as script (or None),
            parsed flags)
    """

    # Make the help output a little less jarring.
    help_factory = (lambda prog: argparse.RawDescriptionHelpFormatter(
        prog=prog, max_help_position=28))
    parser = argparse.ArgumentParser(prog='mypy',
                                     epilog=FOOTER,
                                     formatter_class=help_factory)

    def parse_version(v):
        m = re.match(r'\A(\d)\.(\d+)\Z', v)
        if m:
            return int(m.group(1)), int(m.group(2))
        else:
            raise argparse.ArgumentTypeError(
                "Invalid python version '{}' (expected format: 'x.y')".format(
                    v))

    parser.add_argument('-v',
                        '--verbose',
                        action='count',
                        help="more verbose messages")
    parser.add_argument(
        '-V',
        '--version',
        action='version',  # type: ignore # see typeshed#124
        version='%(prog)s ' + __version__)
    parser.add_argument('--python-version',
                        type=parse_version,
                        metavar='x.y',
                        help='use Python x.y')
    parser.add_argument('--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('--silent',
                        action='store_true',
                        help="deprecated name for --silent-imports")
    parser.add_argument(
        '--almost-silent',
        action='store_true',
        help="like --silent-imports but reports the imports as errors")
    parser.add_argument(
        '--disallow-untyped-calls',
        action='store_true',
        help="disallow calling functions without type annotations"
        " from functions with type annotations")
    parser.add_argument(
        '--disallow-untyped-defs',
        action='store_true',
        help="disallow defining functions without type annotations"
        " or with incomplete type annotations")
    parser.add_argument(
        '--check-untyped-defs',
        action='store_true',
        help="type check the interior of functions without type annotations")
    parser.add_argument(
        '--warn-incomplete-stub',
        action='store_true',
        help="warn if missing type annotation in typeshed, only relevant with"
        " --check-untyped-defs enabled")
    parser.add_argument(
        '--warn-redundant-casts',
        action='store_true',
        help="warn about casting an expression to its inferred type")
    parser.add_argument('--fast-parser',
                        action='store_true',
                        help="enable experimental fast parser")
    parser.add_argument('-i',
                        '--incremental',
                        action='store_true',
                        help="enable experimental module cache")
    parser.add_argument('--strict-optional',
                        action='store_true',
                        help="enable experimental strict Optional checks")
    parser.add_argument('-f',
                        '--dirty-stubs',
                        action='store_true',
                        help="don't warn if typeshed is out of sync")
    parser.add_argument('--pdb',
                        action='store_true',
                        help="invoke pdb on fatal error")
    parser.add_argument('--use-python-path',
                        action='store_true',
                        help="an anti-pattern")
    parser.add_argument('--stats', action='store_true', help="dump stats")
    parser.add_argument('--inferstats',
                        action='store_true',
                        help="dump type inference stats")
    parser.add_argument('--custom-typing',
                        metavar='MODULE',
                        help="use a custom typing module")

    report_group = parser.add_argument_group(
        title='report generation',
        description='Generate a report in the specified format.')
    report_group.add_argument('--html-report', metavar='DIR')
    report_group.add_argument('--old-html-report', metavar='DIR')
    report_group.add_argument('--xslt-html-report', metavar='DIR')
    report_group.add_argument('--xml-report', metavar='DIR')
    report_group.add_argument('--txt-report', metavar='DIR')
    report_group.add_argument('--xslt-txt-report', metavar='DIR')
    report_group.add_argument('--linecount-report', metavar='DIR')

    code_group = parser.add_argument_group(
        title='How to specify the code to type check')
    code_group.add_argument(
        '-m',
        '--module',
        action='append',
        dest='modules',
        help="type-check module; can repeat for more modules")
    # TODO: `mypy -c A -c B` and `mypy -p A -p B` currently silently
    # ignore A (last option wins).  Perhaps -c, -m and -p could just
    # be command-line flags that modify how we interpret self.files?
    code_group.add_argument('-c',
                            '--command',
                            help="type-check program passed in as string")
    code_group.add_argument('-p',
                            '--package',
                            help="type-check all files in a directory")
    code_group.add_argument('files',
                            nargs='*',
                            help="type-check given files or directories")

    args = parser.parse_args()

    # --use-python-path is no longer supported; explain why.
    if args.use_python_path:
        parser.error(
            "Sorry, --use-python-path is no longer supported.\n"
            "If you are trying this because your code depends on a library module,\n"
            "you should really investigate how to obtain stubs for that module.\n"
            "See https://github.com/python/mypy/issues/1411 for more discussion."
        )
    # --silent is deprecated; warn about this.
    if args.silent:
        print("Warning: --silent is deprecated; use --silent-imports",
              file=sys.stderr)

    # Check for invalid argument combinations.
    code_methods = sum(
        bool(c)
        for c in [args.modules, args.command, args.package, args.files])
    if code_methods == 0:
        parser.error("Missing target module, package, files, or command.")
    elif code_methods > 1:
        parser.error(
            "May only specify one of: module, package, files, or command.")

    if args.use_python_path and args.python_version and args.python_version[
            0] == 2:
        parser.error(
            'Python version 2 (or --py2) specified, '
            'but --use-python-path will search in sys.path of Python 3')

    # Set options.
    options = Options()
    options.dirty_stubs = args.dirty_stubs
    options.python_path = args.use_python_path
    options.pdb = args.pdb
    options.custom_typing_module = args.custom_typing

    # Set build flags.
    if args.python_version is not None:
        options.pyversion = args.python_version

    if args.verbose:
        options.build_flags.extend(args.verbose * [build.VERBOSE])

    if args.stats:
        options.build_flags.append(build.DUMP_TYPE_STATS)

    if args.inferstats:
        options.build_flags.append(build.DUMP_INFER_STATS)

    if args.silent_imports or args.silent:
        options.build_flags.append(build.SILENT_IMPORTS)
    if args.almost_silent:
        options.build_flags.append(build.SILENT_IMPORTS)
        options.build_flags.append(build.ALMOST_SILENT)

    if args.disallow_untyped_calls:
        options.build_flags.append(build.DISALLOW_UNTYPED_CALLS)

    if args.disallow_untyped_defs:
        options.build_flags.append(build.DISALLOW_UNTYPED_DEFS)

    if args.check_untyped_defs:
        options.build_flags.append(build.CHECK_UNTYPED_DEFS)

    if args.warn_incomplete_stub:
        options.build_flags.append(build.WARN_INCOMPLETE_STUB)
    if args.warn_redundant_casts:
        options.build_flags.append(build.WARN_REDUNDANT_CASTS)

    # experimental
    if args.fast_parser:
        options.build_flags.append(build.FAST_PARSER)
    if args.incremental:
        options.build_flags.append(build.INCREMENTAL)
    if args.strict_optional:
        experiments.STRICT_OPTIONAL = True

    # Set reports.
    for flag, val in vars(args).items():
        if flag.endswith('_report') and val is not None:
            report_type = flag[:-7].replace('_', '-')
            report_dir = val
            options.report_dirs[report_type] = report_dir

    # Set target.
    if args.modules:
        options.build_flags.append(build.MODULE)
        targets = [BuildSource(None, m, None) for m in args.modules]
        return targets, options
    elif args.package:
        if os.sep in args.package or os.altsep and os.altsep in args.package:
            fail("Package name '{}' cannot have a slash in it.".format(
                args.package))
        options.build_flags.append(build.MODULE)
        lib_path = [os.getcwd()] + build.mypy_path()
        targets = build.find_modules_recursive(args.package, lib_path)
        if not targets:
            fail("Can't find package '{}'".format(args.package))
        return targets, options
    elif args.command:
        options.build_flags.append(build.PROGRAM_TEXT)
        return [BuildSource(None, None, args.command)], options
    else:
        targets = []
        for f in args.files:
            if f.endswith(PY_EXTENSIONS):
                targets.append(BuildSource(f, crawl_up(f)[1], None))
            elif os.path.isdir(f):
                sub_targets = expand_dir(f)
                if not sub_targets:
                    fail("There are no .py[i] files in directory '{}'".format(
                        f))
                targets.extend(sub_targets)
            else:
                targets.append(BuildSource(f, None, None))
        return targets, options
コード例 #11
0
ファイル: main.py プロジェクト: lugnitdgp/avskr2.0
def process_options(
        args: List[str],
        require_targets: bool = True) -> Tuple[List[BuildSource], Options]:
    """Parse command line arguments."""

    # Make the help output a little less jarring.
    help_factory = (lambda prog: argparse.RawDescriptionHelpFormatter(
        prog=prog, max_help_position=28))
    parser = argparse.ArgumentParser(prog='mypy',
                                     epilog=FOOTER,
                                     fromfile_prefix_chars='@',
                                     formatter_class=help_factory)

    # 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
    # parsed into the separate special_opts namespace object.
    parser.add_argument('-v',
                        '--verbose',
                        action='count',
                        dest='verbosity',
                        help="more verbose messages")
    parser.add_argument('-V',
                        '--version',
                        action='version',
                        version='%(prog)s ' + __version__)
    parser.add_argument('--python-version',
                        type=parse_version,
                        metavar='x.y',
                        help='use Python x.y')
    parser.add_argument(
        '--platform',
        action='store',
        metavar='PLATFORM',
        help="typecheck special-cased code for the given OS platform "
        "(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(
        '--disallow-untyped-calls',
        action='store_true',
        help="disallow calling functions without type annotations"
        " from functions with type annotations")
    parser.add_argument(
        '--disallow-untyped-defs',
        action='store_true',
        help="disallow defining functions without type annotations"
        " or with incomplete type annotations")
    parser.add_argument(
        '--check-untyped-defs',
        action='store_true',
        help="type check the interior of functions without type annotations")
    parser.add_argument(
        '--disallow-subclassing-any',
        action='store_true',
        help="disallow subclassing values of type 'Any' when defining classes")
    parser.add_argument(
        '--warn-incomplete-stub',
        action='store_true',
        help="warn if missing type annotation in typeshed, only relevant with"
        " --check-untyped-defs enabled")
    parser.add_argument(
        '--warn-redundant-casts',
        action='store_true',
        help="warn about casting an expression to its inferred type")
    parser.add_argument('--warn-no-return',
                        action='store_true',
                        help="warn about functions that end without returning")
    parser.add_argument('--warn-unused-ignores',
                        action='store_true',
                        help="warn about unneeded '# type: ignore' comments")
    parser.add_argument('--hide-error-context',
                        action='store_true',
                        dest='hide_error_context',
                        help="Hide context notes before errors")
    parser.add_argument('--fast-parser',
                        action='store_true',
                        help="enable experimental fast parser")
    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',
                        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 "
        "(experimental -- read documentation before using!).  "
        "Implies --strict-optional.  Has the undesirable side-effect of "
        "suppressing other errors in non-whitelisted files.")
    parser.add_argument('--junit-xml',
                        help="write junit.xml to the given file")
    parser.add_argument('--pdb',
                        action='store_true',
                        help="invoke pdb on fatal error")
    parser.add_argument('--show-traceback',
                        '--tb',
                        action='store_true',
                        help="show traceback on fatal error")
    parser.add_argument('--stats',
                        action='store_true',
                        dest='dump_type_stats',
                        help="dump stats")
    parser.add_argument('--inferstats',
                        action='store_true',
                        dest='dump_inference_stats',
                        help="dump type inference stats")
    parser.add_argument('--custom-typing',
                        metavar='MODULE',
                        dest='custom_typing_module',
                        help="use a custom typing module")
    parser.add_argument('--custom-typeshed-dir',
                        metavar='DIR',
                        help="use the custom typeshed in DIR")
    parser.add_argument('--scripts-are-modules',
                        action='store_true',
                        help="Script x becomes module x instead of __main__")
    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',
                        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)")
    # 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
    # information from a mypy run without having to change the file in-place
    # (e.g. by adding a call to reveal_type).
    parser.add_argument('--shadow-file',
                        metavar='PATH',
                        nargs=2,
                        dest='shadow_file',
                        help=argparse.SUPPRESS)
    # --debug-cache will disable any cache-related compressions/optimizations,
    # which will make the cache writing process output pretty-printed JSON (which
    # 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)

    report_group = parser.add_argument_group(
        title='report generation',
        description='Generate a report in the specified format.')
    for report_type in sorted(reporter_classes):
        report_group.add_argument('--%s-report' %
                                  report_type.replace('_', '-'),
                                  metavar='DIR',
                                  dest='special-opts:%s_report' % report_type)

    code_group = parser.add_argument_group(
        title='How to specify the code to type check')
    code_group.add_argument(
        '-m',
        '--module',
        action='append',
        metavar='MODULE',
        dest='special-opts:modules',
        help="type-check module; can repeat for more modules")
    # TODO: `mypy -p A -p B` currently silently ignores ignores A
    # (last option wins).  Perhaps -c, -m and -p could just be
    # command-line flags that modify how we interpret self.files?
    code_group.add_argument('-c',
                            '--command',
                            action='append',
                            metavar='PROGRAM_TEXT',
                            dest='special-opts:command',
                            help="type-check program passed in as string")
    code_group.add_argument('-p',
                            '--package',
                            metavar='PACKAGE',
                            dest='special-opts:package',
                            help="type-check all files in a directory")
    code_group.add_argument(metavar='files',
                            nargs='*',
                            dest='special-opts:files',
                            help="type-check given files or directories")

    # Parse arguments once into a dummy namespace so we can get the
    # filename for the config file.
    dummy = argparse.Namespace()
    parser.parse_args(args, dummy)
    config_file = dummy.config_file or defaults.CONFIG_FILE

    # Parse config file first, so command line can override.
    options = Options()
    if config_file and os.path.exists(config_file):
        parse_config_file(options, config_file)

    # Parse command line for real, using a split namespace.
    special_opts = argparse.Namespace()
    parser.parse_args(args,
                      SplitNamespace(options, special_opts, 'special-opts:'))

    # --use-python-path is no longer supported; explain why.
    if special_opts.use_python_path:
        parser.error(
            "Sorry, --use-python-path is no longer supported.\n"
            "If you are trying this because your code depends on a library module,\n"
            "you should really investigate how to obtain stubs for that module.\n"
            "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
    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.",
            file=sys.stderr)

    # Check for invalid argument combinations.
    if require_targets:
        code_methods = sum(
            bool(c) for c in [
                special_opts.modules, special_opts.command,
                special_opts.package, special_opts.files
            ])
        if code_methods == 0:
            parser.error("Missing target module, package, files, or command.")
        elif code_methods > 1:
            parser.error(
                "May only specify one of: module, package, files, or command.")

    # Set build flags.
    if options.strict_optional_whitelist is not None:
        # TODO: Deprecate, then kill this flag
        options.strict_optional = True
    if options.strict_optional:
        experiments.STRICT_OPTIONAL = True
    if special_opts.find_occurrences:
        experiments.find_occurrences = special_opts.find_occurrences.split('.')
        if len(experiments.find_occurrences) < 2:
            parser.error("Can only find occurrences of class members.")
        if len(experiments.find_occurrences) != 2:
            parser.error(
                "Can only find occurrences of non-nested class members.")

    # Set reports.
    for flag, val in vars(special_opts).items():
        if flag.endswith('_report') and val is not None:
            report_type = flag[:-7].replace('_', '-')
            report_dir = val
            options.report_dirs[report_type] = report_dir

    # Set target.
    if special_opts.modules:
        options.build_type = BuildType.MODULE
        targets = [BuildSource(None, m, None) for m in special_opts.modules]
        return targets, options
    elif special_opts.package:
        if os.sep in special_opts.package or os.altsep and os.altsep in special_opts.package:
            fail("Package name '{}' cannot have a slash in it.".format(
                special_opts.package))
        options.build_type = BuildType.MODULE
        lib_path = [os.getcwd()] + build.mypy_path()
        targets = build.find_modules_recursive(special_opts.package, lib_path)
        if not targets:
            fail("Can't find package '{}'".format(special_opts.package))
        return targets, options
    elif special_opts.command:
        options.build_type = BuildType.PROGRAM_TEXT
        return [BuildSource(None, None,
                            '\n'.join(special_opts.command))], options
    else:
        targets = []
        for f in special_opts.files:
            if f.endswith(PY_EXTENSIONS):
                targets.append(BuildSource(f, crawl_up(f)[1], None))
            elif os.path.isdir(f):
                sub_targets = expand_dir(f)
                if not sub_targets:
                    fail("There are no .py[i] files in directory '{}'".format(
                        f))
                targets.extend(sub_targets)
            else:
                mod = os.path.basename(
                    f) if options.scripts_are_modules else None
                targets.append(BuildSource(f, mod, None))
        return targets, options
コード例 #12
0
def process_options(args: List[str],
                    require_targets: bool = True,
                    server_options: bool = False,
                    fscache: Optional[FileSystemCache] = None,
                    ) -> Tuple[List[BuildSource], Options]:
    """Parse command line arguments.

    If a FileSystemCache is passed in, and package_root options are given,
    call fscache.set_package_root() to set the cache's package root.
    """

    parser = argparse.ArgumentParser(prog='mypy', epilog=FOOTER,
                                     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: Optional[str] = None,
                            default: bool,
                            dest: Optional[str] = None,
                            help: str,
                            strict_flag: bool = False,
                            group: Optional[argparse._ActionsContainer] = None
                            ) -> None:
        if inverse is None:
            inverse = invert_flag_name(flag)
        if group is None:
            group = parser

        if help is not argparse.SUPPRESS:
            help += " (inverse: {})".format(inverse)

        arg = group.add_argument(flag,
                                 action='store_false' if default else 'store_true',
                                 dest=dest,
                                 help=help)
        dest = arg.dest
        arg = group.add_argument(inverse,
                                 action='store_true' if default else 'store_false',
                                 dest=dest,
                                 help=argparse.SUPPRESS)
        if strict_flag:
            assert dest is not None
            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
    # parsed into the separate special_opts namespace object.
    parser.add_argument('-v', '--verbose', action='count', dest='verbosity',
                        help="more verbose messages")
    parser.add_argument('-V', '--version', action='version',
                        version='%(prog)s ' + __version__,
                        help="show program's version number and exit")

    config_group = parser.add_argument_group(
        title='config file',
        description="Use a config file instead of command line arguments.")
    config_group.add_argument(
        '--config-file',
        help="configuration file, must have a [mypy] section "
             "(defaults to {})".format(', '.join(defaults.CONFIG_FILES)))
    add_invertible_flag('--warn-unused-configs', default=False, strict_flag=True,
                        help="warn about unused '[mypy-<pattern>]' config sections",
                        group=config_group)

    imports_group = parser.add_argument_group(
        title='import discovery',
        description="Configure how imports are discovered and followed.")
    imports_group.add_argument(
        '--ignore-missing-imports', action='store_true',
        help="silently ignore imports of missing modules")
    imports_group.add_argument(
        '--follow-imports', choices=['normal', 'silent', 'skip', 'error'],
        default='normal', help="how to treat imports (default normal)")
    imports_group.add_argument(
        '--python-executable', action='store', metavar='EXECUTABLE',
        help="Python executable used for finding PEP 561 compliant installed"
             " packages and stubs",
        dest='special-opts:python_executable')
    imports_group.add_argument(
        '--no-site-packages', action='store_true',
        dest='special-opts:no_executable',
        help="do not search for installed PEP 561 compliant packages")

    platform_group = parser.add_argument_group(
        title='platform configuration',
        description="Type check code assuming certain runtime conditions.")
    platform_group.add_argument(
        '--python-version', type=parse_version, metavar='x.y',
        help='type check code assuming it will be running on Python x.y',
        dest='special-opts:python_version')
    platform_group.add_argument(
        '-2', '--py2', dest='python_version', action='store_const',
        const=defaults.PYTHON2_VERSION,
        help="use Python 2 mode (same as --python-version 2.7)")
    platform_group.add_argument(
        '--platform', action='store', metavar='PLATFORM',
        help="type check special-cased code for the given OS platform "
             "(defaults to sys.platform)")
    platform_group.add_argument(
        '--always-true', metavar='NAME', action='append', default=[],
        help="additional variable to be considered True (may be repeated)")
    platform_group.add_argument(
        '--always-false', metavar='NAME', action='append', default=[],
        help="additional variable to be considered False (may be repeated)")

    disallow_any_group = parser.add_argument_group(
        title='Any type restrictions',
        description="Disallow the use of the 'Any' type under certain conditions.")
    disallow_any_group.add_argument(
        '--disallow-any-unimported', default=False, action='store_true',
        help="disallow Any types resulting from unfollowed imports")
    add_invertible_flag('--disallow-subclassing-any', default=False, strict_flag=True,
                        help="disallow subclassing values of type 'Any' when defining classes",
                        group=disallow_any_group)
    disallow_any_group.add_argument(
        '--disallow-any-expr', default=False, action='store_true',
        help='disallow all expressions that have type Any')
    disallow_any_group.add_argument(
        '--disallow-any-decorated', default=False, action='store_true',
        help='disallow functions that have Any in their signature '
             'after decorator transformation')
    disallow_any_group.add_argument(
        '--disallow-any-explicit', default=False, action='store_true',
        help='disallow explicit Any in type positions')
    disallow_any_group.add_argument(
        '--disallow-any-generics', default=False, action='store_true',
        help='disallow usage of generic types that do not specify explicit '
             'type parameters')

    untyped_group = parser.add_argument_group(
        title='untyped definitions and calls',
        description="Configure how untyped definitions and calls are handled.")
    add_invertible_flag('--disallow-untyped-calls', default=False, strict_flag=True,
                        help="disallow calling functions without type annotations"
                        " from functions with type annotations",
                        group=untyped_group)
    add_invertible_flag('--disallow-untyped-defs', default=False, strict_flag=True,
                        help="disallow defining functions without type annotations"
                        " or with incomplete type annotations",
                        group=untyped_group)
    add_invertible_flag('--disallow-incomplete-defs', default=False, strict_flag=True,
                        help="disallow defining functions with incomplete type annotations",
                        group=untyped_group)
    add_invertible_flag('--check-untyped-defs', default=False, strict_flag=True,
                        help="type check the interior of functions without type annotations",
                        group=untyped_group)
    add_invertible_flag('--warn-incomplete-stub', default=False,
                        help="warn if missing type annotation in typeshed, only relevant with"
                             " --check-untyped-defs enabled",
                        group=untyped_group)

    none_group = parser.add_argument_group(
        title='None and Optional handling',
        description="Adjust how values of type 'None' are handled.")
    add_invertible_flag('--no-implicit-optional', default=False, strict_flag=True,
                        help="don't assume arguments with default values of None are Optional",
                        group=none_group)
    none_group.add_argument(
        '--strict-optional', action='store_true',
        help=argparse.SUPPRESS)
    none_group.add_argument(
        '--no-strict-optional', action='store_false', dest='strict_optional',
        help="disable strict Optional checks (inverse: --strict-optional)")
    none_group.add_argument(
        '--strict-optional-whitelist', metavar='GLOB', nargs='*',
        help="suppress strict Optional errors in all but the provided files; "
             "implies --strict-optional (may suppress certain other errors "
             "in non-whitelisted files)")

    lint_group = parser.add_argument_group(
        title='warnings',
        description="Detect code that is sound but redundant or problematic.")
    add_invertible_flag('--warn-redundant-casts', default=False, strict_flag=True,
                        help="warn about casting an expression to its inferred type",
                        group=lint_group)
    add_invertible_flag('--no-warn-no-return', dest='warn_no_return', default=True,
                        help="do not warn about functions that end without returning",
                        group=lint_group)
    add_invertible_flag('--warn-return-any', default=False, strict_flag=True,
                        help="warn about returning values of type Any"
                             " from non-Any typed functions",
                        group=lint_group)
    add_invertible_flag('--warn-unused-ignores', default=False, strict_flag=True,
                        help="warn about unneeded '# type: ignore' comments",
                        group=lint_group)

    strictness_group = parser.add_argument_group(
        title='other strictness checks',
        description="Other miscellaneous strictness checks.")
    add_invertible_flag('--disallow-untyped-decorators', default=False, strict_flag=True,
                        help="disallow decorating typed functions with untyped decorators",
                        group=strictness_group)

    incremental_group = parser.add_argument_group(
        title='incremental mode',
        description="Adjust how mypy incrementally type checks and caches modules.")
    incremental_group.add_argument(
        '-i', '--incremental', action='store_true',
        help=argparse.SUPPRESS)
    incremental_group.add_argument(
        '--no-incremental', action='store_false', dest='incremental',
        help="disable module cache (inverse: --incremental)")
    incremental_group.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))
    incremental_group.add_argument(
        '--cache-fine-grained', action='store_true',
        help="include fine-grained dependency information in the cache for the mypy daemon")
    incremental_group.add_argument(
        '--quick-and-dirty', action='store_true',
        help="use cache even if dependencies out of date (implies --incremental)")
    incremental_group.add_argument(
        '--skip-version-check', action='store_true',
        help="allow using cache written by older mypy version")

    internals_group = parser.add_argument_group(
        title='mypy internals',
        description="Debug and customize mypy internals.")
    internals_group.add_argument(
        '--pdb', action='store_true', help="invoke pdb on fatal error")
    internals_group.add_argument(
        '--show-traceback', '--tb', action='store_true',
        help="show traceback on fatal error")
    internals_group.add_argument(
        '--custom-typing', metavar='MODULE', dest='custom_typing_module',
        help="use a custom typing module")
    internals_group.add_argument(
        '--custom-typeshed-dir', metavar='DIR',
        help="use the custom typeshed in DIR")
    internals_group.add_argument(
        '--shadow-file', nargs=2, metavar=('SOURCE_FILE', 'SHADOW_FILE'),
        dest='shadow_file', action='append',
        help="when encountering SOURCE_FILE, read and type check "
             "the contents of SHADOW_FILE instead.")

    error_group = parser.add_argument_group(
        title='error reporting',
        description="Adjust the amount of detail shown in error messages.")
    add_invertible_flag('--show-error-context', default=False,
                        dest='show_error_context',
                        help='precede errors with "note:" messages explaining context',
                        group=error_group)
    add_invertible_flag('--show-column-numbers', default=False,
                        help="show column numbers in error messages",
                        group=error_group)

    analysis_group = parser.add_argument_group(
        title='extra analysis',
        description="Extract additional information and analysis.")
    analysis_group.add_argument(
        '--stats', action='store_true', dest='dump_type_stats', help=argparse.SUPPRESS)
    analysis_group.add_argument(
        '--inferstats', action='store_true', dest='dump_inference_stats',
        help=argparse.SUPPRESS)
    analysis_group.add_argument(
        '--find-occurrences', metavar='CLASS.MEMBER',
        dest='special-opts:find_occurrences',
        help="print out all usages of a class member (experimental)")

    strict_help = "strict mode; enables the following flags: {}".format(
        ", ".join(strict_flag_names))
    strictness_group.add_argument(
        '--strict', action='store_true', dest='special-opts:strict',
        help=strict_help)

    report_group = parser.add_argument_group(
        title='report generation',
        description='Generate a report in the specified format.')
    for report_type in sorted(reporter_classes):
        report_group.add_argument('--%s-report' % report_type.replace('_', '-'),
                                  metavar='DIR',
                                  dest='special-opts:%s_report' % report_type)

    other_group = parser.add_argument_group(
        title='miscellaneous',
        description="Other miscellaneous flags.")
    other_group.add_argument(
        '--junit-xml', help="write junit.xml to the given file")
    other_group.add_argument(
        '--scripts-are-modules', action='store_true',
        help="script x becomes module x instead of __main__")

    if server_options:
        # TODO: This flag is superfluous; remove after a short transition (2018-03-16)
        other_group.add_argument(
            '--experimental', action='store_true', dest='fine_grained_incremental',
            help="enable fine-grained incremental mode")
        other_group.add_argument(
            '--use-fine-grained-cache', action='store_true',
            help="use the cache in fine-grained incremental mode")

    # hidden options
    # --debug-cache will disable any cache-related compressions/optimizations,
    # which will make the cache writing process output pretty-printed JSON (which
    # is easier to debug).
    parser.add_argument('--debug-cache', action='store_true', help=argparse.SUPPRESS)
    # --dump-deps will dump all fine-grained dependencies to stdout
    parser.add_argument('--dump-deps', 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)
    # --semantic-analysis-only does exactly that.
    parser.add_argument('--semantic-analysis-only', action='store_true', help=argparse.SUPPRESS)
    # --local-partial-types disallows partial types spanning module top level and a function
    # (implicitly defined in fine-grained incremental mode)
    parser.add_argument('--local-partial-types', action='store_true', help=argparse.SUPPRESS)
    # --bazel changes some behaviors for use with Bazel (https://bazel.build).
    parser.add_argument('--bazel', action='store_true', help=argparse.SUPPRESS)
    # --package-root adds a directory below which directories are considered
    # packages even without __init__.py.  May be repeated.
    parser.add_argument('--package-root', metavar='ROOT', action='append', default=[],
                        help=argparse.SUPPRESS)
    # --cache-map FILE ... gives a mapping from source files to cache files.
    # Each triple of arguments is a source file, a cache meta file, and a cache data file.
    # Modules not mentioned in the file will go through cache_dir.
    # Must be followed by another flag or by '--' (and then only file args may follow).
    parser.add_argument('--cache-map', nargs='+', dest='special-opts:cache_map',
                        help=argparse.SUPPRESS)

    # deprecated options
    parser.add_argument('--disallow-any', dest='special-opts:disallow_any',
                        help=argparse.SUPPRESS)
    add_invertible_flag('--strict-boolean', default=False,
                        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)
    parser.add_argument('--fast-parser', action='store_true', dest='special-opts:fast_parser',
                        help=argparse.SUPPRESS)
    parser.add_argument('--no-fast-parser', action='store_true',
                        dest='special-opts:no_fast_parser',
                        help=argparse.SUPPRESS)

    code_group = parser.add_argument_group(title='specifying which code to type check')
    code_group.add_argument('-m', '--module', action='append', metavar='MODULE',
                            default=[],
                            dest='special-opts:modules',
                            help="type-check module; can repeat for more modules")
    code_group.add_argument('-p', '--package', action='append', metavar='PACKAGE',
                            default=[],
                            dest='special-opts:packages',
                            help="type-check package recursively; can be repeated")
    code_group.add_argument('-c', '--command', action='append', metavar='PROGRAM_TEXT',
                            dest='special-opts:command',
                            help="type-check program passed in as string")
    code_group.add_argument(metavar='files', nargs='*', dest='special-opts:files',
                            help="type-check given files or directories")

    # Parse arguments once into a dummy namespace so we can get the
    # filename for the config file and know if the user requested all strict options.
    dummy = argparse.Namespace()
    parser.parse_args(args, dummy)
    config_file = dummy.config_file
    if config_file is not None and not os.path.exists(config_file):
        parser.error("Cannot find config file '%s'" % config_file)

    # Parse config file first, so command line can override.
    options = Options()
    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:'))

    # --use-python-path is no longer supported; explain why.
    if special_opts.use_python_path:
        parser.error("Sorry, --use-python-path is no longer supported.\n"
                     "If you are trying this because your code depends on a library module,\n"
                     "you should really investigate how to obtain stubs for that module.\n"
                     "See https://github.com/python/mypy/issues/1411 for more discussion."
                     )

    # Process deprecated options
    if special_opts.disallow_any:
        print("--disallow-any option was split up into multiple flags. "
              "See http://mypy.readthedocs.io/en/latest/command_line.html#disallow-any-flags")
    if options.strict_boolean:
        print("Warning: --strict-boolean is deprecated; "
              "see https://github.com/python/mypy/issues/3195", file=sys.stderr)
    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.",
              file=sys.stderr)
    if special_opts.fast_parser:
        print("Warning: --fast-parser is now the default (and only) parser.")
    if special_opts.no_fast_parser:
        print("Warning: --no-fast-parser no longer has any effect.  The fast parser "
              "is now mypy's default and only parser.")

    try:
        infer_python_version_and_executable(options, special_opts)
    except PythonExecutableInferenceError as e:
        parser.error(str(e))

    if special_opts.no_executable:
        options.python_executable = None

    # Check for invalid argument combinations.
    if require_targets:
        code_methods = sum(bool(c) for c in [special_opts.modules + special_opts.packages,
                                             special_opts.command,
                                             special_opts.files])
        if code_methods == 0:
            parser.error("Missing target module, package, files, or command.")
        elif code_methods > 1:
            parser.error("May only specify one of: module/package, files, or command.")

    # Check for overlapping `--always-true` and `--always-false` flags.
    overlap = set(options.always_true) & set(options.always_false)
    if overlap:
        parser.error("You can't make a variable always true and always false (%s)" %
                     ', '.join(sorted(overlap)))

    # Set build flags.
    if options.strict_optional_whitelist is not None:
        # TODO: Deprecate, then kill this flag
        options.strict_optional = True
    if special_opts.find_occurrences:
        experiments.find_occurrences = special_opts.find_occurrences.split('.')
        assert experiments.find_occurrences is not None
        if len(experiments.find_occurrences) < 2:
            parser.error("Can only find occurrences of class members.")
        if len(experiments.find_occurrences) != 2:
            parser.error("Can only find occurrences of non-nested class members.")

    # Set reports.
    for flag, val in vars(special_opts).items():
        if flag.endswith('_report') and val is not None:
            report_type = flag[:-7].replace('_', '-')
            report_dir = val
            options.report_dirs[report_type] = report_dir

    # Process --package-root.
    if options.package_root:
        process_package_roots(fscache, parser, options)

    # Process --cache-map.
    if special_opts.cache_map:
        process_cache_map(parser, special_opts, options)

    # Let quick_and_dirty imply incremental.
    if options.quick_and_dirty:
        options.incremental = True

    # Set target.
    if special_opts.modules + special_opts.packages:
        options.build_type = BuildType.MODULE
        lib_path = [os.getcwd()] + build.mypy_path()
        targets = []
        # TODO: use the same cache that the BuildManager will
        cache = build.FindModuleCache(fscache)
        for p in special_opts.packages:
            if os.sep in p or os.altsep and os.altsep in p:
                fail("Package name '{}' cannot have a slash in it.".format(p))
            p_targets = cache.find_modules_recursive(p, tuple(lib_path), options.python_executable)
            if not p_targets:
                fail("Can't find package '{}'".format(p))
            targets.extend(p_targets)
        for m in special_opts.modules:
            targets.append(BuildSource(None, m, None))
        return targets, options
    elif special_opts.command:
        options.build_type = BuildType.PROGRAM_TEXT
        targets = [BuildSource(None, None, '\n'.join(special_opts.command))]
        return targets, options
    else:
        try:
            targets = create_source_list(special_opts.files, options, fscache)
        except InvalidSourceList as e:
            fail(str(e))
        return targets, options
コード例 #13
0
def process_options(args: List[str]) -> Tuple[List[BuildSource], Options]:
    """Process command line arguments.

    Return (mypy program path (or None),
            module to run as script (or None),
            parsed flags)
    """
    # TODO: Rewrite using argparse.
    options = Options()
    help = False
    ver = False
    while args and args[0].startswith('-'):
        if args[0] in ('--verbose', '-v'):
            options.build_flags.append(build.VERBOSE)
            args = args[1:]
        elif args[0] == '--py2':
            # Use Python 2 mode.
            options.pyversion = defaults.PYTHON2_VERSION
            args = args[1:]
        elif args[0] == '--python-version':
            version_components = args[1].split(".")[0:2]
            if len(version_components) != 2:
                fail("Invalid python version {} (expected format: 'x.y')".
                     format(repr(args[1])))
            if not all(item.isdigit() for item in version_components):
                fail("Found non-digit in python version: {}".format(args[1]))
            options.pyversion = (int(version_components[0]),
                                 int(version_components[1]))
            args = args[2:]
        elif args[0] == '-f' or args[0] == '--dirty-stubs':
            options.dirty_stubs = True
            args = args[1:]
        elif args[0] == '-m' and args[1:]:
            if args[2:]:
                fail("No extra argument should appear after '-m mod'")
            options.build_flags.append(build.MODULE)
            return [BuildSource(None, args[1], None)], options
        elif args[0] == '--package' and args[1:]:
            if args[2:]:
                fail("No extra argument should appear after '--package dir'")
            options.build_flags.append(build.MODULE)
            lib_path = [os.getcwd()] + build.mypy_path()
            targets = build.find_modules_recursive(args[1], lib_path)
            if not targets:
                fail("Can't find package '{}'".format(args[1]))
            return targets, options
        elif args[0] == '-c' and args[1:]:
            if args[2:]:
                fail("No extra argument should appear after '-c string'")
            options.build_flags.append(build.PROGRAM_TEXT)
            return [BuildSource(None, None, args[1])], options
        elif args[0] in ('-h', '--help'):
            help = True
            args = args[1:]
        elif args[0] == '--stats':
            options.build_flags.append(build.DUMP_TYPE_STATS)
            args = args[1:]
        elif args[0] == '--inferstats':
            options.build_flags.append(build.DUMP_INFER_STATS)
            args = args[1:]
        elif args[0] == '--custom-typing' and args[1:]:
            options.custom_typing_module = args[1]
            args = args[2:]
        elif is_report(args[0]) and args[1:]:
            report_type = args[0][2:-7]
            report_dir = args[1]
            options.report_dirs[report_type] = report_dir
            args = args[2:]
        elif args[0] == '--use-python-path':
            options.python_path = True
            args = args[1:]
        elif args[0] in ('--silent-imports', '--silent', '-s'):
            options.build_flags.append(build.SILENT_IMPORTS)
            args = args[1:]
        elif args[0] == '--pdb':
            options.pdb = True
            args = args[1:]
        elif args[0] == '--implicit-any':
            options.implicit_any = True
            args = args[1:]
        elif args[0] == '--fast-parser':
            options.build_flags.append(build.FAST_PARSER)
            args = args[1:]
        elif args[0] == '--disallow-untyped-calls':
            options.build_flags.append(build.DISALLOW_UNTYPED_CALLS)
            args = args[1:]
        elif args[0] in ('--version', '-V'):
            ver = True
            args = args[1:]
        else:
            usage('Unknown option: {}'.format(args[0]))

    if help:
        usage()

    if ver:
        version()

    if not args:
        usage('Missing target file or module')

    if options.python_path and options.pyversion[0] == 2:
        usage('Python version 2 (or --py2) specified, '
              'but --use-python-path will search in sys.path of Python 3')

    if build.FAST_PARSER in options.build_flags and options.pyversion[0] == 2:
        usage('The experimental fast parser is only compatible with Python 3, '
              'but Python 2 specified.')

    targets = []
    for arg in args:
        if arg.endswith(PY_EXTENSIONS):
            targets.append(BuildSource(arg, crawl_up(arg)[1], None))
        elif os.path.isdir(arg):
            targets.extend(expand_dir(arg))
        else:
            targets.append(BuildSource(arg, None, None))
    return targets, options
コード例 #14
0
    def check(self, filename: str, base_dir: str = None) -> bool:
        """
        Typechecks the given file and collects all type information needed for
        the translation to Viper
        """
        def report_errors(errors: List[str]) -> None:
            for error in errors:
                logger.info(error)
            raise TypeException(errors)

        module_name = None
        if base_dir is not None:
            relpath = os.path.relpath(filename, base_dir)
            if '..' not in relpath:
                relpath = relpath.replace('/', '.').replace('\\', '.')
                if relpath.endswith('.py'):
                    relpath = relpath[:-3]
                elif relpath.endswith('.pyi'):
                    relpath = relpath[:-4]
                module_name = relpath
        try:
            options_strict = self._create_options(True)
            res_strict = mypy.build.build(
                [BuildSource(filename, module_name, None, base_dir=base_dir)],
                options_strict)

            if res_strict.errors:
                # Run mypy a second time with strict optional checking disabled,
                # s.t. we don't get overapproximated none-related errors.
                options_non_strict = self._create_options(False)
                res_non_strict = mypy.build.build([
                    BuildSource(filename, module_name, None, base_dir=base_dir)
                ], options_non_strict)
                if res_non_strict.errors:
                    report_errors(res_non_strict.errors)
            relevant_files = [next(iter(res_strict.graph))]
            i = 0
            while i < len(relevant_files):
                name = relevant_files[i]
                for dep_name in res_strict.graph[name].dep_line_map:
                    if dep_name in IGNORED_IMPORTS:
                        continue
                    if dep_name not in relevant_files:
                        relevant_files.append(dep_name)
                i += 1
            relevant_files = [(name, res_strict.files[name])
                              for name in relevant_files]
            for name, file in relevant_files:
                real_name = None
                if name == module_name:
                    real_name = name.split('.')
                    name = '__main__'
                self.files[name] = file.path
                visitor = TypeVisitor(res_strict.types, name,
                                      file.ignored_lines, real_name)
                visitor.prefix = name.split('.')
                visitor.visit(file)
                self.all_types.update(visitor.all_types)
                self.alt_types.update(visitor.alt_types)
                self.type_aliases.update(visitor.type_aliases)
                self.type_vars.update(visitor.type_vars)
            return True
        except mypy.errors.CompileError as e:
            report_errors(e.messages)
コード例 #15
0
ファイル: main.py プロジェクト: jamesemmott/mypy
def process_options(
    args: List[str],
    require_targets: bool = True,
    server_options: bool = False,
) -> Tuple[List[BuildSource], Options]:
    """Parse command line arguments."""

    parser = argparse.ArgumentParser(prog='mypy',
                                     epilog=FOOTER,
                                     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: Optional[str] = None,
                            default: bool,
                            dest: Optional[str] = None,
                            help: str,
                            strict_flag: bool = False) -> None:
        if inverse is None:
            inverse = invert_flag_name(flag)

        if help is not argparse.SUPPRESS:
            help += " (inverse: {})".format(inverse)

        arg = parser.add_argument(
            flag,  # type: ignore  # incorrect stub for add_argument
            action='store_false' if default else 'store_true',
            dest=dest,
            help=help)
        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:
            assert dest is not None
            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
    # parsed into the separate special_opts namespace object.
    parser.add_argument('-v',
                        '--verbose',
                        action='count',
                        dest='verbosity',
                        help="more verbose messages")
    parser.add_argument('-V',
                        '--version',
                        action='version',
                        version='%(prog)s ' + __version__)
    parser.add_argument('--python-version',
                        type=parse_version,
                        metavar='x.y',
                        help='use Python x.y')
    parser.add_argument(
        '--platform',
        action='store',
        metavar='PLATFORM',
        help="typecheck special-cased code for the given OS platform "
        "(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('--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-any-unimported',
        default=False,
        action='store_true',
        help="disallow Any types resulting from unfollowed imports")
    parser.add_argument('--disallow-any-expr',
                        default=False,
                        action='store_true',
                        help='disallow all expressions that have type Any')
    parser.add_argument(
        '--disallow-any-decorated',
        default=False,
        action='store_true',
        help='disallow functions that have Any in their signature '
        'after decorator transformation')
    parser.add_argument('--disallow-any-explicit',
                        default=False,
                        action='store_true',
                        help='disallow explicit Any in type positions')
    parser.add_argument(
        '--disallow-any-generics',
        default=False,
        action='store_true',
        help='disallow usage of generic types that do not specify explicit '
        'type parameters')
    add_invertible_flag(
        '--disallow-untyped-calls',
        default=False,
        strict_flag=True,
        help="disallow calling functions without type annotations"
        " from functions with type annotations")
    add_invertible_flag(
        '--disallow-untyped-defs',
        default=False,
        strict_flag=True,
        help="disallow defining functions without type annotations"
        " or with incomplete type annotations")
    add_invertible_flag(
        '--disallow-incomplete-defs',
        default=False,
        strict_flag=True,
        help="disallow defining functions with incomplete type annotations")
    add_invertible_flag(
        '--check-untyped-defs',
        default=False,
        strict_flag=True,
        help="type check the interior of functions without type annotations")
    add_invertible_flag(
        '--disallow-subclassing-any',
        default=False,
        strict_flag=True,
        help="disallow subclassing values of type 'Any' when defining classes")
    add_invertible_flag(
        '--warn-incomplete-stub',
        default=False,
        help="warn if missing type annotation in typeshed, only relevant with"
        " --check-untyped-defs enabled")
    add_invertible_flag(
        '--disallow-untyped-decorators',
        default=False,
        strict_flag=True,
        help="disallow decorating typed functions with untyped decorators")
    add_invertible_flag(
        '--warn-redundant-casts',
        default=False,
        strict_flag=True,
        help="warn about casting an expression to its inferred type")
    add_invertible_flag(
        '--no-warn-no-return',
        dest='warn_no_return',
        default=True,
        help="do not warn about functions that end without returning")
    add_invertible_flag('--warn-return-any',
                        default=False,
                        strict_flag=True,
                        help="warn about returning values of type Any"
                        " from non-Any typed functions")
    add_invertible_flag('--warn-unused-ignores',
                        default=False,
                        strict_flag=True,
                        help="warn about unneeded '# type: ignore' comments")
    add_invertible_flag(
        '--warn-unused-configs',
        default=False,
        strict_flag=True,
        help="warn about unused '[mypy-<pattern>]' config sections")
    add_invertible_flag(
        '--show-error-context',
        default=False,
        dest='show_error_context',
        help='Precede errors with "note:" messages explaining context')
    add_invertible_flag(
        '--no-implicit-optional',
        default=False,
        strict_flag=True,
        help="don't assume arguments with default values of None are Optional")
    parser.add_argument(
        '-i',
        '--incremental',
        action='store_true',
        help="enable module cache, (inverse: --no-incremental)")
    parser.add_argument('--no-incremental',
                        action='store_false',
                        dest='incremental',
                        help=argparse.SUPPRESS)
    parser.add_argument('--quick-and-dirty',
                        action='store_true',
                        help="use cache even if dependencies out of date "
                        "(implies --incremental)")
    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(
        '--cache-fine-grained',
        action='store_true',
        help="include fine-grained dependency information in the cache")
    parser.add_argument('--skip-version-check',
                        action='store_true',
                        help="allow using cache written by older mypy version")
    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 "
        "(experimental -- read documentation before using!).  "
        "Implies --strict-optional.  Has the undesirable side-effect of "
        "suppressing other errors in non-whitelisted files.")
    parser.add_argument('--junit-xml',
                        help="write junit.xml to the given file")
    parser.add_argument('--pdb',
                        action='store_true',
                        help="invoke pdb on fatal error")
    parser.add_argument('--show-traceback',
                        '--tb',
                        action='store_true',
                        help="show traceback on fatal error")
    parser.add_argument('--stats',
                        action='store_true',
                        dest='dump_type_stats',
                        help="dump stats")
    parser.add_argument('--inferstats',
                        action='store_true',
                        dest='dump_inference_stats',
                        help="dump type inference stats")
    parser.add_argument('--custom-typing',
                        metavar='MODULE',
                        dest='custom_typing_module',
                        help="use a custom typing module")
    parser.add_argument('--custom-typeshed-dir',
                        metavar='DIR',
                        help="use the custom typeshed in DIR")
    parser.add_argument('--scripts-are-modules',
                        action='store_true',
                        help="Script x becomes module x instead of __main__")
    parser.add_argument('--config-file',
                        help="Configuration file, must have a [mypy] section "
                        "(defaults to {})".format(defaults.CONFIG_FILE))
    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)")
    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)
    parser.add_argument('--shadow-file',
                        nargs=2,
                        metavar=('SOURCE_FILE', 'SHADOW_FILE'),
                        dest='shadow_file',
                        help='Typecheck SHADOW_FILE in place of SOURCE_FILE.')
    # hidden options
    # --debug-cache will disable any cache-related compressions/optimizations,
    # which will make the cache writing process output pretty-printed JSON (which
    # is easier to debug).
    parser.add_argument('--debug-cache',
                        action='store_true',
                        help=argparse.SUPPRESS)
    # --dump-deps will dump all fine-grained dependencies to stdout
    parser.add_argument('--dump-deps',
                        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)
    # --semantic-analysis-only does exactly that.
    parser.add_argument('--semantic-analysis-only',
                        action='store_true',
                        help=argparse.SUPPRESS)
    # --local-partial-types disallows partial types spanning module top level and a function
    # (implicitly defined in fine-grained incremental mode)
    parser.add_argument('--local-partial-types',
                        action='store_true',
                        help=argparse.SUPPRESS)
    # deprecated options
    parser.add_argument('--disallow-any',
                        dest='special-opts:disallow_any',
                        help=argparse.SUPPRESS)
    add_invertible_flag('--strict-boolean',
                        default=False,
                        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)
    parser.add_argument('--fast-parser',
                        action='store_true',
                        dest='special-opts:fast_parser',
                        help=argparse.SUPPRESS)
    parser.add_argument('--no-fast-parser',
                        action='store_true',
                        dest='special-opts:no_fast_parser',
                        help=argparse.SUPPRESS)
    if server_options:
        parser.add_argument('--experimental',
                            action='store_true',
                            dest='fine_grained_incremental',
                            help="enable fine-grained incremental mode")
        parser.add_argument(
            '--use-fine-grained-cache',
            action='store_true',
            help="use the cache in fine-grained incremental mode")

    report_group = parser.add_argument_group(
        title='report generation',
        description='Generate a report in the specified format.')
    for report_type in sorted(reporter_classes):
        report_group.add_argument('--%s-report' %
                                  report_type.replace('_', '-'),
                                  metavar='DIR',
                                  dest='special-opts:%s_report' % report_type)

    code_group = parser.add_argument_group(
        title='How to specify the code to type check')
    code_group.add_argument(
        '-m',
        '--module',
        action='append',
        metavar='MODULE',
        dest='special-opts:modules',
        help="type-check module; can repeat for more modules")
    # TODO: `mypy -p A -p B` currently silently ignores A
    # (last option wins).  Perhaps -c, -m and -p could just be
    # command-line flags that modify how we interpret self.files?
    code_group.add_argument('-c',
                            '--command',
                            action='append',
                            metavar='PROGRAM_TEXT',
                            dest='special-opts:command',
                            help="type-check program passed in as string")
    code_group.add_argument('-p',
                            '--package',
                            metavar='PACKAGE',
                            dest='special-opts:package',
                            help="type-check all files in a directory")
    code_group.add_argument(metavar='files',
                            nargs='*',
                            dest='special-opts:files',
                            help="type-check given files or directories")

    # Parse arguments once into a dummy namespace so we can get the
    # filename for the config file and know if the user requested all strict options.
    dummy = argparse.Namespace()
    parser.parse_args(args, dummy)
    config_file = dummy.config_file
    if config_file is not None and not os.path.exists(config_file):
        parser.error("Cannot find config file '%s'" % config_file)

    # Parse config file first, so command line can override.
    options = Options()
    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:'))

    # --use-python-path is no longer supported; explain why.
    if special_opts.use_python_path:
        parser.error(
            "Sorry, --use-python-path is no longer supported.\n"
            "If you are trying this because your code depends on a library module,\n"
            "you should really investigate how to obtain stubs for that module.\n"
            "See https://github.com/python/mypy/issues/1411 for more discussion."
        )

    # Process deprecated options
    if special_opts.disallow_any:
        print(
            "--disallow-any option was split up into multiple flags. "
            "See http://mypy.readthedocs.io/en/latest/command_line.html#disallow-any-flags"
        )
    if options.strict_boolean:
        print(
            "Warning: --strict-boolean is deprecated; "
            "see https://github.com/python/mypy/issues/3195",
            file=sys.stderr)
    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.",
            file=sys.stderr)
    if special_opts.fast_parser:
        print("Warning: --fast-parser is now the default (and only) parser.")
    if special_opts.no_fast_parser:
        print(
            "Warning: --no-fast-parser no longer has any effect.  The fast parser "
            "is now mypy's default and only parser.")

    # Check for invalid argument combinations.
    if require_targets:
        code_methods = sum(
            bool(c) for c in [
                special_opts.modules, special_opts.command,
                special_opts.package, special_opts.files
            ])
        if code_methods == 0:
            parser.error("Missing target module, package, files, or command.")
        elif code_methods > 1:
            parser.error(
                "May only specify one of: module, package, files, or command.")

    # Set build flags.
    if options.strict_optional_whitelist is not None:
        # TODO: Deprecate, then kill this flag
        options.strict_optional = True
    if special_opts.find_occurrences:
        experiments.find_occurrences = special_opts.find_occurrences.split('.')
        assert experiments.find_occurrences is not None
        if len(experiments.find_occurrences) < 2:
            parser.error("Can only find occurrences of class members.")
        if len(experiments.find_occurrences) != 2:
            parser.error(
                "Can only find occurrences of non-nested class members.")

    # Set reports.
    for flag, val in vars(special_opts).items():
        if flag.endswith('_report') and val is not None:
            report_type = flag[:-7].replace('_', '-')
            report_dir = val
            options.report_dirs[report_type] = report_dir

    # Let quick_and_dirty imply incremental.
    if options.quick_and_dirty:
        options.incremental = True

    # Set target.
    if special_opts.modules:
        options.build_type = BuildType.MODULE
        targets = [BuildSource(None, m, None) for m in special_opts.modules]
        return targets, options
    elif special_opts.package:
        if os.sep in special_opts.package or os.altsep and os.altsep in special_opts.package:
            fail("Package name '{}' cannot have a slash in it.".format(
                special_opts.package))
        options.build_type = BuildType.MODULE
        lib_path = [os.getcwd()] + build.mypy_path()
        # TODO: use the same cache as the BuildManager will
        targets = build.FindModuleCache().find_modules_recursive(
            special_opts.package, lib_path)
        if not targets:
            fail("Can't find package '{}'".format(special_opts.package))
        return targets, options
    elif special_opts.command:
        options.build_type = BuildType.PROGRAM_TEXT
        targets = [BuildSource(None, None, '\n'.join(special_opts.command))]
        return targets, options
    else:
        targets = create_source_list(special_opts.files, options)
        return targets, options
コード例 #16
0
def process_options(args: List[str]) -> Tuple[List[BuildSource], Options]:
    """Process command line arguments.

    Return (mypy program path (or None),
            module to run as script (or None),
            parsed flags)
    """

    # Make the help output a little less jarring.
    help_factory = (lambda prog: argparse.RawDescriptionHelpFormatter(
        prog=prog, max_help_position=28))
    parser = argparse.ArgumentParser(prog='mypy',
                                     epilog=FOOTER,
                                     formatter_class=help_factory)

    def parse_version(v: str) -> Tuple[int, int]:
        m = re.match(r'\A(\d)\.(\d+)\Z', v)
        if m:
            return int(m.group(1)), int(m.group(2))
        else:
            raise argparse.ArgumentTypeError(
                "Invalid python version '{}' (expected format: 'x.y')".format(
                    v))

    # 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
    # parsed into the separate special_opts namespace object.
    parser.add_argument('-v',
                        '--verbose',
                        action='count',
                        dest='verbosity',
                        help="more verbose messages")
    parser.add_argument('-V',
                        '--version',
                        action='version',
                        version='%(prog)s ' + __version__)
    parser.add_argument('--python-version',
                        type=parse_version,
                        metavar='x.y',
                        help='use Python x.y')
    parser.add_argument('--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('--silent',
                        action='store_true',
                        dest='special-opts:silent',
                        help="deprecated name for --silent-imports")
    parser.add_argument(
        '--almost-silent',
        action='store_true',
        help="like --silent-imports but reports the imports as errors")
    parser.add_argument(
        '--disallow-untyped-calls',
        action='store_true',
        help="disallow calling functions without type annotations"
        " from functions with type annotations")
    parser.add_argument(
        '--disallow-untyped-defs',
        action='store_true',
        help="disallow defining functions without type annotations"
        " or with incomplete type annotations")
    parser.add_argument(
        '--check-untyped-defs',
        action='store_true',
        help="type check the interior of functions without type annotations")
    parser.add_argument(
        '--warn-incomplete-stub',
        action='store_true',
        help="warn if missing type annotation in typeshed, only relevant with"
        " --check-untyped-defs enabled")
    parser.add_argument(
        '--warn-redundant-casts',
        action='store_true',
        help="warn about casting an expression to its inferred type")
    parser.add_argument('--warn-unused-ignores',
                        action='store_true',
                        help="warn about unneeded '# type: ignore' comments")
    parser.add_argument('--fast-parser',
                        action='store_true',
                        help="enable experimental fast parser")
    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.MYPY_CACHE))
    parser.add_argument('--strict-optional',
                        action='store_true',
                        dest='special-opts:strict_optional',
                        help="enable experimental strict Optional checks")
    parser.add_argument('-f',
                        '--dirty-stubs',
                        action='store_true',
                        help="don't warn if typeshed is out of sync")
    parser.add_argument('--pdb',
                        action='store_true',
                        help="invoke pdb on fatal error")
    parser.add_argument('--use-python-path',
                        action='store_true',
                        dest='special-opts:use_python_path',
                        help="an anti-pattern")
    parser.add_argument('--stats',
                        action='store_true',
                        dest='dump_type_stats',
                        help="dump stats")
    parser.add_argument('--inferstats',
                        action='store_true',
                        dest='dump_inference_stats',
                        help="dump type inference stats")
    parser.add_argument('--custom-typing',
                        metavar='MODULE',
                        dest='custom_typing_module',
                        help="use a custom typing module")

    report_group = parser.add_argument_group(
        title='report generation',
        description='Generate a report in the specified format.')
    report_group.add_argument('--html-report',
                              metavar='DIR',
                              dest='special-opts:html_report')
    report_group.add_argument('--old-html-report',
                              metavar='DIR',
                              dest='special-opts:old_html_report')
    report_group.add_argument('--xslt-html-report',
                              metavar='DIR',
                              dest='special-opts:xslt_html_report')
    report_group.add_argument('--xml-report',
                              metavar='DIR',
                              dest='special-opts:xml_report')
    report_group.add_argument('--txt-report',
                              metavar='DIR',
                              dest='special-opts:txt_report')
    report_group.add_argument('--xslt-txt-report',
                              metavar='DIR',
                              dest='special-opts:xslt_txt_report')
    report_group.add_argument('--linecount-report',
                              metavar='DIR',
                              dest='special-opts:linecount_report')

    code_group = parser.add_argument_group(
        title='How to specify the code to type check')
    code_group.add_argument(
        '-m',
        '--module',
        action='append',
        metavar='MODULE',
        dest='special-opts:modules',
        help="type-check module; can repeat for more modules")
    # TODO: `mypy -c A -c B` and `mypy -p A -p B` currently silently
    # ignore A (last option wins).  Perhaps -c, -m and -p could just
    # be command-line flags that modify how we interpret self.files?
    code_group.add_argument('-c',
                            '--command',
                            metavar='PROGRAM_TEXT',
                            dest='special-opts:command',
                            help="type-check program passed in as string")
    code_group.add_argument('-p',
                            '--package',
                            metavar='PACKAGE',
                            dest='special-opts:package',
                            help="type-check all files in a directory")
    code_group.add_argument(metavar='files',
                            nargs='*',
                            dest='special-opts:files',
                            help="type-check given files or directories")

    options = Options()
    special_opts = argparse.Namespace()
    parser.parse_args(args,
                      SplitNamespace(options, special_opts, 'special-opts:'))

    # --use-python-path is no longer supported; explain why.
    if special_opts.use_python_path:
        parser.error(
            "Sorry, --use-python-path is no longer supported.\n"
            "If you are trying this because your code depends on a library module,\n"
            "you should really investigate how to obtain stubs for that module.\n"
            "See https://github.com/python/mypy/issues/1411 for more discussion."
        )

    # --silent is deprecated; warn about this.
    if special_opts.silent:
        print("Warning: --silent is deprecated; use --silent-imports",
              file=sys.stderr)
        options.silent_imports = True

    # Check for invalid argument combinations.
    code_methods = sum(
        bool(c) for c in [
            special_opts.modules, special_opts.command, special_opts.package,
            special_opts.files
        ])
    if code_methods == 0:
        parser.error("Missing target module, package, files, or command.")
    elif code_methods > 1:
        parser.error(
            "May only specify one of: module, package, files, or command.")

    # Set build flags.
    if special_opts.strict_optional:
        experiments.STRICT_OPTIONAL = True

    # Set reports.
    for flag, val in vars(special_opts).items():
        if flag.endswith('_report') and val is not None:
            report_type = flag[:-7].replace('_', '-')
            report_dir = val
            options.report_dirs[report_type] = report_dir

    # Set target.
    if special_opts.modules:
        options.build_type = BuildType.MODULE
        targets = [BuildSource(None, m, None) for m in special_opts.modules]
        return targets, options
    elif special_opts.package:
        if os.sep in special_opts.package or os.altsep and os.altsep in special_opts.package:
            fail("Package name '{}' cannot have a slash in it.".format(
                special_opts.package))
        options.build_type = BuildType.MODULE
        lib_path = [os.getcwd()] + build.mypy_path()
        targets = build.find_modules_recursive(special_opts.package, lib_path)
        if not targets:
            fail("Can't find package '{}'".format(special_opts.package))
        return targets, options
    elif special_opts.command:
        options.build_type = BuildType.PROGRAM_TEXT
        return [BuildSource(None, None, special_opts.command)], options
    else:
        targets = []
        for f in special_opts.files:
            if f.endswith(PY_EXTENSIONS):
                targets.append(BuildSource(f, crawl_up(f)[1], None))
            elif os.path.isdir(f):
                sub_targets = expand_dir(f)
                if not sub_targets:
                    fail("There are no .py[i] files in directory '{}'".format(
                        f))
                targets.extend(sub_targets)
            else:
                targets.append(BuildSource(f, None, None))
        return targets, options
コード例 #17
0
def process_options() -> Tuple[List[BuildSource], Options]:
    """Process command line arguments.

    Return (mypy program path (or None),
            module to run as script (or None),
            parsed flags)
    """

    # Make the help output a little less jarring.
    help_factory = (lambda prog: argparse.RawDescriptionHelpFormatter(
        prog=prog, max_help_position=28))
    parser = argparse.ArgumentParser(prog='mypy',
                                     epilog=FOOTER,
                                     formatter_class=help_factory)

    def parse_version(v):
        m = re.match(r'\A(\d)\.(\d+)\Z', v)
        if m:
            return int(m.group(1)), int(m.group(2))
        else:
            raise argparse.ArgumentTypeError(
                "Invalid python version '{}' (expected format: 'x.y')".format(
                    v))

    parser.add_argument('-v',
                        '--verbose',
                        action='count',
                        help="more verbose messages")
    parser.add_argument(
        '-V',
        '--version',
        action='version',  # type: ignore # see typeshed#124
        version='%(prog)s ' + __version__)
    parser.add_argument('--python-version',
                        type=parse_version,
                        metavar='x.y',
                        help='use Python x.y')
    parser.add_argument('--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('--silent',
                        action='store_true',
                        help="deprecated name for --silent-imports")
    parser.add_argument(
        '--almost-silent',
        action='store_true',
        help="like --silent-imports but reports the imports as errors")
    parser.add_argument(
        '--disallow-untyped-calls',
        action='store_true',
        help="disallow calling functions without type annotations"
        " from functions with type annotations")
    parser.add_argument(
        '--disallow-untyped-defs',
        action='store_true',
        help="disallow defining functions without type annotations"
        " or with incomplete type annotations")
    parser.add_argument(
        '--check-untyped-defs',
        action='store_true',
        help="type check the interior of functions without type annotations")
    parser.add_argument('--fast-parser',
                        action='store_true',
                        help="enable experimental fast parser")
    parser.add_argument('-i',
                        '--incremental',
                        action='store_true',
                        help="enable experimental module cache")
    parser.add_argument('-f',
                        '--dirty-stubs',
                        action='store_true',
                        help="don't warn if typeshed is out of sync")
    parser.add_argument('--pdb',
                        action='store_true',
                        help="invoke pdb on fatal error")
    parser.add_argument(
        '--use-python-path',
        action='store_true',
        help="search for modules in sys.path of running Python")
    parser.add_argument('--stats', action='store_true', help="dump stats")
    parser.add_argument('--inferstats',
                        action='store_true',
                        help="dump type inference stats")
    parser.add_argument('--custom-typing',
                        metavar='MODULE',
                        help="use a custom typing module")

    report_group = parser.add_argument_group(
        title='report generation',
        description='Generate a report in the specified format.')
    report_group.add_argument('--html-report', metavar='DIR')
    report_group.add_argument('--old-html-report', metavar='DIR')
    report_group.add_argument('--xslt-html-report', metavar='DIR')
    report_group.add_argument('--xml-report', metavar='DIR')
    report_group.add_argument('--txt-report', metavar='DIR')
    report_group.add_argument('--xslt-txt-report', metavar='DIR')
    report_group.add_argument('--linecount-report', metavar='DIR')

    code_group = parser.add_argument_group(
        title='How to specify the code to type check')
    code_group.add_argument(
        '-m',
        '--module',
        action='append',
        dest='modules',
        help="type-check module; can repeat for more modules")
    code_group.add_argument('-c',
                            '--command',
                            help="type-check program passed in as string")
    code_group.add_argument('-p',
                            '--package',
                            help="type-check all files in a directory")
    code_group.add_argument('files',
                            nargs='*',
                            help="type-check given files or directories")

    args = parser.parse_args()

    # Check for invalid argument combinations.
    code_methods = sum(
        bool(c)
        for c in [args.modules, args.command, args.package, args.files])
    if code_methods == 0:
        parser.error("Missing target module, package, files, or command.")
    elif code_methods > 1:
        parser.error(
            "May only specify one of: module, package, files, or command.")

    if args.use_python_path and args.python_version and args.python_version[
            0] == 2:
        parser.error(
            'Python version 2 (or --py2) specified, '
            'but --use-python-path will search in sys.path of Python 3')

    if args.fast_parser and args.python_version and args.python_version[0] == 2:
        parser.error(
            'The experimental fast parser is only compatible with Python 3, '
            'but Python 2 specified.')

    # Set options.
    options = Options()
    options.dirty_stubs = args.dirty_stubs
    options.python_path = args.use_python_path
    options.pdb = args.pdb
    options.custom_typing_module = args.custom_typing

    # Set build flags.
    if args.python_version is not None:
        options.pyversion = args.python_version

    if args.verbose:
        options.build_flags.extend(args.verbose * [build.VERBOSE])

    if args.stats:
        options.build_flags.append(build.DUMP_TYPE_STATS)

    if args.inferstats:
        options.build_flags.append(build.DUMP_INFER_STATS)

    if args.silent_imports or args.silent:
        options.build_flags.append(build.SILENT_IMPORTS)
    if args.almost_silent:
        options.build_flags.append(build.SILENT_IMPORTS)
        options.build_flags.append(build.ALMOST_SILENT)

    if args.disallow_untyped_calls:
        options.build_flags.append(build.DISALLOW_UNTYPED_CALLS)

    if args.disallow_untyped_defs:
        options.build_flags.append(build.DISALLOW_UNTYPED_DEFS)

    if args.check_untyped_defs:
        options.build_flags.append(build.CHECK_UNTYPED_DEFS)

    # experimental
    if args.fast_parser:
        options.build_flags.append(build.FAST_PARSER)
    if args.incremental:
        options.build_flags.append(build.INCREMENTAL)

    # Set reports.
    for flag, val in vars(args).items():
        if flag.endswith('_report') and val is not None:
            report_type = flag[:-7].replace('_', '-')
            report_dir = val
            options.report_dirs[report_type] = report_dir

    # Set target.
    if args.modules:
        options.build_flags.append(build.MODULE)
        targets = [BuildSource(None, m, None) for m in args.modules]
        return targets, options
    elif args.package:
        options.build_flags.append(build.MODULE)
        lib_path = [os.getcwd()] + build.mypy_path()
        targets = build.find_modules_recursive(args.package, lib_path)
        if not targets:
            fail("Can't find package '{}'".format(args.package))
        return targets, options
    elif args.command:
        options.build_flags.append(build.PROGRAM_TEXT)
        return [BuildSource(None, None, args.command)], options
    else:
        targets = []
        for f in args.files:
            if f.endswith(PY_EXTENSIONS):
                targets.append(BuildSource(f, crawl_up(f)[1], None))
            elif os.path.isdir(f):
                targets.extend(expand_dir(f))
            else:
                targets.append(BuildSource(f, None, None))
        return targets, options
コード例 #18
0
ファイル: main.py プロジェクト: raph-amiard/mypy
def process_options(args: List[str]) -> Tuple[List[BuildSource], Options]:
    """Process command line arguments.

    Return (mypy program path (or None),
            module to run as script (or None),
            parsed flags)
    """
    options = Options()
    help = False
    ver = False
    while args and args[0].startswith('-'):
        if args[0] == '--verbose':
            options.build_flags.append(build.VERBOSE)
            args = args[1:]
        elif args[0] == '--py2':
            # Use Python 2 mode.
            options.pyversion = defaults.PYTHON2_VERSION
            args = args[1:]
        elif args[0] == '--python-version':
            version_components = args[1].split(".")[0:2]
            if len(version_components) != 2:
                fail("Invalid python version {} (expected format: 'x.y')".
                     format(repr(args[1])))
            if not all(item.isdigit() for item in version_components):
                fail("Found non-digit in python version: {}".format(args[1]))
            options.pyversion = (int(version_components[0]),
                                 int(version_components[1]))
            args = args[2:]
        elif args[0] == '-f' or args[0] == '--dirty-stubs':
            options.dirty_stubs = True
            args = args[1:]
        elif args[0] == '-m' and args[1:]:
            options.build_flags.append(build.MODULE)
            return [BuildSource(None, args[1], None)], options
        elif args[0] == '--package' and args[1:]:
            options.build_flags.append(build.MODULE)
            lib_path = [os.getcwd()] + build.mypy_path()
            targets = build.find_modules_recursive(args[1], lib_path)
            if not targets:
                fail("Can't find package '{}'".format(args[1]))
            return targets, options
        elif args[0] == '-c' and args[1:]:
            options.build_flags.append(build.PROGRAM_TEXT)
            return [BuildSource(None, None, args[1])], options
        elif args[0] in ('-h', '--help'):
            help = True
            args = args[1:]
        elif args[0] == '--stats':
            options.build_flags.append(build.DUMP_TYPE_STATS)
            args = args[1:]
        elif args[0] == '--inferstats':
            options.build_flags.append(build.DUMP_INFER_STATS)
            args = args[1:]
        elif args[0] == '--custom-typing' and args[1:]:
            options.custom_typing_module = args[1]
            args = args[2:]
        elif is_report(args[0]) and args[1:]:
            report_type = args[0][2:-7]
            report_dir = args[1]
            options.report_dirs[report_type] = report_dir
            args = args[2:]
        elif args[0] == '--use-python-path':
            options.python_path = True
            args = args[1:]
        elif args[0] == '--version':
            ver = True
            args = args[1:]
        else:
            usage('Unknown option: {}'.format(args[0]))

    if help:
        usage()

    if ver:
        version()

    if not args:
        usage('Missing target file or module')

    if args[1:]:
        usage('Extra argument: {}'.format(args[1]))

    if options.python_path and options.pyversion[0] == 2:
        usage('Python version 2 (or --py2) specified, '
              'but --use-python-path will search in sys.path of Python 3')

    return [BuildSource(args[0], None, None)], options
コード例 #19
0
    def run_case_once(self,
                      testcase: DataDrivenTestCase,
                      operations: List[FileOperation] = [],
                      incremental_step: int = 0) -> None:
        original_program_text = '\n'.join(testcase.input)
        module_data = self.parse_module(original_program_text,
                                        incremental_step)

        if incremental_step == 0 or incremental_step == 1:
            # In run 1, copy program text to program file.
            for module_name, program_path, program_text in module_data:
                if module_name == '__main__':
                    with open(program_path, 'w') as f:
                        f.write(program_text)
                    break
        elif incremental_step > 1:
            # In runs 2+, copy *.[num] files to * files.
            for op in operations:
                if isinstance(op, UpdateFile):
                    # Modify/create file
                    copy_and_fudge_mtime(op.source_path, op.target_path)
                else:
                    # Delete file
                    # Use retries to work around potential flakiness on Windows (AppVeyor).
                    path = op.path
                    retry_on_error(lambda: os.remove(path))

        # Parse options after moving files (in case mypy.ini is being moved).
        options = parse_options(original_program_text, testcase,
                                incremental_step)
        options.use_builtins_fixtures = True
        options.show_traceback = True
        if 'optional' in testcase.file:
            options.strict_optional = True
        if incremental_step and options.incremental:
            # Don't overwrite # flags: --no-incremental in incremental test cases
            options.incremental = True
        else:
            options.incremental = False
            # Don't waste time writing cache unless we are specifically looking for it
            if 'writescache' not in testcase.name.lower():
                options.cache_dir = os.devnull

        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
            sources.append(
                BuildSource(program_path, module_name,
                            None if incremental_step else program_text))

        res = None
        try:
            res = build.build(sources=sources,
                              options=options,
                              alt_lib_path=test_temp_dir)
            a = res.errors
        except CompileError as e:
            a = e.messages
        a = normalize_error_messages(a)

        # Make sure error messages match
        if incremental_step == 0:
            # Not incremental
            msg = 'Unexpected type checker output ({}, line {})'
            output = testcase.output
        elif incremental_step == 1:
            msg = 'Unexpected type checker output in incremental, run 1 ({}, line {})'
            output = testcase.output
        elif incremental_step > 1:
            msg = ('Unexpected type checker output in incremental, run {}'.
                   format(incremental_step) + ' ({}, line {})')
            output = testcase.output2.get(incremental_step, [])
        else:
            raise AssertionError()

        if output != a and testcase.config.getoption('--update-data', False):
            update_testcase_output(testcase, a)
        assert_string_arrays_equal(output, a,
                                   msg.format(testcase.file, testcase.line))

        if res:
            if options.cache_dir != os.devnull:
                self.verify_cache(module_data, res.errors, res.manager,
                                  res.graph)

            if incremental_step > 1:
                suffix = '' if incremental_step == 2 else str(
                    incremental_step - 1)
                assert_module_equivalence(
                    'rechecked' + suffix,
                    testcase.expected_rechecked_modules.get(incremental_step -
                                                            1),
                    res.manager.rechecked_modules)
                assert_module_equivalence(
                    'stale' + suffix,
                    testcase.expected_stale_modules.get(incremental_step - 1),
                    res.manager.stale_modules)