From 26bd80c97dc2b6081544a14a01a2bf14328f2990 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 22 May 2026 19:20:51 +0000 Subject: [PATCH] fix: detect and repair broken _decimal C extension before pip install When python3-full is not installed on Debian/Ubuntu (common with Python 3.13), the _decimal C extension fails to import, crashing any pip invocation with RuntimeError. Add _python3_decimal_ok() to probe for this, and _fix_python3_decimal() to install python3-full (apt) or python3-libs (dnf). _install_pip() now calls these before attempting any ensurepip/pip operations so the error is fixed automatically rather than producing an unrecoverable crash. https://claude.ai/code/session_01Lrg3UV7AJN9KRXyTTWGgZi --- bootstrap_environment.py | 47 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/bootstrap_environment.py b/bootstrap_environment.py index fd6c295..ac89c8a 100755 --- a/bootstrap_environment.py +++ b/bootstrap_environment.py @@ -1071,6 +1071,37 @@ def _default_install_path(pkg: CustomPackage) -> Optional[Path]: return _DEFAULT_INSTALL_PATHS.get(pkg.name.lower()) +def _python3_decimal_ok() -> bool: + """Return True if Python 3's _decimal C extension loads without error.""" + if not has_cmd("python3"): + return False + try: + r = subprocess.run( + ["python3", "-c", "from decimal import Decimal"], + capture_output=True, check=False, timeout=10, + ) + return r.returncode == 0 + except subprocess.TimeoutExpired: + return False + + +def _fix_python3_decimal() -> bool: + """Install missing Python C-extension packages to fix a broken _decimal import. + + On Debian/Ubuntu, python3.X ships without its C extensions when only the + base package is installed; python3-full (or the versioned equivalent) pulls + in _decimal, _hashlib, etc. On RPM distros python3-libs covers this. + Returns True if the extension works after the attempt. + """ + if PKG_MGR == "apt-get": + # python3-full is the meta-package that pulls in all C extensions for + # the default python3 on Debian/Ubuntu (including _decimal via libmpdec). + run(["apt-get", "install", "-y", "python3-full"], as_sudo=True, check=False) + elif PKG_MGR == "dnf": + run(["dnf", "install", "-y", "python3-libs"], as_sudo=True, check=False) + return _python3_decimal_ok() + + def _pip_installed() -> bool: if not has_cmd("python3"): return False @@ -1210,6 +1241,22 @@ def _install_pip() -> None: if not has_cmd("python3"): err("python3 is not installed — cannot install pip") return + + # Guard against a broken _decimal C extension (e.g. Python 3.13 on Ubuntu/Debian + # when python3-full is not installed). Any pip invocation will immediately crash + # with RuntimeError if this module is missing, so fix it before proceeding. + if not _python3_decimal_ok(): + warn("Python 3 _decimal C extension failed to import — attempting fix ...") + if _fix_python3_decimal(): + print(" Python 3 _decimal extension restored.") + else: + err( + "Python 3 _decimal C extension could not be fixed. " + "Run: sudo apt-get install python3-full (Debian/Ubuntu) " + "or: sudo dnf install python3-libs (Fedora/RHEL)" + ) + return + print(" Bootstrapping pip via 'python3 -m ensurepip --upgrade' ...") bootstrap = run( ["python3", "-m", "ensurepip", "--upgrade"], -- 2.47.3