def run_case(self, testcase: DataDrivenTestCase) -> None: src = "\n".join(testcase.input) options = Options() options.show_traceback = True options.python_version = sys.version_info[:2] if testcase.name.endswith("_36"): options.python_version = (3, 6) else: options.python_version = sys.version_info[:2] options.plugins = ["trio_typing.plugin"] # must specify something for config_file, else the plugins don't get loaded options.config_file = "/dev/null" result = build.build( sources=[BuildSource("main", None, src)], options=options ) assert_string_arrays_equal( testcase.output, result.errors, "Unexpected output from {0.file} line {0.line}".format(testcase), )
def parse_config_file(options: Options, filename: Optional[str]) -> None: """Parse a config file into an Options object. Errors are written to stderr but are not fatal. If filename is None, fall back to default config file and then to setup.cfg. """ if filename is not None: config_files = (filename, ) # type: Tuple[str, ...] else: config_files = (defaults.CONFIG_FILE, ) + SHARED_CONFIG_FILES parser = configparser.RawConfigParser() for config_file in config_files: if not os.path.exists(config_file): continue try: parser.read(config_file) except configparser.Error as err: print("%s: %s" % (config_file, err), file=sys.stderr) else: file_read = config_file options.config_file = file_read break else: return if 'mypy' not in parser: if filename or file_read not in SHARED_CONFIG_FILES: print("%s: No [mypy] section in config file" % file_read, file=sys.stderr) else: section = parser['mypy'] prefix = '%s: [%s]' % (file_read, 'mypy') updates, report_dirs = parse_section(prefix, options, section) for k, v in updates.items(): setattr(options, k, v) options.report_dirs.update(report_dirs) for name, section in parser.items(): if name.startswith('mypy-'): prefix = '%s: [%s]' % (file_read, name) updates, report_dirs = parse_section(prefix, options, section) if report_dirs: print( "%s: Per-module sections should not specify reports (%s)" % (prefix, ', '.join(s + '_report' for s in sorted(report_dirs))), file=sys.stderr) if set(updates) - Options.PER_MODULE_OPTIONS: print( "%s: Per-module sections should only specify per-module flags (%s)" % (prefix, ', '.join( sorted(set(updates) - Options.PER_MODULE_OPTIONS))), file=sys.stderr) updates = { k: v for k, v in updates.items() if k in Options.PER_MODULE_OPTIONS } globs = name[5:] for glob in globs.split(','): # For backwards compatibility, replace (back)slashes with dots. glob = glob.replace(os.sep, '.') if os.altsep: glob = glob.replace(os.altsep, '.') pattern = re.compile(fnmatch.translate(glob)) options.per_module_options[pattern] = updates options.unused_configs[pattern] = glob
def parse_config_file(options: Options, filename: Optional[str]) -> None: """Parse a config file into an Options object. Errors are written to stderr but are not fatal. If filename is None, fall back to default config files. """ if filename is not None: config_files = (filename,) # type: Tuple[str, ...] else: config_files = tuple(map(os.path.expanduser, defaults.CONFIG_FILES)) parser = configparser.RawConfigParser() for config_file in config_files: if not os.path.exists(config_file): continue try: parser.read(config_file) except configparser.Error as err: print("%s: %s" % (config_file, err), file=sys.stderr) else: file_read = config_file options.config_file = file_read break else: return if 'mypy' not in parser: if filename or file_read not in defaults.SHARED_CONFIG_FILES: print("%s: No [mypy] section in config file" % file_read, file=sys.stderr) else: section = parser['mypy'] prefix = '%s: [%s]' % (file_read, 'mypy') updates, report_dirs = parse_section(prefix, options, section) for k, v in updates.items(): setattr(options, k, v) options.report_dirs.update(report_dirs) for name, section in parser.items(): if name.startswith('mypy-'): prefix = '%s: [%s]' % (file_read, name) updates, report_dirs = parse_section(prefix, options, section) if report_dirs: print("%s: Per-module sections should not specify reports (%s)" % (prefix, ', '.join(s + '_report' for s in sorted(report_dirs))), file=sys.stderr) if set(updates) - PER_MODULE_OPTIONS: print("%s: Per-module sections should only specify per-module flags (%s)" % (prefix, ', '.join(sorted(set(updates) - PER_MODULE_OPTIONS))), file=sys.stderr) updates = {k: v for k, v in updates.items() if k in PER_MODULE_OPTIONS} globs = name[5:] for glob in globs.split(','): # For backwards compatibility, replace (back)slashes with dots. glob = glob.replace(os.sep, '.') if os.altsep: glob = glob.replace(os.altsep, '.') if (any(c in glob for c in '?[]!') or any('*' in x and x != '*' for x in glob.split('.'))): print("%s: Patterns must be fully-qualified module names, optionally " "with '*' in some components (e.g spam.*.eggs.*)" % prefix, file=sys.stderr) else: options.per_module_options[glob] = updates
def test_coherence(self) -> None: options = Options() _, parsed_options = process_options([], require_targets=False) # FIX: test this too. Requires changing working dir to avoid finding 'setup.cfg' options.config_file = parsed_options.config_file assert_equal(options.snapshot(), parsed_options.snapshot())
def parse_config_file(options: Options, set_strict_flags: Callable[[], None], filename: Optional[str], stdout: Optional[TextIO] = None, stderr: Optional[TextIO] = None) -> None: """Parse a config file into an Options object. Errors are written to stderr but are not fatal. If filename is None, fall back to default config files. """ stdout = stdout or sys.stdout stderr = stderr or sys.stderr if filename is not None: config_files: Tuple[str, ...] = (filename,) else: config_files = tuple(map(os.path.expanduser, defaults.CONFIG_FILES)) config_parser = configparser.RawConfigParser() for config_file in config_files: if not os.path.exists(config_file): continue try: if is_toml(config_file): with open(config_file, encoding="utf-8") as f: toml_data = tomli.loads(f.read()) # Filter down to just mypy relevant toml keys toml_data = toml_data.get('tool', {}) if 'mypy' not in toml_data: continue toml_data = {'mypy': toml_data['mypy']} parser: MutableMapping[str, Any] = destructure_overrides(toml_data) config_types = toml_config_types else: config_parser.read(config_file) parser = config_parser config_types = ini_config_types except (tomli.TOMLDecodeError, configparser.Error, ConfigTOMLValueError) as err: print("%s: %s" % (config_file, err), file=stderr) else: if config_file in defaults.SHARED_CONFIG_FILES and 'mypy' not in parser: continue file_read = config_file options.config_file = file_read break else: return os.environ['MYPY_CONFIG_FILE_DIR'] = os.path.dirname( os.path.abspath(config_file)) if 'mypy' not in parser: if filename or file_read not in defaults.SHARED_CONFIG_FILES: print("%s: No [mypy] section in config file" % file_read, file=stderr) else: section = parser['mypy'] prefix = '%s: [%s]: ' % (file_read, 'mypy') updates, report_dirs = parse_section( prefix, options, set_strict_flags, section, config_types, stderr) for k, v in updates.items(): setattr(options, k, v) options.report_dirs.update(report_dirs) for name, section in parser.items(): if name.startswith('mypy-'): prefix = get_prefix(file_read, name) updates, report_dirs = parse_section( prefix, options, set_strict_flags, section, config_types, stderr) if report_dirs: print("%sPer-module sections should not specify reports (%s)" % (prefix, ', '.join(s + '_report' for s in sorted(report_dirs))), file=stderr) if set(updates) - PER_MODULE_OPTIONS: print("%sPer-module sections should only specify per-module flags (%s)" % (prefix, ', '.join(sorted(set(updates) - PER_MODULE_OPTIONS))), file=stderr) updates = {k: v for k, v in updates.items() if k in PER_MODULE_OPTIONS} globs = name[5:] for glob in globs.split(','): # For backwards compatibility, replace (back)slashes with dots. glob = glob.replace(os.sep, '.') if os.altsep: glob = glob.replace(os.altsep, '.') if (any(c in glob for c in '?[]!') or any('*' in x and x != '*' for x in glob.split('.'))): print("%sPatterns must be fully-qualified module names, optionally " "with '*' in some components (e.g spam.*.eggs.*)" % prefix, file=stderr) else: options.per_module_options[glob] = updates
def test_stubs(args: argparse.Namespace, use_builtins_fixtures: bool = False) -> int: """This is stubtest! It's time to test the stubs!""" # Load the allowlist. This is a series of strings corresponding to Error.object_desc # Values in the dict will store whether we used the allowlist entry or not. allowlist = { entry: False for allowlist_file in args.allowlist for entry in get_allowlist_entries(allowlist_file) } allowlist_regexes = {entry: re.compile(entry) for entry in allowlist} # If we need to generate an allowlist, we store Error.object_desc for each error here. generated_allowlist = set() modules = args.modules if args.check_typeshed: assert not args.modules, "Cannot pass both --check-typeshed and a list of modules" modules = get_typeshed_stdlib_modules(args.custom_typeshed_dir) annoying_modules = {"antigravity", "this"} modules = [m for m in modules if m not in annoying_modules] assert modules, "No modules to check" options = Options() options.incremental = False options.custom_typeshed_dir = args.custom_typeshed_dir options.config_file = args.mypy_config_file options.use_builtins_fixtures = use_builtins_fixtures if options.config_file: def set_strict_flags() -> None: # not needed yet return parse_config_file(options, set_strict_flags, options.config_file, sys.stdout, sys.stderr) try: modules = build_stubs(modules, options, find_submodules=not args.check_typeshed) except RuntimeError: return 1 exit_code = 0 for module in modules: for error in test_module(module): # Filter errors if args.ignore_missing_stub and error.is_missing_stub(): continue if args.ignore_positional_only and error.is_positional_only_related( ): continue if error.object_desc in allowlist: allowlist[error.object_desc] = True continue is_allowlisted = False for w in allowlist: if allowlist_regexes[w].fullmatch(error.object_desc): allowlist[w] = True is_allowlisted = True break if is_allowlisted: continue # We have errors, so change exit code, and output whatever necessary exit_code = 1 if args.generate_allowlist: generated_allowlist.add(error.object_desc) continue print(error.get_description(concise=args.concise)) # Print unused allowlist entries if not args.ignore_unused_allowlist: for w in allowlist: # Don't consider an entry unused if it regex-matches the empty string # This lets us allowlist errors that don't manifest at all on some systems if not allowlist[w] and not allowlist_regexes[w].fullmatch(""): exit_code = 1 print("note: unused allowlist entry {}".format(w)) # Print the generated allowlist if args.generate_allowlist: for e in sorted(generated_allowlist): print(e) exit_code = 0 return exit_code
def parse_config_file(options: Options, filename: Optional[str]) -> None: """Parse a config file into an Options object. Errors are written to stderr but are not fatal. If filename is None, fall back to default config file and then to setup.cfg. """ if filename is not None: config_files = (filename,) # type: Tuple[str, ...] else: config_files = (defaults.CONFIG_FILE,) + SHARED_CONFIG_FILES parser = configparser.RawConfigParser() for config_file in config_files: if not os.path.exists(config_file): continue try: parser.read(config_file) except configparser.Error as err: print("%s: %s" % (config_file, err), file=sys.stderr) else: file_read = config_file options.config_file = file_read break else: return if 'mypy' not in parser: if filename or file_read not in SHARED_CONFIG_FILES: print("%s: No [mypy] section in config file" % file_read, file=sys.stderr) else: section = parser['mypy'] prefix = '%s: [%s]' % (file_read, 'mypy') updates, report_dirs = parse_section(prefix, options, section) for k, v in updates.items(): setattr(options, k, v) options.report_dirs.update(report_dirs) for name, section in parser.items(): if name.startswith('mypy-'): prefix = '%s: [%s]' % (file_read, name) updates, report_dirs = parse_section(prefix, options, section) if report_dirs: print("%s: Per-module sections should not specify reports (%s)" % (prefix, ', '.join(s + '_report' for s in sorted(report_dirs))), file=sys.stderr) if set(updates) - Options.PER_MODULE_OPTIONS: print("%s: Per-module sections should only specify per-module flags (%s)" % (prefix, ', '.join(sorted(set(updates) - Options.PER_MODULE_OPTIONS))), file=sys.stderr) updates = {k: v for k, v in updates.items() if k in Options.PER_MODULE_OPTIONS} globs = name[5:] for glob in globs.split(','): # For backwards compatibility, replace (back)slashes with dots. glob = glob.replace(os.sep, '.') if os.altsep: glob = glob.replace(os.altsep, '.') pattern = re.compile(fnmatch.translate(glob)) options.per_module_options[pattern] = updates options.unused_configs[pattern] = glob