feat: process-reuse support + native log redirection (1.3.0)#4
Merged
Conversation
Two related additions to libdart_bridge, mainly to fix the Android
back-button-exit / process-reuse scenario where the OS keeps the OS
process alive but restarts only the Dart VM. The old socket-based
flet plugin coped via "skip Py_Initialize if Python is already up"
plus the still-running socket Flet server accepting the new client.
This binary did neither.
## Process reuse (Android lifecycle)
Three new C exports + one Python module method form the contract Dart
and Python use to swap to fresh native ports without re-initializing
Python:
C exports:
- int dart_bridge_is_python_initialized(void)
- void dart_bridge_signal_dart_session(int n, const char* const* labels,
const int64_t* ports)
- int dart_bridge_install_stdio_redirect(void)
Python module method:
- dart_bridge.add_session_restart_handler(callback)
callback receives a {label: port} dict on every new Dart VM signal.
serious_python_run() no longer aborts when Py_IsInitialized() is true;
instead it applies the new env vars (so anything reading os.environ
sees current values) and fires dart_bridge_signal_dart_session with the
labeled port map reconstructed from this run's config (currently
"protocol" ← FLET_DART_BRIDGE_PORT, "exit" ← FLET_DART_BRIDGE_EXIT_PORT).
Registered Python handlers (flet's FletDartBridgeServer restart loop,
the flet build template's sys.exit patcher, etc.) then rewire to the
new ports — the running Python state, user-level singletons, and
in-memory data are preserved.
## stdout/stderr → native log
Right after Py_Initialize, sys.stdout / sys.stderr are replaced with
file-like wrappers whose write() forwards to a platform-specific
native log sink:
- Android: __android_log_write under tag "flet.python"
(INFO for stdout, ERROR for stderr — visible via
`adb logcat -s flet.python:*`)
- iOS/macOS: os_log_with_type (OS_LOG_TYPE_DEFAULT / _ERROR),
visible in Console.app
- Other (desktop Linux/Windows): fwrite passthrough — preserves
`flet run` console output and CI logs.
Installed by dart_bridge_install_stdio_redirect(), called from
sp_run_python() immediately after Py_Initialize so even bootstrap-time
prints (sys.path injection, module loads, top-level user-app statements)
land in the platform log instead of vanishing into /dev/null.
The implementation lives at the Python sys.stdout layer (not fd 1/2
replacement via dup2 + pipe + reader thread) — simpler, no extra
thread, catches print() and traceback writes directly. Native crashes
that bypass Python's stdio aren't captured here.
## Other changes
- dart_bridge_clear_handlers also clears the session-restart subscriber
list, since those PyObject* refs are held the same way as port handlers.
- CMakeLists.txt: version bump 1.2.2 → 1.3.0 (binary-compatible adds;
existing consumers keep working without code changes).
Smoke-tested locally on macOS: dylib builds clean, all new symbols
present in `nm -gU`, ctypes-harness exercise of the "no embedded
interpreter" code paths doesn't crash.
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.
Summary
Py_Initializeif Python is already up" + a still-running socket Flet server. The current dart_bridge transport had neither, so process-reuse would either crash (re-init) or silently hang (stale port handlers).sys.stdout/sys.stderrredirection to native log sinks (logcat /os_log/ stderr) installed right afterPy_Initialize— restores the old plugin'sadb logcat -s flet.python:*visibility lost during the dart_bridge migration.Wire / API contract
New C exports (callable from Dart via FFI):
New Python module method (built-in
dart_bridge):Behavioral change in
serious_python_run(): when called withPy_IsInitialized()already true, no longer refuses with an error — instead applies the new env vars and firesdart_bridge_signal_dart_sessionwith the labeled port map reconstructed from this run's config ("protocol"←FLET_DART_BRIDGE_PORT,"exit"←FLET_DART_BRIDGE_EXIT_PORT). Returns 0 immediately.Registered Python handlers (flet's
FletDartBridgeServerrestart loop, the flet build template'ssys.exitpatcher) rewire to the new ports — user-level Python state and singletons are preserved across the Dart VM restart.Native log redirection
Right after
Py_Initialize,sys.stdout/sys.stderrare replaced with file-like wrappers whosewrite()forwards to a platform-specific native log sink:__android_log_writeunder tagflet.python(INFO for stdout, ERROR for stderr — visible viaadb logcat -s flet.python:*)os_log_with_type(visible in Console.app)fwritepassthrough — preservesflet runconsole output and CI logs unchangedInstalled by
dart_bridge_install_stdio_redirect(), called fromsp_run_python()immediately afterPy_Initializeso even bootstrap-time prints (sys.path injection, top-level user-app statements) land in the platform log instead of vanishing into/dev/nullon mobile.Implementation is at the Python
sys.stdoutlayer (not fd 1/2 replacement viadup2+ pipe + reader thread) — simpler, no extra thread, catchesprint()and traceback writes directly. Native crashes that bypass Python's stdio aren't captured here (separate concern).Other changes
dart_bridge_clear_handlersalso clears the session-restart subscriber list, since thosePyObject*refs are held the same way as port handlers.CMakeLists.txt: version bump1.2.2→1.3.0(binary-compatible adds — existing consumers keep working without code changes; the new exports are looked up softly on the Dart side vialookupOrNull).Test plan
cmake --buildclean, all four new exports present innm -gU libdart_bridge.dylib.dart_bridge_is_python_initialized+dart_bridge_signal_dart_sessionagainst the "no embedded interpreter" code path — no crashes.Py_Initializecrash in logcat,flet.pythontag carries app's print output.flet build apk/macos/windows/linuxbuilds —is_python_initializedreturns 0 on first run,signal_dart_sessionis a no-op, behavior unchanged from 1.2.2.Consumer rollout
flet-dev/serious-python: Dart FFI bindings for the new exports uselookupOrNullso pre-1.3.0 binaries still load (calls degrade to no-op). Companion branch ready.flet-dev/flet:flet.app.run_asyncdart_bridge branch grows a_DartBridgeServerHandlewrapper that swaps in a newFletDartBridgeServeronadd_session_restart_handlercallback. flet build template'sinitBridgescallssignalDartSessionon every startup.python.dartbootstrap makes_exit_portmutable and subscribes to restart events.Plan doc: the-new-challenge-at-joyful-thacker.md (local plan file).