blob: 44cfcf63fa3d9fcb5c29ab0e528ed9240620ee27 [file] [log] [blame]
import glob
import json
import pathlib
import sys
import zipfile
# Generator is the modules_mapping.json file generator.
class Generator:
stdout = None
stderr = None
def __init__(self, stdout, stderr):
self.stdout = stdout
self.stderr = stderr
# dig_wheel analyses the wheel .whl file determining the modules it provides
# by looking at the directory structure.
def dig_wheel(self, wheel):
mapping = {}
wheel_paths = glob.glob(wheel["path"])
assert (
len(wheel_paths) != 0
), "wheel not found for {}: searched for {}".format(
wheel["name"], wheel["path"],
)
wheel_path = wheel_paths[0]
assert (
"UNKNOWN" not in wheel_path
), "unknown-named wheel found for {}: possibly bad compilation".format(
wheel["name"],
)
with zipfile.ZipFile(wheel_path, "r") as zip_file:
for path in zip_file.namelist():
if is_metadata(path):
continue
ext = pathlib.Path(path).suffix
if ext == ".py" or ext == ".so":
# Note the '/' here means that the __init__.py is not in the
# root of the wheel, therefore we can index the directory
# where this file is as an importable package.
if path.endswith("/__init__.py"):
module = path[: -len("/__init__.py")].replace("/", ".")
mapping[module] = wheel["name"]
# Always index the module file.
if ext == ".so":
# Also remove extra metadata that is embeded as part of
# the file name as an extra extension.
ext = ''.join(pathlib.Path(path).suffixes)
module = path[: -len(ext)].replace("/", ".")
mapping[module] = wheel["name"]
return mapping
# run is the entrypoint for the generator.
def run(self, wheels):
mapping = {}
for wheel_json in wheels:
wheel = json.loads(wheel_json)
try:
mapping.update(self.dig_wheel(wheel))
except AssertionError as error:
print(error, file=self.stderr)
return 1
mapping_json = json.dumps(mapping)
print(mapping_json, file=self.stdout)
self.stdout.flush()
return 0
# is_metadata checks if the path is in a metadata directory.
# Ref: https://www.python.org/dev/peps/pep-0427/#file-contents.
def is_metadata(path):
top_level = path.split("/")[0].lower()
return top_level.endswith(".dist-info") or top_level.endswith(".data")
if __name__ == "__main__":
wheels = sys.argv[1:]
generator = Generator(sys.stdout, sys.stderr)
exit(generator.run(wheels))