Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions importlib_metadata/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import collections

from . import _meta
from .compat import py39
from .compat import py39, py311
from ._collections import FreezableDefaultDict, Pair
from ._compat import (
NullFinder,
Expand Down Expand Up @@ -570,9 +570,8 @@ def _read_files_egginfo_installed(self):
return

paths = (
(subdir / name)
.resolve()
.relative_to(self.locate_file('').resolve())
py311.relative_fix((subdir / name).resolve())
.relative_to(self.locate_file('').resolve(), walk_up=True)
.as_posix()
for name in text.splitlines()
)
Expand Down
22 changes: 22 additions & 0 deletions importlib_metadata/compat/py311.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import os
import pathlib
import sys
import types


def wrap(path): # pragma: no cover
"""
Workaround for https://github.com/python/cpython/issues/84538
to add backward compatibility for walk_up=True.
An example affected package is dask-labextension, which uses
jupyter-packaging to install JupyterLab javascript files outside
of site-packages.
"""

def relative_to(root, *, walk_up=False):
return pathlib.Path(os.path.relpath(path, root))

return types.SimpleNamespace(relative_to=relative_to)


relative_fix = wrap if sys.version_info < (3, 12) else lambda x: x
1 change: 1 addition & 0 deletions newsfragments/455.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
When reading installed files from an egg, use ``relative_to(walk_up=True)`` to honor files installed outside of the installation root.
34 changes: 34 additions & 0 deletions tests/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,40 @@ def main():
}


class EggInfoPkgPipInstalledExternalDataFiles(OnSysPath, SiteBuilder):
files: FilesSpec = {
"egg_with_module_pkg.egg-info": {
"PKG-INFO": "Name: egg_with_module-pkg",
# SOURCES.txt is made from the source archive, and contains files
# (setup.py) that are not present after installation.
"SOURCES.txt": """
egg_with_module.py
setup.py
egg_with_module.json
egg_with_module_pkg.egg-info/PKG-INFO
egg_with_module_pkg.egg-info/SOURCES.txt
egg_with_module_pkg.egg-info/top_level.txt
""",
# installed-files.txt is written by pip, and is a strictly more
# accurate source than SOURCES.txt as to the installed contents of
# the package.
"installed-files.txt": """
../../../etc/jupyter/jupyter_notebook_config.d/relative.json
/etc/jupyter/jupyter_notebook_config.d/absolute.json
../egg_with_module.py
PKG-INFO
SOURCES.txt
top_level.txt
""",
# missing top_level.txt (to trigger fallback to installed-files.txt)
},
"egg_with_module.py": """
def main():
print("hello world")
""",
}


class EggInfoPkgPipInstalledNoModules(OnSysPath, SiteBuilder):
files: FilesSpec = {
"egg_with_no_modules_pkg.egg-info": {
Expand Down
1 change: 1 addition & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class APITests(
fixtures.EggInfoPkg,
fixtures.EggInfoPkgPipInstalledNoToplevel,
fixtures.EggInfoPkgPipInstalledNoModules,
fixtures.EggInfoPkgPipInstalledExternalDataFiles,
fixtures.EggInfoPkgSourcesFallback,
fixtures.DistInfoPkg,
fixtures.DistInfoPkgWithDot,
Expand Down