mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-18 04:41:56 +00:00
fix(install): also patch psutil on Termux fresh-install path
The Termux update path (PR #22814) prebuilds psutil from a marker-patched sdist so 'platform android is not supported' doesn't kill it. The same psutil setup.py error blocks fresh installs via scripts/install.sh — only the update path was wired up. Without this, a brand-new Termux user can't get past the very first 'pip install -e .[termux-all]' call. - New scripts/install_psutil_android.py — standalone version of the same patcher hermes_cli/main.py uses, callable from bash. - scripts/install.sh detects sys.platform == 'android' and runs the patcher before pip install. - TODO note added to both copies pointing at upstream https://github.com/giampaolo/psutil/pull/2762; remove both when that ships. Note: we keep psutil as a base dep on Android (do not adopt the proposed sys_platform != 'android' marker in pyproject). Removing it would crash five unguarded 'import psutil' sites at runtime (tools/code_execution_tool.py, tools/tts_tool.py, tools/process_registry.py (2x), gateway/platforms/whatsapp.py).
This commit is contained in:
parent
6d5d467d39
commit
c179bdab3c
3 changed files with 135 additions and 0 deletions
|
|
@ -6587,6 +6587,11 @@ def _install_psutil_android_compat(
|
||||||
|
|
||||||
We patch only the extracted build tree used for this install attempt;
|
We patch only the extracted build tree used for this install attempt;
|
||||||
nothing is persisted in the repository.
|
nothing is persisted in the repository.
|
||||||
|
|
||||||
|
Stopgap: remove this once https://github.com/giampaolo/psutil/pull/2762
|
||||||
|
merges and ships in a release. ``scripts/install_psutil_android.py``
|
||||||
|
contains the same logic for ``scripts/install.sh`` (fresh installs).
|
||||||
|
Both copies should be removed together.
|
||||||
"""
|
"""
|
||||||
import tarfile
|
import tarfile
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
|
||||||
|
|
@ -985,6 +985,19 @@ install_deps() {
|
||||||
|
|
||||||
"$PIP_PYTHON" -m pip install --upgrade pip setuptools wheel >/dev/null
|
"$PIP_PYTHON" -m pip install --upgrade pip setuptools wheel >/dev/null
|
||||||
|
|
||||||
|
# On Android, psutil's setup.py rejects sys.platform == 'android' before
|
||||||
|
# it ever invokes the C build, so the next pip install would fail at
|
||||||
|
# "platform android is not supported". Prebuild psutil from the official
|
||||||
|
# sdist with a one-line marker patch (Linux source path is fine on
|
||||||
|
# Android). Stopgap until psutil#2762 ships upstream.
|
||||||
|
if "$PIP_PYTHON" -c 'import sys; raise SystemExit(0 if sys.platform == "android" else 1)' 2>/dev/null; then
|
||||||
|
log_info "Android Python detected: prebuilding psutil compatibility shim..."
|
||||||
|
if ! "$PIP_PYTHON" "$INSTALL_DIR/scripts/install_psutil_android.py" --pip "$PIP_PYTHON -m pip"; then
|
||||||
|
log_warn "psutil Android prebuild failed — package install will likely fail next."
|
||||||
|
log_info "Workaround: manually rerun 'python scripts/install_psutil_android.py' once your toolchain is set up."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Try the broad Termux profile first (best-effort "install all" for Android),
|
# Try the broad Termux profile first (best-effort "install all" for Android),
|
||||||
# then fall back to the conservative Termux baseline, then base package.
|
# then fall back to the conservative Termux baseline, then base package.
|
||||||
if ! "$PIP_PYTHON" -m pip install -e '.[termux-all]' -c constraints-termux.txt; then
|
if ! "$PIP_PYTHON" -m pip install -e '.[termux-all]' -c constraints-termux.txt; then
|
||||||
|
|
|
||||||
117
scripts/install_psutil_android.py
Executable file
117
scripts/install_psutil_android.py
Executable file
|
|
@ -0,0 +1,117 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Install psutil on Termux/Android by patching upstream platform detection.
|
||||||
|
|
||||||
|
psutil's setup currently gates Linux sources behind
|
||||||
|
``sys.platform.startswith('linux')``. On Termux, Python reports
|
||||||
|
``sys.platform == 'android'``, so ``pip install psutil`` aborts with
|
||||||
|
"platform android is not supported" — even though psutil compiles fine
|
||||||
|
when the Linux source path is reused.
|
||||||
|
|
||||||
|
This script downloads the official psutil sdist, applies a one-line
|
||||||
|
patch (``LINUX = sys.platform.startswith(("linux", "android"))``), and
|
||||||
|
installs the patched tree with ``pip install --no-build-isolation``.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python scripts/install_psutil_android.py [--pip "/path/to/pip"] [--uv]
|
||||||
|
|
||||||
|
When neither flag is given, the script auto-detects ``uv`` on PATH and
|
||||||
|
falls back to ``<sys.executable> -m pip``.
|
||||||
|
|
||||||
|
This is a stopgap. Remove once psutil upstream merges
|
||||||
|
https://github.com/giampaolo/psutil/pull/2762 and ships a release.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tarfile
|
||||||
|
import tempfile
|
||||||
|
import urllib.request
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Pin a version we know patches cleanly. Update when a newer psutil
|
||||||
|
# changes the marker line shape and we need to follow upstream.
|
||||||
|
PSUTIL_URL = (
|
||||||
|
"https://files.pythonhosted.org/packages/aa/c6/"
|
||||||
|
"d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/"
|
||||||
|
"psutil-7.2.2.tar.gz"
|
||||||
|
)
|
||||||
|
|
||||||
|
MARKER = 'LINUX = sys.platform.startswith("linux")'
|
||||||
|
REPLACEMENT = 'LINUX = sys.platform.startswith(("linux", "android"))'
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_install_cmd(pip_arg: str | None, prefer_uv: bool) -> list[str]:
|
||||||
|
if pip_arg:
|
||||||
|
return pip_arg.split()
|
||||||
|
if prefer_uv:
|
||||||
|
uv = shutil.which("uv")
|
||||||
|
if not uv:
|
||||||
|
sys.exit("--uv requested but no uv on PATH")
|
||||||
|
return [uv, "pip"]
|
||||||
|
auto_uv = shutil.which("uv")
|
||||||
|
if auto_uv:
|
||||||
|
return [auto_uv, "pip"]
|
||||||
|
return [sys.executable, "-m", "pip"]
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument(
|
||||||
|
"--pip",
|
||||||
|
help="Explicit installer command (e.g. '/usr/bin/uv pip' or 'python -m pip')",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--uv",
|
||||||
|
action="store_true",
|
||||||
|
help="Force using uv (errors out if uv is not on PATH)",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
install_cmd_prefix = _resolve_install_cmd(args.pip, args.uv)
|
||||||
|
|
||||||
|
print(
|
||||||
|
"→ Termux/Android: prebuilding psutil with Linux source path "
|
||||||
|
"compatibility shim (see psutil#2762)..."
|
||||||
|
)
|
||||||
|
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
tmp_path = Path(tmp)
|
||||||
|
archive = tmp_path / "psutil.tar.gz"
|
||||||
|
urllib.request.urlretrieve(PSUTIL_URL, archive)
|
||||||
|
with tarfile.open(archive) as tar:
|
||||||
|
tar.extractall(tmp_path)
|
||||||
|
|
||||||
|
try:
|
||||||
|
src_root = next(
|
||||||
|
p for p in tmp_path.iterdir()
|
||||||
|
if p.is_dir() and p.name.startswith("psutil-")
|
||||||
|
)
|
||||||
|
except StopIteration:
|
||||||
|
sys.exit("psutil sdist did not contain a psutil-* directory")
|
||||||
|
|
||||||
|
common_py = src_root / "psutil" / "_common.py"
|
||||||
|
content = common_py.read_text(encoding="utf-8")
|
||||||
|
if MARKER not in content:
|
||||||
|
sys.exit(
|
||||||
|
"psutil Android compatibility patch marker not found — "
|
||||||
|
"upstream may have changed the LINUX detection line. "
|
||||||
|
"Update MARKER/REPLACEMENT in this script."
|
||||||
|
)
|
||||||
|
common_py.write_text(content.replace(MARKER, REPLACEMENT), encoding="utf-8")
|
||||||
|
|
||||||
|
cmd = install_cmd_prefix + ["install", "--no-build-isolation", str(src_root)]
|
||||||
|
print(f" $ {' '.join(cmd)}")
|
||||||
|
result = subprocess.run(cmd)
|
||||||
|
if result.returncode != 0:
|
||||||
|
return result.returncode
|
||||||
|
|
||||||
|
print("✓ psutil installed via Android compatibility shim")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
Loading…
Add table
Add a link
Reference in a new issue