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 <89152624+StanFromIreland@users.noreply.github.com>
This commit is contained in:
Łukasz Langa 2025-04-09 01:01:36 +02:00 committed by GitHub
parent f5f1ac84b3
commit 16dcb576f7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 75 additions and 2 deletions

View file

@ -59,4 +59,5 @@ jobs:
cache: pip
cache-dependency-path: Tools/requirements-dev.txt
- run: pip install -r Tools/requirements-dev.txt
- run: python3 Misc/mypy/make_symlinks.py --symlink
- run: mypy --config-file ${{ matrix.target }}/mypy.ini

View file

@ -4,6 +4,7 @@ This directory stores symlinks to standard library modules and packages
that are fully type-annotated and ready to be used in type checking of
the rest of the stdlib or Tools/ and so on.
## Why this is necessary
Due to most of the standard library being untyped, we prefer not to
point mypy directly at `Lib/` for type checking. Additionally, mypy
as a tool does not support shadowing typing-related standard libraries
@ -11,6 +12,13 @@ like `types`, `typing`, and `collections.abc`.
So instead, we set `mypy_path` to include this directory,
which only links modules and packages we know are safe to be
type-checked themselves and used as dependencies.
type-checked themselves and used as dependencies. See
`Lib/_pyrepl/mypy.ini` for a usage example.
See `Lib/_pyrepl/mypy.ini` for an example.
## I want to add a new type-checked module
Add it to `typed-stdlib.txt` and run `make_symlinks.py --symlink`.
## I don't see any symlinks in this directory
The symlinks in this directory are skipped in source tarballs
in Python releases. This ensures they don't end up in the SBOM. To
recreate them, run the `make_symlinks.py --symlink` script.

60
Misc/mypy/make_symlinks.py Executable file
View file

@ -0,0 +1,60 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import os
from pathlib import Path
CURRENT_DIR = Path(__file__).parent
MISC_DIR = CURRENT_DIR.parent
REPO_ROOT = MISC_DIR.parent
LIB_DIR = REPO_ROOT / "Lib"
FILE_LIST = CURRENT_DIR / "typed-stdlib.txt"
parser = argparse.ArgumentParser(prog="make_symlinks.py")
parser.add_argument(
"--symlink",
action="store_true",
help="Create symlinks",
)
parser.add_argument(
"--clean",
action="store_true",
help="Delete any pre-existing symlinks",
)
args = parser.parse_args()
if args.clean:
for entry in CURRENT_DIR.glob("*"):
if entry.is_symlink():
entry_at_root = entry.relative_to(REPO_ROOT)
print(f"removing pre-existing {entry_at_root}")
entry.unlink()
for link in FILE_LIST.read_text().splitlines():
link = link.strip()
if not link or link.startswith('#'):
continue
src = LIB_DIR / link
dst = CURRENT_DIR / link
src_at_root = src.relative_to(REPO_ROOT)
dst_at_root = dst.relative_to(REPO_ROOT)
if (
dst.is_symlink()
and src.resolve(strict=True) == dst.resolve(strict=True)
):
continue
if not args.symlink and args.clean:
# when the user called --clean without --symlink, don't report missing
# symlinks that we just deleted ourselves
continue
# we specifically want to create relative-path links with ..
src_rel = os.path.relpath(src, CURRENT_DIR)
action = "symlinking" if args.symlink else "missing symlink to"
print(f"{action} {src_at_root} at {dst_at_root}")
if args.symlink:
os.symlink(src_rel, dst)

View file

@ -0,0 +1,4 @@
# These libraries in the stdlib can be type-checked.
_colorize.py
_pyrepl