Files
sentinel/tools/fast_editable.py
2024-02-15 17:01:50 +00:00

113 lines
3.7 KiB
Python

#!/usr/bin/env python3
from __future__ import annotations
import argparse
import configparser
import os.path
import stat
import sys
import sysconfig
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--path", default=".")
args = parser.parse_args()
# simulate `pip install -e .` -- but bypass setuptools
def r(p: str) -> str:
return os.path.relpath(p, ".")
# must be in a virtualenv
assert not sys.flags.no_site, sys.flags.no_site
site_dir = sysconfig.get_path("purelib")
bin_dir = sysconfig.get_path("scripts")
assert "/.venv/" in site_dir, site_dir
cfg = configparser.RawConfigParser()
cfg.read(os.path.join(args.path, "setup.cfg"))
package_name = cfg["metadata"]["name"]
package_dir = cfg["options"].get("package_dir", "").strip()
if package_dir not in {"", "=src"}:
raise SystemExit(f"unsupported package_dir={package_dir!r}")
project_root = os.path.abspath(args.path)
if package_dir == "=src":
source_root = os.path.join(project_root, "src")
project_root_relative = "../"
else:
source_root = project_root
project_root_relative = "."
# egg-link indicates that the software is installed
egg_link = os.path.join(site_dir, f"{package_name}.egg-link")
print(f"writing {r(egg_link)}...")
with open(egg_link, "w") as f:
f.write(f"{source_root}\n{project_root_relative}")
# easy-install.pth is how code gets imported
easy_install_pth = os.path.join(site_dir, "easy-install.pth")
print(f"adding {r(source_root)} to {r(easy_install_pth)}...")
try:
with open(easy_install_pth) as f:
easy_install_paths = f.read().splitlines()
except OSError:
easy_install_paths = []
if source_root not in easy_install_paths:
easy_install_paths.append(source_root)
with open(easy_install_pth, "w") as f:
f.write("\n".join(easy_install_paths) + "\n")
# 0. create bin scripts for anything in `console_scripts`
console_scripts = cfg["options.entry_points"]["console_scripts"].strip()
for line in console_scripts.splitlines():
entry, rest = line.split(" = ")
mod, attr = rest.split(":")
binary = os.path.join(bin_dir, entry)
print(f"writing {r(binary)}...")
with open(binary, "w") as f:
f.write(
f"#!{sys.executable}\n"
f"from {mod} import {attr}\n"
f'if __name__ == "__main__":\n'
f" raise SystemExit({attr}())\n"
)
mode = os.stat(binary).st_mode
mode |= stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
os.chmod(binary, mode)
# 0. write out the `sentry.egg-info` directory in `src/`
egg_info_dir = os.path.join(source_root, f"{package_name}.egg-info")
print(f"creating {r(egg_info_dir)}...")
os.makedirs(egg_info_dir, exist_ok=True)
entry_points_txt = os.path.join(egg_info_dir, "entry_points.txt")
print(f"writing {r(entry_points_txt)}...")
ep_cfg = configparser.RawConfigParser()
for section, eps in cfg["options.entry_points"].items():
ep_cfg.add_section(section)
for ep in eps.strip().splitlines():
k, v = ep.split(" = ")
ep_cfg[section][k] = v
with open(entry_points_txt, "w") as f:
ep_cfg.write(f)
pkg_info = os.path.join(egg_info_dir, "PKG-INFO")
print(f"writing {r(pkg_info)}...")
with open(pkg_info, "w") as f:
f.write(
f"Metadata-Version: 2.1\n"
f"Name: {package_name}\n"
f"Version: {cfg['metadata']['version']}\n"
)
return 0
if __name__ == "__main__":
raise SystemExit(main())