Merge pull request #205 from electrobrian/packaging/unpinned-build-backends

Integrate packaging fix after merging current main without rewriting contributor history.

Validated head: 92db941ed0 (GitHub verified signature).
Local Python 3.12 CPU validation: 636 selected tests passed; Ruff F, actionlint, uv lock --check, conditional policy, risk map, import and CLI checks passed. Built sdist and wheel. No changed executable production lines.
Hosted CI: https://github.com/elder-plinius/OBLITERATUS/actions/runs/35549530703 (success).
Android runtime and Docker runtime not exercised.
This commit is contained in:
Joseph Magly
2026-09-20 21:04:56 -04:00
committed by GitHub
8 changed files with 114 additions and 7 deletions
+5 -3
View File
@@ -10,15 +10,17 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
WORKDIR /app
# Install Python deps first (cache layer)
# Install Python deps first (cache layer). requirements.txt is the core
# package graph; the Spaces extra adds Gradio for app.py without pulling
# the quantization extra.
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy project
COPY . .
# Install the package itself (for obliteratus imports)
RUN pip install --no-cache-dir .
# Install the package itself (for obliteratus imports) plus the UI extra.
RUN pip install --no-cache-dir ".[spaces]"
# Run as non-root user for security
RUN useradd -m appuser
+3 -1
View File
@@ -170,7 +170,9 @@ and rollback requirements.
Install `.[spaces,quantization]` instead when the UI must load supported
bitsandbytes 8-bit or 4-bit models. Jetson users must follow the dedicated
[Jetson bootstrap](docs/platforms/jetson.md); its bitsandbytes path is not yet
supported.
supported. Android/Termux is not a supported runtime; if you install there
anyway, follow the [Android install notes](docs/platforms/android.md) so pip
does not isolate a second setuptools or replace host wheels.
### 3. Google Colab (free GPU)
+3
View File
@@ -20,6 +20,9 @@
"scripts/select_pr_tests.py",
"scripts/setup_jetson.py",
"pyproject.toml",
"requirements.txt",
"Dockerfile",
"docs/platforms/**",
"uv.lock",
"tests/fixtures/distributed_checkpoints/**"
],
+56
View File
@@ -0,0 +1,56 @@
# Android / Termux install notes
Android is not a supported OBLITERATUS runtime. These notes exist so a
Termux (or other Android Python) install does not fight host wheels or
download a second setuptools behind PEP 517 isolation.
`sys.platform` on current Android CPython is `android`, not `linux`. The
locked CPU PyTorch index in `pyproject.toml` is intentionally limited to
`linux` and `win32` manylinux/Windows wheels. Do not point Android at that
index.
## Layout
1. Install the platform's own `python`, `setuptools`, and scientific
wheels first (`torch`, `numpy`, and whatever Termux already packages).
Those builds carry the Android patches that PyPI wheels do not.
`safetensors` and `tokenizers` are Rust extensions; rustup does not
host `aarch64-linux-android`, so install Termux `rust` before pip
and export the API level Python was built against (24 on current
Termux):
```bash
pkg install rust
export ANDROID_API_LEVEL=24
```
2. Isolated pip builds of meson packages (pandas, matplotlib) will try
to compile `cmake`/`ninja` from source and can hang under proot.
Install those binaries from Termux first (`pkg install ninja cmake
python-matplotlib python-scipy python-pillow python-psutil
python-pyarrow python-greenlet python-brotli python-cryptography
python-rpds-py python-msgpack libraqm harfbuzz`). PyPI `psutil`
sdists reject `sys.platform == "android"`; the Termux package is
patched. Termux matplotlib needs `libraqm`. PyPI `tokenizers` abi3
wheels fail on CPython 3.14 (`PyBaseObject_Type`); rebuild without
the `abi3` cargo feature so the extension is `cp314` instead of
`abi3`. Then install OBLITERATUS without build isolation:
```bash
python -m pip install --no-build-isolation -e ".[spaces]"
```
3. If pip still tries to compile `torch` or `numpy` from source, install
the project over the host packages instead:
```bash
python -m pip install --no-build-isolation --no-deps -e .
python -m pip install --no-build-isolation "gradio>=6.7,<7.0"
```
Do not use `uv sync --locked` on Android. The lock resolves Linux/Windows
CPU PyTorch and does not contain `android_*` wheels for torch, numpy,
pyarrow, tokenizers, or bitsandbytes.
The `quantization` and `qwen-hybrid` extras are CUDA-oriented and are not
an Android install path.
+3 -1
View File
@@ -1,5 +1,7 @@
[build-system]
requires = ["setuptools==80.9.0", "wheel==0.45.1"]
# Lower bound only. Exact backend pins block platform-specific setuptools patches.
# A separate wheel package is not required for the PEP 517 backend.
requires = ["setuptools>=80.9.0"]
build-backend = "setuptools.build_meta"
[project]
+2 -2
View File
@@ -1,4 +1,5 @@
gradio>=6.7,<7.0
# Mirrors [project].dependencies in pyproject.toml.
# Optional extras (spaces, quantization, qwen-hybrid, dev) are not included.
torch>=2.0
transformers>=4.40
datasets>=2.14
@@ -13,4 +14,3 @@ numpy>=1.24
scikit-learn>=1.3
tqdm>=4.64
optuna>=4.9,<5
bitsandbytes>=0.46.1
+22
View File
@@ -225,6 +225,28 @@ def test_ci_evidence_names_the_pull_request_head_not_the_synthetic_merge_commit(
assert '--head-sha "$GITHUB_SHA"' not in workflow
def test_build_backend_requirements_use_lower_bounds_not_exact_pins():
pyproject = (ROOT / "pyproject.toml").read_text(encoding="utf-8")
match = re.search(
r"(?ms)^\[build-system\]\n(?:#[^\n]*\n)*requires = \[([^\]]+)\]",
pyproject,
)
assert match is not None
requires = re.findall(r'"([^"]+)"', match.group(1))
assert requires
for requirement in requires:
spec = requirement.split(";", 1)[0]
name = re.split(r"[<>=!~]", spec, maxsplit=1)[0].strip()
assert name != "wheel", requirement
assert "==" not in spec, requirement
assert ">=" in spec, requirement
lower = re.findall(r">=([^,]+)", spec)
upper = re.findall(r"<=([^,]+)", spec)
assert not (
lower and upper and lower[0] == upper[0]
), f"collapsed exact pin written as a range: {requirement}"
def test_ci_runs_checkpoint_portability_contracts_on_windows():
workflow = WORKFLOW.read_text(encoding="utf-8")
checkpoint_job = workflow.split(" checkpoint-windows:\n", maxsplit=1)[1].split(
+20
View File
@@ -217,6 +217,26 @@ def test_bitsandbytes_is_opt_in_for_quantization_only():
assert extras["quantization"] == ["bitsandbytes>=0.46.1"]
def test_requirements_txt_mirrors_core_pyproject_dependencies():
metadata = tomllib.loads((ROOT / "pyproject.toml").read_text())
requirements = [
line.strip()
for line in (ROOT / "requirements.txt").read_text().splitlines()
if line.strip() and not line.startswith("#")
]
assert requirements == metadata["project"]["dependencies"]
assert not any(value.startswith("bitsandbytes") for value in requirements)
assert not any(value.startswith("gradio") for value in requirements)
def test_dockerfile_installs_spaces_extra_not_quantization():
dockerfile = (ROOT / "Dockerfile").read_text()
assert 'pip install --no-cache-dir ".[spaces]"' in dockerfile
assert "bitsandbytes" not in dockerfile
def test_jetson_issue_form_requires_reproducible_sanitized_hardware_evidence():
form = yaml.safe_load(
(ROOT / ".github" / "ISSUE_TEMPLATE" / "jetson-runtime.yml").read_text(),