blob: 295a218deb177644d49b6546980d65bc87494b43 [file] [edit]
#!/usr/bin/env python3
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
"""Run every tensor-shape stub test: static and runtime, for every library.
This is the single entry point CI uses, internally and on GitHub, so that all
of the shape coverage lands in one job rather than one job per library. The
per-package `run_pyrefly.py` and `run_runtime_tests.py` remain the things to
reach for while iterating on a single library.
Builds Pyrefly before checking, and needs the shared virtualenv from
bootstrap_venv.py for the runtime half. `--static-only` drops the virtualenv
requirement entirely,
which is the usual mode when changing Pyrefly rather than the stubs. Nothing
here downloads anything.
"""
from __future__ import annotations
import argparse
import subprocess
import sys
from pathlib import Path
from shape_testing import pyrefly_command, TENSOR_SHAPES_ROOT, venv_python
PACKAGES: tuple[str, ...] = (
"pyrefly-torch-stubs",
"pyrefly-numpy-stubs",
"pyrefly-jax-stubs",
)
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument(
"--pyrefly",
type=Path,
default=None,
help="use this binary as is; the only mode that does not build Pyrefly first",
)
parser.add_argument(
"--buck",
action="store_true",
help="build and run Pyrefly with Buck instead of Cargo",
)
parser.add_argument(
"--release",
action="store_true",
help="build with the Cargo release profile instead of debug",
)
parser.add_argument(
"--python",
type=Path,
default=None,
help="interpreter with torch/numpy/jax installed (default: the shared virtualenv)",
)
parser.add_argument(
"--static-only",
action="store_true",
help="only type check; needs no virtualenv",
)
parser.add_argument(
"--runtime-only",
action="store_true",
help="only execute the suites against the real libraries",
)
parser.add_argument("--nocapture", action="store_true")
args = parser.parse_args()
if args.static_only and args.runtime_only:
raise SystemExit("--static-only and --runtime-only are mutually exclusive")
# Resolve both toolchains before running anything, so a missing virtualenv
# fails immediately rather than after several minutes of type checking.
pyrefly = (
None
if args.runtime_only
else pyrefly_command(
explicit=args.pyrefly, buck=args.buck, release=args.release
)
)
python = (
None
if args.static_only
else venv_python(
args.python,
extra_hint=(
"Pass --static-only to run just the type checking, which needs no\n"
"virtualenv. That is usually the right mode when working on Pyrefly\n"
"itself rather than on the stubs, since CI runs the runtime tests.\n\n"
),
)
)
failures: list[str] = []
for package in PACKAGES:
package_root = TENSOR_SHAPES_ROOT / package
if pyrefly is not None:
step = f"{package} static"
print(f"\n=== {step} ===", flush=True)
command = [sys.executable, str(package_root / "run_pyrefly.py")]
# Forward the already-resolved binary rather than re-passing the
# flags, so that the three packages share one build. `--pyrefly`,
# $PYREFLY and $CARGO_TARGET_DIR may all be relative to this
# process's directory, and the child runs from a different one. A
# single-element command is a binary path; anything longer is the
# `buck2 run` invocation, which needs no resolving.
if len(pyrefly) == 1:
command.extend(["--pyrefly", pyrefly[0]])
else:
command.append("--buck")
if args.nocapture:
command.append("--nocapture")
if not run(command):
failures.append(step)
if python is not None:
step = f"{package} runtime"
print(f"\n=== {step} ===", flush=True)
if not run([str(python), str(package_root / "run_runtime_tests.py")]):
failures.append(step)
if failures:
print("\nFAILED: " + ", ".join(failures), file=sys.stderr, flush=True)
return 1
print("\nAll tensor-shape tests passed.", flush=True)
return 0
def run(command: list[str]) -> bool:
print("+ " + " ".join(command), flush=True)
return subprocess.run(command, cwd=TENSOR_SHAPES_ROOT).returncode == 0
if __name__ == "__main__":
sys.exit(main())