Backend fixes: Pd message-object hot inlet, Python stable ABI, Max CRT guard#181
Merged
Conversation
A message object's leftmost inlet is hot: a message sent to it must set the first parameter and immediately run the object so it emits output (this is what the Max backend already does via proxy_getinlet). process_inputs() was consuming every parameter message -- including the first inlet's -- as set-only and returning before default_process() could run the object, so the hot inlet went silent. Give process_inputs() a skip_first flag, set by the message processor, so a message addressed to the first parameter falls through to default_process() (set + process + output) while the cold inlets (params 1..n, with their own Pd inlets) stay set-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Version-specific extension modules only import on the exact CPython they were built against (python3XY.dll). Add an opt-in stable-ABI build (AVND_PYTHON_STABLE_ABI) linking the limited API / stable ABI (Py_LIMITED_API, python3.dll) through Python_add_library(USE_SABI), for one module that loads on any CPython >= the configured version. It is OFF by default: pybind11 uses the full CPython C API (PyFrameObject / PyCodeObject internals, PyList_GET_ITEM, ...) and does not compile under Py_LIMITED_API, so the stable-ABI path is only usable with a limited-API-capable binding. When enabled it is wired correctly: it needs the Development.SABIModule component to link python3.lib, and the decision is cached so avnd_make_python() -- called from the consumer's scope -- sees it. The default keeps the working version-specific build, which loads on the Python it is built for; switching from full Development to Development.Module also stops the module linking the interpreter library. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Max externals must link the static CRT (/MT); the MSVC_RUNTIME_LIBRARY property only delivers that when policy CMP0091 is NEW in the scope that creates the target, which a function in this module cannot change. If the consuming project pins an older policy (cmake_minimum_required < 3.15, or an explicit OLD), the request is dropped and the external builds /MD, which corrupts Max's heap on load. Emit a warning at Max-target creation when CMP0091 is not NEW, so the misconfiguration is caught rather than shipped as a broken external. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
jcelerier
force-pushed
the
fix/pd-hot-inlet-python-abi
branch
from
July 22, 2026 19:25
4641580 to
5ca1f26
Compare
Two fixes to the Max message backend that left most objects unusable. Registration: ext_main registered the class under the object's c_name() metadata, but Max looks an object box up by the external's filename. When the two differ the file loads yet no class of that name exists, so the object never instantiates. Pass the external's C_NAME into the metaclasses -- as the Pd backend already does -- and register under it; the audio and jitter metaclasses take the same parameter so ext_main passes it uniformly. Hot inlet: a message matching a port name on the leftmost (hot) inlet was consumed by process_inputs() as set-only, returning before the object ran, so a [Name v1 v2( message set the value but produced no output. Run a processing pass after setting, matching the other inlet-0 paths. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes to the Pd, Python and Max backends. Validated with a full local build of a 26-object add-on across all three backends (Windows / MSVC, CPython 3.13).
Pd — restore the message object's hot inlet
A message object's leftmost inlet is hot: a message sent to it should set the first parameter and run a processing pass so the object emits output (the Max backend now does the same).
process_inputs()was consuming every parameter message — the first inlet's included — as set-only and returning beforedefault_process()could run the object, so the hot inlet produced nothing.process_inputs()gains askip_firstflag so a message to the first parameter falls through todefault_process()(set + process + output); cold inlets stay set-only.Validated: a message to inlet 0 with no trailing bang now produces output; the pre-fix build produces nothing.
Max — register objects under the external filename, and run the hot inlet
Two issues that left most Max objects unusable:
ext_mainregistered the class under the object'sc_name()metadata, but Max looks an object box up by the external's filename. When they differ the file loads yet no class of that name exists, so the object never instantiates. The external'sC_NAMEis now passed into the metaclasses (as Pd already does) and used forclass_new; the audio and jitter metaclasses take the same parameter soext_mainpasses it uniformly.[Name v1 v2(message produced no output. It now runs a processing pass after setting, like the other inlet-0 paths.Python — opt-in stable-ABI build; use
Development.ModuleAdds an opt-in stable-ABI build (
AVND_PYTHON_STABLE_ABI, OFF by default) that links the limited API (Py_LIMITED_API,python3.dll) viaPython_add_library(USE_SABI). It is off by default because pybind11 uses the full CPython C API and does not compile underPy_LIMITED_API, so the stable-ABI path is only usable with a limited-API-capable binding; when enabled it needs theDevelopment.SABIModulecomponent to linkpython3.lib, and the decision is cached soavnd_make_python()(called from the consumer's scope) sees it. The default keeps the working version-specific build; switching from fullDevelopmenttoDevelopment.Modulealso stops the module linking the interpreter library.Validated: stable ABI on → link switches to
python3.libbut pybind11 fails to compile (as noted); default version-specific build imports cleanly on the CPython it is built against (26/26 on 3.13).Max — warn when the static-CRT request is silently dropped
MSVC_RUNTIME_LIBRARY(/MT, required by the Max SDK) only takes effect whenCMP0091is NEW in the scope creating the target, which a function here cannot change. Emit a warning at target creation when it is not NEW, so a consuming project that pins an older policy — and would silently ship/MDexternals that crash Max — is caught. (The corresponding consumer-side fix is to require CMake >= 3.15.)🤖 Generated with Claude Code