From d71f4098b942d23a3ab2cf127d671ce56117438b Mon Sep 17 00:00:00 2001 From: Filipe Cavalcanti Date: Thu, 11 Jun 2026 08:15:53 -0300 Subject: [PATCH 1/2] interpreters/python: update repack_wheel script Updates the repack_wheel_add_pyc.py script to support other packages, not only pip. Signed-off-by: Filipe Cavalcanti --- interpreters/python/repack_wheel_add_pyc.py | 65 ++++++++++++--------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/interpreters/python/repack_wheel_add_pyc.py b/interpreters/python/repack_wheel_add_pyc.py index fc2d913f7ed..ba27f7d071d 100644 --- a/interpreters/python/repack_wheel_add_pyc.py +++ b/interpreters/python/repack_wheel_add_pyc.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: Apache-2.0 # -# Repack a wheel in-place: compile pip/*.py to legacy sibling *.pyc (compileall -# -b: required for zipimport, which does not read PEP 3147 __pycache__/ names), -# remove the .py sources, and rewrite *.dist-info/RECORD. +# Repack a wheel in-place: compile package/*.py to legacy sibling *.pyc +# (compileall -b: required for zipimport, which does not read PEP 3147 +# __pycache__/ names), remove the .py sources, and rewrite *.dist-info/RECORD. from __future__ import annotations @@ -23,25 +23,27 @@ def wheel_record_hash(data: bytes) -> str: return base64.urlsafe_b64encode(digest).decode("ascii").rstrip("=") -def wheel_has_pip_py_sources(zf: zipfile.ZipFile) -> bool: - return any(n.startswith("pip/") and n.endswith(".py") for n in zf.namelist()) +def wheel_has_py_sources(zf: zipfile.ZipFile, package: str) -> bool: + prefix = f"{package}/" + return any(n.startswith(prefix) and n.endswith(".py") for n in zf.namelist()) -def wheel_has_legacy_pip_bytecode(zf: zipfile.ZipFile) -> bool: - return "pip/__init__.pyc" in zf.namelist() +def wheel_has_legacy_bytecode(zf: zipfile.ZipFile, package: str) -> bool: + return f"{package}/__init__.pyc" in zf.namelist() -def strip_pip_py_sources(pip_dir: Path) -> int: - """Remove pip/**/*.py after sibling legacy *.pyc exists (compileall -b output).""" +def strip_py_sources(pkg_dir: Path, package: str) -> int: + """Remove package/**/*.py after sibling legacy *.pyc exists (compileall -b output).""" removed = 0 - for path in sorted(pip_dir.rglob("*.py")): + for path in sorted(pkg_dir.rglob("*.py")): if not path.is_file(): continue pyc = path.with_suffix(".pyc") if not pyc.is_file(): - rel = path.relative_to(pip_dir) + rel = path.relative_to(pkg_dir) raise SystemExit( - f"missing legacy .pyc for pip/{rel.as_posix()}, refusing to delete source" + f"missing legacy .pyc for {package}/{rel.as_posix()}, " + "refusing to delete source" ) path.unlink() removed += 1 @@ -70,41 +72,43 @@ def rebuild_record(root: Path) -> None: record_path.write_text("\n".join(lines) + "\n", encoding="utf-8") -def repack(whl_path: Path, *, force: bool) -> None: +def repack(whl_path: Path, *, package: str, force: bool) -> None: whl_path = whl_path.resolve() if not whl_path.is_file(): raise SystemExit(f"missing wheel: {whl_path}") with zipfile.ZipFile(whl_path) as zf: - has_py = wheel_has_pip_py_sources(zf) + has_py = wheel_has_py_sources(zf, package) if not has_py: - if not wheel_has_legacy_pip_bytecode(zf): + if not wheel_has_legacy_bytecode(zf, package): raise SystemExit( - "repack_wheel_add_pyc: wheel has no pip/*.py and no pip/__init__.pyc " - "(corrupt or old tool output). Delete the bundled pip-*.whl and rebuild." + f"repack_wheel_add_pyc: wheel has no {package}/*.py and no " + f"{package}/__init__.pyc (corrupt or old tool output). " + f"Delete the bundled wheel and rebuild." ) if not force: print( - f"repack_wheel_add_pyc: skip (pip already bytecode-only): {whl_path.name}" + f"repack_wheel_add_pyc: skip ({package} already bytecode-only): " + f"{whl_path.name}" ) return - tmpdir = tempfile.mkdtemp(prefix="pip-whl-pyc-") + tmpdir = tempfile.mkdtemp(prefix=f"{package}-whl-pyc-") try: root = Path(tmpdir) with zipfile.ZipFile(whl_path) as zf: zf.extractall(root) - pip_dir = root / "pip" - if not pip_dir.is_dir(): - raise SystemExit("wheel has no pip/ top-level package") + pkg_dir = root / package + if not pkg_dir.is_dir(): + raise SystemExit(f"wheel has no {package}/ top-level package") subprocess.run( - [sys.executable, "-m", "compileall", "-q", "-f", "-b", str(pip_dir)], + [sys.executable, "-m", "compileall", "-q", "-f", "-b", str(pkg_dir)], cwd=str(root), check=True, ) - n_py = strip_pip_py_sources(pip_dir) + n_py = strip_py_sources(pkg_dir, package) rebuild_record(root) out_path = whl_path.with_suffix(whl_path.suffix + ".tmp") @@ -116,7 +120,8 @@ def repack(whl_path: Path, *, force: bool) -> None: out_path.replace(whl_path) print( - f"repack_wheel_add_pyc: bytecode-only pip ({n_py} .py removed) -> {whl_path.name}" + f"repack_wheel_add_pyc: bytecode-only {package} ({n_py} .py removed) -> " + f"{whl_path.name}" ) finally: shutil.rmtree(tmpdir, ignore_errors=True) @@ -125,14 +130,20 @@ def repack(whl_path: Path, *, force: bool) -> None: def main() -> None: ap = argparse.ArgumentParser(description=__doc__) ap.add_argument("wheel", type=Path, help="path to .whl (updated in place)") + ap.add_argument( + "-p", + "--package", + default="pip", + help="top-level package directory inside the wheel (default: pip)", + ) ap.add_argument( "-f", "--force", action="store_true", - help="repack even if pip is already .pyc-only", + help="repack even if the package is already .pyc-only", ) args = ap.parse_args() - repack(args.wheel, force=args.force) + repack(args.wheel, package=args.package, force=args.force) if __name__ == "__main__": From 86858b689c2c1be09eb8b458ec426d32279d9790 Mon Sep 17 00:00:00 2001 From: Filipe Cavalcanti Date: Thu, 11 Jun 2026 08:16:59 -0300 Subject: [PATCH 2/2] interpreters/python: support nuttx-periphery package Adds support for installing nuttx-periphery Python package by default on Python support. This package provides API for accessing Nuttx character drivers. Signed-off-by: Filipe Cavalcanti --- interpreters/python/Kconfig | 7 +++++++ interpreters/python/Makefile | 30 +++++++++++++++++++++++++----- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/interpreters/python/Kconfig b/interpreters/python/Kconfig index a0582835f49..752bb08b531 100644 --- a/interpreters/python/Kconfig +++ b/interpreters/python/Kconfig @@ -47,6 +47,13 @@ config INTERPRETERS_CPYTHON_ENABLE_PIP site-packages .pth entry that points to ensurepip's bundled wheel. Disable this to skip pip wheel download/integration entirely. +config INTERPRETERS_CPYTHON_INSTALL_NUTTX_PACKAGE + bool "Install NuttX Python package" + default y + ---help--- + Installs the nuttx-periphery Python package into the CPython module image. + This allows importing the nuttx-periphery package into the CPython interpreter, + which provides a Python interface to the NuttX's peripheral drivers. config INTERPRETERS_CPYTHON_PYTHONPATH string "CPython Python path" diff --git a/interpreters/python/Makefile b/interpreters/python/Makefile index 1b1392cac88..36aaed3ab97 100644 --- a/interpreters/python/Makefile +++ b/interpreters/python/Makefile @@ -179,25 +179,45 @@ $(TARGETBUILD)/Makefile: $(HOSTPYTHON) $(CONFIG_SITE) $(SETUP_LOCAL) $(Q) sed -i 's/^#define HAVE_LIBB2 1/\/* #undef HAVE_LIBB2 *\//' $(TARGETBUILD)/pyconfig.h $(Q) sed -i 's/-lb2//g' $(TARGETBUILD)/Makefile +BUNDLED_WHEELS_DIR = $(CPYTHON_PATH)/Lib/ensurepip/_bundled +# PyPI downloads use the host system pip, not $(HOSTPYTHON)'s ensurepip bundle: +# pip 24.x vendors urllib3 that breaks on Python 3.13 (zlib.Decompress.unused_data). +HOSTPIP = python3 -m pip + $(TARGETLIBPYTHON): $(TARGETBUILD)/Makefile +ifneq ($(or $(filter y,$(CONFIG_INTERPRETERS_CPYTHON_ENABLE_PIP)),$(filter y,$(CONFIG_INTERPRETERS_CPYTHON_INSTALL_NUTTX_PACKAGE))),) + $(Q) mkdir -p $(BUNDLED_WHEELS_DIR) +endif ifeq ($(CONFIG_INTERPRETERS_CPYTHON_ENABLE_PIP),y) - $(Q) mkdir -p $(CPYTHON_PATH)/Lib/ensurepip/_bundled $(Q) ( \ PIP_WHEEL_VERSION=$$($(HOSTPYTHON) -c "import ensurepip; print(ensurepip._PIP_VERSION)"); \ - PIP_WHEEL=$(CPYTHON_PATH)/Lib/ensurepip/_bundled/pip-$${PIP_WHEEL_VERSION}-py3-none-any.whl; \ + PIP_WHEEL=$(BUNDLED_WHEELS_DIR)/pip-$${PIP_WHEEL_VERSION}-py3-none-any.whl; \ if [ ! -f "$${PIP_WHEEL}" ]; then \ echo "Fetching pip wheel $${PIP_WHEEL_VERSION} for ensurepip bundle"; \ - $(HOSTPYTHON) -m pip download --only-binary=:all: --no-deps --dest $(CPYTHON_PATH)/Lib/ensurepip/_bundled pip==$${PIP_WHEEL_VERSION}; \ + $(HOSTPIP) download --only-binary=:all: --no-deps --dest $(BUNDLED_WHEELS_DIR) pip==$${PIP_WHEEL_VERSION}; \ fi; \ echo "Pre-compiling pip wheel with build Python (must match embedded CPython version)"; \ - $(HOSTPYTHON) $(CURDIR)/repack_wheel_add_pyc.py "$${PIP_WHEEL}"; \ + $(HOSTPYTHON) $(CURDIR)/repack_wheel_add_pyc.py --package pip "$${PIP_WHEEL}"; \ + ) +endif +ifeq ($(CONFIG_INTERPRETERS_CPYTHON_INSTALL_NUTTX_PACKAGE),y) + $(Q) ( \ + set -e; \ + NUTTX_WHEEL=$$(ls $(BUNDLED_WHEELS_DIR)/nuttx_periphery-*-py3-none-any.whl 2>/dev/null | head -1 || true); \ + if [ -z "$${NUTTX_WHEEL}" ]; then \ + echo "Fetching latest nuttx-periphery wheel for ensurepip bundle"; \ + $(HOSTPIP) download --only-binary=:all: --no-deps --dest $(BUNDLED_WHEELS_DIR) nuttx-periphery; \ + NUTTX_WHEEL=$$(ls $(BUNDLED_WHEELS_DIR)/nuttx_periphery-*-py3-none-any.whl); \ + fi; \ + echo "Pre-compiling nuttx-periphery wheel with build Python (must match embedded CPython version)"; \ + $(HOSTPYTHON) $(CURDIR)/repack_wheel_add_pyc.py --package nuttx_periphery "$${NUTTX_WHEEL}"; \ ) endif $(MAKE) -C $(TARGETBUILD) regen-frozen $(MAKE) -C $(TARGETBUILD) libpython$(CPYTHON_VERSION_MINOR).a wasm_stdlib $(Q) ( cp $(TARGETBUILD)/libpython$(CPYTHON_VERSION_MINOR).a $(TARGETLIBPYTHON) ) $(Q) $(UNPACK) $(TARGETMODULESPACK) -d $(TARGETMODULES)/python$(CPYTHON_VERSION_MINOR) -ifeq ($(CONFIG_INTERPRETERS_CPYTHON_ENABLE_PIP),y) +ifneq ($(or $(filter y,$(CONFIG_INTERPRETERS_CPYTHON_ENABLE_PIP)),$(filter y,$(CONFIG_INTERPRETERS_CPYTHON_INSTALL_NUTTX_PACKAGE))),) $(Q) mkdir -p $(TARGETMODULES)/python$(CPYTHON_VERSION_MINOR)/site-packages $(Q) ( \ set -e; \