Spaces:
Running
Running
| """Build pure-Python wheel and sdist artifacts with only the Python stdlib. | |
| This is used by local release validation in environments where the setuptools | |
| build frontend hangs before reaching the project backend. | |
| """ | |
| from __future__ import annotations | |
| import base64 | |
| import csv | |
| import hashlib | |
| import io | |
| import tarfile | |
| import tomllib | |
| import zipfile | |
| from pathlib import Path | |
| ROOT = Path(__file__).resolve().parents[1] | |
| def _hash_record(data: bytes) -> tuple[str, str]: | |
| digest = base64.urlsafe_b64encode(hashlib.sha256(data).digest()).decode().rstrip("=") | |
| return f"sha256={digest}", str(len(data)) | |
| def _metadata(project: dict) -> str: | |
| lines = [ | |
| "Metadata-Version: 2.4", | |
| f"Name: {project['name']}", | |
| f"Version: {project['version']}", | |
| f"Summary: {project.get('description', '')}", | |
| "License-Expression: Apache-2.0", | |
| f"Requires-Python: {project.get('requires-python', '')}", | |
| "Description-Content-Type: text/markdown", | |
| "License-File: LICENSE", | |
| ] | |
| for extra, deps in project.get("optional-dependencies", {}).items(): | |
| lines.append(f"Provides-Extra: {extra}") | |
| for dep in deps: | |
| lines.append(f'Requires-Dist: {dep}; extra == "{extra}"') | |
| lines.append("") | |
| lines.append((ROOT / "README.md").read_text(encoding="utf-8")) | |
| return "\n".join(lines) + "\n" | |
| def _build_wheel(out_dir: Path, project: dict) -> Path: | |
| name = project["name"] | |
| version = project["version"] | |
| dist_name = name.replace("-", "_") | |
| dist_info = f"{dist_name}-{version}.dist-info" | |
| entries: dict[str, bytes] = {} | |
| for path in sorted((ROOT / "stem_ai").glob("*.py")): | |
| entries[f"stem_ai/{path.name}"] = path.read_bytes() | |
| entries[f"{dist_info}/METADATA"] = _metadata(project).encode("utf-8") | |
| entries[f"{dist_info}/WHEEL"] = ( | |
| b"Wheel-Version: 1.0\n" | |
| b"Generator: stem-ai-stdlib-build\n" | |
| b"Root-Is-Purelib: true\n" | |
| b"Tag: py3-none-any\n" | |
| ) | |
| entries[f"{dist_info}/entry_points.txt"] = b"[console_scripts]\nstem = stem_ai.cli:main\n" | |
| entries[f"{dist_info}/top_level.txt"] = b"stem_ai\n" | |
| entries[f"{dist_info}/licenses/LICENSE"] = (ROOT / "LICENSE").read_bytes() | |
| rows = [] | |
| for arcname, data in entries.items(): | |
| digest, size = _hash_record(data) | |
| rows.append([arcname, digest, size]) | |
| rows.append([f"{dist_info}/RECORD", "", ""]) | |
| record = io.StringIO() | |
| csv.writer(record, lineterminator="\n").writerows(rows) | |
| entries[f"{dist_info}/RECORD"] = record.getvalue().encode("utf-8") | |
| wheel_path = out_dir / f"{dist_name}-{version}-py3-none-any.whl" | |
| with zipfile.ZipFile(wheel_path, "w", compression=zipfile.ZIP_DEFLATED) as wheel: | |
| for arcname, data in entries.items(): | |
| wheel.writestr(arcname, data) | |
| return wheel_path | |
| def _build_sdist(out_dir: Path, project: dict) -> Path: | |
| name = project["name"].replace("-", "_") | |
| version = project["version"] | |
| prefix = f"{name}-{version}" | |
| files = [ | |
| "CHANGELOG.md", | |
| "CONTRIBUTING.md", | |
| "LICENSE", | |
| "MANIFEST.in", | |
| "README.md", | |
| "pyproject.toml", | |
| "requirements.txt", | |
| ] | |
| files += [str(p.relative_to(ROOT)).replace("\\", "/") for p in sorted((ROOT / "stem_ai").glob("*.py"))] | |
| files += [str(p.relative_to(ROOT)).replace("\\", "/") for p in sorted((ROOT / "tests").glob("*.py"))] | |
| sdist_path = out_dir / f"{prefix}.tar.gz" | |
| with tarfile.open(sdist_path, "w:gz") as sdist: | |
| for rel in files: | |
| path = ROOT / rel | |
| if path.exists(): | |
| sdist.add(path, arcname=f"{prefix}/{rel}") | |
| return sdist_path | |
| def main() -> None: | |
| project = tomllib.loads((ROOT / "pyproject.toml").read_text(encoding="utf-8"))["project"] | |
| out_dir = ROOT / "dist" | |
| out_dir.mkdir(exist_ok=True) | |
| wheel = _build_wheel(out_dir, project) | |
| sdist = _build_sdist(out_dir, project) | |
| print(wheel) | |
| print(sdist) | |
| if __name__ == "__main__": | |
| main() | |