Desktop multiprocessing support: PYTHONINSPECT removal, version-keyed dart_bridge staging, dart_bridge 1.5.0 keep-alives#228
Merged
Conversation
All four platform implementations set PYTHONINSPECT=1 among the env vars they setenv() before Py_Initialize. The variable has no effect on the embedded interpreter itself — nothing in the embedded lifecycle runs pymain, which is the only consumer of Py_InspectFlag — but because the vars are written into the real process environment, every child process the app spawns inherits it. That inheritance becomes actively harmful with multiprocessing child interception (dart_bridge >= 1.5.0, flet-dev/flet#4283): a serviced worker child IS a real interpreter run through Py_Main, and an inherited PYTHONINSPECT would hold it open in interactive mode after its -c command completes instead of exiting. It equally affects any python interpreter a user launches via subprocess from a packaged app. dart_bridge's serious_python_main also unsets the variable defensively (apps may run against an older serious_python), but the right fix is to stop leaking it in the first place.
…xports dart_bridge 1.5.0 adds serious_python_is_mp_invocation / serious_python_main (flet-dev/flet#4283): the host app's main.swift dlsym's them before NSApplicationMain to detect CPython child command lines produced by multiprocessing's spawn machinery and service them as a plain headless interpreter instead of booting a second GUI instance. On Apple platforms dart_bridge is a static archive linked into the host executable, and both entry points are dlsym-only — there is no static call site anywhere, so the app link's -dead_strip would discard them. Extend the existing keep-alive block (which already protects serious_python_run and the DartBridge_* exports for Dart FFI's DynamicLibrary.process() lookups) with references to the two new symbols. This is belt-and-braces: dart_bridge 1.5.0 also marks its exports __attribute__((used)) (no-dead-strip at the atom level), but the keep-alive keeps working against toolchains or archives where that attribute is absent, and documents the dlsym contract in the one place Apple linkage is wired.
prepare_macos.sh / prepare_ios.sh guarded the dart_bridge extraction with a bare directory-exists check, so once dist_* held an extracted dart_bridge.xcframework, a dart_bridge version bump kept staging the stale extraction from the previous version — the freshly downloaded zip in the version-keyed cache was never unpacked. (The Python dist half of the script already solved the identical problem with a .python_build_id marker.) Key the extraction the same way: a .dart_bridge_version marker next to the extracted xcframework, re-extracting (rm -rf + unzip) whenever it is missing or disagrees with the requested version. Also fold the duplicated "$python_full_version-$python_build_date" expression into a single pb_id variable used for both the cache dir and the dist marker.
Draft the 4.3.0 CHANGELOG sections across all five packages for the multiprocessing work (flet-dev/flet#4283): - serious_python: the dart_bridge 1.5.0 child-interception contract (serious_python_is_mp_invocation / serious_python_main, plus the _w wide-char variants on Windows) and the PYTHONINSPECT removal. - serious_python_darwin: dead-strip protection for the new exports and the version-keyed dart_bridge extraction in prepare_{macos,ios}.sh. - serious_python_windows / _linux: platform-specific consumption notes (wWinMain / main.cc dlopen; the Python 3.14 forkserver-default angle on Linux). - serious_python_android: PYTHONINSPECT removal only (no behavior change — Android doesn't support process spawning). Also scope the multiprocessing recommendation in docs/dedicated-data-channels.md: it applies to desktop hosts whose binary services the spawn re-exec protocol; iOS/Android forbid spawning child processes. The version numbers reference the upcoming dart_bridge 1.5.0 / serious_python 4.3.0 releases; the pubspec version bumps and the dart_bridge_version pin update land with the release commit once dart_bridge 1.5.0 is tagged.
…dide 314.0.2) Regenerated via `dart run serious_python:gen_version_tables --release-date 20260708`. Delivers dart_bridge 1.5.0 with the multiprocessing child-interception exports; Pyodide for 3.14 bumped 314.0.1 -> 314.0.2. CPython versions unchanged (3.12.13 / 3.13.14 / 3.14.6). 4.3.0 changelogs updated to record the snapshot.
Remove the generic 'Version bump aligning with the serious_python_* 4.3.0 release' line from the CHANGELOG entries across all platform implementations (Android, Darwin, Linux). The specific technical changes and dart_bridge updates are sufficient to document the release.
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.
Part of flet-dev/flet#4283. Depends on flet-dev/dart-bridge#9 (must be merged and tagged 1.5.0 first). Consumed by flet-dev/flet#6662, whose build-template runners perform the actual argv interception.
Context
dart_bridge 1.5.0 adds
serious_python_is_mp_invocation/serious_python_main(plus_wwide-char variants on Windows): host apps call them first thing inmainto detect CPython child command lines produced by multiprocessing's spawn machinery and service them as a plain headless interpreter instead of re-launching the GUI. This PR contains serious-python's share of that fix.Changes
PYTHONINSPECT=1is no longer stamped into the host process environment by any platform implementation. It had no effect on the embedded interpreter itself (nothing in the embedded lifecycle runs pymain, the only consumer ofPy_InspectFlag), but the platform plugins setenv it process-wide, so every child process inherited it — and a serviced multiprocessing worker IS a real interpreter run throughPy_Main, which an inheritedPYTHONINSPECTwould hold open in interactive mode after its-ccommand completed. It equally affected any python interpreter users launch viasubprocessfrom a packaged app. dart_bridge'sserious_python_mainalso unsets it defensively, but the right fix is to stop leaking it.SeriousPythonPlugin.swiftgains keep-alive references for the two new exports. On Apple platforms dart_bridge is a static archive linked into the host executable and both entry points are dlsym-only, so without a static reference the app link's-dead_stripwould discard them. This extends the existing keep-alive block that already protectsserious_python_runand theDartBridge_*exports for Dart FFI. It is belt-and-braces alongside dart_bridge 1.5.0's own__attribute__((used))marking, and it documents the dlsym contract at the one place Apple linkage is wired.prepare_macos.sh/prepare_ios.sh: the extracteddart_bridge.xcframeworkindist_*is now keyed to the dart_bridge version via a.dart_bridge_versionmarker, mirroring the existing.python_build_idmarker. Previously the extraction was guarded by a bare directory-exists check, so a dart_bridge version bump silently kept staging the stale extraction from the previous version.docs/dedicated-data-channels.mdnow carries the desktop-only scope caveat.Coordination notes for the release
The Swift keep-alives make
serious_python_darwinhard-require dart_bridge >= 1.5.0 at link time, so this must not release before that tag exists. Thedart_bridge_versionpin bump itself is not in this PR: it flows from flet-dev/python-build'smanifest.jsonvia the generatedpython_versions.properties/python_versions.dart, and lands with the usual release commit together with the 4.3.0 pubspec bumps.Verification
Verified end-to-end inside
flet buildapps on macOS (arm64), Windows 11 (arm64 host, x64 app), and Ubuntu 22.04 aarch64, together with flet-dev/dart-bridge#9 and flet-dev/flet#6662: worker functions defined in the app'smain.pypickle and round-trip,ProcessPoolExecutorcompletes with worker reuse and cpu-scale speedups, children run headless (no Flutter engine, no windows), the resource tracker functions, and all children exit cleanly, under both spawn and Linux's 3.14 forkserver default.