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
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.
\ No newline at end of file
+## 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.
--- /dev/null
+#!/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)