diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 94fdf2571d953..d079f528debb3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,6 +48,13 @@ repos: language: system types: [rust] priority: 0 + - id: check-mypy-primer-sha + name: Check mypy-primer SHA is in sync + entry: python3 scripts/check_mypy_primer_sha.py + language: system + pass_filenames: false + files: ^scripts/(mypy_primer\.sh|setup_primer_project\.py)$ + priority: 0 # Prettier - repo: https://github.com/rbubley/mirrors-prettier diff --git a/pyproject.toml b/pyproject.toml index d09842c850472..f67681e564d98 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -80,6 +80,7 @@ select = [ "PGH", # pygrep-hooks "PYI", # flake8-pyi "RUF", + "S602", # flake8-bandit: subprocess-popen-with-shell-equals-true ] ignore = [ diff --git a/scripts/check_mypy_primer_sha.py b/scripts/check_mypy_primer_sha.py new file mode 100644 index 0000000000000..06b5287969ba8 --- /dev/null +++ b/scripts/check_mypy_primer_sha.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +"""Check that the mypy-primer SHA in setup_primer_project.py matches mypy_primer.sh.""" + +from __future__ import annotations + +import re +import sys +from pathlib import Path + +ROOT = Path(__file__).parent.parent + +SH_FILE = ROOT / "scripts" / "mypy_primer.sh" +PY_FILE = ROOT / "scripts" / "setup_primer_project.py" + +SHA_RE = re.compile(r"mypy_primer@([0-9a-f]{40})") +REV_RE = re.compile(r'rev\s*=\s*"([0-9a-f]{40})"') + + +def extract(pattern: re.Pattern[str], path: Path, label: str) -> str: + m = pattern.search(path.read_text()) + if m is None: + print(f"error: could not find {label} in {path}", file=sys.stderr) + sys.exit(1) + return m.group(1) + + +sh_sha = extract(SHA_RE, SH_FILE, "mypy_primer SHA") +py_sha = extract(REV_RE, PY_FILE, "mypy-primer rev") + +if sh_sha != py_sha: + print( + f"error: mypy-primer SHA mismatch\n" + f" {SH_FILE.relative_to(ROOT)}: {sh_sha}\n" + f" {PY_FILE.relative_to(ROOT)}: {py_sha}\n" + f"Update the rev in {PY_FILE.relative_to(ROOT)} to match {SH_FILE.relative_to(ROOT)}.", + file=sys.stderr, + ) + sys.exit(1) + +print(f"ok: mypy-primer SHA is in sync ({sh_sha[:12]}...)") diff --git a/scripts/setup_primer_project.py b/scripts/setup_primer_project.py index f670a74e6f80a..c1530f76b96e1 100755 --- a/scripts/setup_primer_project.py +++ b/scripts/setup_primer_project.py @@ -5,7 +5,7 @@ # dependencies = ["mypy-primer"] # # [tool.uv.sources] -# mypy-primer = { git = "https://github.com/hauntsaninja/mypy_primer" } +# mypy-primer = { git = "https://github.com/hauntsaninja/mypy_primer", rev = "850d65d9da947ef9e02498b6f07598e7c8401641" } # /// """Clone a mypy-primer project and set up a virtualenv with its dependencies installed. @@ -90,13 +90,13 @@ def main() -> None: if project.install_cmd: install_cmd = project.install_cmd.format(install=install_base) print(f"Running install command: {install_cmd}") - subprocess.run(install_cmd, shell=True, cwd=target_dir, check=True) + subprocess.run(shlex.split(install_cmd), cwd=target_dir, check=True) # Install listed dependencies (matching primer's setup()) if project.deps: - deps_cmd = f"{install_base} {' '.join(project.deps)}" + deps_cmd_parts = shlex.split(install_base) + project.deps print(f"Installing dependencies: {', '.join(project.deps)}") - subprocess.run(deps_cmd, shell=True, cwd=target_dir, check=True) + subprocess.run(deps_cmd_parts, cwd=target_dir, check=True) print(f"\nDone! Project set up at {target_dir}") print(f"Activate the venv with: source {venv_dir}/bin/activate") diff --git a/scripts/ty_benchmark/src/benchmark/__init__.py b/scripts/ty_benchmark/src/benchmark/__init__.py index 76593e71da5c2..91b4466bdf7a0 100644 --- a/scripts/ty_benchmark/src/benchmark/__init__.py +++ b/scripts/ty_benchmark/src/benchmark/__init__.py @@ -1,7 +1,6 @@ from __future__ import annotations import logging -import shlex import subprocess import sys from pathlib import Path diff --git a/scripts/ty_benchmark/src/benchmark/snapshot.py b/scripts/ty_benchmark/src/benchmark/snapshot.py index 01ddae816ea93..57db1f869cdcd 100644 --- a/scripts/ty_benchmark/src/benchmark/snapshot.py +++ b/scripts/ty_benchmark/src/benchmark/snapshot.py @@ -4,9 +4,15 @@ import logging import re import subprocess +import sys from pathlib import Path from typing import Mapping, NamedTuple +if sys.platform == "win32": + import mslex as shlex +else: + import shlex + from benchmark import Command @@ -68,8 +74,7 @@ def run(self, *, cwd: Path, env: Mapping[str, str]): if command.prepare: logging.info(f"Running prepare: {command.prepare}") subprocess.run( - command.prepare, - shell=True, + shlex.split(command.prepare), cwd=cwd, env=env, capture_output=True,