From c5270cd088f71044032c252e5824ef0db5c05b85 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Mon, 4 Apr 2022 20:47:57 +0200 Subject: [PATCH] Also include package dist-info for editable installs For editable installs the .egg-info/.dist-info does not contain a list of installed files. Calculate the list of files to include from the toplevels in the distribution. Fixes #417 --- doc/changelog.rst | 2 ++ py2app/_pkg_meta.py | 62 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/doc/changelog.rst b/doc/changelog.rst index 3efba41..d3d7177 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -4,6 +4,8 @@ Release history py2app 0.28 ----------- +* #417: Also include package dist-info for editable installs + * The qt5 and qt6 recipes used dodge logic to detect if the Qt library itself is inside the python package, resulting in duplicate copies of Qt. diff --git a/py2app/_pkg_meta.py b/py2app/_pkg_meta.py index 739e9b8..4bf0122 100644 --- a/py2app/_pkg_meta.py +++ b/py2app/_pkg_meta.py @@ -3,7 +3,7 @@ IGNORED_DISTINFO = set(["installed-files.txt", "RECORD"]) # noqa: C405 -def update_metadata_cache(infos, dist_info_path): +def update_metadata_cache_distinfo(infos, dist_info_path): """ Update mapping from filename to dist_info directory for all files installed by the package described @@ -40,6 +40,56 @@ def update_metadata_cache(infos, dist_info_path): ) ] = dist_info_path +def update_metadata_cache_distlink(infos, dist_link_path): + """ + Update mapping from filename to dist_info directory + for all files in the package installed in editable mode. + + *dist_link_path* is the .egg-link file for the package + """ + # An editable install does not contain a listing of installed + # files. + with open(dist_link_path, "r") as fp: + dn = fp.readline()[:-1] + + + # The list of files and directories that would have been + # in the list of installed items. + to_include = [] + + # First look for the dist-info for this editable install + for fn in os.listdir(dn): + if fn.endswith(".egg-info") or fn.endswith(".dist-info"): + dist_info_path = os.path.join(dn, fn) + to_include.append(dist_info_path) + try: + with open(os.path.join(dist_info_path, "top_level.txt"), "r") as fp: + toplevels = fp.read().splitlines() + except OSError: + continue + break + else: + # No dist-info for this install + return + + # Then look for the toplevels for this package + for fn in os.listdir(dn): + if fn in toplevels or fn.rstrip(".py") in toplevels: + to_include.append(os.path.join(dn, fn)) + + # Finally recursively add all items found to + # the cache. + add_recursive(infos, dist_info_path, to_include) + +def add_recursive(infos, dist_info_path, to_include): + """ Add items from to_include to infos, recursively + walking into directories """ + for item in to_include: + if os.path.isdir(item): + add_recursive(infos, dist_info_path, [os.path.join(item, fn) for fn in os.listdir(item)]) + + else: + infos[item] = dist_info_path def scan_for_metadata(path): """ @@ -52,7 +102,13 @@ def scan_for_metadata(path): if not os.path.isdir(dirname): continue for nm in os.listdir(dirname): - if nm.endswith(".egg-info") or nm.endswith(".dist-info"): - update_metadata_cache(infos, os.path.join(dirname, nm)) + if nm.endswith(".egg-link"): + # Editable install, these will be found later on in + # *path* as well, but contain metadata without a list + # of installed files. + update_metadata_cache_distlink(infos, os.path.join(dirname, nm)) + + elif nm.endswith(".egg-info") or nm.endswith(".dist-info"): + update_metadata_cache_distinfo(infos, os.path.join(dirname, nm)) return infos