Ctrl K
ComunidadGuías…

scripts/build_modules.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env python3
"""Build disjoint official wheels from one source revision.

Run with --output dist/modules. Optional --module selects individual wheels;
the default builds core, all modules, and the CLI compatibility metapackage.
This prepares local artifacts only; it never uploads or installs packages.
"""

from __future__ import annotations

import argparse
import json
from pathlib import Path
import shutil
import subprocess
import sys
import tempfile
import zipfile

ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
from build_metadata import optional_dependencies, project_version
from build_support import owner


def build(output: Path, selected: list[str]) -> list[Path]:
    modules = {
        path.stem: json.loads(path.read_text())
        for path in (ROOT / "deeptutor/modules/manifests").glob("*.json")
    }
    version = project_version(ROOT)
    names = selected or [*modules, "cli"]
    output.mkdir(parents=True, exist_ok=True)
    files = [
        path
        for base in ("deeptutor", "deeptutor_cli", "deeptutor_web")
        for path in (ROOT / base).rglob("*")
        if path.is_file()
        and "__pycache__" not in path.parts
        and "tests" not in path.parts
        and path.suffix not in {".pyc", ".pyo"}
    ]
    ownership = {path: owner(path.relative_to(ROOT).as_posix()) for path in files}
    built = []
    for name in names:
        if name not in {*modules, "cli"}:
            raise ValueError(f"Unknown module: {name}")
        with tempfile.TemporaryDirectory(prefix=f"deeptutor-build-{name}-") as directory:
            stage = Path(directory)
            module = modules.get(name)
            package_name = module["distribution"] if module else f"deeptutor-{name}"
            if module:
                for source in files:
                    if ownership[source] != name:
                        continue
                    dest = stage / source.relative_to(ROOT)
                    dest.parent.mkdir(parents=True, exist_ok=True)
                    shutil.copy2(source, dest)
            dependencies = list(module["requirements"]) if module else []
            if name != "core":
                dependencies += [f"deeptutor=={version}"]
                dependencies += [
                    f"{modules[dep]['distribution']}=={version}"
                    for dep in (module["requires"] if module else [])
                ]
            config = f"""[build-system]
requires = ["setuptools>=61", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = {json.dumps(package_name)}
version = {json.dumps(version)}
description = {json.dumps("DeepTutor " + name + " module")}
requires-python = ">=3.11,<3.15"
dependencies = {json.dumps(dependencies)}
[tool.setuptools.packages.find]
where = ["."]
include = ["deeptutor*", "deeptutor_cli*", "deeptutor_web*"]
namespaces = true
[tool.setuptools.package-data]
"deeptutor_web" = ["**/*", ".next/**/*"]
"*" = ["*.json", "*.yaml", "*.yml", "*.md", "*.sh", "*.j2", "*.jinja", "*.ts", "*.tsx", "*.mjs", "*.js", "*.css", "*.html", "*.svg", "*.png", "*.woff2"]
"""
            if name == "core":
                config += '\n[project.scripts]\ndeeptutor = "deeptutor_cli.main:main"\n'
                config += "\n[project.optional-dependencies]\n"
                for key, values in optional_dependencies(ROOT).items():
                    config += f"{json.dumps(key)} = {json.dumps(values)}\n"
            for group, entries in (module or {}).get("entry_points", {}).items():
                config += f"\n[project.entry-points.{json.dumps(group)}]\n"
                for key, value in entries.items():
                    config += f"{json.dumps(key)} = {json.dumps(value)}\n"
            (stage / "MANIFEST.in").write_text(
                "recursive-include deeptutor_web *\nrecursive-include deeptutor_web/.next *\n"
            )
            (stage / "pyproject.toml").write_text(config)
            result = subprocess.run(
                [
                    sys.executable,
                    "-m",
                    "pip",
                    "wheel",
                    "--no-deps",
                    "--no-build-isolation",
                    "--wheel-dir",
                    str(output.resolve()),
                    str(stage),
                ],
                text=True,
                capture_output=True,
            )
            if result.returncode:
                raise RuntimeError(result.stdout + result.stderr)
            wheel = output / f"{package_name.replace('-', '_')}-{version}-py3-none-any.whl"
            if not wheel.is_file():
                raise RuntimeError(f"Expected wheel missing: {wheel}")
            built.append(wheel)
            print(wheel.name, flush=True)
    # One installed file has one owner; namespace parents need no duplicate init.
    owners = {}
    for wheel in built:
        with zipfile.ZipFile(wheel) as archive:
            for path in archive.namelist():
                if ".dist-info/" in path or path.endswith("/"):
                    continue
                if path in owners:
                    raise RuntimeError(f"Wheel overlap: {path} in {owners[path]} and {wheel.name}")
                owners[path] = wheel.name
    return built


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--output", type=Path, default=ROOT / "dist/modules")
    parser.add_argument("--module", action="append", default=[])
    args = parser.parse_args()
    build(args.output, args.module)