Skip to content

Commit b65ef3a

Browse files
authored
Make third party stubtest output more succinct (#7853)
stubtest_third_party.py will now only print a single line when the test succeeds for a certain distribution, unless the "-v" option is given. When the test fails or "-v" is given, will still print the full output. The success status is now colored to make spotting failures easier. stdout/stderr is now used consistently: The distribution name and success status is always printed to stdout, all other output goes to stderr. Running the script with "2>/dev/null" will only show the success status of the test, one per line.
1 parent 7ca8303 commit b65ef3a

File tree

3 files changed

+69
-28
lines changed

3 files changed

+69
-28
lines changed

.github/workflows/daily.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ jobs:
5151
with:
5252
python-version: 3.9
5353
- name: Install dependencies
54-
run: pip install $(grep tomli== requirements-tests.txt)
54+
run: pip install $(grep tomli== requirements-tests.txt) termcolor
5555
- name: Install apt packages
5656
run: |
5757
sudo apt update

.github/workflows/tests.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,14 @@ jobs:
8080
version: ${{ env.PYRIGHT_VERSION }}
8181
python-platform: ${{ matrix.python-platform }}
8282
python-version: ${{ matrix.python-version }}
83-
no-comments: ${{ matrix.python-version != '3.9' || matrix.python-platform != 'Linux' }} # Having each job create the same comment is too noisy.
83+
no-comments: ${{ matrix.python-version != '3.9' || matrix.python-platform != 'Linux' }} # Having each job create the same comment is too noisy.
8484
project: ./pyrightconfig.stricter.json
8585
- uses: jakebailey/pyright-action@v1
8686
with:
8787
version: ${{ env.PYRIGHT_VERSION }}
8888
python-platform: ${{ matrix.python-platform }}
8989
python-version: ${{ matrix.python-version }}
90-
no-comments: ${{ matrix.python-version != '3.9' || matrix.python-platform != 'Linux' }} # Having each job create the same comment is too noisy.
90+
no-comments: ${{ matrix.python-version != '3.9' || matrix.python-platform != 'Linux' }} # Having each job create the same comment is too noisy.
9191

9292
stubtest-stdlib:
9393
name: Check stdlib with stubtest
@@ -124,7 +124,7 @@ jobs:
124124
with:
125125
python-version: 3.9
126126
- name: Install dependencies
127-
run: pip install $(grep tomli== requirements-tests.txt)
127+
run: pip install $(grep tomli== requirements-tests.txt) termcolor
128128
- name: Run stubtest
129129
run: |
130130
STUBS=$(

tests/stubtest_third_party.py

Lines changed: 65 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,39 @@
1010
import tempfile
1111
import venv
1212
from pathlib import Path
13-
from typing import NoReturn
13+
from typing import TYPE_CHECKING, NoReturn
1414

1515
import tomli
1616

17+
if TYPE_CHECKING:
18+
19+
def colored(__str: str, __style: str) -> str:
20+
...
21+
22+
else:
23+
try:
24+
from termcolor import colored
25+
except ImportError:
26+
27+
def colored(s: str, _: str) -> str:
28+
return s
29+
1730

1831
@functools.lru_cache()
1932
def get_mypy_req() -> str:
2033
with open("requirements-tests.txt") as f:
2134
return next(line.strip() for line in f if "mypy" in line)
2235

2336

24-
def run_stubtest(dist: Path) -> bool:
37+
def run_stubtest(dist: Path, *, verbose: bool = False) -> bool:
2538
with open(dist / "METADATA.toml") as f:
2639
metadata = dict(tomli.loads(f.read()))
2740

41+
print(f"{dist.name}...", end="")
42+
2843
if not metadata.get("stubtest", True):
29-
print(f"Skipping stubtest for {dist.name}\n\n")
44+
print(colored(" skipping", "yellow"))
45+
print()
3046
return True
3147

3248
with tempfile.TemporaryDirectory() as tmp:
@@ -47,9 +63,7 @@ def run_stubtest(dist: Path) -> bool:
4763
pip_cmd = [pip_exe, "install", "-r", str(req_path)]
4864
subprocess.run(pip_cmd, check=True, capture_output=True)
4965
except subprocess.CalledProcessError as e:
50-
print(f"Failed to install requirements for {dist.name}", file=sys.stderr)
51-
print(e.stdout.decode(), file=sys.stderr)
52-
print(e.stderr.decode(), file=sys.stderr)
66+
print_command_failure("Failed to install requirements", e)
5367
return False
5468

5569
# We need stubtest to be able to import the package, so install mypy into the venv
@@ -58,18 +72,15 @@ def run_stubtest(dist: Path) -> bool:
5872
dists_to_install = [dist_req, get_mypy_req()]
5973
dists_to_install.extend(metadata.get("requires", []))
6074
pip_cmd = [pip_exe, "install"] + dists_to_install
61-
print(" ".join(pip_cmd), file=sys.stderr)
6275
try:
6376
subprocess.run(pip_cmd, check=True, capture_output=True)
6477
except subprocess.CalledProcessError as e:
65-
print(f"Failed to install {dist.name}", file=sys.stderr)
66-
print(e.stdout.decode(), file=sys.stderr)
67-
print(e.stderr.decode(), file=sys.stderr)
78+
print_command_failure("Failed to install", e)
6879
return False
6980

7081
packages_to_check = [d.name for d in dist.iterdir() if d.is_dir() and d.name.isidentifier()]
7182
modules_to_check = [d.stem for d in dist.iterdir() if d.is_file() and d.suffix == ".pyi"]
72-
cmd = [
83+
stubtest_cmd = [
7384
python_exe,
7485
"-m",
7586
"mypy.stubtest",
@@ -85,35 +96,62 @@ def run_stubtest(dist: Path) -> bool:
8596
]
8697
allowlist_path = dist / "@tests/stubtest_allowlist.txt"
8798
if allowlist_path.exists():
88-
cmd.extend(["--allowlist", str(allowlist_path)])
99+
stubtest_cmd.extend(["--allowlist", str(allowlist_path)])
89100

90101
try:
91-
print(f"MYPYPATH={dist}", " ".join(cmd), file=sys.stderr)
92-
subprocess.run(cmd, env={"MYPYPATH": str(dist), "MYPY_FORCE_COLOR": "1"}, check=True)
93-
except subprocess.CalledProcessError:
94-
print(f"stubtest failed for {dist.name}", file=sys.stderr)
95-
print("\n\n", file=sys.stderr)
102+
subprocess.run(stubtest_cmd, env={"MYPYPATH": str(dist), "MYPY_FORCE_COLOR": "1"}, check=True, capture_output=True)
103+
except subprocess.CalledProcessError as e:
104+
print(colored(" fail", "red"))
105+
print_commands(dist, pip_cmd, stubtest_cmd)
106+
print_command_output(e)
96107

97108
print("Ran with the following environment:", file=sys.stderr)
98-
subprocess.run([pip_exe, "freeze"])
109+
ret = subprocess.run([pip_exe, "freeze"], capture_output=True)
110+
print_command_output(ret)
99111

100112
if allowlist_path.exists():
101113
print(
102114
f'To fix "unused allowlist" errors, remove the corresponding entries from {allowlist_path}', file=sys.stderr
103115
)
116+
print(file=sys.stderr)
104117
else:
105118
print(f"Re-running stubtest with --generate-allowlist.\nAdd the following to {allowlist_path}:", file=sys.stderr)
106-
subprocess.run(cmd + ["--generate-allowlist"], env={"MYPYPATH": str(dist)})
107-
print("\n\n", file=sys.stderr)
119+
ret = subprocess.run(stubtest_cmd + ["--generate-allowlist"], env={"MYPYPATH": str(dist)}, capture_output=True)
120+
print_command_output(ret)
121+
108122
return False
109123
else:
110-
print(f"stubtest succeeded for {dist.name}", file=sys.stderr)
111-
print("\n\n", file=sys.stderr)
124+
print(colored(" success", "green"))
125+
126+
if verbose:
127+
print_commands(dist, pip_cmd, stubtest_cmd)
128+
112129
return True
113130

114131

132+
def print_commands(dist: Path, pip_cmd: list[str], stubtest_cmd: list[str]) -> None:
133+
print(file=sys.stderr)
134+
print(" ".join(pip_cmd), file=sys.stderr)
135+
print(f"MYPYPATH={dist}", " ".join(stubtest_cmd), file=sys.stderr)
136+
print(file=sys.stderr)
137+
138+
139+
def print_command_failure(message: str, e: subprocess.CalledProcessError) -> None:
140+
print(colored(" fail", "red"))
141+
print(file=sys.stderr)
142+
print(message, file=sys.stderr)
143+
print_command_output(e)
144+
145+
146+
def print_command_output(e: subprocess.CalledProcessError | subprocess.CompletedProcess[bytes]) -> None:
147+
print(e.stdout.decode(), end="", file=sys.stderr)
148+
print(e.stderr.decode(), end="", file=sys.stderr)
149+
print(file=sys.stderr)
150+
151+
115152
def main() -> NoReturn:
116153
parser = argparse.ArgumentParser()
154+
parser.add_argument("-v", "--verbose", action="store_true", help="verbose output")
117155
parser.add_argument("--num-shards", type=int, default=1)
118156
parser.add_argument("--shard-index", type=int, default=0)
119157
parser.add_argument("dists", metavar="DISTRIBUTION", type=str, nargs=argparse.ZERO_OR_MORE)
@@ -129,10 +167,13 @@ def main() -> NoReturn:
129167
for i, dist in enumerate(dists):
130168
if i % args.num_shards != args.shard_index:
131169
continue
132-
if not run_stubtest(dist):
170+
if not run_stubtest(dist, verbose=args.verbose):
133171
result = 1
134172
sys.exit(result)
135173

136174

137175
if __name__ == "__main__":
138-
main()
176+
try:
177+
main()
178+
except KeyboardInterrupt:
179+
pass

0 commit comments

Comments
 (0)