Skip to content

Commit 16dcb57

Browse files
gh-131507: Add a way to recreate the Misc/mypy symlinks if missing (#132274)
They will be removed in source tarballs so they don't appear in the SBOM. Co-authored-by: Stan Ulbrych <[email protected]>
1 parent f5f1ac8 commit 16dcb57

File tree

4 files changed

+75
-2
lines changed

4 files changed

+75
-2
lines changed

.github/workflows/mypy.yml

+1
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,5 @@ jobs:
5959
cache: pip
6060
cache-dependency-path: Tools/requirements-dev.txt
6161
- run: pip install -r Tools/requirements-dev.txt
62+
- run: python3 Misc/mypy/make_symlinks.py --symlink
6263
- run: mypy --config-file ${{ matrix.target }}/mypy.ini

Misc/mypy/README.md

+10-2
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,21 @@ This directory stores symlinks to standard library modules and packages
44
that are fully type-annotated and ready to be used in type checking of
55
the rest of the stdlib or Tools/ and so on.
66

7+
## Why this is necessary
78
Due to most of the standard library being untyped, we prefer not to
89
point mypy directly at `Lib/` for type checking. Additionally, mypy
910
as a tool does not support shadowing typing-related standard libraries
1011
like `types`, `typing`, and `collections.abc`.
1112

1213
So instead, we set `mypy_path` to include this directory,
1314
which only links modules and packages we know are safe to be
14-
type-checked themselves and used as dependencies.
15+
type-checked themselves and used as dependencies. See
16+
`Lib/_pyrepl/mypy.ini` for a usage example.
1517

16-
See `Lib/_pyrepl/mypy.ini` for an example.
18+
## I want to add a new type-checked module
19+
Add it to `typed-stdlib.txt` and run `make_symlinks.py --symlink`.
20+
21+
## I don't see any symlinks in this directory
22+
The symlinks in this directory are skipped in source tarballs
23+
in Python releases. This ensures they don't end up in the SBOM. To
24+
recreate them, run the `make_symlinks.py --symlink` script.

Misc/mypy/make_symlinks.py

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/usr/bin/env python3
2+
from __future__ import annotations
3+
4+
import argparse
5+
import os
6+
from pathlib import Path
7+
8+
CURRENT_DIR = Path(__file__).parent
9+
MISC_DIR = CURRENT_DIR.parent
10+
REPO_ROOT = MISC_DIR.parent
11+
LIB_DIR = REPO_ROOT / "Lib"
12+
FILE_LIST = CURRENT_DIR / "typed-stdlib.txt"
13+
14+
parser = argparse.ArgumentParser(prog="make_symlinks.py")
15+
parser.add_argument(
16+
"--symlink",
17+
action="store_true",
18+
help="Create symlinks",
19+
)
20+
parser.add_argument(
21+
"--clean",
22+
action="store_true",
23+
help="Delete any pre-existing symlinks",
24+
)
25+
26+
args = parser.parse_args()
27+
28+
if args.clean:
29+
for entry in CURRENT_DIR.glob("*"):
30+
if entry.is_symlink():
31+
entry_at_root = entry.relative_to(REPO_ROOT)
32+
print(f"removing pre-existing {entry_at_root}")
33+
entry.unlink()
34+
35+
for link in FILE_LIST.read_text().splitlines():
36+
link = link.strip()
37+
if not link or link.startswith('#'):
38+
continue
39+
40+
src = LIB_DIR / link
41+
dst = CURRENT_DIR / link
42+
src_at_root = src.relative_to(REPO_ROOT)
43+
dst_at_root = dst.relative_to(REPO_ROOT)
44+
if (
45+
dst.is_symlink()
46+
and src.resolve(strict=True) == dst.resolve(strict=True)
47+
):
48+
continue
49+
50+
if not args.symlink and args.clean:
51+
# when the user called --clean without --symlink, don't report missing
52+
# symlinks that we just deleted ourselves
53+
continue
54+
55+
# we specifically want to create relative-path links with ..
56+
src_rel = os.path.relpath(src, CURRENT_DIR)
57+
action = "symlinking" if args.symlink else "missing symlink to"
58+
print(f"{action} {src_at_root} at {dst_at_root}")
59+
if args.symlink:
60+
os.symlink(src_rel, dst)

Misc/mypy/typed-stdlib.txt

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# These libraries in the stdlib can be type-checked.
2+
3+
_colorize.py
4+
_pyrepl

0 commit comments

Comments
 (0)