diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml index be0d5b7cad71..68499e98e606 100644 --- a/.github/workflows/daily.yml +++ b/.github/workflows/daily.yml @@ -96,7 +96,7 @@ jobs: PYTHON_EXECUTABLE="python" fi - $PYTHON_EXECUTABLE tests/stubtest_third_party.py --specified-platforms-only --num-shards 4 --shard-index ${{ matrix.shard-index }} + $PYTHON_EXECUTABLE tests/stubtest_third_party.py --ci-platforms-only --num-shards 4 --shard-index ${{ matrix.shard-index }} stub-uploader: name: stub_uploader tests diff --git a/.github/workflows/stubtest_third_party.yml b/.github/workflows/stubtest_third_party.yml index 69ec7c6ac625..66cdf7e28054 100644 --- a/.github/workflows/stubtest_third_party.yml +++ b/.github/workflows/stubtest_third_party.yml @@ -85,7 +85,7 @@ jobs: PYTHON_EXECUTABLE="python" fi - $PYTHON_EXECUTABLE tests/stubtest_third_party.py --specified-platforms-only $STUBS + $PYTHON_EXECUTABLE tests/stubtest_third_party.py --ci-platforms-only $STUBS else echo "Nothing to test" fi diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5ebfae6b49df..db79aba8ea7d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -247,11 +247,16 @@ This has the following keys: that need to be installed for stubtest to run successfully * `choco_dependencies` (default: `[]`): A list of Windows Chocolatey packages that need to be installed for stubtest to run successfully -* `platforms` (default: `["linux"]`): A list of OSes on which to run stubtest. - Can contain `win32`, `linux`, and `darwin` values. - If not specified, stubtest is run only on `linux`. - Only add extra OSes to the test - if there are platform-specific branches in a stubs package. +* `supported_platforms` (default: all platforms): A list of OSes on which + stubtest can be run. When a package is not platform-specific, this should + not be set. If the package is platform-specific, this should usually be set + to the supported platforms, unless stubtest is known to fail on a + specific platform. +* `ci_platforms` (default: `["linux"]`): A list of OSes on which to run + stubtest as part of our continuous integration (CI) tests. Can contain + `win32`, `linux`, and `darwin` values. If not specified, stubtest is run + only on `linux`. Only add extra OSes to the test if there are + platform-specific branches in a stubs package. `*_dependencies` are usually packages needed to `pip install` the implementation distribution. diff --git a/lib/ts_utils/metadata.py b/lib/ts_utils/metadata.py index 33948bdb8b36..388454de6dbf 100644 --- a/lib/ts_utils/metadata.py +++ b/lib/ts_utils/metadata.py @@ -32,6 +32,7 @@ "read_stubtest_settings", ] +DEFAULT_STUBTEST_PLATFORMS = ["linux"] _STUBTEST_PLATFORM_MAPPING: Final = {"linux": "apt_dependencies", "darwin": "brew_dependencies", "win32": "choco_dependencies"} # Some older websites have a bad pattern of using query params for navigation. @@ -69,7 +70,8 @@ class StubtestSettings: choco_dependencies: list[str] extras: list[str] ignore_missing_stub: bool - platforms: list[str] + supported_platforms: list[str] | None # None means all platforms + ci_platforms: list[str] stubtest_requirements: list[str] def system_requirements_for_platform(self, platform: str) -> list[str]: @@ -91,25 +93,32 @@ def read_stubtest_settings(distribution: str) -> StubtestSettings: choco_dependencies: object = data.get("choco_dependencies", []) extras: object = data.get("extras", []) ignore_missing_stub: object = data.get("ignore_missing_stub", False) - specified_platforms: object = data.get("platforms", ["linux"]) + supported_platforms: object = data.get("supported_platforms") + ci_platforms: object = data.get("ci_platforms", DEFAULT_STUBTEST_PLATFORMS) stubtest_requirements: object = data.get("stubtest_requirements", []) assert type(skip) is bool assert type(ignore_missing_stub) is bool # It doesn't work for type-narrowing if we use a for loop here... - assert _is_list_of_strings(specified_platforms) + assert supported_platforms is None or _is_list_of_strings(supported_platforms) + assert _is_list_of_strings(ci_platforms) assert _is_list_of_strings(apt_dependencies) assert _is_list_of_strings(brew_dependencies) assert _is_list_of_strings(choco_dependencies) assert _is_list_of_strings(extras) assert _is_list_of_strings(stubtest_requirements) - unrecognised_platforms = set(specified_platforms) - _STUBTEST_PLATFORM_MAPPING.keys() - assert not unrecognised_platforms, f"Unrecognised platforms specified for {distribution!r}: {unrecognised_platforms}" + unrecognised_platforms = set(ci_platforms) - _STUBTEST_PLATFORM_MAPPING.keys() + assert not unrecognised_platforms, f"Unrecognised ci_platforms specified for {distribution!r}: {unrecognised_platforms}" + + if supported_platforms is not None: + assert set(ci_platforms).issubset( + supported_platforms + ), f"ci_platforms must be a subset of supported_platforms for {distribution!r}" for platform, dep_key in _STUBTEST_PLATFORM_MAPPING.items(): - if platform not in specified_platforms: + if platform not in ci_platforms: assert dep_key not in data, ( f"Stubtest is not run on {platform} in CI for {distribution!r}, " f"but {dep_key!r} are specified in METADATA.toml" @@ -122,7 +131,8 @@ def read_stubtest_settings(distribution: str) -> StubtestSettings: choco_dependencies=choco_dependencies, extras=extras, ignore_missing_stub=ignore_missing_stub, - platforms=specified_platforms, + supported_platforms=supported_platforms, + ci_platforms=ci_platforms, stubtest_requirements=stubtest_requirements, ) @@ -176,7 +186,8 @@ def is_obsolete(self) -> bool: "choco_dependencies", "extras", "ignore_missing_stub", - "platforms", + "supported_platforms", + "ci_platforms", "stubtest_requirements", } } diff --git a/lib/ts_utils/utils.py b/lib/ts_utils/utils.py index 522db807a29e..b0d3e4f6ef88 100644 --- a/lib/ts_utils/utils.py +++ b/lib/ts_utils/utils.py @@ -50,6 +50,10 @@ def print_info(message: str) -> None: print(colored(message, "blue")) +def print_warning(message: str) -> None: + print(colored(message), "yellow") + + def print_error(error: str, end: str = "\n", fix_path: tuple[str, str] = ("", "")) -> None: error_split = error.split("\n") old, new = fix_path diff --git a/stubs/JACK-Client/METADATA.toml b/stubs/JACK-Client/METADATA.toml index f18e0144f591..b37b5b295512 100644 --- a/stubs/JACK-Client/METADATA.toml +++ b/stubs/JACK-Client/METADATA.toml @@ -5,7 +5,7 @@ requires = ["numpy>=1.20", "types-cffi"] [tool.stubtest] # darwin and win32 are equivalent -platforms = ["darwin", "linux"] +ci_platforms = ["darwin", "linux"] apt_dependencies = ["libjack-dev"] brew_dependencies = ["jack"] # No need to install on the CI. Leaving here as information for Windows contributors. diff --git a/stubs/PyScreeze/METADATA.toml b/stubs/PyScreeze/METADATA.toml index efd1ec87729c..723e24d731c9 100644 --- a/stubs/PyScreeze/METADATA.toml +++ b/stubs/PyScreeze/METADATA.toml @@ -4,7 +4,7 @@ requires = ["Pillow>=10.3.0"] [tool.stubtest] # Linux has extra constants, win32 has different definitions -platforms = ["linux", "win32"] +ci_platforms = ["linux", "win32"] # PyScreeze has an odd setup.py file # that doesn't list Pillow as a dependency for py312+ yet: # https://github.com/asweigart/pyscreeze/blob/eeca245a135cf171c163b3691300138518efa64e/setup.py#L38-L46 diff --git a/stubs/RPi.GPIO/METADATA.toml b/stubs/RPi.GPIO/METADATA.toml index 557e12238713..c70910d69e4e 100644 --- a/stubs/RPi.GPIO/METADATA.toml +++ b/stubs/RPi.GPIO/METADATA.toml @@ -2,7 +2,9 @@ version = "0.7.*" upstream_repository = "https://sourceforge.net/p/raspberry-gpio-python/code/" [tool.stubtest] -# When stubtest tries to import this module: -# error: RPi.GPIO failed to import. RuntimeError: This module can only be run on a Raspberry Pi! +# This package is only supported on Raspberry Pi hardware, which identifies +# itself as 'linux'. When run on other hardware, it raises a RuntimeError: +# RPi.GPIO failed to import. RuntimeError: This module can only be run on a Raspberry Pi! # https://sourceforge.net/p/raspberry-gpio-python/code/ci/08048dd1894a6b09a104557b6eaa6bb68b6baac5/tree/source/py_gpio.c#l1008 -skip = true +supported_platforms = [] +ci_platforms = [] diff --git a/stubs/aiofiles/METADATA.toml b/stubs/aiofiles/METADATA.toml index d649874b9a1e..fdab36a21cca 100644 --- a/stubs/aiofiles/METADATA.toml +++ b/stubs/aiofiles/METADATA.toml @@ -3,4 +3,4 @@ upstream_repository = "https://github.com/Tinche/aiofiles" [tool.stubtest] # linux and darwin are equivalent -platforms = ["linux", "win32"] +ci_platforms = ["linux", "win32"] diff --git a/stubs/antlr4-python3-runtime/METADATA.toml b/stubs/antlr4-python3-runtime/METADATA.toml index db215adfe920..ae22994662ff 100644 --- a/stubs/antlr4-python3-runtime/METADATA.toml +++ b/stubs/antlr4-python3-runtime/METADATA.toml @@ -3,4 +3,4 @@ upstream_repository = "https://github.com/antlr/antlr4" [tool.stubtest] ignore_missing_stub = true -platforms = ["linux", "win32"] +ci_platforms = ["linux", "win32"] diff --git a/stubs/cffi/METADATA.toml b/stubs/cffi/METADATA.toml index 357ce0ff0599..abf41186a33b 100644 --- a/stubs/cffi/METADATA.toml +++ b/stubs/cffi/METADATA.toml @@ -4,4 +4,4 @@ requires = ["types-setuptools"] [tool.stubtest] # linux and darwin are mostly equivalent, except for a single `RTLD_DEEPBIND` variable -platforms = ["linux", "win32"] +ci_platforms = ["linux", "win32"] diff --git a/stubs/colorama/METADATA.toml b/stubs/colorama/METADATA.toml index 40a45767ba6c..980206462d55 100644 --- a/stubs/colorama/METADATA.toml +++ b/stubs/colorama/METADATA.toml @@ -2,4 +2,4 @@ version = "0.4.*" upstream_repository = "https://github.com/tartley/colorama" [tool.stubtest] -platforms = ["linux", "win32"] +ci_platforms = ["linux", "win32"] diff --git a/stubs/gdb/METADATA.toml b/stubs/gdb/METADATA.toml index 475788246998..cea97d694253 100644 --- a/stubs/gdb/METADATA.toml +++ b/stubs/gdb/METADATA.toml @@ -11,5 +11,5 @@ extra_description = """\ """ [tool.stubtest] -platforms = ["linux"] +ci_platforms = ["linux"] apt_dependencies = ["gdb"] diff --git a/stubs/gevent/METADATA.toml b/stubs/gevent/METADATA.toml index 960f4c760e7f..907042850696 100644 --- a/stubs/gevent/METADATA.toml +++ b/stubs/gevent/METADATA.toml @@ -5,7 +5,7 @@ requires = ["types-greenlet", "types-psutil"] [tool.stubtest] # Run stubtest on all platforms, since there is some platform specific stuff # especially in the stdlib module replacement -platforms = ["linux", "darwin", "win32"] +ci_platforms = ["linux", "darwin", "win32"] # for testing the ffi loop implementations on all platforms stubtest_requirements = ["cffi", "dnspython"] apt_dependencies = ["libev4", "libev-dev", "libuv1", "libuv1-dev"] diff --git a/stubs/keyboard/METADATA.toml b/stubs/keyboard/METADATA.toml index c9280f44feac..6a2335fd9e45 100644 --- a/stubs/keyboard/METADATA.toml +++ b/stubs/keyboard/METADATA.toml @@ -6,4 +6,4 @@ upstream_repository = "https://github.com/boppreh/keyboard" # It's only by possible mouse buttons and event literal types. # As well as returning a tuple of int/long from keyboard.mouse.get_position # The "mouse" module is obsoleted by the "mouse" package. -# platforms = +# ci_platforms = ["linux"] diff --git a/stubs/paramiko/METADATA.toml b/stubs/paramiko/METADATA.toml index 9358422c4178..d4a050b12886 100644 --- a/stubs/paramiko/METADATA.toml +++ b/stubs/paramiko/METADATA.toml @@ -7,4 +7,4 @@ partial_stub = true [tool.stubtest] ignore_missing_stub = true # linux and darwin are equivalent -platforms = ["linux", "win32"] +ci_platforms = ["linux", "win32"] diff --git a/stubs/psutil/METADATA.toml b/stubs/psutil/METADATA.toml index 9832d06c03a9..85e2bcc2e4e8 100644 --- a/stubs/psutil/METADATA.toml +++ b/stubs/psutil/METADATA.toml @@ -2,4 +2,4 @@ version = "7.0.*" upstream_repository = "https://github.com/giampaolo/psutil" [tool.stubtest] -platforms = ["darwin", "linux", "win32"] +ci_platforms = ["darwin", "linux", "win32"] diff --git a/stubs/pyaudio/METADATA.toml b/stubs/pyaudio/METADATA.toml index 68f013087238..d05035bf5989 100644 --- a/stubs/pyaudio/METADATA.toml +++ b/stubs/pyaudio/METADATA.toml @@ -4,6 +4,6 @@ version = "0.2.*" [tool.stubtest] # linux and win32 are equivalent -platforms = ["darwin", "linux"] +ci_platforms = ["darwin", "linux"] apt_dependencies = ["portaudio19-dev"] brew_dependencies = ["portaudio"] diff --git a/stubs/pycurl/METADATA.toml b/stubs/pycurl/METADATA.toml index d6f49acb07b0..f4c8a993b104 100644 --- a/stubs/pycurl/METADATA.toml +++ b/stubs/pycurl/METADATA.toml @@ -2,4 +2,4 @@ version = "7.45.6" upstream_repository = "https://github.com/pycurl/pycurl" [tool.stubtest] -platforms = ["darwin", "linux", "win32"] +ci_platforms = ["darwin", "linux", "win32"] diff --git a/stubs/pygit2/METADATA.toml b/stubs/pygit2/METADATA.toml index 8f0525f88186..e3aa0e498321 100644 --- a/stubs/pygit2/METADATA.toml +++ b/stubs/pygit2/METADATA.toml @@ -4,6 +4,6 @@ requires = ["types-cffi"] obsolete_since = "1.16.0" # Released on 2024-10-11 [tool.stubtest] -platforms = ["darwin", "linux", "win32"] +ci_platforms = ["darwin", "linux", "win32"] # Does not build on any platform on Python 3.13 as of 2025-03-17. skip = true diff --git a/stubs/pynput/METADATA.toml b/stubs/pynput/METADATA.toml index 4b63115669c3..ef8d6c438859 100644 --- a/stubs/pynput/METADATA.toml +++ b/stubs/pynput/METADATA.toml @@ -2,4 +2,4 @@ version = "~=1.8.1" upstream_repository = "https://github.com/moses-palmer/pynput" [tool.stubtest] -platforms = ["darwin", "linux", "win32"] +ci_platforms = ["darwin", "linux", "win32"] diff --git a/stubs/pyperclip/METADATA.toml b/stubs/pyperclip/METADATA.toml index d8fd275f4016..885e7747f335 100644 --- a/stubs/pyperclip/METADATA.toml +++ b/stubs/pyperclip/METADATA.toml @@ -2,5 +2,5 @@ version = "1.9.*" upstream_repository = "https://github.com/asweigart/pyperclip" [tool.stubtest] -platforms = ["win32", "linux", "darwin"] +ci_platforms = ["win32", "linux", "darwin"] apt_dependencies = ["xclip"] diff --git a/stubs/pyserial/METADATA.toml b/stubs/pyserial/METADATA.toml index 77a573699df3..effb2598ba68 100644 --- a/stubs/pyserial/METADATA.toml +++ b/stubs/pyserial/METADATA.toml @@ -2,5 +2,5 @@ version = "3.5.*" upstream_repository = "https://github.com/pyserial/pyserial" [tool.stubtest] -platforms = ["darwin", "linux", "win32"] +ci_platforms = ["darwin", "linux", "win32"] extras = ["cp2110"] diff --git a/stubs/pywin32/METADATA.toml b/stubs/pywin32/METADATA.toml index 338759b1d81b..711091829aa4 100644 --- a/stubs/pywin32/METADATA.toml +++ b/stubs/pywin32/METADATA.toml @@ -2,4 +2,5 @@ version = "310.*" upstream_repository = "https://github.com/mhammond/pywin32" [tool.stubtest] -platforms = ["win32"] +supported_platforms = ["win32"] +ci_platforms = ["win32"] diff --git a/stubs/setuptools/METADATA.toml b/stubs/setuptools/METADATA.toml index d8a876935bfe..000f6fb31fbd 100644 --- a/stubs/setuptools/METADATA.toml +++ b/stubs/setuptools/METADATA.toml @@ -8,5 +8,5 @@ requires = ["setuptools"] # For pkg_resources [tool.stubtest] # darwin is equivalent to linux for OS-specific methods -platforms = ["linux", "win32"] +ci_platforms = ["linux", "win32"] stubtest_requirements = ["tomli"] diff --git a/stubs/uWSGI/METADATA.toml b/stubs/uWSGI/METADATA.toml index 9807d0b14a09..9324d956c585 100644 --- a/stubs/uWSGI/METADATA.toml +++ b/stubs/uWSGI/METADATA.toml @@ -12,4 +12,4 @@ extra_description = """\ # Run stubtest on MacOS as well, to check that the # uWSGI-specific parts of stubtest_third_party.py # also work there -platforms = ["linux", "darwin"] +ci_platforms = ["linux", "darwin"] diff --git a/stubs/waitress/METADATA.toml b/stubs/waitress/METADATA.toml index b6fe76b59efb..9423798e9ae4 100644 --- a/stubs/waitress/METADATA.toml +++ b/stubs/waitress/METADATA.toml @@ -3,4 +3,4 @@ upstream_repository = "https://github.com/Pylons/waitress" [tool.stubtest] # linux and darwin are equivalent -platforms = ["linux", "win32"] +ci_platforms = ["linux", "win32"] diff --git a/tests/stubtest_third_party.py b/tests/stubtest_third_party.py index 379ba718d1fd..d316fce81ae4 100755 --- a/tests/stubtest_third_party.py +++ b/tests/stubtest_third_party.py @@ -27,12 +27,11 @@ print_info, print_success_msg, print_time, + print_warning, ) -def run_stubtest( - dist: Path, *, verbose: bool = False, specified_platforms_only: bool = False, keep_tmp_dir: bool = False -) -> bool: +def run_stubtest(dist: Path, *, verbose: bool = False, ci_platforms_only: bool = False, keep_tmp_dir: bool = False) -> bool: """Run stubtest for a single distribution.""" dist_name = dist.name @@ -43,14 +42,16 @@ def run_stubtest( stubtest_settings = metadata.stubtest_settings if stubtest_settings.skip: - print(colored("skipping", "yellow")) + print(colored("skipping (skip = true)", "yellow")) return True - if sys.platform not in stubtest_settings.platforms: - if specified_platforms_only: - print(colored("skipping (platform not specified in METADATA.toml)", "yellow")) - return True - print(colored(f"Note: {dist_name} is not currently tested on {sys.platform} in typeshed's CI.", "yellow")) + if stubtest_settings.supported_platforms is not None and sys.platform not in stubtest_settings.supported_platforms: + print(colored("skipping (platform not supported)", "yellow")) + return True + + if ci_platforms_only and sys.platform not in stubtest_settings.ci_platforms: + print(colored("skipping (platform skipped in CI)", "yellow")) + return True if not metadata.requires_python.contains(PYTHON_VERSION): print(colored(f"skipping (requires Python {metadata.requires_python})", "yellow")) @@ -176,6 +177,10 @@ def run_stubtest( else: print_time(time() - t) print_success_msg() + + if sys.platform not in stubtest_settings.ci_platforms: + print_warning(f"Note: {dist_name} is not currently tested on {sys.platform} in typeshed's CI") + if keep_tmp_dir: print_info(f"Virtual environment kept at: {venv_dir}") finally: @@ -385,9 +390,9 @@ def main() -> NoReturn: parser.add_argument("--num-shards", type=int, default=1) parser.add_argument("--shard-index", type=int, default=0) parser.add_argument( - "--specified-platforms-only", + "--ci-platforms-only", action="store_true", - help="skip the test if the current platform is not specified in METADATA.toml/tool.stubtest.platforms", + help="skip the test if the current platform is not specified in METADATA.toml/tool.stubtest.ci-platforms", ) parser.add_argument("--keep-tmp-dir", action="store_true", help="keep the temporary virtualenv") parser.add_argument("dists", metavar="DISTRIBUTION", type=str, nargs=argparse.ZERO_OR_MORE) @@ -404,7 +409,7 @@ def main() -> NoReturn: continue try: if not run_stubtest( - dist, verbose=args.verbose, specified_platforms_only=args.specified_platforms_only, keep_tmp_dir=args.keep_tmp_dir + dist, verbose=args.verbose, ci_platforms_only=args.ci_platforms_only, keep_tmp_dir=args.keep_tmp_dir ): result = 1 except NoSuchStubError as e: