diff --git a/.gitattributes b/.gitattributes
index f2de17522..19c9bb2b0 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -4,6 +4,5 @@
# Set linguist-language to support comments syntax highlight
**/stubtest_allowlist*.txt linguist-language=ini
**/stubtest_allowlists/*.txt linguist-language=ini
-tests/pytype_exclude_list.txt linguist-language=ini
pyrightconfig*.json linguist-language=jsonc
.vscode/*.json linguist-language=jsonc
diff --git a/.github/renovate.json5 b/.github/renovate.json5
index 5b0e2d1e7..0a0312fbc 100644
--- a/.github/renovate.json5
+++ b/.github/renovate.json5
@@ -24,15 +24,15 @@
{
groupName: "most test/lint dependencies",
matchManagers: ["pip_requirements", "pre-commit"],
- matchPackageNames: ["!pytype", "!pyright"],
+ matchPackageNames: ["!pyright"],
description: "Quarterly update of most test dependencies",
schedule: ["every 3 months on the first day of the month"]
},
{
- "groupName": "pytype and pyright",
+ "groupName": "pyright",
"matchManagers": ["pip_requirements"],
- "matchPackageNames": ["pytype", "pyright"],
- "description": "Daily update of pyright and pytype",
+ "matchPackageNames": ["pyright"],
+ "description": "Daily update of pyright",
"schedule": ["before 4am"]
}
]
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 00dfea2e9..c736d18e1 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -34,29 +34,6 @@ jobs:
- run: uv pip install -r requirements-tests.txt --system
- run: python ./tests/check_typeshed_structure.py
- pytype:
- name: "pytype: Check stubs"
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - uses: actions/setup-python@v5
- with:
- # Max supported Python version as of pytype 2024.10.11
- python-version: "3.12"
- - uses: astral-sh/setup-uv@v6
- with:
- version-file: "requirements-tests.txt"
- - run: uv pip install -r requirements-tests.txt --system
- - name: Install external dependencies for 3rd-party stubs
- run: |
- DEPENDENCIES=$( python tests/get_external_stub_requirements.py )
- if [ -n "$DEPENDENCIES" ]; then
- printf "Installing packages:\n $(echo $DEPENDENCIES | sed 's/ /\n /g')\n"
- uv pip install --system $DEPENDENCIES
- fi
- - run: uv pip freeze
- - run: ./tests/pytype_test.py --print-stderr
-
mypy:
name: "mypy: Check stubs"
runs-on: ubuntu-latest
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ab6fc1723..f7061f52a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -83,13 +83,6 @@ Note that some tests require extra setup steps to install the required dependenc
(.venv) > pip install -r requirements-tests.txt
```
- To be able to run pytype tests, you'll also need to install it manually
-as it's currently excluded from the requirements file:
-
- ```powershell
- (.venv) > pip install -U pytype
- ```
-
@@ -104,10 +97,6 @@ as it's currently excluded from the requirements file:
uv pip install -r requirements-tests.txt
```
- ```shell
- uv pip install -U pytype
- ```
-
diff --git a/README.md b/README.md
index ee09529b9..1467aa20b 100644
--- a/README.md
+++ b/README.md
@@ -25,9 +25,9 @@ Typeshed supports Python versions 3.9 to 3.14.
## Using
-If you're just using a type checker ([mypy](https://github.com/python/mypy/),
-[pyright](https://github.com/microsoft/pyright),
-[pytype](https://github.com/google/pytype/), PyCharm, ...), as opposed to
+If you're just using a type checker (e.g. [mypy](https://github.com/python/mypy/),
+[pyright](https://github.com/microsoft/pyright), or PyCharm's built-in type
+checker), as opposed to
developing it, you don't need to interact with the typeshed repo at
all: a copy of standard library part of typeshed is bundled with type checkers.
And type stubs for third party packages and modules you are using can
diff --git a/pyproject.toml b/pyproject.toml
index 322a383e8..1ef50ab4a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -23,7 +23,6 @@ exclude = [
# cache directories, etc.:
".git",
".mypy_cache",
- ".pytype",
]
[tool.ruff.lint]
diff --git a/requirements-tests.txt b/requirements-tests.txt
index 5457f419e..d9dbeb1f8 100644
--- a/requirements-tests.txt
+++ b/requirements-tests.txt
@@ -2,8 +2,6 @@
# be pinned to a specific version to make failure reproducible.
mypy==1.16.1
pyright==1.1.403
-# pytype can be installed on Windows, but requires building wheels, let's not do that on the CI
-pytype==2024.10.11; platform_system != "Windows" and python_version >= "3.10" and python_version < "3.13"
# Libraries used by our various scripts.
# TODO (2025-05-09): Installing this on Python 3.14 on Windows fails at
diff --git a/tests/README.md b/tests/README.md
index e2fd81fb7..a8f2c5e9a 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -1,8 +1,6 @@
This directory contains several tests:
- `tests/mypy_test.py`
tests the stubs with [mypy](https://github.com/python/mypy/)
-- `tests/pytype_test.py` tests the stubs with
-[pytype](https://github.com/google/pytype/).
- `tests/pyright_test.py` tests the stubs with
[pyright](https://github.com/microsoft/pyright).
- `tests/regr_test.py` runs mypy against the test cases for typeshed's
@@ -19,7 +17,7 @@ in the `tests` and `scripts` directories.
To run the tests, follow the [setup instructions](../CONTRIBUTING.md#preparing-the-environment)
in the `CONTRIBUTING.md` document. In particular, you have to run with Python 3.9+.
-In order for `pytype_test` and `pyright_test` to work correctly, some third-party stubs
+In order for `pyright_test` to work correctly, some third-party stubs
may require extra dependencies external to typeshed to be installed in your virtual environment
prior to running the test.
You can list or install all of a stubs package's external dependencies using the following script:
@@ -67,18 +65,6 @@ imported but doesn't check whether stubs match their implementation
Run `python tests/mypy_test.py --help` for information on the various configuration options
for this script.
-## pytype\_test.py
-
-Note: This test cannot be run on Python version < 3.13 as pytype does not yet support
-Python 3.13 and above.
-
-Run using:
-```bash
-(.venv3)$ python3 tests/pytype_test.py
-```
-
-This test works similarly to `mypy_test.py`, except it uses `pytype`.
-
## pyright\_test.py
This test requires [Node.js](https://nodejs.org) to be installed. Although
diff --git a/tests/check_typeshed_structure.py b/tests/check_typeshed_structure.py
index 191173b42..a0e905b99 100755
--- a/tests/check_typeshed_structure.py
+++ b/tests/check_typeshed_structure.py
@@ -25,7 +25,7 @@ extension_descriptions = {".pyi": "stub", ".py": ".py"}
# These type checkers and linters must have exact versions in the requirements file to ensure
# consistent CI runs.
-linters = {"mypy", "pyright", "pytype", "ruff"}
+linters = {"mypy", "pyright", "ruff"}
ALLOWED_PY_FILES_IN_TESTS_DIR = {
"django_settings.py" # This file contains Django settings used by the mypy_django_plugin during stubtest execution.
diff --git a/tests/pytype_exclude_list.txt b/tests/pytype_exclude_list.txt
deleted file mode 100644
index bad4109bf..000000000
--- a/tests/pytype_exclude_list.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-# Pytype exclude list. Files will not be tested with pytype.
-
-# pytype has its own version of these files, and thus doesn't mind if it
-# can't parse the typeshed version:
-stdlib/builtins.pyi
-stdlib/typing.pyi
-
-# can't subscript memoryview for some reason
-stdlib/asyncio/sslproto.pyi
-stdlib/asyncio/transports.pyi
-
-# errors about import statements
-stubs/mysqlclient/MySQLdb/__init__.pyi
-stubs/mysqlclient/MySQLdb/connections.pyi
-stubs/mysqlclient/MySQLdb/cursors.pyi
-
-# twisted not installed during tests
-stubs/pika/pika/adapters/twisted_connection.pyi
-
-# pytype.pytd.visitors.ContainerError: Conflicting values for TypeVar
-stubs/openpyxl/openpyxl/descriptors/nested.pyi
-
-# matplotlib not installed during tests
-stubs/shapely/shapely/plotting.pyi
-
-# matplotlib et al not installed during tests
-stubs/geopandas/geopandas/plotting.pyi
-stubs/geopandas/geopandas/explore.pyi
diff --git a/tests/pytype_test.py b/tests/pytype_test.py
deleted file mode 100755
index 1c5fce2c1..000000000
--- a/tests/pytype_test.py
+++ /dev/null
@@ -1,249 +0,0 @@
-#!/usr/bin/env python3
-# Lack of pytype typing
-# pyright: reportUnknownVariableType=false, reportUnknownMemberType=false, reportUnknownArgumentType=false, reportMissingTypeStubs=false
-"""Test runner for typeshed.
-
-Depends on pytype being installed.
-
-If pytype is installed:
- 1. For every pyi, do nothing if it is in pytype_exclude_list.txt.
- 2. Otherwise, call 'pytype.io.parse_pyi'.
-Option two will load the file and all the builtins, typeshed dependencies. This
-will also discover incorrect usage of imported modules.
-"""
-
-from __future__ import annotations
-
-import sys
-from typing import TYPE_CHECKING
-
-if TYPE_CHECKING:
- assert sys.platform != "win32", "pytype isn't yet installed in CI, but wheels can be built on Windows"
- from _typeshed import StrPath
-if sys.version_info >= (3, 13):
- print("pytype does not support Python 3.13+ yet.", file=sys.stderr)
- sys.exit(1)
-
-import argparse
-import importlib.metadata
-import inspect
-import os
-import traceback
-from collections.abc import Iterable, Sequence
-from pathlib import Path
-
-# pytype is not py.typed https://github.com/google/pytype/issues/1325
-from pytype import config as pytype_config, load_pytd # type: ignore[import]
-from pytype.imports import typeshed # type: ignore[import]
-
-from ts_utils.metadata import read_dependencies
-from ts_utils.paths import STDLIB_PATH, STUBS_PATH, TS_BASE_PATH
-from ts_utils.utils import SupportedVersionsDict, parse_stdlib_versions_file, supported_versions_for_module
-
-TYPESHED_SUBDIRS = [STDLIB_PATH.absolute(), STUBS_PATH.absolute()]
-TYPESHED_HOME = "TYPESHED_HOME"
-EXCLUDE_LIST = TS_BASE_PATH / "tests" / "pytype_exclude_list.txt"
-_LOADERS: dict[str, tuple[pytype_config.Options, load_pytd.Loader]] = {}
-
-
-def main() -> None:
- args = create_parser().parse_args()
- typeshed_location = Path(args.typeshed_location) or Path.cwd()
- check_subdirs_discoverable(TYPESHED_SUBDIRS)
- old_typeshed_home = os.environ.get(TYPESHED_HOME)
- os.environ[TYPESHED_HOME] = str(typeshed_location)
- files_to_test = determine_files_to_test(paths=[Path(file) for file in args.files] or TYPESHED_SUBDIRS)
- run_all_tests(files_to_test=files_to_test, print_stderr=args.print_stderr, dry_run=args.dry_run)
- if old_typeshed_home is None:
- del os.environ[TYPESHED_HOME]
- else:
- os.environ[TYPESHED_HOME] = old_typeshed_home
-
-
-def create_parser() -> argparse.ArgumentParser:
- parser = argparse.ArgumentParser(description="Pytype/typeshed tests.")
- parser.add_argument("-n", "--dry-run", action="store_true", default=False, help="Don't actually run tests")
- # Default to '' so that symlinking typeshed subdirs in cwd will work.
- parser.add_argument("--typeshed-location", type=str, default="", help="Path to typeshed installation.")
- # Set to true to print a stack trace every time an exception is thrown.
- parser.add_argument(
- "--print-stderr", action="store_true", default=False, help="Print stderr every time an error is encountered."
- )
- parser.add_argument(
- "files", metavar="FILE", type=str, nargs="*", help="Files or directories to check. (Default: Check all files.)"
- )
- return parser
-
-
-def run_pytype(*, filename: StrPath, python_version: str, missing_modules: Iterable[str]) -> str | None:
- """Run pytype, returning the stderr if any."""
- if python_version not in _LOADERS:
- options = pytype_config.Options.create("", parse_pyi=True, python_version=python_version)
- # For simplicity, pretends missing modules are part of the stdlib.
- missing_modules = tuple(str(STDLIB_PATH / m) for m in missing_modules)
- loader = load_pytd.create_loader(options, missing_modules)
- _LOADERS[python_version] = (options, loader)
- options, loader = _LOADERS[python_version]
- stderr: str | None
- try:
- with pytype_config.verbosity_from(options):
- ast = loader.load_file(_get_module_name(filename), filename)
- loader.finish_and_verify_ast(ast)
- except Exception:
- stderr = traceback.format_exc()
- else:
- stderr = None
- return stderr
-
-
-def _get_relative(filename: StrPath) -> Path:
- filepath = Path(filename)
- for d in TYPESHED_SUBDIRS:
- try:
- return filepath.absolute().relative_to(d.parent)
- except ValueError:
- continue
- raise ValueError(f"{filepath} not relative to {TYPESHED_SUBDIRS}")
-
-
-def _get_module_name(filename: StrPath) -> str:
- """Convert a filename {subdir}/m.n/module/foo to module.foo."""
- parts = _get_relative(filename).parts
- if parts[0] == "stdlib":
- module_parts = parts[1:]
- else:
- assert parts[0] == "stubs"
- module_parts = parts[2:]
- return ".".join(module_parts).replace(".pyi", "").replace(".__init__", "")
-
-
-def check_subdirs_discoverable(subdir_paths: Iterable[Path]) -> None:
- for p in subdir_paths:
- if not p.is_dir():
- raise SystemExit(f"Cannot find typeshed subdir at {p} (specify parent dir via --typeshed-location)")
-
-
-def determine_files_to_test(*, paths: Iterable[Path]) -> list[Path]:
- """Determine all files to test.
-
- Checks for files in the pytype exclude list and for the stdlib VERSIONS file.
- """
- filenames = find_stubs_in_paths(paths)
- ts = typeshed.Typeshed()
- exclude_list = set(ts.read_blacklist())
- stdlib_module_versions = parse_stdlib_versions_file()
- return [
- f
- for f in sorted(filenames)
- if _get_relative(f).as_posix() not in exclude_list and _is_supported_stdlib_version(stdlib_module_versions, f)
- ]
-
-
-def find_stubs_in_paths(paths: Iterable[Path]) -> list[Path]:
- filenames: list[Path] = []
- for path in paths:
- if path.is_dir():
- for root, _, fns in os.walk(path):
- filenames.extend(Path(root, fn) for fn in fns if fn.endswith(".pyi"))
- else:
- filenames.append(path)
- return filenames
-
-
-def _is_supported_stdlib_version(module_versions: SupportedVersionsDict, filename: StrPath) -> bool:
- parts = _get_relative(filename).parts
- if parts[0] != "stdlib":
- return True
- module_name = _get_module_name(filename)
- min_version, max_version = supported_versions_for_module(module_versions, module_name)
- return min_version <= sys.version_info <= max_version
-
-
-def _get_pkgs_associated_with_requirement(req_name: str) -> list[str]:
- try:
- dist = importlib.metadata.distribution(req_name)
- except importlib.metadata.PackageNotFoundError:
- # The package wasn't installed, probably because an environment
- # marker excluded it.
- return []
- toplevel_txt_contents = dist.read_text("top_level.txt")
- if toplevel_txt_contents is None:
- if dist.files is None:
- raise RuntimeError(f"Can't read find the packages associated with requirement {req_name!r}")
- maybe_modules = [f.parts[0] if len(f.parts) > 1 else inspect.getmodulename(f) for f in dist.files]
- packages = [name for name in maybe_modules if name is not None and "." not in name]
- else:
- packages = toplevel_txt_contents.split()
- # https://peps.python.org/pep-0561/#stub-only-packages
- return sorted({package.removesuffix("-stubs") for package in packages})
-
-
-def get_missing_modules(files_to_test: Iterable[Path]) -> Iterable[str]:
- """Get names of modules that should be treated as missing.
-
- Some typeshed stubs depend on dependencies outside of typeshed. Since pytype
- isn't able to read such dependencies, we instead declare them as "missing"
- modules, so that no errors are reported for them.
-
- Similarly, pytype cannot parse files on its exclude list, so we also treat
- those as missing.
- """
- stub_distributions = set[str]()
- for fi in files_to_test:
- parts = fi.parts
- try:
- idx = parts.index("stubs")
- except ValueError:
- continue
- stub_distributions.add(parts[idx + 1])
-
- missing_modules = {
- associated_package
- for distribution in stub_distributions
- for external_req in read_dependencies(distribution).external_pkgs
- for associated_package in _get_pkgs_associated_with_requirement(external_req.name)
- }
-
- with EXCLUDE_LIST.open() as f:
- for line in f:
- if not line.startswith("stubs/"):
- # Skips comments, empty lines, and stdlib files, which are in
- # the exclude list because pytype has its own version.
- continue
- _ts_subdir, _distribution, module_path = line.strip().split("/", 2)
- missing_modules.add(module_path.removesuffix(".pyi"))
- return missing_modules
-
-
-def run_all_tests(*, files_to_test: Sequence[Path], print_stderr: bool, dry_run: bool) -> None:
- bad: list[tuple[StrPath, str, str]] = []
- errors = 0
- total_tests = len(files_to_test)
- missing_modules = get_missing_modules(files_to_test)
- python_version = f"{sys.version_info.major}.{sys.version_info.minor}"
- print("Testing files with pytype...")
- for i, file_to_test in enumerate(files_to_test):
- if dry_run:
- stderr = None
- else:
- stderr = run_pytype(filename=file_to_test, python_version=python_version, missing_modules=missing_modules)
- if stderr:
- if print_stderr:
- print(f"\n{stderr}")
- errors += 1
- stacktrace_final_line = stderr.rstrip().rsplit("\n", 1)[-1]
- bad.append((_get_relative(file_to_test), python_version, stacktrace_final_line))
-
- runs = i + 1
- if runs % 25 == 0:
- print(f" {runs:3d}/{total_tests:d} with {errors:3d} errors")
-
- print(f"Ran pytype with {total_tests:d} pyis, got {errors:d} errors.")
- for f, v, err in bad:
- print(f"\n{f} ({v}): {err}")
- if errors:
- raise SystemExit("\nRun again with --print-stderr to get the full stacktrace.")
-
-
-if __name__ == "__main__":
- main()
diff --git a/tests/runtests.py b/tests/runtests.py
index efffc84df..296cb8ccd 100755
--- a/tests/runtests.py
+++ b/tests/runtests.py
@@ -6,7 +6,6 @@ import json
import re
import subprocess
import sys
-from importlib.util import find_spec
from pathlib import Path
from ts_utils.metadata import get_oldest_supported_python, read_metadata
@@ -77,7 +76,6 @@ def main() -> None:
python_version = get_oldest_supported_python()
stubtest_result: subprocess.CompletedProcess[bytes] | None = None
- pytype_result: subprocess.CompletedProcess[bytes] | None = None
print("\nRunning pre-commit...")
pre_commit_result = subprocess.run(["pre-commit", "run", "--files", *path.rglob("*")], check=False)
@@ -125,17 +123,6 @@ def main() -> None:
else:
print(colored("\nSkipping stubtest since mypy failed.", "yellow"))
- if find_spec("pytype"):
- print("\nRunning pytype...")
- pytype_result = subprocess.run([sys.executable, "tests/pytype_test.py", path], check=False)
- else:
- print(
- colored(
- f"\nSkipping pytype on Windows. You need to install it first: `{sys.executable} -m pip install pytype` .",
- "yellow",
- )
- )
-
cases_path = test_cases_path(stub if folder == "stubs" else "stdlib")
if not cases_path.exists():
# No test means they all ran successfully (0 out of 0). Not all 3rd-party stubs have regression tests.
@@ -186,7 +173,6 @@ def main() -> None:
pyright_returncode,
mypy_result.returncode,
getattr(stubtest_result, "returncode", 0),
- getattr(pytype_result, "returncode", 0),
pyright_testcases_returncode,
regr_test_returncode,
]
@@ -218,10 +204,6 @@ def main() -> None:
print("stubtest:", _SKIPPED)
else:
print("stubtest:", _SUCCESS if stubtest_result.returncode == 0 else _FAILED)
- if not pytype_result:
- print("pytype:", _SKIPPED)
- else:
- print("pytype:", _SUCCESS if pytype_result.returncode == 0 else _FAILED)
if pyright_testcases_skipped:
print("Pyright regression tests:", _SKIPPED)
else: