subagent transcript
session 17f09f89 · workflow wf_6c234df9
delegated task
GOAL: port PySide6 + shiboken6 to the FreeCAD WebAssembly toolchain (static, no-dlopen, asyncify, no-pthreads) so FreeCAD's Python/PySide workbenches
▸ Magik
GOAL: port PySide6 + shiboken6 to the FreeCAD WebAssembly toolchain (static, no-dlopen, asyncify,
no-pthreads) so FreeCAD's Python/PySide workbenches and macros work in the browser. This is the
keystone dependency. Ground EVERYTHING in the real files — investigate, do not speculate.
ESTABLISHED GROUND TRUTH (verified this session):
- PySide+shiboken SOURCE: /tmp/pyside-setup (git branch 6.11, matches Qt). sources/shiboken6,
sources/shiboken6_generator, sources/pyside6 (check exact dir names). Built via setup.py +
coin_build_instructions.py OR raw CMake per sources/*/CMakeLists.txt + README.*.md at repo root.
- HOST toolchain (x86-64 Linux): clang/llvm 21 with libclang at /usr/lib/llvm-21/lib/libclang.so
(llvm-config = 21.1.8); host CPython 3.14.4 (/usr/bin/python3, headers
/opt/toolchains/src/Python-3.14.4/Include); host Qt 6.11.1 at /opt/toolchains/qt/6.11.1/gcc_64.
- TARGET (wasm): emscripten 4.0.12 (/opt/toolchains/emsdk); Qt 6.11.1 asyncify single-thread at
/opt/toolchains/qt-asyncify/6.11.1/wasm_singlethread (QT_FEATURE_thread == -1, i.e. -no-feature-thread);
wasm CPython 3.14 at /opt/toolchains/python-wasm (headers include/python3.14, lib/python3.14.zip).
FreeCAD-wasm build dir: /opt/toolchains/src/freecad-gui-build (reads Qt6_DIR=qt-asyncify/.../wasm_singlethread).
- FreeCAD build links Python C-extensions STATICALLY and registers them via PyImport_AppendInittab in
a generated src/Main/WasmInittab*.cpp (NO dlopen on wasm). PySide bindings must follow this model:
build as STATIC libs, expose PyInit_* for each module (PySide6.QtCore etc.), register in the inittab.
- FreeCAD source: /home/magik6k/lcad-wasm/freecad-port/FreeCAD (branch wasm-port). It ships a PySide
shim at src/Ext/PySide (a compat layer) and uses shiboken (getCppPointer/wrapInstance) e.g. in
Gui/PythonWrapper.cpp. FREECAD_USE_PYSIDE/FREECAD_USE_SHIBOKEN currently OFF.
- KNOWN HARD ISSUES to design around: (1) static + NO dlopen — PySide's normal build emits shared
modules; must emit static + inittab. (2) Qt is -no-feature-thread — shiboken typesystem for QtCore
references QThread/QMutex/etc.; those must be excluded/guarded. (3) CPython 3.14 C-API changes vs
what PySide 6.11 targets. (4) asyncify + JS-exceptions (-fexceptions) flags must match FreeCAD's;
libshiboken/libpyside signal dispatch must be single-thread/asyncify safe. (5) binary size (wasm is
already 196 MB; each Qt binding module is tens of MB — target ONLY the modules FreeCAD needs).
DELIVERABLES — do BOTH:
1. Write a thorough, concrete, execution-ready plan to /home/magik6k/lcad-wasm/pyside-port/<SLUG>/plan.md with exact commands,
cmake args, file paths, patch snippets (precise enough to apply), and any test outputs you gathered.
Put patch/new-file contents as separate files in that dir. If you build/test something, capture the
real output.
2. Return the structured summary (schema). Depth goes in plan.md; keep summary fields tight.
Token cost is not a concern — be exhaustive and correct. Prefer running real commands (grep, ls,
cmake --help, small test builds) over speculation.
=== YOUR SCOPE: CPython 3.14 C-API compatibility for shiboken/PySide 6.11 (slug: py314-compat) ===
Does shiboken6/PySide6 6.11 build against CPython 3.14? Py 3.14 changed/removed C-API (e.g. structmember.h moves, _PyObject internals, PyType_Spec changes, Py_TRASHCAN, tp_ macros). Grep sources/shiboken6/libshiboken + sources/pyside6/libpyside for likely-affected C-API. Check the pyside-setup git branch 6.11 for whether it already claims 3.14 support (build_scripts, README, setup.py python_requires). Cross-reference with our host Python 3.14.4 headers (/opt/toolchains/src/Python-3.14.4/Include). List the C-API breakages and the patches. Try compiling one libshiboken .cpp against Python 3.14 headers to surface real errors.
Create /home/magik6k/lcad-wasm/pyside-port/py314-compat/ and write plan.md (required) + any patches. Return the schema with deliverableFile=/home/magik6k/lcad-wasm/pyside-port/py314-compat/plan.md.
Fable
thinking
I'm going to start exploring the pyside-setup source code and Python 3.14 headers to understand what's happening, then work through several angles in parallel.
Fable
I'll investigate the real sources. Let me start by exploring the structure.
Fable
⚙ Bash {"command": "ls /tmp/pyside-setup/sources/ 2>/dev/null; echo \"---shiboken6---\"; ls /tmp/pyside-setup/sources/shiboken6/ 2>/dev/null; echo \"---libshiboken---\"; ls /tmp/pyside-setup/sources/shiboken6/libshiboken/ 2>/dev/null; echo \"---li…
Fable
⚙ Bash {"command": "cd /tmp/pyside-setup && git branch 2>/dev/null; git log --oneline -3 2>/dev/null; echo \"---python_requires---\"; grep -rn \"python_requires\\|3.14\\|3, 14\\|3,14\" setup.py build_scripts/ 2>/dev/null | head -40", "description"…
Fable
⚙ Bash {"command": "ls /opt/toolchains/src/Python-3.14.4/Include/ 2>/dev/null | head; echo \"---structmember---\"; ls /opt/toolchains/src/Python-3.14.4/Include/structmember.h /opt/toolchains/src/Python-3.14.4/Include/cpython/ 2>/dev/null; echo \"=…
Fable
thinking
I need to verify that structmember.h is still available in Python 3.14, then cross-check the Python version requirements in pyproject against what PySide 6.11 supports, and examine how the C-API is being used.
Fable
Let me check pyside's claimed Python support and grep for likely-affected C-API.
Fable
⚙ Bash {"command": "cd /tmp/pyside-setup; echo \"===requires-python===\"; grep -rn \"requires-python\\|Programming Language :: Python\" pyproject.toml *.toml 2>/dev/null; echo \"===version files===\"; cat sources/shiboken6/shiboken_version.py 2>/d…
Fable
⚙ Bash {"command": "cd /tmp/pyside-setup/sources; echo \"=== structmember usage ===\"; grep -rn \"structmember.h\\|T_OBJECT\\|T_INT\\|PyMemberDef\" shiboken6/libshiboken pyside6/libpyside 2>/dev/null | head; echo \"=== Py_TRASHCAN ===\"; grep -rn …
Fable
thinking
I need to check the pyproject.toml for the requires-python setting, but the grep came up empty. Looking at the actual files, I can see that sbktypefactory.cpp is directly accessing type internals through tp_ member manipulation, which is a key issue. The Python 3.14 changes are substantial, so I need to examine the critical files like basewrapper.cpp, basewrapper_p.h, and pep384impl.cpp to understand the full scope of C-API usage that's affected.
Fable
⚙ Bash {"command": "cd /tmp/pyside-setup; find . -maxdepth 2 -name \"pyproject.toml\" -o -maxdepth 2 -name \"setup.py\" | head; echo \"===root pyproject===\"; cat pyproject.toml 2>/dev/null | head -40; echo \"=== shiboken pyproject ===\"; find . -…
Fable
⚙ Bash {"command": "cd /tmp/pyside-setup; for f in $(find . -name \"pyproject.toml*\"); do echo \"=== $f ===\"; grep -n \"requires-python\\|Python :: 3\" \"$f\"; done", "description": "Grep requires-python across pyproject files"}
Fable
⚙ Bash {"command": "cd /tmp/pyside-setup/sources; echo \"=== more risky internals ===\"; grep -rn \"tp_dict\\|tp_mro\\|tp_bases\\|tp_subclasses\\|tp_getattro\\|tp_setattro\\|ht_type\\|_PyObject_GetDictPtr\\|_PyObject_MakeTpCall\\|tp_dictoffset\\|t…
Fable
thinking
I'm noticing that PySide 6.11 officially supports Python 3.14 according to the pyproject.toml configuration, which means shiboken6 and PySide6 are already building against CPython 3.14. Now I need to check if the codebase includes the necessary version guards for 3.14, specifically looking for the hex representation `0x030E` and related Python version checks.
Fable
PySide 6.11 declares `requires-python = ">=3.10, <3.15"` with a 3.14 classifier. Let me verify actual 3.14 code guards exist.
Fable
⚙ Bash {"command": "cd /tmp/pyside-setup/sources; echo \"=== 3.14 hex guards (0x030E) ===\"; grep -rn \"0x030E\\|0x030e\\|0x030D\\|0x030d\\|0x030C\" shiboken6/libshiboken pyside6/libpyside 2>/dev/null | head -40; echo; echo \"=== PY_VERSION_HEX to…
Fable
⚙ Bash {"command": "cd /tmp/pyside-setup/sources; grep -rn \"PY_VERSION_HEX\" shiboken6/libshiboken pyside6/libpyside 2>/dev/null | grep -iv \"0x0308\\|0x0309\\|0x030A\\|0x030a\\|0x0307\" | head -50", "description": "List PY_VERSION_HEX guards exc…
Fable
thinking
I'm seeing explicit Python 3.14 runtime guards in the source files, which confirms PySide 6.11 already has support for it. Now I need to verify this by attempting to compile a libshiboken C++ file against Python 3.14 headers to see if there are any actual compatibility issues.
Fable
The source has explicit `0x030E00` (3.14) runtime guards. Now let me attempt a real compile against Python 3.14 headers.
Fable
⚙ Bash {"command": "cd /tmp/pyside-setup/sources/shiboken6/libshiboken; echo \"=== includes needed by a simple TU ===\"; head -40 sbkstring.cpp; echo \"=== generated headers referenced? ===\"; grep -rln \"sbkversion.h\\|shibokenmacros.h\" *.cpp | …
Fable
thinking
I need to figure out what sbkversion.h is and whether I can generate it or create a stub. Let me try compiling sbkstring.cpp to see what actually breaks, then I'll set up the include paths and compile against Python 3.14 using the host clang compiler.
Fable
⚙ Bash {"command": "cd /tmp/pyside-setup/sources/shiboken6/libshiboken; grep -rln \"sbkversion.h\" *.h *.cpp; echo \"=== create sbkversion.h stub if needed ===\"; cat sbkversion.h.in", "description": "Find sbkversion.h usage and template"}
Fable
⚙ Bash {"command": "mkdir -p /tmp/sbktest/gen\ncat > /tmp/sbktest/gen/sbkversion.h <<'EOF'\n#ifndef SBKVERSION_H\n#define SBKVERSION_H\n#define SHIBOKEN_VERSION \"6.11.0\"\n#define SHIBOKEN_MAJOR_VERSION 6\n#define SHIBOKEN_MINOR_VERSION 11\n#defi…
Fable
thinking
Looking for clang via the llvm-21 path, and I need pyconfig.h for the host compile test. There's a native build version in cross-build/build/pyconfig.h, but the simplest approach is to check if there's an installed Python 3.14 with dev headers—I see /usr/bin/python3 is 3.14.4, so I'll use sysconfig to get its include directory.
Fable
⚙ Bash {"command": "ls /usr/lib/llvm-21/bin/clang* 2>/dev/null; echo \"---\"; /usr/bin/python3 -c \"import sysconfig; print(sysconfig.get_path('include')); print(sysconfig.get_config_var('CONFINCLUDEPY'))\"; echo \"--- check pyconfig in host inclu…
Fable
⚙ Bash {"command": "ls /usr/include/python3.14/ 2>/dev/null | head; echo \"=== find any clang ===\"; ls /usr/lib/llvm-21/bin/ 2>/dev/null | grep -i clang; which gcc g++ 2>/dev/null; echo \"=== full py 3.14 headers install? ===\"; ls /usr/include/p…
Fable
thinking
The /usr/include/python3.14 directory exists but is empty—the ls command failed because Python.h isn't there, meaning the dev headers aren't installed on the host system. The actual headers are in the source tree's Include directory, but I need to figure out where pyconfig.h is coming from, likely the cross-build or native build configuration.
For the compiler, g++ is available and should work fine for testing C-API compatibility, though I should check if clang is accessible since llvm-21 exists in the system.
Fable
⚙ Bash {"command": "ls /usr/lib/llvm-21/bin/ | head -30; echo \"=== emsdk clang ===\"; ls /opt/toolchains/emsdk/upstream/bin/clang* 2>/dev/null | head", "description": "List llvm-21 bin and emsdk clang"}
Fable
thinking
I'll use g++ for the host compile test since it's the simplest way to surface C-API errors. I'm planning to compile a few key files—sbkstring.cpp, sbkfeature_base.cpp (which has the 3.14 opcode analysis), and sbkmodule.cpp—and I need to set up the include paths to point to libshiboken's own directory, the generated files directory for sbkversion.h, Python's Include directory, and the pyconfig.h location.
Fable
⚙ Bash {"command": "cd /tmp/pyside-setup/sources/shiboken6/libshiboken\nPYINC=/opt/toolchains/src/Python-3.14.4/Include\nPYCONF=/opt/toolchains/src/Python-3.14.4/cross-build/build\nGEN=/tmp/sbktest/gen\nLIB=$(pwd)\nfor f in sbkstring.cpp helper.cp…
Fable
thinking
I'm testing the most complex modules that dig into Python's internal type structures—the ones that manipulate type offsets, trash can macros, and PEP 384 limited API shims—to make sure they all compile cleanly against Python 3.14 headers, then running a full compilation pass.
Fable
All four clean. Let me compile the internals-heavy ones and the whole set.
Fable
⚙ Bash {"command": "cd /tmp/pyside-setup/sources/shiboken6/libshiboken\nPYINC=/opt/toolchains/src/Python-3.14.4/Include\nPYCONF=/opt/toolchains/src/Python-3.14.4/cross-build/build\nGEN=/tmp/sbktest/gen; LIB=$(pwd)\nfail=0\nfor f in $(ls *.cpp); do…
Fable
thinking
The numpy-related compilation failures are just missing C headers in the include path, not a Python 3.14 compatibility issue—they're guarded by HAVE_NUMPY and only built when numpy is available, which FreeCAD likely doesn't need for shiboken anyway. So these aren't actual blockers for the port.
The core internal files all compile cleanly against Python 3.14.4, so now I should verify the numpy failures are purely header-related, check if the signature module compiles, and see what's needed for libpyside (though Qt headers might make that harder to test).
Fable
The numpy failures are just missing numpy headers, not a 3.14 issue. Let me confirm numpy conditionality and test the signature module.
Fable
⚙ Bash {"command": "cd /tmp/pyside-setup/sources/shiboken6/libshiboken\necho \"=== numpy guard in CMakeLists ===\"; grep -n -i \"numpy\\|NUMPY\" CMakeLists.txt | head -20\necho \"=== signature dir ===\"; ls signature/\nPYINC=/opt/toolchains/src/Py…
Fable
thinking
The signature_globals.cpp compilation failure stems from embed/signature_inc.h being a generated file that's created during the build process, not an actual Python 3.14 compatibility issue. The numpy-related files only compile when the appropriate build flags are set, and those are optional dependencies that can be disabled. So shiboken6 6.11's libshiboken actually compiles without problems against CPython 3.14.4.
Let me verify the numpy files aren't a 3.14 issue by checking if numpy headers are available on this system and testing compilation with them present.
Fable
Let me confirm the numpy files compile when numpy headers are present (proving they're not 3.14 issues).
Fable
⚙ Bash {"command": "NPINC=$(/usr/bin/python3 -c \"import numpy;print(numpy.get_include())\" 2>/dev/null); echo \"numpy inc=$NPINC\"\ncd /tmp/pyside-setup/sources/shiboken6/libshiboken\nPYINC=/opt/toolchains/src/Python-3.14.4/Include\nPYCONF=/opt/t…
Fable
thinking
The numpy compilation failures are just missing headers since numpy support is disabled by default and not needed for FreeCAD's shiboken integration. Now I'm diving into the 3.14-specific opcode handling in the codebase to understand what it actually does.
Fable
All confirmed: the only failures are missing numpy/generated headers, orthogonal to 3.14. Let me examine the actual 3.14-specific code paths to document them.
Fable
⚙ Bash {"command": "cd /tmp/pyside-setup/sources/shiboken6/libshiboken; sed -n '95,145p' sbkfeature_base.cpp; echo \"=== sbkmodule 295-320 ===\"; sed -n '295,320p' sbkmodule.cpp", "description": "Read 3.14 opcode/module code paths"}
Fable
thinking
I'm looking at the critical parts of shiboken's feature detection system—it parses Python bytecode opcodes to identify `from __feature__ import` statements and wildcard imports, relying on hardcoded opcode values that vary by Python version. The good news is that the 3.14 opcodes are already defined in the codebase, so I need to verify those values are correct.
Let me check the actual opcode definitions in Python 3.14.4 using the dis module to confirm they match what's already in the source.
Fable
These hardcoded 3.14 opcodes are the fragile part. Let me verify them against the actual Python 3.14.4 opcode values.
Fable
⚙ Bash {"command": "/usr/bin/python3 - <<'EOF'\nimport opcode, dis\no = opcode.opmap\nwant = ['LOAD_ATTR','CALL','PUSH_NULL','LOAD_CONST','IMPORT_NAME']\nsrc = {'LOAD_ATTR':80,'CALL':52,'PUSH_NULL':33,'LOAD_CONST':82,'IMPORT_NAME':73}\nimport sys\…
Fable
thinking
The opcodes all match Python 3.14.4 perfectly, which confirms shiboken 6.11 has full Python 3.14 support. Now I'm digging into libpyside to check for any version-specific concerns—I'll search for C-API issues and version guards there, plus examine the shiboken generator more closely.
Fable
All 3.14 opcodes match exactly. Now let me check libpyside for 3.14 concerns and any use of removed/deprecated C-API.
Fable
⚙ Bash {"command": "cd /tmp/pyside-setup/sources/pyside6/libpyside\necho \"=== version guards ===\"; grep -rn \"PY_VERSION_HEX\\|0x030E\\|0x030D\\|0x030C\" *.cpp *.h 2>/dev/null\necho \"=== potentially removed/changed C-API in 3.14 ===\"\ngrep -rn…
Fable
thinking
Libpyside doesn't need version guards since it relies on shiboken's pep384impl helpers to handle version differences centrally, and PyDict_GetItem is still available in Python 3.14 even though it's soft-deprecated. The version-specific logic is abstracted away in libshiboken, so libpyside doesn't have direct 3.14 concerns.
I should verify a couple things: that PyDict_GetItem still exists in the 3.14 headers as a borrowed reference, and check whether shiboken uses the old T_ macros or the newer Py_T_ constants from structmember.h, since those moved in 3.14.
Fable
⚙ Bash {"command": "cd /tmp/pyside-setup/sources\necho \"=== structmember T_ constant usage (deprecated in 3.14, still present) ===\"\ngrep -rn \"\\bT_OBJECT\\b\\|\\bT_INT\\b\\|\\bT_CHAR\\b\\|\\bT_STRING\\b\\|\\bT_PYSSIZET\\b\\|Py_T_OBJECT\\|Py_RE…
Fable
thinking
Structmember.h is still included in 3.14 for backward compatibility, and shiboken doesn't directly reference the bare T_ constants—it just includes the header defensively in sbkpython.h. PyDict_GetItem remains available too. Based on my verification, shiboken6/PySide6 6.11 already has full CPython 3.14 support without needing any C-API patches, though I should keep an eye on the wasm/emscripten build and numpy considerations.
Now I'm thinking about the emscripten compilation side—clang-22 will be compiling against the wasm Python 3.14 headers, which might have different pyconfig settings. Let me do a quick syntax check by compiling a couple of key files like basewrapper.cpp and sbktypefacto …
Fable
structmember.h still ships (deprecated), shiboken doesn't use bare T_ constants. Let me do one final check: compile key files with the actual emscripten clang against the wasm Python 3.14 headers.
Fable
⚙ Bash {"command": "source /opt/toolchains/emsdk/emsdk_env.sh 2>/dev/null\nWPYINC=/opt/toolchains/python-wasm/include/python3.14\nls $WPYINC/Python.h $WPYINC/pyconfig.h 2>/dev/null\ncd /tmp/pyside-setup/sources/shiboken6/libshiboken\nGEN=/tmp/sbkt…
Fable
thinking
The libshiboken internals compile cleanly with emscripten against the actual wasm CPython headers, which confirms everything is working correctly. Now I'm going to do a full compilation of the complete libshiboken set with emcc and then document the plan.
Fable
All internals-heavy files compile with emscripten against wasm Python 3.14. Let me run the full libshiboken set under emcc, then write the plan.
Fable
⚙ Bash {"command": "source /opt/toolchains/emsdk/emsdk_env.sh 2>/dev/null\nWPYINC=/opt/toolchains/python-wasm/include/python3.14\ncd /tmp/pyside-setup/sources/shiboken6/libshiboken\nGEN=/tmp/sbktest/gen; LIB=$(pwd); fail=0; okc=0\nfor f in $(ls *.…
Fable
All 24 non-numpy libshiboken TUs compile clean under emscripten against wasm Python 3.14. Let me write the deliverable.
Fable
⚙ Bash {"command": "mkdir -p /home/magik6k/lcad-wasm/pyside-port/py314-compat", "description": "Create deliverable dir"}
Fable
⚙ Write {"file_path": "/home/magik6k/lcad-wasm/pyside-port/py314-compat/plan.md", "content": "# CPython 3.14 C-API compatibility for shiboken6 / PySide6 6.11\n\n**Scope:** Does shiboken6/PySide6 6.11 build against CPython 3.14? What C-API breakages…
Fable
Now the reproducible verification script, opcode check, and the sbkversion.h stub as artifacts.
Fable
⚙ Write {"file_path": "/home/magik6k/lcad-wasm/pyside-port/py314-compat/sbkversion.h", "content": "/* Stub of the CMake-generated sbkversion.h (from libshiboken/sbkversion.h.in)\n * used only for standalone syntax-compile tests against Python 3.14.…
Fable
⚙ Write {"file_path": "/home/magik6k/lcad-wasm/pyside-port/py314-compat/test_opcodes.py", "content": "#!/usr/bin/env python3\n\"\"\"Verify shiboken 6.11's hardcoded 3.14 bytecode opcodes match the running Python.\n\nshiboken's feature-select / impo…
Fable
⚙ Write {"file_path": "/home/magik6k/lcad-wasm/pyside-port/py314-compat/verify.sh", "content": "#!/usr/bin/env bash\n# Reproduce the Python 3.14 C-API compatibility evidence for shiboken6 6.11.\n# Exits non-zero if any non-numpy libshiboken TU fail…
Fable
Let me run the verify script end-to-end to confirm the artifact reproduces.
Fable
⚙ Bash {"command": "chmod +x /home/magik6k/lcad-wasm/pyside-port/py314-compat/verify.sh\nbash /home/magik6k/lcad-wasm/pyside-port/py314-compat/verify.sh 2>&1 | tail -30", "description": "Run the reproducible verify script"}
Fable
The verify script reproduces cleanly end-to-end. My investigation is complete.
Fable
⚙ StructuredOutput {"__unparsedToolInput": {"raw": "{\"slug\": \"py314-compat\", \"title\": \"CPython 3.14 C-API compatibility for shiboken6/PySide6 6.11\", \"verdict\": \"COMPATIBLE — no patches needed. PySide/shiboken 6.11 already officially targets CPython…
Fable
⚙ StructuredOutput {"slug": "py314-compat", "title": "CPython 3.14 C-API compatibility for shiboken6/PySide6 6.11", "verdict": "COMPATIBLE — no patches needed. PySide/shiboken 6.11 already officially targets CPython 3.14 (wheel_artifacts/pyproject.toml.base d…