Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
114 commits
Select commit Hold shift + click to select a range
8458555
Add serious_python_bridge plugin: dart_bridge C source + Python exten…
FeodorFitsner Jun 8, 2026
2b7733c
Add registerPythonExtension API to serious_python_darwin
FeodorFitsner Jun 8, 2026
9a33d58
Scaffold serious_python_bridge Flutter plugin: pubspec + Dart PythonB…
FeodorFitsner Jun 8, 2026
7044e7f
Add Linux + Windows CMakeLists for libflet_bridge
FeodorFitsner Jun 8, 2026
84f5a2c
Add darwin podspec + Swift plugin class registering dart_bridge inittab
FeodorFitsner Jun 8, 2026
6bdb507
Add Android NDK build for libflet_bridge
FeodorFitsner Jun 8, 2026
77c8010
Add bridge example app (Flutter UI + Python echo)
FeodorFitsner Jun 8, 2026
473aa7c
Add release CI: cibuildwheel wheels + GitHub Release attachment
FeodorFitsner Jun 8, 2026
e17bc8f
Switch dart_bridge to abi3 + add Android NDK cross-build matrix
FeodorFitsner Jun 8, 2026
69277f3
Add throwaway test-bridge-build workflow
FeodorFitsner Jun 8, 2026
07cc7ab
Fix Android cross-build: switch to mobile-forge tarball for headers
FeodorFitsner Jun 8, 2026
22114e8
Bridge round-trip end-to-end working on macOS (1KB integration test p…
FeodorFitsner Jun 8, 2026
60e2966
Wire bridge example into CI as a macOS matrix job
FeodorFitsner Jun 8, 2026
63c4fb9
Speed up dart-bridge iteration: scope ci.yml to main + add macOS brid…
FeodorFitsner Jun 8, 2026
08bbadc
Add iOS/Linux/Windows bridge example tests + bump Flutter to 3.41.7
FeodorFitsner Jun 8, 2026
446434e
Fix iOS + Linux/Windows bridge_example CI failures
FeodorFitsner Jun 8, 2026
e172cb9
Fix Linux/Windows bridge_example: skip 32-bit wheels + link Windows a…
FeodorFitsner Jun 8, 2026
5c3f559
Fix i686 skip pattern: *_i686 (not *-linux_i686)
FeodorFitsner Jun 9, 2026
e3aa566
Mark Linux/Windows bridge_example continue-on-error (symbol-split def…
FeodorFitsner Jun 9, 2026
1b86e28
Shim split: dart_bridge.c (Dart-callable) + dart_bridge_shim.c (Pytho…
FeodorFitsner Jun 9, 2026
e934616
Fix Windows + add Android + fix Linux ARM64 matrix
FeodorFitsner Jun 9, 2026
58059a3
Add missing Android upload-artifact + Windows shim diagnostics
FeodorFitsner Jun 9, 2026
43aab91
Windows shim: search exe dir for flet_bridge.dll when default path fails
FeodorFitsner Jun 9, 2026
8ed03fa
Windows shim file-log + Android cd-each-line + diagnostics step
FeodorFitsner Jun 9, 2026
e862690
Fix Windows bundling (python3.dll) + Android tarball (mobile-forge + …
FeodorFitsner Jun 9, 2026
9abcf40
Windows shim: log to %TEMP%, add PyInit-entry marker, diagnostics ste…
FeodorFitsner Jun 9, 2026
4d07051
Windows shim log goes next to .exe + direct import test in diagnostics
FeodorFitsner Jun 9, 2026
c0901ee
Windows: use --profile to match abi3 wheel; Android: log extractPytho…
FeodorFitsner Jun 9, 2026
dd7b729
Windows: always ship Release Python; Android: thread PYTHON_DIST_DIR
FeodorFitsner Jun 9, 2026
af1a620
Windows: /NODEFAULTLIB:pythonXY_d.lib; Android: hoist pythonStagingRoot
FeodorFitsner Jun 9, 2026
9a2344b
Windows: ship both Release+Debug dart_bridge.pyd in the wheel
FeodorFitsner Jun 9, 2026
20f39f4
Debug wheel build: add /LIBPATH so pyconfig.h auto-link finds python3…
FeodorFitsner Jun 9, 2026
193d099
Widen dart_bridge.pyd search inside wheel + log unpacked tree for dia…
FeodorFitsner Jun 9, 2026
c36ce41
Name Debug variant dart_bridge_d.pyd (no version tag) so Python 3.13/…
FeodorFitsner Jun 9, 2026
d0b0b11
Android: add post-failure diagnostics inspecting jniLibs, intermediat…
FeodorFitsner Jun 9, 2026
fa6c44d
Android example: set extractNativeLibs=true so libpython3.X.so lands …
FeodorFitsner Jun 9, 2026
4d9acab
Android cross-build: compile dart_bridge_shim.c (not dart_bridge.c)
FeodorFitsner Jun 9, 2026
4419332
Android: don't link libpython into dart_bridge.abi3.so
FeodorFitsner Jun 9, 2026
f3c7fd1
Android: cross-build matrix expands to abi × python_version
FeodorFitsner Jun 9, 2026
74ae640
Android cross-build: drop hardcoded .13 patch version; use wildcards
FeodorFitsner Jun 9, 2026
b5c2c26
iOS: cache python-ios-dart framework + log step timestamps
FeodorFitsner Jun 9, 2026
1092769
Darwin: absorb Python lifecycle into dart_bridge.xcframework + add Py…
FeodorFitsner Jun 10, 2026
d0997a7
Move + rewrite bridge_example for PythonBridge API
FeodorFitsner Jun 10, 2026
ae8d265
Declare serious_python_darwin as static_framework for xcframework ven…
FeodorFitsner Jun 10, 2026
f5e71f0
Pin DART_BRIDGE_VERSION to 1.2.1 (uniform .a names across xcframework…
FeodorFitsner Jun 10, 2026
5703baa
PythonBridge.send: don't throw on Py_Initialize-not-yet (rc=-2)
FeodorFitsner Jun 10, 2026
a4ece59
Android: absorb Python lifecycle into libdart_bridge.so
FeodorFitsner Jun 10, 2026
c28bec3
Windows: absorb Python lifecycle into dart_bridge[_d].dll
FeodorFitsner Jun 10, 2026
195b604
Windows: robustify CopyPythonDLLs target + verbose flutter test
FeodorFitsner Jun 10, 2026
07dea03
Windows: skip site-packages copy when source dir is missing + propagate
FeodorFitsner Jun 10, 2026
5410eb6
Drop Windows-specific script:\"\" hack from SeriousPython.run
FeodorFitsner Jun 10, 2026
cea8ffb
Re-enable macOS/iOS/Android jobs in test-bridge-build (Windows is gre…
FeodorFitsner Jun 10, 2026
41751be
Linux: absorb Python lifecycle into libdart_bridge.so
FeodorFitsner Jun 10, 2026
398140b
Pin DART_BRIDGE_VERSION to 1.2.3 for Linux (DT_RPATH $ORIGIN)
FeodorFitsner Jun 10, 2026
b924527
Re-enable all platforms in test-bridge-build (Linux is green now)
FeodorFitsner Jun 10, 2026
992ffab
Phase 2: delete serious_python_bridge plugin, fold bridge_example int…
FeodorFitsner Jun 10, 2026
11333b5
flet_example: bump flet ^0.28.3 → ^0.85.3 + rename version → flet_ver…
FeodorFitsner Jun 10, 2026
4bb8dd6
flet_example: align with flet 0.85.x API
FeodorFitsner Jun 10, 2026
a7594bc
flet_example: bump package_info_plus to ^9.0.0 + regenerate plugin re…
FeodorFitsner Jun 10, 2026
1059ad1
ci: bump --requirements flet pin 0.28.3 → 0.85.3 (match Dart side)
FeodorFitsner Jun 10, 2026
5c6bd9e
ci: iOS simulator os_version → 'latest' (macos-26 no longer has iOS 1…
FeodorFitsner Jun 10, 2026
ec963aa
ci: drop flet version pin (use latest)
FeodorFitsner Jun 10, 2026
860aa09
ci: iOS simulator → iPhone 17 Pro Max / iOS 26.5 (matches macos-26 de…
FeodorFitsner Jun 11, 2026
34714f5
flet_example: use ValueKey explicitly in integration test (matches Fl…
FeodorFitsner Jun 11, 2026
cf5f1a3
flet_example: find by icon, not key (Flet's ValueKey<Object> doesn't …
FeodorFitsner Jun 11, 2026
399c754
flet_example: pin flet to git dart-bridge branch (carries ValueKey<T>…
FeodorFitsner Jun 11, 2026
6401883
flet_example/android: bump Kotlin Gradle Plugin 1.8.22 → 2.1.0
FeodorFitsner Jun 11, 2026
9441342
flet_example/android: bump AGP to 8.9.1 + Gradle to 8.11.1
FeodorFitsner Jun 11, 2026
fb53f65
flet_example/android: bump KGP 2.1.0 → 2.3.21 (match screen_brightnes…
FeodorFitsner Jun 11, 2026
300cd94
flet_example/android: extractNativeLibs=true (match bridge_example)
FeodorFitsner Jun 11, 2026
47d7cab
ci: pin FLET_VERSION + scope flet_example matrix to Python 3.12
FeodorFitsner Jun 11, 2026
36fb334
ci: limit only iOS+Android flet_example matrices to 3.12 (msgpack whe…
FeodorFitsner Jun 11, 2026
dc6a265
ci: unify test job naming → 'Test <Flet|Bridge> example on <platform>…
FeodorFitsner Jun 11, 2026
8a90cbc
ci: add concurrency group (cancel in-flight runs on same ref, preserv…
FeodorFitsner Jun 11, 2026
1588d71
add flet_ffi_example exercising dart_bridge FFI transport
FeodorFitsner Jun 11, 2026
d58f22f
ci: add flet_ffi_example matrix; gate other apps off for now
FeodorFitsner Jun 11, 2026
5137fd0
ci: pre-clone flet shallowly; surface package-command progress
FeodorFitsner Jun 11, 2026
ce5942d
ci: drop the shallow-clone workaround, pip git source install was fine
FeodorFitsner Jun 11, 2026
0ee751d
Revert "ci: drop the shallow-clone workaround, pip git source install…
FeodorFitsner Jun 11, 2026
a3ce583
ci: expand iOS/Android matrices to 3.12+3.13+3.14
FeodorFitsner Jun 11, 2026
1dd6ae9
ci: un-gate flet_ffi_macos/ios/android/linux now that Windows is green
FeodorFitsner Jun 11, 2026
6a87c5e
cache downloads in ~/.flet/cache (FLET_CACHE_DIR) across all plugins
FeodorFitsner Jun 11, 2026
ddf8829
windows: copy cached dart_bridge DLLs to stable name in build tree
FeodorFitsner Jun 11, 2026
fdb3bf7
darwin: stop leaking the .pod symlink into the production .app
FeodorFitsner Jun 11, 2026
2dd3c96
darwin: strip embedded Python.app from Python.framework
FeodorFitsner Jun 11, 2026
cf66671
ci: bump actions/checkout v4 -> v6 (Node 24)
FeodorFitsner Jun 11, 2026
4329b6b
Bump 3.13.14 / 3.14.6 / Pyodide 314.0.0; migrate to date-based python…
FeodorFitsner Jun 12, 2026
9b4fa87
Add `serious_python:main version [--json]` + CI vars from registry
FeodorFitsner Jun 12, 2026
e9f8d30
fix(ci): strip `dart run`'s build preamble before piping JSON into jq
FeodorFitsner Jun 12, 2026
35d112e
Bump astral-sh/setup-uv to v8.2.0
FeodorFitsner Jun 12, 2026
e9611ab
Some examples cleanup
FeodorFitsner Jun 12, 2026
f10dc84
ci(resolve-python-vars): surface dart run stdout+stderr for diagnosis
FeodorFitsner Jun 12, 2026
4d69b4b
fix(ci): strip dart-run noise via byte-level `{` extraction, not line…
FeodorFitsner Jun 12, 2026
96507e6
Bump actions/cache to v5 in CI workflow
FeodorFitsner Jun 12, 2026
d342a1f
Bump simulator-action to v5
FeodorFitsner Jun 12, 2026
97ad66a
consolidate examples; extend bridge_example with perf + memory tests
FeodorFitsner Jun 12, 2026
b4eee25
bridge_example: README covers two-channel design + perf/memory baseline
FeodorFitsner Jun 12, 2026
5c7c940
ci(bridge_example): invoke each integration test file separately
FeodorFitsner Jun 12, 2026
abca892
ci(linux): share a single Xvfb across the three test invocations
FeodorFitsner Jun 12, 2026
5729e49
Add docs for dedicated data channels
FeodorFitsner Jun 13, 2026
fc0f486
ci: silence the two Node 20 deprecation warnings
FeodorFitsner Jun 13, 2026
212efe7
ci: pin Windows runner to windows-2025-vs2026
FeodorFitsner Jun 13, 2026
91bd28f
remove scaffold getPlatformVersion across all platform plugins
FeodorFitsner Jun 13, 2026
3d9c505
Centralize Python runtime versions via a date-keyed manifest
FeodorFitsner Jun 14, 2026
0e50c20
Upgrade to Flutter 3.44.2 across the repo
FeodorFitsner Jun 14, 2026
1fbf829
Drop bare in-place version-switching machinery
FeodorFitsner Jun 15, 2026
91a14b2
Prepare 3.0.0 release
FeodorFitsner Jun 15, 2026
473a8c0
feat: FFI bindings for libdart_bridge 1.3.0 session-restart + native log
FeodorFitsner Jun 16, 2026
befc364
Bump dart_bridge to 1.3.0 across all platform plugins
FeodorFitsner Jun 16, 2026
4294a47
Bump dart_bridge to 1.3.1 across all platform plugins
FeodorFitsner Jun 16, 2026
6d96274
Bump dart_bridge to 1.3.2
FeodorFitsner Jun 16, 2026
6794126
Android: memory-map native modules from the APK (drop fake-.so / useL…
FeodorFitsner Jun 17, 2026
ab92a09
Remove the Android x86 (32-bit Intel) ABI
FeodorFitsner Jun 17, 2026
3633b78
Complete the 3.0.0 CHANGELOGs
FeodorFitsner Jun 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 2 additions & 2 deletions .fvmrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"flutter": "3.29.3"
}
"flutter": "3.44.2"
}
382 changes: 290 additions & 92 deletions .github/workflows/ci.yml

Large diffs are not rendered by default.

11 changes: 10 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,13 @@ dist/

# Python
.venv/
__pypackages__/
__pypackages__/
*.so
*.pyd
*.dylib
*.dll
*.egg-info/
*.egg/

# FVM
.fvm/
29 changes: 29 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,35 @@ Bump `serious_python` dependency version with `flutter pub get` in example lock

Update `CHANGELOG.md`.

## Python runtime versions

The supported Python versions, their CPython / Pyodide / dart_bridge details, and
which versions get built are all defined in a single source of truth: the
**`manifest.json`** in [`flet-dev/python-build`](https://github.com/flet-dev/python-build),
published as an asset on each date-keyed (`YYYYMMDD`) release.

serious_python pins one release date (`pythonReleaseDate`) and commits generated
snapshots of that manifest — **never hand-edit these**:

* `src/serious_python/lib/src/python_versions.dart` — read by the CLI commands
* `python_versions.properties` in each platform package — read by the
Android / Darwin / Linux / Windows build configs

To add or bump a Python / Pyodide / dart_bridge version:

1. Edit `manifest.json` in python-build and cut a new release (run the **Build
Python Packages** workflow with a `YYYYMMDD` `release_date`).
2. Regenerate the snapshots from the new release:

```
cd src/serious_python
dart run serious_python:gen_version_tables --release-date <YYYYMMDD>
```

(Omit `--release-date` to re-fetch the currently pinned release.)
3. Commit the regenerated `python_versions.dart` and `python_versions.properties`
files. CI's **Version tables in sync with manifest** job fails if they drift.

## Getting token for automatic publishing to pub.dev

Token locations on different OSes: https://stackoverflow.com/a/70487480/1435891
Expand Down
376 changes: 376 additions & 0 deletions docs/dedicated-data-channels.md

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions src/serious_python/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
## 3.0.0

* **New in-process transport (dart_bridge FFI).** `SeriousPython.run` can now run the embedded interpreter **in-process** through the `dart_bridge` FFI bridge instead of talking to it over a socket. The Python lifecycle (initialize / run / teardown) is absorbed into the `dart_bridge` native library on every platform — `dart_bridge.xcframework` (iOS/macOS), `libdart_bridge.so` (Android/Linux), and `dart_bridge.dll` / `dart_bridge.pyd` (Windows) — and a new `PythonBridge` API exposes a MsgPack control channel plus dedicated binary data channels between Dart and Python. See the `bridge_example` app. The bundled `dart_bridge` is **1.4.0**.
* **Android native packaging — memory-mapped from the APK.** Python extension modules are relocated into `jniLibs` and loaded **directly from the APK** (mmap, no extraction) by a custom importer that resolves them from `.soref` markers; pure Python ships in stored, ABI-common asset zips read via `zipimport` (no per-ABI duplication). Apps no longer need `useLegacyPackaging` / `keepDebugSymbols` — the brittle per-app packaging config is gone. Set **`SERIOUS_PYTHON_ANDROID_EXTRACT_PACKAGES`** (comma-separated relative paths) to ship path-hungry packages extracted to disk. The dart-bridge Android binary uses the full CPython API (`PyConfig`) to install the importer before `site` runs.
* **Breaking change:** requires Flutter **3.44.2** / Dart 3.12+. The Android plugin moves to AGP **8.11.1**, `compileSdk` **36**, Java **17**, and the Kotlin-DSL Gradle build (`build.gradle.kts`).
* Python runtime versions are now a committed snapshot of `flet-dev/python-build`'s date-keyed `manifest.json`, generated by `dart run serious_python:gen_version_tables`. **`SERIOUS_PYTHON_VERSION`** (short, e.g. `3.14`) is the single input — the full CPython version, python-build release date, Pyodide version + platform tag, and `dart_bridge` version all derive from it. `SERIOUS_PYTHON_FULL_VERSION`, `SERIOUS_PYTHON_BUILD_DATE`, and `DART_BRIDGE_VERSION` remain as rarely-needed escape hatches. The native build configs (Android `build.gradle`, Darwin podspec, Linux/Windows `CMakeLists.txt`) read the generated `python_versions.properties`, and a CI job fails if the snapshots drift from the manifest. This replaces the per-config hardcoded defaults and the `flet build`-exported `SERIOUS_PYTHON_FULL_VERSION` / `SERIOUS_PYTHON_BUILD_DATE` introduced in 2.0.0.
* Bundle **3.12.13 / 3.13.14 / 3.14.6** (python-build `20260614`); Pyodide **0.27.7 / 0.29.4 / 314.0.0** (314.0.0 GA, up from the 314.0.0a2 in 2.0.0).
* Add **`dart run serious_python:main version [--json]`** — prints the serious_python version, the pinned python-build release, and the supported Python / Pyodide / dart_bridge matrix.
* The embedded Darwin runtime re-extracts when the selected Python version changes (a version marker guards `dist_ios` / `dist_macos`), so a clean build after switching `--python-version` can't mix C-extension ABIs (`bad magic number` / `unknown slot ID`).
* Cache downloaded Python distributions and `dart_bridge` artifacts under `$FLET_CACHE_DIR` (default `~/.flet/cache`) across all platforms.
* Remove the scaffold `getPlatformVersion` method from the platform plugins.
* Drop the `x86` (32-bit Intel) Android ABI — Flutter no longer produces it. Android builds target `arm64-v8a` + `x86_64` (plus `armeabi-v7a` on Python 3.12); the `x86` wheel platform-tag entry and the Android packaging rules referencing it are removed.
* **Breaking change:** the `configure` command (and the bare in-place version-switching machinery, including `stageDarwinRuntime`) is removed. Switching the bundled Python version between builds is now handled by a clean rebuild — `flet build` wipes its build dir on a version change, and the Darwin `dist_ios` / `dist_macos` version marker re-extracts the runtime — so a separate `serious_python configure` step is no longer needed.
* **Bug fix:** the Pyodide 0.29 wheel platform tag for the 3.13 row was `pyodide-2025.0-wasm32`, but Pyodide publishes 0.29 wheels under `pyemscripten_2025_0_wasm32`; corrected to `pyemscripten-2025.0-wasm32` so `flet build web --python-version 3.13` matches native wheels.

## 2.0.0

* **Breaking change:** the `package` command's default Python is now the latest supported stable (3.14), up from the previously implicit 3.12. Scripts that ran `dart run serious_python:main package …` without `--python-version` will now download CPython 3.14, install 3.14 wheels, and use the matching Pyodide / Android platform tags. Pin explicitly with `--python-version 3.12` (or `SERIOUS_PYTHON_VERSION=3.12`) to preserve the old behavior.
Expand Down
93 changes: 73 additions & 20 deletions src/serious_python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,33 @@ the `--python-version X.Y` flag of `serious_python:main package` (or the
plugin build scripts). Defaults to the latest supported version when nothing
is specified.

| Short | CPython runtime | Pyodide (web) | Pyodide wheel platform tag |
| ----- | --------------- | ------------- | -------------------------- |
| 3.12 | 3.12.13 | 0.27.7 | `pyodide-2024.0-wasm32` |
| 3.13 | 3.13.13 | 0.29.4 | `pyodide-2025.0-wasm32` |
| 3.14 | 3.14.5 | 314.0.0a2 | `pyemscripten-2026.0-wasm32`|
| Short | CPython runtime | Pyodide (web) | Pyodide wheel platform tag |
| ----- | --------------- | ------------- | -------------------------------- |
| 3.12 | 3.12.13 | 0.27.7 | `pyodide-2024.0-wasm32` |
| 3.13 | 3.13.14 | 0.29.4 | `pyemscripten-2025.0-wasm32` |
| 3.14 | 3.14.6 | 314.0.0 | `pyemscripten-2026.0-wasm32` |

The default is the latest stable row (currently **3.14**) when neither
`--python-version` nor `SERIOUS_PYTHON_VERSION` is set. When running through
[`flet build`](https://flet.dev/docs/publish/), the same resolution is
applied to `[project].requires-python` in your `pyproject.toml`, so most
users never need to touch this flag directly.

Source of truth: the `_pythonReleases` map in
[`bin/package_command.dart`](bin/package_command.dart) (Dart side) and
`flet_cli/utils/python_versions.py` (Python side). Adding a new short version
means appending a row to both. Pre-release CPython lines (e.g. 3.15) can be
listed with `prerelease: true` so they're opt-in via explicit
`--python-version 3.15` (or `requires-python = "==3.15.*"`) without becoming
`SERIOUS_PYTHON_VERSION` (short, e.g. `3.14`) is the only input you set — the
full version, python-build release date, Pyodide version/tag, and dart_bridge
version all derive from it. (`SERIOUS_PYTHON_FULL_VERSION`,
`SERIOUS_PYTHON_BUILD_DATE`, `DART_BRIDGE_VERSION` exist as rarely-needed escape
hatches.) A single `export SERIOUS_PYTHON_VERSION=3.13` covers both the
packaging phase and the later Flutter build.

Source of truth: the date-keyed `manifest.json` published by
[`flet-dev/python-build`](https://github.com/flet-dev/python-build).
serious_python pins one release and commits generated snapshots of it —
`lib/src/python_versions.dart` (used by the CLI) and a `python_versions.properties`
in each platform package (read by the native build configs). To bump versions
see [CONTRIBUTING.md](CONTRIBUTING.md); never hand-edit the generated files.
Pre-release CPython lines are marked `prerelease: true`, so they're opt-in via
explicit `--python-version` (or `requires-python = "==3.15.*"`) without becoming
the auto-resolved default.

## Usage
Expand Down Expand Up @@ -142,6 +151,11 @@ read by each platform plugin's build script (`build.gradle`, the
phase and the Flutter build phase. See the [Python versions](#python-versions)
table above for the matching CPython and Pyodide releases.

> **Note:** changing the bundled Python version for an app you've already built
> requires a clean build (delete the app's `build/` directory, or run
> `flutter clean`) so stale compiled bytecode from the previous version isn't
> reused.

#### Installing requirements

Python app dependencies are installed with the `--requirements` option (alias `-r`). The value is passed verbatim to `pip`, so any flag pip accepts works. Pass each dependency as its own option to support specifiers that contain commas:
Expand Down Expand Up @@ -193,6 +207,47 @@ Additional Python binary packages for iOS and Android can be built with adding a

Request additional packages for iOS and Android on [Flet Discussions - Packages](https://github.com/flet-dev/flet/discussions/categories/packages).

## How packaging works

`dart run serious_python:main package` assembles two things, which the platform plugin then bundles into your Flutter app:

1. **The CPython runtime + standard library** — a per-target build downloaded from [flet-dev/python-build](https://github.com/flet-dev/python-build) (and, for native extensions, [mobile-forge](https://github.com/flet-dev/mobile-forge)) and bundled by the plugin at build time.
2. **Your app + its dependencies** — your Python sources are zipped into an **asset** (`app/app.zip` by default), and `pip`-installed packages are placed where each platform expects them.

At runtime the plugin sets `PYTHONHOME` / `PYTHONPATH` (or, on Android, installs a custom importer) so the interpreter finds the stdlib, your dependencies, and your app.

The on-disk layout differs per platform, mostly because each OS has different rules for shipping **native (compiled) extension modules** — the `.so`/`.pyd`/`.dylib` files inside packages like `numpy`:

| Platform | Standard library | Site-packages (deps) | Native extension modules | Architectures |
| --- | --- | --- | --- | --- |
| **Android** | `stdlib.zip` asset, read via `zipimport` | `sitepackages.zip` asset, read via `zipimport` | relocated to `jniLibs/<abi>/`, **memory-mapped from the APK** (no extraction), resolved by a custom importer | natives per-ABI in `jniLibs`; pure zips are ABI-common (shipped once) |
| **iOS** | dir inside the framework resource bundle | dir inside the framework resource bundle | each `.so` wrapped in a signed `.framework` inside an `.xcframework`, loaded via CPython's `AppleFrameworkLoader` (`.fwork` markers) | device `arm64` + simulator `arm64`/`x86_64` xcframework slices |
| **macOS** | dir inside the framework resource bundle | dir (universal) | universal (`lipo`'d `arm64`+`x86_64`) `.so`, loaded directly | `arm64`+`x86_64` merged into fat binaries |
| **Linux** | `<exe-dir>/python<X.Y>/` | `<exe-dir>/site-packages/` | on-disk `.so` (in `lib-dynload` / package dirs) | one of `x86_64` / `aarch64` per build |
| **Windows** | `<exe-dir>/Lib/` | `<exe-dir>/site-packages/` | on-disk `.pyd`/`.dll` in `<exe-dir>/DLLs/` | `x86_64` |
| **Web** | bundled inside Pyodide | `__pypackages__/` inside `app.zip` | Pyodide WebAssembly wheels | `wasm32` |

### Your app program (all platforms)

`package` copies your Python sources into a temp dir (honoring `--exclude` globs, optionally compiling to `.pyc` with `--compile-app`), zips it to `app/app.zip` (override with `-a`/`--asset`), and writes an `app.zip.hash` next to it. At runtime the asset is extracted once to the app's support directory (`<app-support>/flet/app`), guarded by a hash + an optional `invalidateKey` (typically your app version) so it only re-extracts when the bundle changes — debug builds always re-extract. Your app dir is placed first on `sys.path`; a sibling `__pypackages__/` is also added (so you can vendor pure-Python deps next to your code).

`pip install` output goes to `build/site-packages` by default (override with the `SERIOUS_PYTHON_SITE_PACKAGES` env var). For mobile, packages are installed **per architecture** (a `sitecustomize.py` shim spoofs the wheel platform tag so the correct mobile wheels resolve), then merged or split per platform as shown above.

### Android specifics

- **Pure Python** (stdlib + dependencies) ships in two **stored** (uncompressed) ABI-common zips — `stdlib.zip` and `sitepackages.zip` — copied once to the app's files dir and imported in place via `zipimport`. Final `sys.path` (highest first): your app dir, the extract dir, `sitepackages.zip`, `stdlib.zip`.
- **Native modules** (stdlib `lib-dynload` and site-package extensions) are relocated to `jniLibs/<abi>/lib<mangled>.so` and loaded **directly from the APK** (memory-mapped, never extracted to disk); a `sys.meta_path` finder resolves them from `.soref` markers left in the zips. This is why Android needs **no** `useLegacyPackaging` / `keepDebugSymbols` config and the stdlib is **not** duplicated per ABI.
- **Path-hungry packages** (those that read bundled data via `__file__` / `pkg_resources` rather than `importlib.resources`) can be shipped extracted to disk instead of inside the zip — list them (comma-separated relative paths) in `SERIOUS_PYTHON_ANDROID_EXTRACT_PACKAGES`; they go into `extract.zip` and are unpacked to disk at first launch.
- Works for both **single APK** (`flutter build apk`) and **Play Store App Bundles** (per-ABI config splits); under legacy packaging / `minSdk < 23` the same finder falls back to loading from the extracted `nativeLibraryDir`.

### iOS / macOS specifics

The CPython runtime, stdlib, and (on iOS) native extensions are bundled into `serious_python_darwin.framework` as resources. On **iOS**, the App Store forbids loose `.dylib`s, so every native extension `.so` is repackaged into a signed `.framework` inside an `.xcframework`, with a `.fwork` text marker left at the module's import path; CPython's `AppleFrameworkLoader` reads the marker and loads the framework binary. On **macOS**, native extensions stay as plain `.so`, merged into universal (`arm64`+`x86_64`) binaries at package time. `PYTHONHOME` is the framework's resource path; `sys.path` includes `<resources>/site-packages`, `<resources>/stdlib`, and `<resources>/stdlib/lib-dynload`.

### Linux / Windows specifics

The CPython runtime (`libpython3.so` + `libpython<X.Y>.so` on Linux; `python3.dll` + `python<XY>.dll` on Windows), `libdart_bridge`, the stdlib, and native modules are copied next to your app's executable at build time. `PYTHONHOME` is the executable's directory. On Windows, extension modules (`.pyd`) and their dependent DLLs live in `<exe-dir>/DLLs/`, which is added to `sys.path`.

## Platform notes

### Build matrix
Expand Down Expand Up @@ -224,17 +279,15 @@ MACOSX_DEPLOYMENT_TARGET = 10.15;

### Android

To make `serious_python` work in your own Android app:

If you build an App Bundle Edit `android/gradle.properties` and add the flag:
No special native-library packaging config is required (see [How packaging works](#how-packaging-works)). serious_python loads native modules directly from the APK and ships pure Python in stored asset zips, so you don't need `useLegacyPackaging`, `keepDebugSymbols`, `extractNativeLibs`, or `android.bundle.enableUncompressedNativeLibs`. Just use a `minSdk` of 23+ so native libs stay uncompressed/page-aligned in the APK:

```kotlin
android {
defaultConfig {
minSdk = 23
}
}
```
android.bundle.enableUncompressedNativeLibs=false
```

If you build an APK Make sure `android/app/src/AndroidManifest.xml` has `android:extractNativeLibs="true"` in the `<application>` tag.

For more information, see the [public issue](https://issuetracker.google.com/issues/147096055).

## Troubleshooting

Expand Down
Loading
Loading