mirror of
https://github.com/davidhalter/typeshed.git
synced 2026-02-28 04:22:21 +08:00
Add optional requires_python field to third-party stubs metadata (#10724)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
@@ -25,8 +25,9 @@ from typing_extensions import Annotated, TypeAlias
|
||||
|
||||
import tomli
|
||||
|
||||
from parse_metadata import PackageDependencies, get_recursive_requirements
|
||||
from parse_metadata import PackageDependencies, get_recursive_requirements, read_metadata
|
||||
from utils import (
|
||||
PYTHON_VERSION,
|
||||
VERSIONS_RE as VERSION_LINE_RE,
|
||||
VenvInfo,
|
||||
colored,
|
||||
@@ -307,6 +308,7 @@ def add_third_party_files(
|
||||
class TestResults(NamedTuple):
|
||||
exit_code: int
|
||||
files_checked: int
|
||||
packages_skipped: int = 0
|
||||
|
||||
|
||||
def test_third_party_distribution(
|
||||
@@ -393,6 +395,9 @@ def install_requirements_for_venv(venv_info: VenvInfo, args: TestConfig, externa
|
||||
|
||||
def setup_virtual_environments(distributions: dict[str, PackageDependencies], args: TestConfig, tempdir: Path) -> None:
|
||||
"""Logic necessary for testing stubs with non-types dependencies in isolated environments."""
|
||||
if not distributions:
|
||||
return # hooray! Nothing to do
|
||||
|
||||
# STAGE 1: Determine which (if any) stubs packages require virtual environments.
|
||||
# Group stubs packages according to their external-requirements sets
|
||||
|
||||
@@ -471,6 +476,7 @@ def setup_virtual_environments(distributions: dict[str, PackageDependencies], ar
|
||||
def test_third_party_stubs(code: int, args: TestConfig, tempdir: Path) -> TestResults:
|
||||
print("Testing third-party packages...")
|
||||
files_checked = 0
|
||||
packages_skipped = 0
|
||||
gitignore_spec = get_gitignore_spec()
|
||||
distributions_to_check: dict[str, PackageDependencies] = {}
|
||||
|
||||
@@ -480,6 +486,21 @@ def test_third_party_stubs(code: int, args: TestConfig, tempdir: Path) -> TestRe
|
||||
if spec_matches_path(gitignore_spec, distribution_path):
|
||||
continue
|
||||
|
||||
metadata = read_metadata(distribution)
|
||||
if not metadata.requires_python.contains(PYTHON_VERSION):
|
||||
msg = (
|
||||
f"skipping {distribution!r} (requires Python {metadata.requires_python}; "
|
||||
f"test is being run using Python {PYTHON_VERSION})"
|
||||
)
|
||||
print(colored(msg, "yellow"))
|
||||
packages_skipped += 1
|
||||
continue
|
||||
if not metadata.requires_python.contains(args.version):
|
||||
msg = f"skipping {distribution!r} for target Python {args.version} (requires Python {metadata.requires_python})"
|
||||
print(colored(msg, "yellow"))
|
||||
packages_skipped += 1
|
||||
continue
|
||||
|
||||
if (
|
||||
distribution_path in args.filter
|
||||
or Path("stubs") in args.filter
|
||||
@@ -487,40 +508,51 @@ def test_third_party_stubs(code: int, args: TestConfig, tempdir: Path) -> TestRe
|
||||
):
|
||||
distributions_to_check[distribution] = get_recursive_requirements(distribution)
|
||||
|
||||
# If it's the first time test_third_party_stubs() has been called during this session,
|
||||
# setup the necessary virtual environments for testing the third-party stubs.
|
||||
# It should only be necessary to call setup_virtual_environments() once per session.
|
||||
if not _DISTRIBUTION_TO_VENV_MAPPING:
|
||||
setup_virtual_environments(distributions_to_check, args, tempdir)
|
||||
# Setup the necessary virtual environments for testing the third-party stubs.
|
||||
# Note that some stubs may not be tested on all Python versions
|
||||
# (due to version incompatibilities),
|
||||
# so we can't guarantee that setup_virtual_environments()
|
||||
# will only be called once per session.
|
||||
distributions_without_venv = {
|
||||
distribution: requirements
|
||||
for distribution, requirements in distributions_to_check.items()
|
||||
if distribution not in _DISTRIBUTION_TO_VENV_MAPPING
|
||||
}
|
||||
setup_virtual_environments(distributions_without_venv, args, tempdir)
|
||||
|
||||
assert _DISTRIBUTION_TO_VENV_MAPPING.keys() == distributions_to_check.keys()
|
||||
# Check that there is a venv for every distribution we're testing.
|
||||
# Some venvs may exist from previous runs but are skipped in this run.
|
||||
assert _DISTRIBUTION_TO_VENV_MAPPING.keys() >= distributions_to_check.keys()
|
||||
|
||||
for distribution, venv_info in _DISTRIBUTION_TO_VENV_MAPPING.items():
|
||||
for distribution in distributions_to_check:
|
||||
venv_info = _DISTRIBUTION_TO_VENV_MAPPING[distribution]
|
||||
non_types_dependencies = venv_info.python_exe != sys.executable
|
||||
this_code, checked = test_third_party_distribution(
|
||||
this_code, checked, _ = test_third_party_distribution(
|
||||
distribution, args, venv_info=venv_info, non_types_dependencies=non_types_dependencies
|
||||
)
|
||||
code = max(code, this_code)
|
||||
files_checked += checked
|
||||
|
||||
return TestResults(code, files_checked)
|
||||
return TestResults(code, files_checked, packages_skipped)
|
||||
|
||||
|
||||
def test_typeshed(code: int, args: TestConfig, tempdir: Path) -> TestResults:
|
||||
print(f"*** Testing Python {args.version} on {args.platform}")
|
||||
files_checked_this_version = 0
|
||||
packages_skipped_this_version = 0
|
||||
stdlib_dir, stubs_dir = Path("stdlib"), Path("stubs")
|
||||
if stdlib_dir in args.filter or any(stdlib_dir in path.parents for path in args.filter):
|
||||
code, stdlib_files_checked = test_stdlib(code, args)
|
||||
code, stdlib_files_checked, _ = test_stdlib(code, args)
|
||||
files_checked_this_version += stdlib_files_checked
|
||||
print()
|
||||
|
||||
if stubs_dir in args.filter or any(stubs_dir in path.parents for path in args.filter):
|
||||
code, third_party_files_checked = test_third_party_stubs(code, args, tempdir)
|
||||
code, third_party_files_checked, third_party_packages_skipped = test_third_party_stubs(code, args, tempdir)
|
||||
files_checked_this_version += third_party_files_checked
|
||||
packages_skipped_this_version = third_party_packages_skipped
|
||||
print()
|
||||
|
||||
return TestResults(code, files_checked_this_version)
|
||||
return TestResults(code, files_checked_this_version, packages_skipped_this_version)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
@@ -531,19 +563,27 @@ def main() -> None:
|
||||
exclude = args.exclude or []
|
||||
code = 0
|
||||
total_files_checked = 0
|
||||
total_packages_skipped = 0
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
td_path = Path(td)
|
||||
for version, platform in product(versions, platforms):
|
||||
config = TestConfig(args.verbose, filter, exclude, version, platform)
|
||||
code, files_checked_this_version = test_typeshed(code, args=config, tempdir=td_path)
|
||||
code, files_checked_this_version, packages_skipped_this_version = test_typeshed(code, args=config, tempdir=td_path)
|
||||
total_files_checked += files_checked_this_version
|
||||
total_packages_skipped += packages_skipped_this_version
|
||||
if code:
|
||||
print_error(f"--- exit status {code}, {total_files_checked} files checked ---")
|
||||
plural = "" if total_files_checked == 1 else "s"
|
||||
print_error(f"--- exit status {code}, {total_files_checked} file{plural} checked ---")
|
||||
sys.exit(code)
|
||||
if not total_files_checked:
|
||||
if total_packages_skipped:
|
||||
plural = "" if total_packages_skipped == 1 else "s"
|
||||
print(colored(f"--- {total_packages_skipped} package{plural} skipped ---", "yellow"))
|
||||
if total_files_checked:
|
||||
plural = "" if total_files_checked == 1 else "s"
|
||||
print(colored(f"--- success, {total_files_checked} file{plural} checked ---", "green"))
|
||||
else:
|
||||
print_error("--- nothing to do; exit 1 ---")
|
||||
sys.exit(1)
|
||||
print(colored(f"--- success, {total_files_checked} files checked ---", "green"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user