diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 6d1c6b5b5e6347..139c134b089ae7 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -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 - run: mypy --config-file ${{ matrix.target }}/mypy.ini diff --git a/Makefile.pre.in b/Makefile.pre.in index e10c78d6403472..d65d5bddef652e 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -3206,6 +3206,7 @@ distclean: clobber docclean Modules/ld_so_aix Modules/python.exp Misc/python.pc \ Misc/python-embed.pc Misc/python-config.sh -rm -f python*-gdb.py + -find Misc/mypy -type l -delete # Issue #28258: set LC_ALL to avoid issues with Estonian locale. # Expansion is performed here by shell (spawned by make) itself before # arguments are passed to find. So LC_ALL=C must be set as a separate diff --git a/Misc/mypy/.gitignore b/Misc/mypy/.gitignore new file mode 100644 index 00000000000000..5b2c526787e984 --- /dev/null +++ b/Misc/mypy/.gitignore @@ -0,0 +1,5 @@ +# This list is also used in make_symlinks.py, only put actual concrete paths below +# (no wildcards!). + +_colorize.py +_pyrepl diff --git a/Misc/mypy/README.md b/Misc/mypy/README.md index 05eda6c0b82f0a..99846426e30579 100644 --- a/Misc/mypy/README.md +++ b/Misc/mypy/README.md @@ -11,6 +11,17 @@ 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 an example. -See `Lib/_pyrepl/mypy.ini` for an example. \ No newline at end of file +At the same time, we don't want symlinks to be checked into the +repository as they would end up being shipped as part of the source +tarballs, which can create compatibility issues with unpacking on +operating systems without symlink support. Additionally, those symlinks +would have to be part of the SBOM, which we don't want either. + +Instead, this directory ships with a `make_symlinks.py` script, which +creates the symlinks when they are needed. This happens automatically +on GitHub Actions. You will have to run it manually for a local +type-checking workflow. Note that `make distclean` removes the symlinks +to ensure that the produced distribution is clean. diff --git a/Misc/mypy/_colorize.py b/Misc/mypy/_colorize.py deleted file mode 120000 index 9b7304769ec30b..00000000000000 --- a/Misc/mypy/_colorize.py +++ /dev/null @@ -1 +0,0 @@ -../../Lib/_colorize.py \ No newline at end of file diff --git a/Misc/mypy/_pyrepl b/Misc/mypy/_pyrepl deleted file mode 120000 index bd7b69909663b6..00000000000000 --- a/Misc/mypy/_pyrepl +++ /dev/null @@ -1 +0,0 @@ -../../Lib/_pyrepl \ No newline at end of file diff --git a/Misc/mypy/make_symlinks.py b/Misc/mypy/make_symlinks.py new file mode 100755 index 00000000000000..ed895d0024dd65 --- /dev/null +++ b/Misc/mypy/make_symlinks.py @@ -0,0 +1,55 @@ +#!/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" + +parser = argparse.ArgumentParser(prog="make_symlinks.py") +parser.add_argument( + "--dry-run", + action="store_true", + help="Don't actually symlink or delete anything", +) +parser.add_argument( + "--force", + action="store_true", + help="Delete destination paths if they exist", +) + +args = parser.parse_args() + +for link in (CURRENT_DIR / ".gitignore").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 args.force: + if dst.exists(): + if args.dry_run: + print(f"{dst_at_root} already exists, would delete") + else: + print(f"{dst_at_root} already exists, deleting") + dst.unlink() + elif ( + dst.is_symlink() + and src.resolve(strict=True) == dst.resolve(strict=True) + ): + print(f"{dst_at_root} already exists, skipping") + continue + + # we specifically want relative path links with .. + src_rel = os.path.relpath(src, CURRENT_DIR) + action = "symlinking" if not args.dry_run else "would symlink" + print(f"{action} {src_at_root} at {dst_at_root}") + if not args.dry_run: + os.symlink(src_rel, dst)
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: