Enable Ruff flake8-use-pathlib (PTH) (#13795)

Port existing code to pathlib
This commit is contained in:
Avasam
2025-05-05 12:59:43 -04:00
committed by GitHub
parent 0eb44e574c
commit 4265ee7c72
11 changed files with 101 additions and 113 deletions
+1
View File
@@ -11,6 +11,7 @@ STUBS_PATH: Final = TS_BASE_PATH / "stubs"
PYPROJECT_PATH: Final = TS_BASE_PATH / "pyproject.toml"
REQUIREMENTS_PATH: Final = TS_BASE_PATH / "requirements-tests.txt"
GITIGNORE_PATH: Final = TS_BASE_PATH / ".gitignore"
TESTS_DIR: Final = "@tests"
TEST_CASES_DIR: Final = "test_cases"
+3 -4
View File
@@ -3,7 +3,6 @@
from __future__ import annotations
import functools
import os
import re
import sys
import tempfile
@@ -16,7 +15,7 @@ from typing_extensions import TypeAlias
import pathspec
from packaging.requirements import Requirement
from .paths import REQUIREMENTS_PATH, STDLIB_PATH, STUBS_PATH, TEST_CASES_DIR, allowlists_path, test_cases_path
from .paths import GITIGNORE_PATH, REQUIREMENTS_PATH, STDLIB_PATH, STUBS_PATH, TEST_CASES_DIR, allowlists_path, test_cases_path
if TYPE_CHECKING:
from _typeshed import OpenTextMode
@@ -215,7 +214,7 @@ else:
def NamedTemporaryFile(mode: OpenTextMode) -> TemporaryFileWrapper[str]: # noqa: N802
def close(self: TemporaryFileWrapper[str]) -> None:
TemporaryFileWrapper.close(self) # pyright: ignore[reportUnknownMemberType]
os.remove(self.name)
Path(self.name).unlink()
temp = tempfile.NamedTemporaryFile(mode, delete=False) # noqa: SIM115, TID251
temp.close = MethodType(close, temp) # type: ignore[method-assign]
@@ -229,7 +228,7 @@ else:
@functools.cache
def get_gitignore_spec() -> pathspec.PathSpec:
with open(".gitignore", encoding="UTF-8") as f:
with GITIGNORE_PATH.open(encoding="UTF-8") as f:
return pathspec.GitIgnoreSpec.from_lines(f)
+3 -3
View File
@@ -54,6 +54,7 @@ select = [
"PGH", # pygrep-hooks
"PIE", # flake8-pie
"PL", # Pylint
"PTH", # flake8-use-pathlib
"RSE", # flake8-raise
"RUF", # Ruff-specific and unused-noqa
"SLOT", # flake8-slots
@@ -80,9 +81,6 @@ select = [
"FURB187", # Use of assignment of `reversed` on list `{name}`
# Used for lint.flake8-import-conventions.aliases
"ICN001", # `{name}` should be imported as `{asname}`
# Autofixable flake8-use-pathlib only
"PTH201", # Do not pass the current directory explicitly to `Path`
"PTH210", # Invalid suffix passed to `.with_suffix()`
# PYI: only enable rules that have autofixes and that we always want to fix (even manually),
# avoids duplicate # noqa with flake8-pyi
"PYI009", # Empty body should contain `...`, not pass
@@ -167,6 +165,8 @@ ignore = [
"PLR2004", # Magic value used in comparison, consider replacing `{value}` with a constant variable
# Keep codeflow path separation explicit
"PLR5501", # Use `elif` instead of `else` then `if`, to reduce indentation
# Often just leads to redundant more verbose code when needing an actual str
"PTH208", # Use `pathlib.Path.iterdir()` instead.
# Allow FIXME
"TD001", # Invalid TODO tag: `{tag}`
# Git blame is sufficient
+22 -22
View File
@@ -12,19 +12,20 @@ from __future__ import annotations
import argparse
import asyncio
import glob
import os.path
import re
import subprocess
import sys
import urllib.parse
from http import HTTPStatus
from importlib.metadata import distribution
from pathlib import Path
import aiohttp
import termcolor
PYRIGHT_CONFIG = "pyrightconfig.stricter.json"
from ts_utils.paths import STDLIB_PATH, STUBS_PATH
PYRIGHT_CONFIG = Path("pyrightconfig.stricter.json")
def search_pip_freeze_output(project: str, output: str) -> tuple[str, str] | None:
@@ -52,22 +53,22 @@ def get_installed_package_info(project: str) -> tuple[str, str] | None:
return search_pip_freeze_output(project, r.stdout)
def run_stubgen(package: str, output: str) -> None:
def run_stubgen(package: str, output: Path) -> None:
print(f"Running stubgen: stubgen -o {output} -p {package}")
subprocess.run(["stubgen", "-o", output, "-p", package, "--export-less"], check=True)
def run_stubdefaulter(stub_dir: str) -> None:
def run_stubdefaulter(stub_dir: Path) -> None:
print(f"Running stubdefaulter: stubdefaulter --packages {stub_dir}")
subprocess.run(["stubdefaulter", "--packages", stub_dir], check=False)
def run_black(stub_dir: str) -> None:
def run_black(stub_dir: Path) -> None:
print(f"Running Black: black {stub_dir}")
subprocess.run(["pre-commit", "run", "black", "--files", *glob.iglob(f"{stub_dir}/**/*.pyi")], check=False)
subprocess.run(["pre-commit", "run", "black", "--files", *stub_dir.rglob("*.pyi")], check=False)
def run_ruff(stub_dir: str) -> None:
def run_ruff(stub_dir: Path) -> None:
print(f"Running Ruff: ruff check {stub_dir} --fix-only")
subprocess.run([sys.executable, "-m", "ruff", "check", stub_dir, "--fix-only"], check=False)
@@ -115,14 +116,14 @@ async def get_upstream_repo_url(project: str) -> str | None:
return None
def create_metadata(project: str, stub_dir: str, version: str) -> None:
def create_metadata(project: str, stub_dir: Path, version: str) -> None:
"""Create a METADATA.toml file."""
match = re.match(r"[0-9]+.[0-9]+", version)
if match is None:
sys.exit(f"Error: Cannot parse version number: {version}")
filename = os.path.join(stub_dir, "METADATA.toml")
filename = stub_dir / "METADATA.toml"
version = match.group(0)
if os.path.exists(filename):
if filename.exists():
return
metadata = f'version = "{version}.*"\n'
upstream_repo_url = asyncio.run(get_upstream_repo_url(project))
@@ -135,13 +136,12 @@ def create_metadata(project: str, stub_dir: str, version: str) -> None:
else:
metadata += f'upstream_repository = "{upstream_repo_url}"\n'
print(f"Writing {filename}")
with open(filename, "w", encoding="UTF-8") as file:
file.write(metadata)
filename.write_text(metadata, encoding="UTF-8")
def add_pyright_exclusion(stub_dir: str) -> None:
def add_pyright_exclusion(stub_dir: Path) -> None:
"""Exclude stub_dir from strict pyright checks."""
with open(PYRIGHT_CONFIG, encoding="UTF-8") as f:
with PYRIGHT_CONFIG.open(encoding="UTF-8") as f:
lines = f.readlines()
i = 0
while i < len(lines) and not lines[i].strip().startswith('"exclude": ['):
@@ -167,7 +167,7 @@ def add_pyright_exclusion(stub_dir: str) -> None:
third_party_excludes[-1] = last_line + "\n"
# Must use forward slash in the .json file
line_to_add = f' "{stub_dir}",\n'.replace("\\", "/")
line_to_add = f' "{stub_dir.as_posix()}",\n'
if line_to_add in third_party_excludes:
print(f"{PYRIGHT_CONFIG} already up-to-date")
@@ -177,7 +177,7 @@ def add_pyright_exclusion(stub_dir: str) -> None:
third_party_excludes.sort(key=str.lower)
print(f"Updating {PYRIGHT_CONFIG}")
with open(PYRIGHT_CONFIG, "w", encoding="UTF-8") as f:
with PYRIGHT_CONFIG.open("w", encoding="UTF-8") as f:
f.writelines(before_third_party_excludes)
f.writelines(third_party_excludes)
f.writelines(after_third_party_excludes)
@@ -194,7 +194,7 @@ def main() -> None:
parser.add_argument("--package", help="generate stubs for this Python package (default is autodetected)")
args = parser.parse_args()
project = args.project
package = args.package
package: str = args.package
if not re.match(r"[a-zA-Z0-9-_.]+$", project):
sys.exit(f"Invalid character in project name: {project!r}")
@@ -214,7 +214,7 @@ def main() -> None:
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)
if not os.path.isdir("stubs") or not os.path.isdir("stdlib"):
if not STUBS_PATH.is_dir() or not STDLIB_PATH.is_dir():
sys.exit("Error: Current working directory must be the root of typeshed repository")
# Get normalized project name and version of installed package.
@@ -226,9 +226,9 @@ def main() -> None:
sys.exit(1)
project, version = info
stub_dir = os.path.join("stubs", project)
package_dir = os.path.join(stub_dir, package)
if os.path.exists(package_dir):
stub_dir = STUBS_PATH / project
package_dir = stub_dir / package
if package_dir.exists():
sys.exit(f"Error: {package_dir} already exists (delete it first)")
run_stubgen(package, stub_dir)
+4 -3
View File
@@ -4,6 +4,7 @@ import subprocess
import sys
from collections.abc import Iterable
from http.client import HTTPResponse
from pathlib import Path
from typing import TYPE_CHECKING
from urllib.request import urlopen
from zipfile import ZipFile
@@ -18,11 +19,11 @@ if TYPE_CHECKING:
MYPY_PROTOBUF_VERSION = mypy_protobuf__version__
def download_file(url: str, destination: StrPath) -> None:
def download_file(url: str, destination: Path) -> None:
print(f"Downloading '{url}' to '{destination}'")
resp: HTTPResponse
with urlopen(url) as resp, open(destination, "wb") as file:
file.write(resp.read())
with urlopen(url) as resp:
destination.write_bytes(resp.read())
def extract_archive(archive_path: StrPath, destination: StrPath) -> None:
+2 -2
View File
@@ -33,7 +33,7 @@ PROTO_FILE_PATTERN = re.compile(r'"//:(.*)_proto"')
def extract_python_version(file_path: Path) -> str:
"""Extract the Python version from https://github.com/protocolbuffers/protobuf/blob/main/version.json ."""
with open(file_path) as file:
with file_path.open() as file:
data: dict[str, Any] = json.load(file)
# The root key will be the protobuf source code version
version = next(iter(data.values()))["languages"]["python"]
@@ -47,7 +47,7 @@ def extract_proto_file_paths(temp_dir: Path) -> list[str]:
as described in py_proto_library calls in
https://github.com/protocolbuffers/protobuf/blob/main/python/dist/BUILD.bazel .
"""
with open(temp_dir / EXTRACTED_PACKAGE_DIR / "python" / "dist" / "BUILD.bazel") as file:
with (temp_dir / EXTRACTED_PACKAGE_DIR / "python" / "dist" / "BUILD.bazel").open() as file:
matched_lines = filter(None, (re.search(PROTO_FILE_PATTERN, line) for line in file))
proto_files = [
EXTRACTED_PACKAGE_DIR + "/src/google/protobuf/" + match.group(1).replace("compiler_", "compiler/") + ".proto"
+3 -6
View File
@@ -6,7 +6,6 @@ Generally, new minor versions are a good time to update the stubs.
from __future__ import annotations
import os
import re
import shutil
import subprocess
@@ -72,21 +71,19 @@ def post_creation() -> None:
for path in STUBS_FOLDER.rglob("*_pb2.pyi"):
print(f"Fixing imports in '{path}'")
with open(path, encoding="utf-8") as file:
filedata = file.read()
filedata = path.read_text(encoding="utf-8")
# Replace the target string
filedata = re.sub(TSL_IMPORT_PATTERN, "\\1tensorflow.tsl.", filedata)
filedata = re.sub(XLA_IMPORT_PATTERN, "\\1tensorflow.compiler.xla.", filedata)
# Write the file out again
with open(path, "w", encoding="utf-8") as file:
file.write(filedata)
path.write_text(filedata, encoding="utf-8")
print()
for to_remove in PROTOS_TO_REMOVE:
file_path = STUBS_FOLDER / "tensorflow" / to_remove
os.remove(file_path)
file_path.unlink()
print(f"Removed '{file_path}'")
+5 -6
View File
@@ -113,11 +113,10 @@ def check_test_cases() -> None:
def check_no_symlinks() -> None:
"""Check that there are no symlinks in the typeshed repository."""
files = [os.path.join(root, file) for root, _, files in os.walk(".") for file in files]
files = [Path(root, file) for root, _, files in os.walk(".") for file in files]
no_symlink = "You cannot use symlinks in typeshed, please copy {} to its link."
for file in files:
_, ext = os.path.splitext(file)
if ext == ".pyi" and os.path.islink(file):
if file.suffix == ".pyi" and file.is_symlink():
raise ValueError(no_symlink.format(file))
@@ -141,18 +140,18 @@ def _find_stdlib_modules() -> set[str]:
modules = set[str]()
for path, _, files in os.walk(STDLIB_PATH):
for filename in files:
base_module = ".".join(os.path.normpath(path).split(os.sep)[1:])
base_module = ".".join(Path(path).parts[1:])
if filename == "__init__.pyi":
modules.add(base_module)
elif filename.endswith(".pyi"):
mod, _ = os.path.splitext(filename)
mod = filename[:-4]
modules.add(f"{base_module}.{mod}" if base_module else mod)
return modules
def check_metadata() -> None:
"""Check that all METADATA.toml files are valid."""
for distribution in os.listdir("stubs"):
for distribution in os.listdir(STUBS_PATH):
# This function does various sanity checks for METADATA.toml files
read_metadata(distribution)
+7 -8
View File
@@ -150,6 +150,8 @@ def match(path: Path, args: TestConfig) -> bool:
def add_files(files: list[Path], module: Path, args: TestConfig) -> None:
"""Add all files in package or module represented by 'name' located in 'root'."""
if module.name.startswith("."):
return
if module.is_file() and module.suffix == ".pyi":
if match(module, args):
files.append(module)
@@ -244,10 +246,8 @@ def add_third_party_files(distribution: str, files: list[Path], args: TestConfig
seen_dists.add(distribution)
seen_dists.update(r.name for r in typeshed_reqs)
root = distribution_path(distribution)
for name in os.listdir(root):
if name.startswith("."):
continue
add_files(files, (root / name), args)
for path in root.iterdir():
add_files(files, path, args)
class TestResult(NamedTuple):
@@ -295,7 +295,7 @@ def test_third_party_distribution(
def test_stdlib(args: TestConfig) -> TestResult:
files: list[Path] = []
for file in STDLIB_PATH.iterdir():
if file.name in ("VERSIONS", TESTS_DIR) or file.name.startswith("."):
if file.name in ("VERSIONS", TESTS_DIR):
continue
add_files(files, file, args)
@@ -525,15 +525,14 @@ def test_third_party_stubs(args: TestConfig, tempdir: Path) -> TestSummary:
def test_typeshed(args: TestConfig, tempdir: Path) -> TestSummary:
print(f"*** Testing Python {args.version} on {args.platform}")
stdlib_dir, stubs_dir = Path("stdlib"), Path("stubs")
summary = TestSummary()
if stdlib_dir in args.filter or any(stdlib_dir in path.parents for path in args.filter):
if STDLIB_PATH in args.filter or any(STDLIB_PATH in path.parents for path in args.filter):
mypy_result, files_checked = test_stdlib(args)
summary.register_result(mypy_result, files_checked)
print()
if stubs_dir in args.filter or any(stubs_dir in path.parents for path in args.filter):
if STUBS_PATH in args.filter or any(STUBS_PATH in path.parents for path in args.filter):
tp_results = test_third_party_stubs(args, tempdir)
summary.merge(tp_results)
print()
+40 -45
View File
@@ -24,7 +24,6 @@ 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
@@ -38,21 +37,22 @@ 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", "stubs"]
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 = args.typeshed_location or os.getcwd()
subdir_paths = [os.path.join(typeshed_location, d) for d in TYPESHED_SUBDIRS]
check_subdirs_discoverable(subdir_paths)
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] = typeshed_location
files_to_test = determine_files_to_test(paths=args.files or subdir_paths)
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]
@@ -75,12 +75,12 @@ def create_parser() -> argparse.ArgumentParser:
return parser
def run_pytype(*, filename: str, python_version: str, missing_modules: Iterable[str]) -> str | None:
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(os.path.join("stdlib", m) for m in missing_modules)
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]
@@ -100,13 +100,13 @@ def _get_relative(filename: StrPath) -> Path:
filepath = Path(filename)
for d in TYPESHED_SUBDIRS:
try:
return filepath.absolute().relative_to(Path(d).absolute().parent)
return filepath.absolute().relative_to(d.parent)
except ValueError:
continue
raise ValueError(f"{filepath} not relative to {TYPESHED_SUBDIRS}")
def _get_module_name(filename: str) -> str:
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":
@@ -117,13 +117,13 @@ def _get_module_name(filename: str) -> str:
return ".".join(module_parts).replace(".pyi", "").replace(".__init__", "")
def check_subdirs_discoverable(subdir_paths: list[str]) -> None:
def check_subdirs_discoverable(subdir_paths: Iterable[Path]) -> None:
for p in subdir_paths:
if not os.path.isdir(p):
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: Sequence[str]) -> list[str]:
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.
@@ -132,28 +132,25 @@ def determine_files_to_test(*, paths: Sequence[str]) -> list[str]:
ts = typeshed.Typeshed()
exclude_list = set(ts.read_blacklist())
stdlib_module_versions = parse_stdlib_versions_file()
files = []
for f in sorted(filenames):
if _get_relative(f).as_posix() in exclude_list:
continue
if not _is_supported_stdlib_version(stdlib_module_versions, f):
continue
files.append(f)
return files
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: Sequence[str]) -> list[str]:
filenames: list[str] = []
def find_stubs_in_paths(paths: Iterable[Path]) -> list[Path]:
filenames: list[Path] = []
for path in paths:
if os.path.isdir(path):
if path.is_dir():
for root, _, fns in os.walk(path):
filenames.extend(os.path.join(root, fn) for fn in fns if fn.endswith(".pyi"))
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: str) -> bool:
def _is_supported_stdlib_version(module_versions: SupportedVersionsDict, filename: StrPath) -> bool:
parts = _get_relative(filename).parts
if parts[0] != "stdlib":
return True
@@ -181,7 +178,7 @@ def _get_pkgs_associated_with_requirement(req_name: str) -> list[str]:
return sorted({package.removesuffix("-stubs") for package in packages})
def get_missing_modules(files_to_test: Sequence[str]) -> Iterable[str]:
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
@@ -191,37 +188,35 @@ def get_missing_modules(files_to_test: Sequence[str]) -> Iterable[str]:
Similarly, pytype cannot parse files on its exclude list, so we also treat
those as missing.
"""
stub_distributions = set()
stub_distributions = set[str]()
for fi in files_to_test:
parts = fi.split(os.sep)
parts = fi.parts
try:
idx = parts.index("stubs")
except ValueError:
continue
stub_distributions.add(parts[idx + 1])
missing_modules = set()
for distribution in stub_distributions:
for external_req in read_dependencies(distribution).external_pkgs:
associated_packages = _get_pkgs_associated_with_requirement(external_req.name)
missing_modules.update(associated_packages)
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)
}
test_dir = os.path.dirname(__file__)
exclude_list = os.path.join(test_dir, "pytype_exclude_list.txt")
with open(exclude_list) as f:
excluded_files = f.readlines()
for fi in excluded_files:
if not fi.startswith("stubs/"):
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
unused_stubs_prefix, unused_pkg, mod_path = fi.split("/", 2) # pyright: ignore[reportUnusedVariable]
missing_modules.add(os.path.splitext(mod_path)[0])
_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[str], print_stderr: bool, dry_run: bool) -> None:
bad = []
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)
+11 -14
View File
@@ -3,7 +3,6 @@ from __future__ import annotations
import argparse
import json
import os
import re
import subprocess
import sys
@@ -13,8 +12,8 @@ from pathlib import Path
from ts_utils.paths import TEST_CASES_DIR, test_cases_path
from ts_utils.utils import colored
_STRICTER_CONFIG_FILE = "pyrightconfig.stricter.json"
_TESTCASES_CONFIG_FILE = "pyrightconfig.testcases.json"
_STRICTER_CONFIG_FILE = Path("pyrightconfig.stricter.json")
_TESTCASES_CONFIG_FILE = Path("pyrightconfig.testcases.json")
_NPX_ERROR_PATTERN = r"error (runn|find)ing npx"
_NPX_ERROR_MESSAGE = colored("\nSkipping Pyright tests: npx is not installed or can't be run!", "yellow")
_SUCCESS = colored("Success", "green")
@@ -33,10 +32,9 @@ def _parse_jsonc(json_text: str) -> str:
return valid_json
def _get_strict_params(stub_path: str) -> list[str]:
with open(_STRICTER_CONFIG_FILE, encoding="UTF-8") as file:
data = json.loads(_parse_jsonc(file.read()))
lower_stub_path = stub_path.lower()
def _get_strict_params(stub_path: Path) -> list[str | Path]:
data = json.loads(_parse_jsonc(_STRICTER_CONFIG_FILE.read_text(encoding="UTF-8")))
lower_stub_path = stub_path.as_posix().lower()
if any(lower_stub_path == stub.lower() for stub in data["exclude"]):
return []
return ["-p", _STRICTER_CONFIG_FILE]
@@ -60,23 +58,22 @@ def main() -> None:
)
parser.add_argument("path", help="Path of the stub to test in format <folder>/<stub>, from the root of the project.")
args = parser.parse_args()
path: str = args.path
path = Path(args.path)
run_stubtest: bool = args.run_stubtest
python_version: str = args.python_version
path_tokens = Path(path).parts
if len(path_tokens) != 2:
if len(path.parts) != 2:
parser.error("'path' argument should be in format <folder>/<stub>.")
folder, stub = path_tokens
folder, stub = path.parts
if folder not in {"stdlib", "stubs"}:
parser.error("Only the 'stdlib' and 'stubs' folders are supported.")
if not os.path.exists(path):
if not path.exists():
parser.error(f"{path=} does not exist.")
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(path).rglob("*")], check=False)
pre_commit_result = subprocess.run(["pre-commit", "run", "--files", *path.rglob("*")], check=False)
print("\nRunning check_typeshed_structure.py...")
check_structure_result = subprocess.run([sys.executable, "tests/check_typeshed_structure.py"], check=False)
@@ -141,7 +138,7 @@ def main() -> None:
regr_test_returncode = 0
else:
print(f"\nRunning Pyright regression tests for Python {python_version}...")
command = [
command: list[str | Path] = [
sys.executable,
"tests/pyright_test.py",
str(cases_path),