Skip to content

Commit

Permalink
Merge pull request #482 from dan-blanchard/fix-relative-to
Browse files Browse the repository at this point in the history
Fix support for egg packages with files outside site-packages by using `pathlib.Path.relative_to(walk_up=True)`.
  • Loading branch information
jaraco authored Jun 23, 2024
2 parents e8998d9 + b94b42e commit 48d2a85
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 4 deletions.
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

0 comments on commit 48d2a85

Please sign in to comment.