diff --git a/scripts/create_baseline_stubs.py b/scripts/create_baseline_stubs.py index b861f98c0..a0dd64f41 100755 --- a/scripts/create_baseline_stubs.py +++ b/scripts/create_baseline_stubs.py @@ -139,9 +139,11 @@ def main() -> None: # The importlib.metadata module is used for projects whose name is different # from the runtime Python package name (example: PyYAML/yaml) if sys.version_info >= (3, 8): - packages = [name for name in distribution(project).read_text("top_level.txt").split() if not name.startswith("_")] - if len(packages) == 1: - package = packages[0] + dist = distribution(project).read_text("top_level.txt") + if dist is not None: + packages = [name for name in dist.split() if not name.startswith("_")] + if len(packages) == 1: + package = packages[0] print(f'Using detected package "{package}" for project "{project}"', file=sys.stderr) print("Suggestion: Try again with --package argument if that's not what you wanted", file=sys.stderr) diff --git a/scripts/stubsabot.py b/scripts/stubsabot.py index 9c36fbdf5..3ba34d31b 100644 --- a/scripts/stubsabot.py +++ b/scripts/stubsabot.py @@ -16,7 +16,7 @@ import urllib.parse import zipfile from dataclasses import dataclass from pathlib import Path -from typing import Any +from typing import Any, TypeVar import aiohttp import packaging.specifiers @@ -24,9 +24,11 @@ import packaging.version import tomli import tomlkit +ActionLevelSelf = TypeVar("ActionLevelSelf", bound="ActionLevel") + class ActionLevel(enum.IntEnum): - def __new__(cls, value: int, doc: str): + def __new__(cls: type[ActionLevelSelf], value: int, doc: str) -> ActionLevelSelf: member = int.__new__(cls, value) member._value_ = value member.__doc__ = doc @@ -191,7 +193,7 @@ TYPESHED_OWNER = "python" @functools.lru_cache() -def get_origin_owner(): +def get_origin_owner() -> str: output = subprocess.check_output(["git", "remote", "get-url", "origin"], text=True) match = re.search(r"(git@github.com:|https://github.com/)(?P[^/]+)/(?P[^/]+).git", output) assert match is not None @@ -199,7 +201,7 @@ def get_origin_owner(): return match.group("owner") -async def create_or_update_pull_request(*, title: str, body: str, branch_name: str, session: aiohttp.ClientSession): +async def create_or_update_pull_request(*, title: str, body: str, branch_name: str, session: aiohttp.ClientSession) -> None: secret = os.environ["GITHUB_TOKEN"] if secret.startswith("ghp"): auth = f"token {secret}" diff --git a/tests/mypy_test.py b/tests/mypy_test.py index 1c1d3467f..9a5908514 100755 --- a/tests/mypy_test.py +++ b/tests/mypy_test.py @@ -174,7 +174,7 @@ def run_mypy( with tempfile.NamedTemporaryFile("w+") as temp: temp.write("[mypy]\n") for dist_conf in configurations: - temp.write("[mypy-%s]\n" % dist_conf.module_name) + temp.write(f"[mypy-{dist_conf.module_name}]\n") for k, v in dist_conf.values.items(): temp.write(f"{k} = {v}\n") temp.flush() @@ -230,19 +230,20 @@ def get_mypy_flags( strict: bool = False, test_suite_run: bool = False, enforce_error_codes: bool = True, + ignore_missing_imports: bool = False, ) -> list[str]: flags = [ "--python-version", - "%d.%d" % (major, minor), + f"{major}.{minor}", "--show-traceback", - "--no-implicit-optional", - "--disallow-untyped-decorators", - "--disallow-any-generics", "--warn-incomplete-stub", "--show-error-codes", "--no-error-summary", - "--strict-equality", ] + if strict: + flags.append("--strict") + else: + flags.extend(["--no-implicit-optional", "--disallow-untyped-decorators", "--disallow-any-generics", "--strict-equality"]) if temp_name is not None: flags.extend(["--config-file", temp_name]) if custom_typeshed: @@ -251,8 +252,6 @@ def get_mypy_flags( flags.extend(["--custom-typeshed-dir", os.path.dirname(os.path.dirname(__file__))]) if args.platform: flags.extend(["--platform", args.platform]) - if strict: - flags.append("--strict") if test_suite_run: flags.append("--namespace-packages") if sys.platform == "win32" or args.platform == "win32": @@ -261,6 +260,8 @@ def get_mypy_flags( flags.append("--no-site-packages") if enforce_error_codes: flags.extend(["--enable-error-code", "ignore-without-code"]) + if ignore_missing_imports: + flags.append("--ignore-missing-imports") return flags @@ -404,6 +405,22 @@ def test_the_test_scripts(code: int, major: int, minor: int, args: argparse.Name return TestResults(code, num_test_files_to_test) +def test_scripts_directory(code: int, major: int, minor: int, args: argparse.Namespace) -> TestResults: + files_to_test = list(Path("scripts").rglob("*.py")) + num_test_files_to_test = len(files_to_test) + flags = get_mypy_flags(args, major, minor, None, strict=True, ignore_missing_imports=True) + print(f"Testing the scripts directory ({num_test_files_to_test} files)...") + print("Running mypy " + " ".join(flags)) + if args.dry_run: + this_code = 0 + else: + this_code = run_mypy_as_subprocess("scripts", flags) + if not this_code: + print_success_msg() + code = max(code, this_code) + return TestResults(code, num_test_files_to_test) + + def test_the_test_cases(code: int, major: int, minor: int, args: argparse.Namespace) -> TestResults: test_case_files = list(map(str, Path("test_cases").rglob("*.py"))) num_test_case_files = len(test_case_files) @@ -439,7 +456,7 @@ def test_typeshed(code: int, major: int, minor: int, args: argparse.Namespace) - print() if minor >= 9: - # Run mypy against our own test suite + # Run mypy against our own test suite and the scripts directory # # Skip this on earlier Python versions, # as we're using new syntax and new functions in some test files @@ -447,6 +464,10 @@ def test_typeshed(code: int, major: int, minor: int, args: argparse.Namespace) - files_checked_this_version += test_script_files_checked print() + code, script_files_checked = test_scripts_directory(code, major, minor, args) + files_checked_this_version += script_files_checked + print() + code, test_case_files_checked = test_the_test_cases(code, major, minor, args) files_checked_this_version += test_case_files_checked print()