From daef4f9c2a6250c259be166d4c9c16caed6ce9a8 Mon Sep 17 00:00:00 2001 From: Paul Swenson Date: Sun, 19 Jul 2026 10:34:58 -0400 Subject: [PATCH 1/7] doom: placeholder for python PR (split from #252) From dfd0f34f7941ab35676310126b899d17627a8f3e Mon Sep 17 00:00:00 2001 From: Paul Swenson <1937248+pdscomp@users.noreply.github.com> Date: Mon, 20 Jul 2026 14:04:12 -0400 Subject: [PATCH 2/7] python: omit fine-grained debug ranges --- meta-opencentauri/recipes-apps/klipper/files/klipper-init-d | 2 +- meta-opencentauri/recipes-apps/moonraker/files/moonraker-init-d | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/meta-opencentauri/recipes-apps/klipper/files/klipper-init-d b/meta-opencentauri/recipes-apps/klipper/files/klipper-init-d index 5d0bc2e9..9ad7c1ab 100644 --- a/meta-opencentauri/recipes-apps/klipper/files/klipper-init-d +++ b/meta-opencentauri/recipes-apps/klipper/files/klipper-init-d @@ -10,7 +10,7 @@ KLIPPY_EXEC="/usr/bin/python3" KLIPPER_PRINTER_CONFIG="/etc/klipper/config/printer.cfg" -KLIPPY_ARGS="-O /usr/share/klipper/klippy/klippy.py $KLIPPER_PRINTER_CONFIG -l /board-resource/klippy.log -a /run/klippy.sock" +KLIPPY_ARGS="-O -X no_debug_ranges /usr/share/klipper/klippy/klippy.py $KLIPPER_PRINTER_CONFIG -l /board-resource/klippy.log -a /run/klippy.sock" PIDFILE="/var/run/klipper.pid" KLIPPER_VAR_CONFIG_BUILDER="/usr/bin/build-klipper-var-config" KLIPPER_CONFIG_VERSION="0.0.8-2" diff --git a/meta-opencentauri/recipes-apps/moonraker/files/moonraker-init-d b/meta-opencentauri/recipes-apps/moonraker/files/moonraker-init-d index 73ef744e..d2a929e1 100644 --- a/meta-opencentauri/recipes-apps/moonraker/files/moonraker-init-d +++ b/meta-opencentauri/recipes-apps/moonraker/files/moonraker-init-d @@ -9,7 +9,7 @@ ### END INIT INFO MOONRAKER_EXEC="/usr/bin/python3" -MOONRAKER_ARGS="-m moonraker.moonraker -d /etc/klipper -l /board-resource/moonraker.log -u /run/moonraker.sock" +MOONRAKER_ARGS="-X no_debug_ranges -m moonraker.moonraker -d /etc/klipper -l /board-resource/moonraker.log -u /run/moonraker.sock" PIDFILE="/var/run/moonraker.pid" export PYTHONPATH="/usr/share/moonraker" From ed2a4bb4d261f820f366697c9bea141a6b016fa5 Mon Sep 17 00:00:00 2001 From: Paul Swenson <1937248+pdscomp@users.noreply.github.com> Date: Mon, 20 Jul 2026 14:11:54 -0400 Subject: [PATCH 3/7] python: ship matching optimized bytecode --- meta-opencentauri/recipes-apps/klipper/klipper_0.13.0.bb | 7 ++++++- .../recipes-apps/moonraker/moonraker_0.10.0.bb | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/meta-opencentauri/recipes-apps/klipper/klipper_0.13.0.bb b/meta-opencentauri/recipes-apps/klipper/klipper_0.13.0.bb index ef4667cf..6bd4d18d 100644 --- a/meta-opencentauri/recipes-apps/klipper/klipper_0.13.0.bb +++ b/meta-opencentauri/recipes-apps/klipper/klipper_0.13.0.bb @@ -12,7 +12,7 @@ SRCREV = "61c0c8d2ef40340781835dd53fb04cc7a454e37a" S = "${WORKDIR}/git" -inherit python3-dir update-rc.d +inherit python3-dir python3native update-rc.d DEPENDS = " \ python3-native \ @@ -71,6 +71,11 @@ do_install() { # Remove any .pyc files to avoid TMPDIR references find ${D} -name '*.pyc' -delete find ${D} -name '__pycache__' -type d -exec rm -rf {} + 2>/dev/null || true + # The target squashfs is read-only, so precompile the exact optimized, + # no-debug-ranges bytecode used by the -O service invocation. Unchecked + # hashes avoid source-path invalidation on the immutable image. + PYTHONNODEBUGRANGES=1 ${PYTHON} -O -m compileall -q -s ${D} -o 1 \ + --invalidation-mode unchecked-hash ${D}${datadir}/klipper # Config directory install -d ${D}${sysconfdir}/klipper diff --git a/meta-opencentauri/recipes-apps/moonraker/moonraker_0.10.0.bb b/meta-opencentauri/recipes-apps/moonraker/moonraker_0.10.0.bb index 5770b60c..ccf78269 100644 --- a/meta-opencentauri/recipes-apps/moonraker/moonraker_0.10.0.bb +++ b/meta-opencentauri/recipes-apps/moonraker/moonraker_0.10.0.bb @@ -23,7 +23,7 @@ S = "${WORKDIR}/git" PR = "r1" -inherit python3-dir update-rc.d +inherit python3-dir python3native update-rc.d DEPENDS = " \ python3-native \ @@ -70,6 +70,11 @@ do_install() { # Install moonraker python package install -d ${D}${datadir}/moonraker cp -r ${S}/moonraker ${D}${datadir}/moonraker/ + # The target squashfs is read-only, so ship matching level-0 bytecode and + # omit fine-grained debug ranges without changing Moonraker optimization + # semantics (its runtime assert checks remain enabled). + PYTHONNODEBUGRANGES=1 ${PYTHON} -m compileall -q -s ${D} \ + --invalidation-mode unchecked-hash ${D}${datadir}/moonraker # Install default moonraker config install -d ${D}${sysconfdir}/klipper From a17bb757181d2afadc82bc5590c4019284bdeb6f Mon Sep 17 00:00:00 2001 From: Paul Swenson <1937248+pdscomp@users.noreply.github.com> Date: Mon, 20 Jul 2026 14:20:33 -0400 Subject: [PATCH 4/7] Revert "python: ship matching optimized bytecode" This reverts commit ed2a4bb4d261f820f366697c9bea141a6b016fa5. --- meta-opencentauri/recipes-apps/klipper/klipper_0.13.0.bb | 7 +------ .../recipes-apps/moonraker/moonraker_0.10.0.bb | 7 +------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/meta-opencentauri/recipes-apps/klipper/klipper_0.13.0.bb b/meta-opencentauri/recipes-apps/klipper/klipper_0.13.0.bb index 6bd4d18d..ef4667cf 100644 --- a/meta-opencentauri/recipes-apps/klipper/klipper_0.13.0.bb +++ b/meta-opencentauri/recipes-apps/klipper/klipper_0.13.0.bb @@ -12,7 +12,7 @@ SRCREV = "61c0c8d2ef40340781835dd53fb04cc7a454e37a" S = "${WORKDIR}/git" -inherit python3-dir python3native update-rc.d +inherit python3-dir update-rc.d DEPENDS = " \ python3-native \ @@ -71,11 +71,6 @@ do_install() { # Remove any .pyc files to avoid TMPDIR references find ${D} -name '*.pyc' -delete find ${D} -name '__pycache__' -type d -exec rm -rf {} + 2>/dev/null || true - # The target squashfs is read-only, so precompile the exact optimized, - # no-debug-ranges bytecode used by the -O service invocation. Unchecked - # hashes avoid source-path invalidation on the immutable image. - PYTHONNODEBUGRANGES=1 ${PYTHON} -O -m compileall -q -s ${D} -o 1 \ - --invalidation-mode unchecked-hash ${D}${datadir}/klipper # Config directory install -d ${D}${sysconfdir}/klipper diff --git a/meta-opencentauri/recipes-apps/moonraker/moonraker_0.10.0.bb b/meta-opencentauri/recipes-apps/moonraker/moonraker_0.10.0.bb index ccf78269..5770b60c 100644 --- a/meta-opencentauri/recipes-apps/moonraker/moonraker_0.10.0.bb +++ b/meta-opencentauri/recipes-apps/moonraker/moonraker_0.10.0.bb @@ -23,7 +23,7 @@ S = "${WORKDIR}/git" PR = "r1" -inherit python3-dir python3native update-rc.d +inherit python3-dir update-rc.d DEPENDS = " \ python3-native \ @@ -70,11 +70,6 @@ do_install() { # Install moonraker python package install -d ${D}${datadir}/moonraker cp -r ${S}/moonraker ${D}${datadir}/moonraker/ - # The target squashfs is read-only, so ship matching level-0 bytecode and - # omit fine-grained debug ranges without changing Moonraker optimization - # semantics (its runtime assert checks remain enabled). - PYTHONNODEBUGRANGES=1 ${PYTHON} -m compileall -q -s ${D} \ - --invalidation-mode unchecked-hash ${D}${datadir}/moonraker # Install default moonraker config install -d ${D}${sysconfdir}/klipper From 33b409b4bb1a33fcf663515c2e156fb63796f637 Mon Sep 17 00:00:00 2001 From: Paul Swenson <1937248+pdscomp@users.noreply.github.com> Date: Mon, 20 Jul 2026 14:20:41 -0400 Subject: [PATCH 5/7] python: cap service malloc arenas at two --- meta-opencentauri/recipes-apps/klipper/files/klipper-init-d | 1 + meta-opencentauri/recipes-apps/moonraker/files/moonraker-init-d | 1 + 2 files changed, 2 insertions(+) diff --git a/meta-opencentauri/recipes-apps/klipper/files/klipper-init-d b/meta-opencentauri/recipes-apps/klipper/files/klipper-init-d index 9ad7c1ab..b65340b5 100644 --- a/meta-opencentauri/recipes-apps/klipper/files/klipper-init-d +++ b/meta-opencentauri/recipes-apps/klipper/files/klipper-init-d @@ -18,6 +18,7 @@ KLIPPER_CONFIG_VERSION_FILE="/etc/klipper-config.ver" KLIPPER_CONFIG_BACKUP_DIR="/etc/klipper/config/config-backups" export TMPDIR="/user-resource/.tmp" +export MALLOC_ARENA_MAX=2 ensure_klipper_config_version() { current_version="" diff --git a/meta-opencentauri/recipes-apps/moonraker/files/moonraker-init-d b/meta-opencentauri/recipes-apps/moonraker/files/moonraker-init-d index d2a929e1..0e16adb2 100644 --- a/meta-opencentauri/recipes-apps/moonraker/files/moonraker-init-d +++ b/meta-opencentauri/recipes-apps/moonraker/files/moonraker-init-d @@ -14,6 +14,7 @@ PIDFILE="/var/run/moonraker.pid" export PYTHONPATH="/usr/share/moonraker" export TMPDIR="/user-resource/.tmp" +export MALLOC_ARENA_MAX=2 case "$1" in start) From 4155af561e18a04303571c4e020ebc4d004bb663 Mon Sep 17 00:00:00 2001 From: Paul Swenson <1937248+pdscomp@users.noreply.github.com> Date: Mon, 20 Jul 2026 14:26:12 -0400 Subject: [PATCH 6/7] Revert "python: cap service malloc arenas at two" This reverts commit 33b409b4bb1a33fcf663515c2e156fb63796f637. --- meta-opencentauri/recipes-apps/klipper/files/klipper-init-d | 1 - meta-opencentauri/recipes-apps/moonraker/files/moonraker-init-d | 1 - 2 files changed, 2 deletions(-) diff --git a/meta-opencentauri/recipes-apps/klipper/files/klipper-init-d b/meta-opencentauri/recipes-apps/klipper/files/klipper-init-d index b65340b5..9ad7c1ab 100644 --- a/meta-opencentauri/recipes-apps/klipper/files/klipper-init-d +++ b/meta-opencentauri/recipes-apps/klipper/files/klipper-init-d @@ -18,7 +18,6 @@ KLIPPER_CONFIG_VERSION_FILE="/etc/klipper-config.ver" KLIPPER_CONFIG_BACKUP_DIR="/etc/klipper/config/config-backups" export TMPDIR="/user-resource/.tmp" -export MALLOC_ARENA_MAX=2 ensure_klipper_config_version() { current_version="" diff --git a/meta-opencentauri/recipes-apps/moonraker/files/moonraker-init-d b/meta-opencentauri/recipes-apps/moonraker/files/moonraker-init-d index 0e16adb2..d2a929e1 100644 --- a/meta-opencentauri/recipes-apps/moonraker/files/moonraker-init-d +++ b/meta-opencentauri/recipes-apps/moonraker/files/moonraker-init-d @@ -14,7 +14,6 @@ PIDFILE="/var/run/moonraker.pid" export PYTHONPATH="/usr/share/moonraker" export TMPDIR="/user-resource/.tmp" -export MALLOC_ARENA_MAX=2 case "$1" in start) From d2035e028f17b95aa65cbf18529d6a7895c478ad Mon Sep 17 00:00:00 2001 From: Paul Swenson <1937248+pdscomp@users.noreply.github.com> Date: Mon, 20 Jul 2026 16:46:18 -0400 Subject: [PATCH 7/7] python: lazy-load optional NumPy modules --- .../0001-Lazy-load-optional-NumPy-users.patch | 107 ++++++++++++++++++ .../recipes-apps/klipper/kalico_2026.02.00.bb | 1 + ...1-Lazy-load-heavy-public-subpackages.patch | 69 +++++++++++ .../python/python3-numpy_%.bbappend | 3 + 4 files changed, 180 insertions(+) create mode 100644 meta-opencentauri/recipes-apps/klipper/files/0001-Lazy-load-optional-NumPy-users.patch create mode 100644 meta-opencentauri/recipes-devtools/python/python3-numpy/0001-Lazy-load-heavy-public-subpackages.patch create mode 100644 meta-opencentauri/recipes-devtools/python/python3-numpy_%.bbappend diff --git a/meta-opencentauri/recipes-apps/klipper/files/0001-Lazy-load-optional-NumPy-users.patch b/meta-opencentauri/recipes-apps/klipper/files/0001-Lazy-load-optional-NumPy-users.patch new file mode 100644 index 00000000..a20f80c1 --- /dev/null +++ b/meta-opencentauri/recipes-apps/klipper/files/0001-Lazy-load-optional-NumPy-users.patch @@ -0,0 +1,107 @@ +From 7f1b2a37d5cb51aabbec6483588f6a58c91bcf31 Mon Sep 17 00:00:00 2001 +From: OpenCentauri +Date: Mon, 20 Jul 2026 15:00:00 -0400 +Subject: [PATCH] Lazy-load optional NumPy users + +Upstream-Status: Pending + +Importing NumPy 1.26 brings a substantial set of numerical modules into the +Klippy process. Do not make every configuration pay that cost merely for JSON +conversion or for temperature-fan control modes which do not use curves. + +Recognize NumPy values by their type metadata in the JSON fallback, which is +only invoked for otherwise non-serializable objects. Import NumPy from the +curve controller itself so watermark and PID temperature fans remain free of +that dependency. +--- + klippy/extras/temperature_fan.py | 13 +++++++------ + klippy/webhooks.py | 15 ++++++++------- + 2 files changed, 15 insertions(+), 13 deletions(-) + +diff --git a/klippy/extras/temperature_fan.py b/klippy/extras/temperature_fan.py +index 757b494..a358d2c 100644 +--- a/klippy/extras/temperature_fan.py ++++ b/klippy/extras/temperature_fan.py +@@ -4,8 +4,6 @@ + # + # This file may be distributed under the terms of the GNU GPLv3 license. + +-import numpy as np +- + from . import fan + + KELVIN_TO_CELSIUS = -273.15 +@@ -250,7 +248,12 @@ class ControlPID: + + class ControlCurve: + def __init__(self, temperature_fan, config): ++ # Curve control is the only temperature-fan mode which needs NumPy. ++ # Keep PID and watermark configurations from importing it at startup. ++ import numpy as np ++ + self.temperature_fan = temperature_fan ++ self.numpy = np + + points = list( + config.getlists("points", seps=(",", "\n"), parser=float, count=2) +@@ -316,19 +319,19 @@ class ControlCurve: + + def temperature_callback(self, read_time, temp): + current_speed = self.temperature_fan.last_speed_value +- upper_temp = np.interp( ++ upper_temp = self.numpy.interp( + current_speed, self.curve_heating[1], self.curve_heating[0] + ) +- lower_temp = np.interp( ++ lower_temp = self.numpy.interp( + current_speed, self.curve_cooling[1], self.curve_cooling[0] + ) + + if temp < lower_temp: +- next_speed = np.interp( ++ next_speed = self.numpy.interp( + temp, self.curve_cooling[0], self.curve_cooling[1] + ) + elif temp > upper_temp: +- next_speed = np.interp( ++ next_speed = self.numpy.interp( + temp, self.curve_heating[0], self.curve_heating[1] + ) + else: +diff --git a/klippy/webhooks.py b/klippy/webhooks.py +index f53cd1b..3037f25 100644 +--- a/klippy/webhooks.py ++++ b/klippy/webhooks.py +@@ -13,8 +13,6 @@ import pwd + import socket + import sys + +-import numpy +- + from . import APP_NAME, gcode + from .extras.danger_options import get_danger_options + +@@ -322,13 +320,15 @@ class ClientConnection: + self.send(result) + + def _json_convert(self, obj): +- # numpy bool/array objects aren't directly serializable; +- # convert to regular types in case they leak into state dicts +- # to avoid a shutdown +- if isinstance(obj, numpy.bool_): +- return bool(obj) +- elif isinstance(obj, numpy.ndarray): ++ # NumPy scalars and arrays are not directly serializable. This ++ # fallback is only called for otherwise unsupported values, so avoid ++ # importing all of NumPy just to recognize them. Both ndarray and ++ # NumPy scalar types provide one of these conversion methods. ++ obj_type = type(obj) ++ if obj_type.__module__.startswith("numpy") and hasattr(obj, "tolist"): + return obj.tolist() ++ if obj_type.__module__.startswith("numpy") and hasattr(obj, "item"): ++ return obj.item() + # anything else will fail + logging.warning( + f"_json_convert: can't serialize object of type {type(obj)}: '{str(obj)}'" +-- +2.47.2 diff --git a/meta-opencentauri/recipes-apps/klipper/kalico_2026.02.00.bb b/meta-opencentauri/recipes-apps/klipper/kalico_2026.02.00.bb index dc8e319a..c47b555e 100644 --- a/meta-opencentauri/recipes-apps/klipper/kalico_2026.02.00.bb +++ b/meta-opencentauri/recipes-apps/klipper/kalico_2026.02.00.bb @@ -7,6 +7,7 @@ FILESEXTRAPATHS:prepend := "${THISDIR}/files:" SRC_URI += " \ file://klipper-init-d \ + file://0001-Lazy-load-optional-NumPy-users.patch \ file://printer.cfg \ file://macros.cfg \ file://machine.cfg \ diff --git a/meta-opencentauri/recipes-devtools/python/python3-numpy/0001-Lazy-load-heavy-public-subpackages.patch b/meta-opencentauri/recipes-devtools/python/python3-numpy/0001-Lazy-load-heavy-public-subpackages.patch new file mode 100644 index 00000000..dc6fabf0 --- /dev/null +++ b/meta-opencentauri/recipes-devtools/python/python3-numpy/0001-Lazy-load-heavy-public-subpackages.patch @@ -0,0 +1,69 @@ +From a9f7ee07c78910755311648026226644a3250c4f Mon Sep 17 00:00:00 2001 +From: OpenCentauri +Date: Mon, 20 Jul 2026 15:00:00 -0400 +Subject: [PATCH] Lazy-load heavy public subpackages + +Upstream-Status: Backport [https://github.com/numpy/numpy/commit/250e1479ce342d9d7ab8a592508f6ce892d4c98b] + +NumPy 1.26 imports several independent numerical subpackages whenever the root +package is imported. Embedded users of core array operations consequently map +FFT, linear algebra, polynomial, random-number, ctypes, and masked-array code +which they may never call. + +Retain the public NumPy 1.26 API by importing these modules on first attribute +access and caching them in the package globals. This follows the lazy public +submodule behavior used by newer NumPy releases while leaving core and lib +namespace initialization unchanged. +--- + numpy/__init__.py | 27 ++++++++++++++++++++++----- + 1 file changed, 22 insertions(+), 5 deletions(-) + +diff --git a/numpy/__init__.py b/numpy/__init__.py +index 63a63bf..54d6a97 100644 +--- a/numpy/__init__.py ++++ b/numpy/__init__.py +@@ -141,12 +141,7 @@ else: + # See gh-14454 and gh-15672 for discussion. + from .lib import * + +- from . import linalg +- from . import fft +- from . import polynomial +- from . import random +- from . import ctypeslib +- from . import ma ++ # Heavy public subpackages are loaded on first access in __getattr__. + from . import matrixlib as _mat + from .matrixlib import * + +@@ -289,6 +284,28 @@ else: + numarray = 'removed' + + def __getattr__(attr): ++ # NumPy 1.26 historically imported these independent subpackages at ++ # root-package startup. Preserve np. while avoiding their ++ # code and extension mappings for core-only embedded workloads. ++ if attr == 'linalg': ++ import numpy.linalg as linalg ++ return linalg ++ elif attr == 'fft': ++ import numpy.fft as fft ++ return fft ++ elif attr == 'random': ++ import numpy.random as random ++ return random ++ elif attr == 'polynomial': ++ import numpy.polynomial as polynomial ++ return polynomial ++ elif attr == 'ma': ++ import numpy.ma as ma ++ return ma ++ elif attr == 'ctypeslib': ++ import numpy.ctypeslib as ctypeslib ++ return ctypeslib ++ + # Warn for expired attributes, and return a dummy function + # that always raises an exception. + import warnings +-- +2.47.2 diff --git a/meta-opencentauri/recipes-devtools/python/python3-numpy_%.bbappend b/meta-opencentauri/recipes-devtools/python/python3-numpy_%.bbappend new file mode 100644 index 00000000..6a50e8d3 --- /dev/null +++ b/meta-opencentauri/recipes-devtools/python/python3-numpy_%.bbappend @@ -0,0 +1,3 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/python3-numpy:" + +SRC_URI += "file://0001-Lazy-load-heavy-public-subpackages.patch"