Skip to content

Commit c9b70d1

Browse files
mattemalexeagle
andauthored
fix(gazelle): handle purelib and platlib packages that don't set 'Root-Is-Purelib: true' (bazel-contrib#768)
Co-authored-by: Alex Eagle <alex@aspect.dev>
1 parent 0609001 commit c9b70d1

File tree

1 file changed

+44
-17
lines changed

1 file changed

+44
-17
lines changed

gazelle/modules_mapping/generator.py

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,15 @@ def __init__(self, stderr, output_file):
1717
# by looking at the directory structure.
1818
def dig_wheel(self, whl):
1919
mapping = {}
20-
wheel_name = get_wheel_name(whl)
2120
with zipfile.ZipFile(whl, "r") as zip_file:
2221
for path in zip_file.namelist():
2322
if is_metadata(path):
24-
continue
25-
ext = pathlib.Path(path).suffix
26-
if ext == ".py" or ext == ".so":
27-
# Note the '/' here means that the __init__.py is not in the
28-
# root of the wheel, therefore we can index the directory
29-
# where this file is as an importable package.
30-
if path.endswith("/__init__.py"):
31-
module = path[: -len("/__init__.py")].replace("/", ".")
32-
mapping[module] = wheel_name
33-
# Always index the module file.
34-
if ext == ".so":
35-
# Also remove extra metadata that is embeded as part of
36-
# the file name as an extra extension.
37-
ext = "".join(pathlib.Path(path).suffixes)
38-
module = path[: -len(ext)].replace("/", ".")
39-
mapping[module] = wheel_name
23+
if data_has_purelib_or_platlib(path):
24+
module_for_path(path, whl, mapping)
25+
else:
26+
continue
27+
else:
28+
module_for_path(path, whl, mapping)
4029
return mapping
4130

4231
# run is the entrypoint for the generator.
@@ -73,6 +62,44 @@ def is_metadata(path):
7362
return top_level.endswith(".dist-info") or top_level.endswith(".data")
7463

7564

65+
# The .data is allowed to contain a full purelib or platlib directory
66+
# These get unpacked into site-packages, so require indexing too.
67+
# This is the same if "Root-Is-Purelib: true" is set and the files are at the root.
68+
# Ref: https://peps.python.org/pep-0427/#what-s-the-deal-with-purelib-vs-platlib
69+
def data_has_purelib_or_platlib(path):
70+
maybe_lib = path.split("/")[1].lower()
71+
return is_metadata(path) and (
72+
maybe_lib == "purelib" or maybe_lib == "platlib"
73+
)
74+
75+
76+
77+
def module_for_path(path, whl, mapping):
78+
ext = pathlib.Path(path).suffix
79+
if ext == ".py" or ext == ".so":
80+
if "purelib" in path or "platlib" in path:
81+
root = "/".join(path.split("/")[2:])
82+
else:
83+
root = path
84+
85+
wheel_name = get_wheel_name(whl)
86+
87+
if root.endswith("/__init__.py"):
88+
# Note the '/' here means that the __init__.py is not in the
89+
# root of the wheel, therefore we can index the directory
90+
# where this file is as an importable package.
91+
module = root[: -len("/__init__.py")].replace("/", ".")
92+
mapping[module] = wheel_name
93+
94+
# Always index the module file.
95+
if ext == ".so":
96+
# Also remove extra metadata that is embeded as part of
97+
# the file name as an extra extension.
98+
ext = ''.join(pathlib.Path(root).suffixes)
99+
module = root[: -len(ext)].replace("/", ".")
100+
mapping[module] = wheel_name
101+
102+
76103
if __name__ == "__main__":
77104
output_file = sys.argv[1]
78105
wheels = sys.argv[2:]

0 commit comments

Comments
 (0)