Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .fvmrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"flutter": "3.44.4"
"flutter": "3.44.8"
}
5 changes: 5 additions & 0 deletions src/serious_python/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 4.4.0

* **iOS/macOS:** fix built iOS apps crashing at startup with `Failed to lookup symbol 'serious_python_run'`. `dart_bridge` now ships as a dynamic framework, so the FFI entry points Dart and Python resolve via `dlsym` stay exported; previously it was static-linked into the app executable, which exports nothing. Release/device builds only. See `serious_python_darwin` 4.4.0.
* Bundled python-build snapshot re-pinned to **20260725** (`dart_bridge` **1.5.1 → 1.6.1**, which carries the dynamic-framework change above; Pyodide 3.14 **314.0.2 → 314.0.3**). Python versions (**3.12.13 / 3.13.14 / 3.14.6**) are unchanged.

## 4.3.6

* **Android:** PEP 734 subinterpreters (Python 3.14's `concurrent.interpreters` / `InterpreterPoolExecutor`) now work in built apps. Previously the main interpreter could import them, but every *subinterpreter* failed to import any relocated C extension (`ModuleNotFoundError: _struct` / `_interpqueues` / ...) — which broke the whole feature (its cross-interpreter transport pickles → `_struct`, and its queues need `_interpqueues`). The native-module finder lives on `sys.meta_path`, which is per-interpreter, and was installed only in the main interpreter. See `serious_python_android` 4.3.6.
Expand Down
8 changes: 4 additions & 4 deletions src/serious_python/lib/src/python_versions.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// GENERATED by `dart run serious_python:gen_version_tables` from python-build's
// manifest.json (release 20260720). Do not edit by hand — edit python-build's
// manifest.json (release 20260725). Do not edit by hand — edit python-build's
// manifest.json, cut a release, bump `pythonReleaseDate`, and regenerate.

const pythonVersionEnvironmentVariable = "SERIOUS_PYTHON_VERSION";
Expand All @@ -10,8 +10,8 @@ const pyodideVersionEnvironmentVariable = "SERIOUS_PYTHON_PYODIDE_VERSION";
const dartBridgeVersionEnvironmentVariable = "DART_BRIDGE_VERSION";

/// python-build release the bundled runtimes come from (YYYYMMDD).
const pythonReleaseDate = "20260720";
const dartBridgeVersion = "1.5.1";
const pythonReleaseDate = "20260725";
const dartBridgeVersion = "1.6.1";
const defaultPythonVersion = "3.14";

class PythonRelease {
Expand Down Expand Up @@ -56,7 +56,7 @@ const pythonReleases = <String, PythonRelease>{
"3.14": PythonRelease(
standaloneVersion: "3.14.6",
standaloneReleaseDate: "20260623",
pyodideVersion: "314.0.2",
pyodideVersion: "314.0.3",
pyodidePlatformTag: "pyemscripten-2026.0-wasm32",
androidAbis: ["arm64-v8a", "x86_64", "armeabi-v7a"],
prerelease: false,
Expand Down
2 changes: 1 addition & 1 deletion src/serious_python/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: serious_python
description: A cross-platform plugin for adding embedded Python runtime to your Flutter apps.
homepage: https://flet.dev
repository: https://github.com/flet-dev/serious-python
version: 4.3.6
version: 4.4.0

platforms:
ios:
Expand Down
4 changes: 4 additions & 0 deletions src/serious_python_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 4.4.0

* Re-pins the bundled python-build snapshot to **20260725** (`dart_bridge` **1.5.1 → 1.6.1**). 1.6.1 changes only how `dart_bridge` is packaged for Apple platforms (static library → dynamic framework, see `serious_python_darwin` 4.4.0); the Android runtime is byte-identical to 20260720.

## 4.3.6

* **PEP 734 subinterpreters now work** (Python 3.14 `concurrent.interpreters` / `InterpreterPoolExecutor`) — enabling true multi-core CPU parallelism inside a single process, which `multiprocessing` cannot provide on Android. Previously the main interpreter imported the machinery fine, but every subinterpreter raised `ModuleNotFoundError` for `_struct` / `_interpqueues` / any other C extension, so `InterpreterPoolExecutor` and the low-level API were unusable. Cause: Android relocates C extensions (native-mmap packaging) and resolves them through `_SorefFinder` on `sys.meta_path`; `sys.meta_path` is per-interpreter, and `install()` only ran in the main interpreter, so a freshly created subinterpreter had a `meta_path` without the finder. Fix: `_sp_bootstrap.install()` now also wraps `concurrent.interpreters.create()` so every new interpreter installs the finder before use. The install runs via `Interpreter.exec()` — a source string, which is pickle-free (unlike `Interpreter.call()`), so it works *before* `_struct` is importable in the child. Because `InterpreterPoolExecutor` builds its workers via `concurrent.interpreters.create()`, the pool is fixed transparently with no user-code change. A no-op before 3.14; iOS/desktop were unaffected.
Expand Down
2 changes: 1 addition & 1 deletion src/serious_python_android/android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ buildscript {
}

group = "com.flet.serious_python_android"
version = "4.3.6"
version = "4.4.0"

rootProject.allprojects {
repositories {
Expand Down
6 changes: 3 additions & 3 deletions src/serious_python_android/android/python_versions.properties
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# GENERATED by `dart run serious_python:gen_version_tables` from
# python-build manifest.json (release 20260720). Do not edit by hand.
# python-build manifest.json (release 20260725). Do not edit by hand.
default_python_version=3.14
dart_bridge_version=1.5.1
python_build_release_date=20260720
dart_bridge_version=1.6.1
python_build_release_date=20260725
3.12.full_version=3.12.13
3.12.android_abis=arm64-v8a,x86_64,armeabi-v7a
3.13.full_version=3.13.14
Expand Down
2 changes: 1 addition & 1 deletion src/serious_python_android/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: serious_python_android
description: Android implementation of the serious_python plugin
homepage: https://flet.dev
repository: https://github.com/flet-dev/serious-python
version: 4.3.6
version: 4.4.0

environment:
sdk: ">=3.0.0 <4.0.0"
Expand Down
5 changes: 5 additions & 0 deletions src/serious_python_darwin/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 4.4.0

* **iOS/macOS: `dart_bridge` now ships as a dynamic framework**, fixing built iOS apps crashing at startup with `Failed to lookup symbol 'serious_python_run': dlsym(RTLD_DEFAULT, serious_python_run): symbol not found`. `dart_bridge`'s FFI entry points (`serious_python_run`, `DartBridge_InitDartApiDL`, `DartBridge_EnqueueMessage`, `PyInit_dart_bridge`) are resolved at runtime via `dlsym` — from Dart through `DynamicLibrary.process()` and from Python through `import dart_bridge`. It previously shipped as a **static** archive linked into the host app *executable*, and an iOS executable exports nothing to the dynamic symbol table by default (the release build also strips local symbols), so those lookups failed. Only release/archive (device) builds were affected — debug/simulator builds don't dead-strip, so the failure did not reproduce there. Android was never affected: its `dart_bridge` is a dynamic `.so`, which exports its symbols. `dart_bridge` **1.6.1** now builds `dart_bridge.xcframework` as a dynamic framework (see flet-dev/dart-bridge#11 and #12), so it is embedded + signed into the app like `Python.xcframework` and its symbols stay exported. The SwiftPM `-all_load` / `-force_load` retention, the CocoaPods `-all_load`, and the plugin's dead-strip keep-alive references are all removed — a loaded image needs none of them.
* Re-pins the bundled python-build snapshot to **20260725** (`dart_bridge` **1.5.1 → 1.6.1**). Python versions (**3.12.13 / 3.13.14 / 3.14.6**) are unchanged and the iOS/macOS CPython runtimes are byte-identical to 20260720.

## 4.3.6

* Re-pins the bundled python-build snapshot to **20260720** (`dart_bridge` **1.5.0 → 1.5.1**). 1.5.1 is a Windows-only UTF-8 startup fix (see `serious_python_windows` 4.3.6); the iOS/macOS runtimes are functionally unchanged from 20260719.
Expand Down
6 changes: 3 additions & 3 deletions src/serious_python_darwin/darwin/python_versions.properties
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# GENERATED by `dart run serious_python:gen_version_tables` from
# python-build manifest.json (release 20260720). Do not edit by hand.
# python-build manifest.json (release 20260725). Do not edit by hand.
default_python_version=3.14
dart_bridge_version=1.5.1
python_build_release_date=20260720
dart_bridge_version=1.6.1
python_build_release_date=20260725
3.12.full_version=3.12.13
3.12.android_abis=arm64-v8a,x86_64,armeabi-v7a
3.13.full_version=3.13.14
Expand Down
16 changes: 9 additions & 7 deletions src/serious_python_darwin/darwin/serious_python_darwin.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#
Pod::Spec.new do |s|
s.name = 'serious_python_darwin'
s.version = '4.3.6'
s.version = '4.4.0'
s.summary = 'A cross-platform plugin for adding embedded Python runtime to your Flutter apps.'
s.description = <<-DESC
A cross-platform plugin for adding embedded Python runtime to your Flutter apps.
Expand All @@ -13,11 +13,10 @@ Pod::Spec.new do |s|
s.license = { :file => '../LICENSE' }
s.author = { 'Appveyor Systems Inc.' => 'hello@flet.dev' }
s.source = { :path => '.' }
# dart_bridge.xcframework (vendored below) contains static .a archives.
# CocoaPods 1.16+ refuses to install vendored xcframeworks with static
# libraries unless the consuming pod is itself declared as a static
# framework. Python.xcframework is also static, so this was always
# implicitly the case — just being explicit now.
# This pod's own Swift code links as a static framework. The vendored
# xcframeworks below (Python, dart_bridge) are dynamic frameworks — they are
# embedded and signed into the host app, and their symbols stay exported so
# the dlsym lookups Dart and Python perform at runtime resolve.
s.static_framework = true
s.source_files = ['serious_python_darwin/Sources/serious_python_darwin/**/*.swift']
s.ios.dependency 'Flutter'
Expand All @@ -26,9 +25,12 @@ Pod::Spec.new do |s|
s.osx.deployment_target = '11.0'

# Flutter.framework does not contain a i386 slice.
# `-all_load` is no longer needed: it existed to retain the static
# libdart_bridge.a against -dead_strip, and dart_bridge now ships as a dynamic
# framework whose symbols are exported from its own loaded image.
s.pod_target_xcconfig = {
'DEFINES_MODULE' => 'YES',
'OTHER_LDFLAGS' => '-ObjC -all_load -lc++'
'OTHER_LDFLAGS' => '-ObjC -lc++'
}
s.swift_version = '5.0'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,36 +44,20 @@ func staged(_ rel: String) -> Bool {
var binaryTargets: [Target] = []
var deps: [Target.Dependency] = [.product(name: "FlutterFramework", package: "FlutterFramework")]

// dart_bridge: static archive, link-only. Its FFI exports are resolved at runtime
// via dlsym (Dart's `DynamicLibrary.process()` and Python's `import dart_bridge`),
// so the whole archive must be retained against the linker's -dead_strip. We
// force-load JUST this archive rather than using a global -all_load: SwiftPM
// applies a target's linkerSettings to the FINAL app executable link, so a global
// -all_load there would force-load EVERY static archive on the link line —
// including plugins that statically bundle the same third-party code (e.g.
// flet-video/media_kit and flet-rive both embed miniaudio), which then collide
// with thousands of duplicate symbols. -force_load on dart_bridge alone mirrors
// what the CocoaPods podspec did via `pod_target_xcconfig` (scoped to this pod's
// own inputs — only dart_bridge among them is a static archive). On macOS the
// xcframework has a single universal slice with a stable path, so we resolve it
// here. On iOS, plugin pods link as dynamic frameworks (no static-archive
// collision) and device/simulator use different slice paths, so the
// slice-agnostic -all_load is kept there (see linkerSettings below).
var macosDartBridgeForceLoad: [String] = []
// dart_bridge: DYNAMIC framework -> embedded + auto-signed in the app. Its FFI
// entry points (serious_python_run, DartBridge_*, PyInit_dart_bridge) are
// EXPORTED from the loaded framework, so the dlsym lookups that Dart
// (`DynamicLibrary.process()`) and Python (`import dart_bridge`) perform resolve
// at runtime. A static library would instead be linked into the app executable,
// which does not export its symbols (and the release build strips them), so
// dlsym would fail with "Failed to lookup symbol 'serious_python_run'". Shipping
// it dynamic mirrors the Android `.so` (CMake SHARED) that already works — see
// flet-dev/dart-bridge apple/build_xcframework.sh. No -all_load/-force_load or
// keep-alive is needed anymore: the framework is a loaded image, not archive
// members that must be pulled in and kept against -dead_strip.
if staged("dart_bridge.xcframework") {
binaryTargets.append(.binaryTarget(name: "dart_bridge", path: "dart_bridge.xcframework"))
deps.append("dart_bridge")
let xc = pkgDir.appendingPathComponent("dart_bridge.xcframework")
if let slices = try? FileManager.default.contentsOfDirectory(
at: xc, includingPropertiesForKeys: nil) {
for slice in slices.sorted(by: { $0.lastPathComponent < $1.lastPathComponent })
where slice.lastPathComponent.hasPrefix("macos") {
let lib = slice.appendingPathComponent("libdart_bridge.a")
if FileManager.default.fileExists(atPath: lib.path) {
macosDartBridgeForceLoad = ["-force_load", lib.path]
}
}
}
}
// Python.framework: dynamic -> embedded + auto-signed. iOS and macOS ship separate
// xcframeworks, so each is platform-conditional.
Expand Down Expand Up @@ -119,15 +103,9 @@ let package = Package(
.copy("Resources/app"),
],
linkerSettings: [
// dart_bridge is now a dynamic framework (embedded, symbols
// exported), so no -all_load / -force_load is needed to retain it.
.unsafeFlags(["-ObjC"]),
// iOS: slice-agnostic -all_load. Plugin pods link as dynamic
// frameworks, so dart_bridge is the only static archive in scope —
// nothing else to double-load. (device/simulator slice paths differ,
// so a single -force_load path isn't possible here.)
.unsafeFlags(["-all_load"], .when(platforms: [.iOS])),
// macOS: force-load ONLY dart_bridge (resolved above), never a global
// -all_load — see the note where macosDartBridgeForceLoad is built.
.unsafeFlags(macosDartBridgeForceLoad, .when(platforms: [.macOS])),
.linkedLibrary("c++"),
]
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,13 @@ import UIKit
import FlutterMacOS
#endif

// Keep-alive references to dart_bridge.xcframework's C entry points. Dart
// resolves them via `DynamicLibrary.process()` at runtime; without these
// static references the host app's linker `-dead_strip` pass could drop
// them even though `-all_load` pulled the archive members in.
@_silgen_name("serious_python_run")
private func _sp_run_keepalive(_ cfg: OpaquePointer?) -> Int32
@_silgen_name("DartBridge_InitDartApiDL")
private func _sp_init_keepalive(_ data: UnsafeMutableRawPointer?) -> Int
@_silgen_name("DartBridge_EnqueueMessage")
private func _sp_enqueue_keepalive(_ data: UnsafePointer<CChar>?, _ len: Int)

// multiprocessing child-interception entry points,
// dlsym'd by the host app's main.swift before NSApplicationMain.
@_silgen_name("serious_python_is_mp_invocation")
private func _sp_is_mp_keepalive(
_ argc: Int32, _ argv: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32
@_silgen_name("serious_python_main")
private func _sp_main_keepalive(
_ argc: Int32, _ argv: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32
// dart_bridge's C entry points (serious_python_run, DartBridge_*,
// serious_python_{is_mp_invocation,main}) used to need keep-alive references
// here: it shipped as a static archive linked into the host app executable,
// which exports nothing, so the dlsym lookups Dart (`DynamicLibrary.process()`)
// and the macOS host's main.swift perform could fail. dart_bridge now ships as a
// dynamic framework (embedded + signed) whose symbols are exported from its own
// loaded image, so no keep-alive is required — see dart-bridge 1.6.0.

/// Thin Flutter plugin: surfaces the python.bundle resource path to Dart.
/// All Python lifecycle now lives in `serious_python_run`
Expand All @@ -42,17 +28,6 @@ public class SeriousPythonPlugin: NSObject, FlutterPlugin {
let channel = FlutterMethodChannel(name: "serious_python", binaryMessenger: messenger)
let instance = SeriousPythonPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)

// Static reference to dart_bridge symbols — see top-of-file note.
// The branch is unreachable at runtime (the registrar argument is
// always non-nil), but the linker only sees a live call site.
if unsafeBitCast(registrar, to: Int.self) == 0 {
_ = _sp_run_keepalive(nil)
_ = _sp_init_keepalive(nil)
_sp_enqueue_keepalive(nil, 0)
_ = _sp_is_mp_keepalive(0, nil)
_ = _sp_main_keepalive(0, nil)
}
}

public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
Expand Down
2 changes: 1 addition & 1 deletion src/serious_python_darwin/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: serious_python_darwin
description: iOS and macOS implementations of the serious_python plugin
homepage: https://flet.dev
repository: https://github.com/flet-dev/serious-python
version: 4.3.6
version: 4.4.0

environment:
# The Swift Package Manager build path needs Flutter 3.44 / Dart 3.11 (the
Expand Down
4 changes: 4 additions & 0 deletions src/serious_python_linux/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 4.4.0

* Re-pins the bundled python-build snapshot to **20260725** (`dart_bridge` **1.5.1 → 1.6.1**). 1.6.1 is an Apple-only packaging change (see `serious_python_darwin` 4.4.0); the Linux runtime is byte-identical to 20260720.

## 4.3.6

* Re-pins the bundled python-build snapshot to **20260720** (`dart_bridge` **1.5.0 → 1.5.1**). 1.5.1 is a Windows-only UTF-8 startup fix (see `serious_python_windows` 4.3.6); the Linux runtime is functionally unchanged from 20260719.
Expand Down
6 changes: 3 additions & 3 deletions src/serious_python_linux/linux/python_versions.properties
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# GENERATED by `dart run serious_python:gen_version_tables` from
# python-build manifest.json (release 20260720). Do not edit by hand.
# python-build manifest.json (release 20260725). Do not edit by hand.
default_python_version=3.14
dart_bridge_version=1.5.1
python_build_release_date=20260720
dart_bridge_version=1.6.1
python_build_release_date=20260725
3.12.full_version=3.12.13
3.12.android_abis=arm64-v8a,x86_64,armeabi-v7a
3.13.full_version=3.13.14
Expand Down
2 changes: 1 addition & 1 deletion src/serious_python_linux/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: serious_python_linux
description: Linux implementations of the serious_python plugin
homepage: https://flet.dev
repository: https://github.com/flet-dev/serious-python
version: 4.3.6
version: 4.4.0

environment:
sdk: '>=3.1.3 <4.0.0'
Expand Down
4 changes: 4 additions & 0 deletions src/serious_python_platform_interface/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 4.4.0

* Version bump aligning with the `serious_python_*` 4.4.0 release.

## 4.3.6

* Version bump aligning with the `serious_python_*` 4.3.6 release (Windows UTF-8 startup fix via `dart_bridge` 1.5.1). No interface changes.
Expand Down
2 changes: 1 addition & 1 deletion src/serious_python_platform_interface/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: serious_python_platform_interface
description: A common platform interface for the serious_python plugin.
homepage: https://flet.dev
repository: https://github.com/flet-dev/serious-python
version: 4.3.6
version: 4.4.0

environment:
sdk: ">=3.0.0 <4.0.0"
Expand Down
4 changes: 4 additions & 0 deletions src/serious_python_windows/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 4.4.0

* Re-pins the bundled python-build snapshot to **20260725** (`dart_bridge` **1.5.1 → 1.6.1**). 1.6.1 is an Apple-only packaging change (see `serious_python_darwin` 4.4.0); the Windows runtime is byte-identical to 20260720.

## 4.3.6

* **Fix Windows startup when app paths or environment values contain non-ASCII characters.** Dart FFI strings arrive as UTF-8, but the Windows boundary passed them straight to the ANSI CRT (`_putenv_s`, `fopen`), corrupting paths/env values through the process code page. The runtime now converts UTF-8 → UTF-16 and uses the wide CRT APIs (`_wputenv_s`, `_wfopen_s`), enables Python UTF-8 mode before `Py_Initialize()`, sets `PYTHONUTF8=1` for multiprocessing helpers, and treats embedded env-setup failures as fatal. Delivered via `dart_bridge` **1.5.1** (bundled python-build snapshot re-pinned to **20260720**). ([flet-dev/flet#6641](https://github.com/flet-dev/flet/issues/6641))
Expand Down
2 changes: 1 addition & 1 deletion src/serious_python_windows/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: serious_python_windows
description: Windows implementations of the serious_python plugin
homepage: https://flet.dev
repository: https://github.com/flet-dev/serious-python
version: 4.3.6
version: 4.4.0

environment:
sdk: '>=3.1.3 <4.0.0'
Expand Down
6 changes: 3 additions & 3 deletions src/serious_python_windows/windows/python_versions.properties
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# GENERATED by `dart run serious_python:gen_version_tables` from
# python-build manifest.json (release 20260720). Do not edit by hand.
# python-build manifest.json (release 20260725). Do not edit by hand.
default_python_version=3.14
dart_bridge_version=1.5.1
python_build_release_date=20260720
dart_bridge_version=1.6.1
python_build_release_date=20260725
3.12.full_version=3.12.13
3.12.android_abis=arm64-v8a,x86_64,armeabi-v7a
3.13.full_version=3.13.14
Expand Down
Loading