From 1b945916c239984a287e84adb353c81bea68e815 Mon Sep 17 00:00:00 2001 From: Chris Novakovic Date: Fri, 8 Nov 2024 11:26:31 +0000 Subject: [PATCH] Really implement `locate_file` method in pex meta path finder The importlib.metadata documentation in Python 3.13 implies that it's fine for a `Distribution`'s implementation of the `locate_file` abstract method to raise an error if the distribution doesn't exist as real files on a real file system, but this error will bubble up when the `files` method is called (because `files` gets a `pathlib.Path`-like object from `locate_file` for each file in `RECORD` and then checks that the path exists by calling `exists()` on each return value). In `locate_file`, construct and return a `zipfile.Path` object pointing to the given file in the distribution in the pex file. `zipfile.Path` takes care of the path existence check itself, so `files` works as expected when running under Python >= 3.12. --- tools/please_pex/pex/pex_main.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tools/please_pex/pex/pex_main.py b/tools/please_pex/pex/pex_main.py index 6ebb551..348ceb2 100644 --- a/tools/please_pex/pex/pex_main.py +++ b/tools/please_pex/pex/pex_main.py @@ -4,12 +4,12 @@ from importlib.abc import MetaPathFinder from importlib.metadata import Distribution from importlib.util import spec_from_loader -from zipfile import ZipFile, ZipInfo, is_zipfile import itertools import os import re import runpy import sys +import zipfile try: @@ -56,11 +56,11 @@ def getsitepackages(prefixes=[sys.prefix, sys.exec_prefix]): PEX_STAMP = '__PEX_STAMP__' # Workaround for https://bugs.python.org/issue15795 -class ZipFileWithPermissions(ZipFile): +class ZipFileWithPermissions(zipfile.ZipFile): """ Custom ZipFile class handling file permissions. """ def _extract_member(self, member, targetpath, pwd): - if not isinstance(member, ZipInfo): + if not isinstance(member, zipfile.ZipInfo): member = self.getinfo(member) targetpath = super(ZipFileWithPermissions, self)._extract_member( @@ -80,7 +80,7 @@ def __init__(self): self.suffixes_by_length = sorted(self.suffixes, key=lambda x: -len(x)) # Identify all the possible modules we could handle. self.modules = {} - if is_zipfile(sys.argv[0]): + if zipfile.is_zipfile(sys.argv[0]): zf = ZipFileWithPermissions(sys.argv[0]) for name in zf.namelist(): path, _ = self.splitext(name) @@ -156,7 +156,10 @@ def read_text(self, filename): return zf.read(name).decode(encoding="utf-8") def locate_file(self, path): - raise RuntimeError("This distribution has no real file system") + return zipfile.Path( + self._pex_file, + at=os.path.join(self._prefix, path) if self._prefix else path, + ) read_text.__doc__ = Distribution.read_text.__doc__ @@ -174,7 +177,7 @@ def __init__(self, module_dir=MODULE_DIR): def _find_all_distributions(self, module_dir): dists = {} - if is_zipfile(sys.argv[0]): + if zipfile.is_zipfile(sys.argv[0]): zf = ZipFileWithPermissions(sys.argv[0]) for name in zf.namelist(): if name and (m := re.search(