From 2beb9b1da4bef8f2ba35618c78d8c54db7961b37 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 27 Jul 2026 08:53:14 -0700 Subject: [PATCH 1/4] Bump to 4.4.2: Xcode build-provenance keys in iOS framework Info.plists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-pins the bundled python-build snapshot to 20260727, which stamps the DT* build-provenance keys Xcode writes into a real framework's Info.plist into every framework generated from a lib-dynload .so, and labels the simulator slice as iPhoneSimulator instead of iPhoneOS (flet-dev/python-build#36). No versions moved: Python 3.12.13 / 3.13.14 / 3.14.6, Pyodide, and dart_bridge 1.6.1 are all unchanged from 20260726. This is the next hypothesis under test for the ITMS-91065 rejection in flet-dev/flet#6724, not a confirmed fix — see the serious_python_darwin 4.4.2 entry for what was ruled out experimentally and what remains open. --- src/serious_python/CHANGELOG.md | 5 +++++ src/serious_python/lib/src/python_versions.dart | 4 ++-- src/serious_python/pubspec.yaml | 2 +- src/serious_python_android/CHANGELOG.md | 4 ++++ src/serious_python_android/android/build.gradle.kts | 2 +- .../android/python_versions.properties | 4 ++-- src/serious_python_android/pubspec.yaml | 2 +- src/serious_python_darwin/CHANGELOG.md | 6 ++++++ src/serious_python_darwin/darwin/python_versions.properties | 4 ++-- .../darwin/serious_python_darwin.podspec | 2 +- src/serious_python_darwin/pubspec.yaml | 2 +- src/serious_python_linux/CHANGELOG.md | 4 ++++ src/serious_python_linux/linux/python_versions.properties | 4 ++-- src/serious_python_linux/pubspec.yaml | 2 +- src/serious_python_platform_interface/CHANGELOG.md | 4 ++++ src/serious_python_platform_interface/pubspec.yaml | 2 +- src/serious_python_windows/CHANGELOG.md | 4 ++++ src/serious_python_windows/pubspec.yaml | 2 +- .../windows/python_versions.properties | 4 ++-- 19 files changed, 45 insertions(+), 18 deletions(-) diff --git a/src/serious_python/CHANGELOG.md b/src/serious_python/CHANGELOG.md index 531d9d6b..a6e77652 100644 --- a/src/serious_python/CHANGELOG.md +++ b/src/serious_python/CHANGELOG.md @@ -1,3 +1,8 @@ +## 4.4.2 + +* **iOS:** the bundled Python frameworks (`_ssl`, `_hashlib` and every other native extension) now carry the build-provenance keys Xcode stamps into a real framework's `Info.plist`, instead of a minimal hand-written one. This is the next hypothesis under test for the `ITMS-91065: Missing signature` App Store rejection in flet-dev/flet#6724 — not a confirmed fix. Only new App Store submissions are affected by that rejection; existing published apps are not. See `serious_python_darwin` 4.4.2. +* Bundled python-build snapshot re-pinned to **20260727**. No versions moved — Python (**3.12.13 / 3.13.14 / 3.14.6**), Pyodide, and `dart_bridge` **1.6.1** are unchanged from 20260726. + ## 4.4.1 * **iOS:** the bundled `_ssl` and `_hashlib` frameworks now ship OpenSSL's official privacy manifest instead of an incomplete stub, correcting what they declare under Apple's third-party SDK requirements. This is a correctness fix and is *not* expected to resolve the `ITMS-91065: Missing signature` App Store rejection reported in flet-dev/flet#6724, which remains under investigation. See `serious_python_darwin` 4.4.1. diff --git a/src/serious_python/lib/src/python_versions.dart b/src/serious_python/lib/src/python_versions.dart index 290dafe5..a4f33077 100644 --- a/src/serious_python/lib/src/python_versions.dart +++ b/src/serious_python/lib/src/python_versions.dart @@ -1,5 +1,5 @@ // GENERATED by `dart run serious_python:gen_version_tables` from python-build's -// manifest.json (release 20260726). Do not edit by hand — edit python-build's +// manifest.json (release 20260727). Do not edit by hand — edit python-build's // manifest.json, cut a release, bump `pythonReleaseDate`, and regenerate. const pythonVersionEnvironmentVariable = "SERIOUS_PYTHON_VERSION"; @@ -10,7 +10,7 @@ const pyodideVersionEnvironmentVariable = "SERIOUS_PYTHON_PYODIDE_VERSION"; const dartBridgeVersionEnvironmentVariable = "DART_BRIDGE_VERSION"; /// python-build release the bundled runtimes come from (YYYYMMDD). -const pythonReleaseDate = "20260726"; +const pythonReleaseDate = "20260727"; const dartBridgeVersion = "1.6.1"; const defaultPythonVersion = "3.14"; diff --git a/src/serious_python/pubspec.yaml b/src/serious_python/pubspec.yaml index 710c050e..cd2a1beb 100644 --- a/src/serious_python/pubspec.yaml +++ b/src/serious_python/pubspec.yaml @@ -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.4.1 +version: 4.4.2 platforms: ios: diff --git a/src/serious_python_android/CHANGELOG.md b/src/serious_python_android/CHANGELOG.md index a14df29b..4b5dd18d 100644 --- a/src/serious_python_android/CHANGELOG.md +++ b/src/serious_python_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.4.2 + +* Re-pins the bundled python-build snapshot to **20260727**. Its only functional change is to the iOS frameworks' `Info.plist` (see `serious_python_darwin` 4.4.2); no versions moved and the Android runtime is unchanged from 20260726. + ## 4.4.1 * Re-pins the bundled python-build snapshot to **20260726**. Its only functional change is an iOS privacy-manifest fix (see `serious_python_darwin` 4.4.1); no versions moved and the Android runtime is unchanged from 20260725. diff --git a/src/serious_python_android/android/build.gradle.kts b/src/serious_python_android/android/build.gradle.kts index 750216d0..a4f813ac 100644 --- a/src/serious_python_android/android/build.gradle.kts +++ b/src/serious_python_android/android/build.gradle.kts @@ -21,7 +21,7 @@ buildscript { } group = "com.flet.serious_python_android" -version = "4.4.1" +version = "4.4.2" rootProject.allprojects { repositories { diff --git a/src/serious_python_android/android/python_versions.properties b/src/serious_python_android/android/python_versions.properties index 2e023242..34e8f7f0 100644 --- a/src/serious_python_android/android/python_versions.properties +++ b/src/serious_python_android/android/python_versions.properties @@ -1,8 +1,8 @@ # GENERATED by `dart run serious_python:gen_version_tables` from -# python-build manifest.json (release 20260726). Do not edit by hand. +# python-build manifest.json (release 20260727). Do not edit by hand. default_python_version=3.14 dart_bridge_version=1.6.1 -python_build_release_date=20260726 +python_build_release_date=20260727 3.12.full_version=3.12.13 3.12.android_abis=arm64-v8a,x86_64,armeabi-v7a 3.13.full_version=3.13.14 diff --git a/src/serious_python_android/pubspec.yaml b/src/serious_python_android/pubspec.yaml index aca254c4..7ceee06c 100644 --- a/src/serious_python_android/pubspec.yaml +++ b/src/serious_python_android/pubspec.yaml @@ -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.4.1 +version: 4.4.2 environment: sdk: ">=3.0.0 <4.0.0" diff --git a/src/serious_python_darwin/CHANGELOG.md b/src/serious_python_darwin/CHANGELOG.md index d116a655..77a756a0 100644 --- a/src/serious_python_darwin/CHANGELOG.md +++ b/src/serious_python_darwin/CHANGELOG.md @@ -1,3 +1,9 @@ +## 4.4.2 + +* **iOS: generated frameworks now carry Xcode's build-provenance keys in their `Info.plist`.** Every framework built from a `lib-dynload` `.so` — `_ssl`, `_hashlib`, and the rest — previously shipped ten hand-written keys and not a single `DT*` key, so to Apple's static analysis the bundle didn't look like anything Xcode had produced. They now carry the same key set Xcode stamps into a real framework (`BuildMachineOSBuild`, `DTCompiler`, `DTPlatformBuild`, `DTPlatformName`, `DTPlatformVersion`, `DTSDKBuild`, `DTSDKName`, `DTXcode`, `DTXcodeBuild`, `UIDeviceFamily`, and `UIRequiredDeviceCapabilities` on the device slice), resolved from the SDK the release was built with. The simulator slice is also labelled correctly now — it previously claimed `CFBundleSupportedPlatforms=iPhoneOS`. See flet-dev/python-build#36. +* This is the next hypothesis under test for the `ITMS-91065: Missing signature` App Store rejection in flet-dev/flet#6724, **not a confirmed fix**. Two other candidates were ruled out experimentally: a signature applied at the source can't matter (re-signing the way `exportArchive` does destroys the certificate chain, team identifier and timestamp, leaving only the identifier string, which is derived from `CFBundleIdentifier` for an unsigned framework anyway), and the privacy manifest can't be the differentiator (a third-party OpenSSL xcframework the App Store accepts ships an empty stub, and our frameworks already carry a manifest — Apple emits no `ITMS-91061` alongside the rejection). The check only fires for new apps, or updates that newly add a listed SDK, so existing published apps are unaffected either way. +* Re-pins the bundled python-build snapshot to **20260727**. No versions moved: Python (**3.12.13 / 3.13.14 / 3.14.6**), Pyodide, and `dart_bridge` **1.6.1** are unchanged from 20260726, and the `Info.plist` change above is the only functional difference on any platform. + ## 4.4.1 * **iOS: `_ssl.framework` and `_hashlib.framework` now carry OpenSSL's official privacy manifest.** Both extensions statically link OpenSSL, so Apple's App Store scan identifies them as containing BoringSSL / openssl_grpc — a listed third-party SDK that must declare a privacy manifest. The bundled `PrivacyInfo.xcprivacy` was a stub: `NSPrivacyAccessedAPITypes` was an empty array, and it carried an `NSPrivacyUsesNonStandardAPIs` key that isn't part of Apple's schema. It is now byte-identical to the manifest OpenSSL publishes for its Apple builds, declaring the file-timestamp API access the library actually performs (`NSPrivacyAccessedAPICategoryFileTimestamp`, reason `C617.1`). See flet-dev/python-build#35. diff --git a/src/serious_python_darwin/darwin/python_versions.properties b/src/serious_python_darwin/darwin/python_versions.properties index 2e023242..34e8f7f0 100644 --- a/src/serious_python_darwin/darwin/python_versions.properties +++ b/src/serious_python_darwin/darwin/python_versions.properties @@ -1,8 +1,8 @@ # GENERATED by `dart run serious_python:gen_version_tables` from -# python-build manifest.json (release 20260726). Do not edit by hand. +# python-build manifest.json (release 20260727). Do not edit by hand. default_python_version=3.14 dart_bridge_version=1.6.1 -python_build_release_date=20260726 +python_build_release_date=20260727 3.12.full_version=3.12.13 3.12.android_abis=arm64-v8a,x86_64,armeabi-v7a 3.13.full_version=3.13.14 diff --git a/src/serious_python_darwin/darwin/serious_python_darwin.podspec b/src/serious_python_darwin/darwin/serious_python_darwin.podspec index 68d2ad0d..a2a3caf0 100644 --- a/src/serious_python_darwin/darwin/serious_python_darwin.podspec +++ b/src/serious_python_darwin/darwin/serious_python_darwin.podspec @@ -4,7 +4,7 @@ # Pod::Spec.new do |s| s.name = 'serious_python_darwin' - s.version = '4.4.1' + s.version = '4.4.2' 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. diff --git a/src/serious_python_darwin/pubspec.yaml b/src/serious_python_darwin/pubspec.yaml index 37f5aea3..7b49b96e 100644 --- a/src/serious_python_darwin/pubspec.yaml +++ b/src/serious_python_darwin/pubspec.yaml @@ -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.4.1 +version: 4.4.2 environment: # The Swift Package Manager build path needs Flutter 3.44 / Dart 3.11 (the diff --git a/src/serious_python_linux/CHANGELOG.md b/src/serious_python_linux/CHANGELOG.md index 5e832c16..b905dfba 100644 --- a/src/serious_python_linux/CHANGELOG.md +++ b/src/serious_python_linux/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.4.2 + +* Re-pins the bundled python-build snapshot to **20260727**. Its only functional change is to the iOS frameworks' `Info.plist` (see `serious_python_darwin` 4.4.2); no versions moved and the Linux runtime is unchanged from 20260726. + ## 4.4.1 * Re-pins the bundled python-build snapshot to **20260726**. Its only functional change is an iOS privacy-manifest fix (see `serious_python_darwin` 4.4.1); no versions moved and the Linux runtime is unchanged from 20260725. diff --git a/src/serious_python_linux/linux/python_versions.properties b/src/serious_python_linux/linux/python_versions.properties index 2e023242..34e8f7f0 100644 --- a/src/serious_python_linux/linux/python_versions.properties +++ b/src/serious_python_linux/linux/python_versions.properties @@ -1,8 +1,8 @@ # GENERATED by `dart run serious_python:gen_version_tables` from -# python-build manifest.json (release 20260726). Do not edit by hand. +# python-build manifest.json (release 20260727). Do not edit by hand. default_python_version=3.14 dart_bridge_version=1.6.1 -python_build_release_date=20260726 +python_build_release_date=20260727 3.12.full_version=3.12.13 3.12.android_abis=arm64-v8a,x86_64,armeabi-v7a 3.13.full_version=3.13.14 diff --git a/src/serious_python_linux/pubspec.yaml b/src/serious_python_linux/pubspec.yaml index 201507ad..6eceec74 100644 --- a/src/serious_python_linux/pubspec.yaml +++ b/src/serious_python_linux/pubspec.yaml @@ -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.4.1 +version: 4.4.2 environment: sdk: '>=3.1.3 <4.0.0' diff --git a/src/serious_python_platform_interface/CHANGELOG.md b/src/serious_python_platform_interface/CHANGELOG.md index e9fe86eb..0288708c 100644 --- a/src/serious_python_platform_interface/CHANGELOG.md +++ b/src/serious_python_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.4.2 + +* Version bump aligning with the `serious_python_*` 4.4.2 release. + ## 4.4.1 * Version bump aligning with the `serious_python_*` 4.4.1 release. diff --git a/src/serious_python_platform_interface/pubspec.yaml b/src/serious_python_platform_interface/pubspec.yaml index 18cf19a9..d6f77f41 100644 --- a/src/serious_python_platform_interface/pubspec.yaml +++ b/src/serious_python_platform_interface/pubspec.yaml @@ -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.4.1 +version: 4.4.2 environment: sdk: ">=3.0.0 <4.0.0" diff --git a/src/serious_python_windows/CHANGELOG.md b/src/serious_python_windows/CHANGELOG.md index d41095bf..93a8faff 100644 --- a/src/serious_python_windows/CHANGELOG.md +++ b/src/serious_python_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.4.2 + +* Re-pins the bundled python-build snapshot to **20260727**. Its only functional change is to the iOS frameworks' `Info.plist` (see `serious_python_darwin` 4.4.2); no versions moved and the Windows runtime is unchanged from 20260726. + ## 4.4.1 * Re-pins the bundled python-build snapshot to **20260726**. Its only functional change is an iOS privacy-manifest fix (see `serious_python_darwin` 4.4.1); no versions moved and the Windows runtime is unchanged from 20260725. diff --git a/src/serious_python_windows/pubspec.yaml b/src/serious_python_windows/pubspec.yaml index 77192b3d..1c99abad 100644 --- a/src/serious_python_windows/pubspec.yaml +++ b/src/serious_python_windows/pubspec.yaml @@ -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.4.1 +version: 4.4.2 environment: sdk: '>=3.1.3 <4.0.0' diff --git a/src/serious_python_windows/windows/python_versions.properties b/src/serious_python_windows/windows/python_versions.properties index 2e023242..34e8f7f0 100644 --- a/src/serious_python_windows/windows/python_versions.properties +++ b/src/serious_python_windows/windows/python_versions.properties @@ -1,8 +1,8 @@ # GENERATED by `dart run serious_python:gen_version_tables` from -# python-build manifest.json (release 20260726). Do not edit by hand. +# python-build manifest.json (release 20260727). Do not edit by hand. default_python_version=3.14 dart_bridge_version=1.6.1 -python_build_release_date=20260726 +python_build_release_date=20260727 3.12.full_version=3.12.13 3.12.android_abis=arm64-v8a,x86_64,armeabi-v7a 3.13.full_version=3.13.14 From abb6dbd58a32c1b3b40fab8bd90ab3be39637703 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 27 Jul 2026 11:24:56 -0700 Subject: [PATCH 2/4] iOS: namespace framework bundle identifiers under the host app Every Python C-extension ships as its own embedded framework, and each carried a fixed `org.python.` CFBundleIdentifier -- org.python.ssl, org.python.hashlib, ... -- byte-identical in every app ever built with serious_python. A framework's bundle identifier also becomes its CODE SIGNING identifier, and that is the one field that survives the `codesign -f` Xcode applies at embed and again at exportArchive (everything else -- certificate chain, team identifier, secure timestamp -- is destroyed). A globally-shared identifier on a framework Apple fingerprints as a listed third-party SDK is the leading explanation for the ITMS-91065 rejection in flet-dev/flet#6724. Setting SERIOUS_PYTHON_BUNDLE_ID rewrites them to `.` with underscores mapped to hyphens, matching CPython's own iOS support (Platforms/Apple/testbed/Python.xcframework/build/utils.sh). The leading hyphen for underscore-prefixed modules (`_ssl` -> `.-ssl`) is deliberate: it is what keeps `_ssl` distinct from `ssl`, and it is the form CPython ships. The rewrite runs before reconcile_framework_install_names, whose ad-hoc re-sign reseals the modified plists; the stdlib xcframeworks are unsigned at that point so editing them invalidates nothing. Module names come from arbitrary wheels, so anything outside CFBundleIdentifier's [A-Za-z0-9.-] is mapped to a hyphen, and an invalid SERIOUS_PYTHON_BUNDLE_ID leaves the defaults in place with a warning rather than emitting a plist that fails late at export. flet passes the value from flet-dev/flet#6731. --- src/serious_python/CHANGELOG.md | 4 +- src/serious_python/README.md | 17 ++++++ src/serious_python_darwin/CHANGELOG.md | 3 +- .../darwin/sync_site_packages.sh | 10 ++++ .../darwin/xcframework_utils.sh | 54 +++++++++++++++++++ 5 files changed, 86 insertions(+), 2 deletions(-) diff --git a/src/serious_python/CHANGELOG.md b/src/serious_python/CHANGELOG.md index a6e77652..c083548c 100644 --- a/src/serious_python/CHANGELOG.md +++ b/src/serious_python/CHANGELOG.md @@ -1,6 +1,8 @@ ## 4.4.2 -* **iOS:** the bundled Python frameworks (`_ssl`, `_hashlib` and every other native extension) now carry the build-provenance keys Xcode stamps into a real framework's `Info.plist`, instead of a minimal hand-written one. This is the next hypothesis under test for the `ITMS-91065: Missing signature` App Store rejection in flet-dev/flet#6724 — not a confirmed fix. Only new App Store submissions are affected by that rejection; existing published apps are not. See `serious_python_darwin` 4.4.2. +* **iOS:** new `SERIOUS_PYTHON_BUNDLE_ID` env var namespaces the generated frameworks' bundle identifiers under your app (`com.example.myapp.-ssl`) instead of a shared `org.python.*` default identical in every app built with serious_python. `flet build` sets it for you; set it yourself for a manual two-step build, in both places you set `SERIOUS_PYTHON_SITE_PACKAGES`. +* **iOS:** the bundled Python frameworks (`_ssl`, `_hashlib` and every other native extension) now carry the build-provenance keys Xcode stamps into a real framework's `Info.plist`, instead of a minimal hand-written one. +* Both are hypotheses under test for the `ITMS-91065: Missing signature` App Store rejection in flet-dev/flet#6724 — neither is a confirmed fix. Only new App Store submissions are affected by that rejection; existing published apps are not. See `serious_python_darwin` 4.4.2. * Bundled python-build snapshot re-pinned to **20260727**. No versions moved — Python (**3.12.13 / 3.13.14 / 3.14.6**), Pyodide, and `dart_bridge` **1.6.1** are unchanged from 20260726. ## 4.4.1 diff --git a/src/serious_python/README.md b/src/serious_python/README.md index 80a0ad27..f591832e 100644 --- a/src/serious_python/README.md +++ b/src/serious_python/README.md @@ -192,6 +192,23 @@ dart run serious_python:main package app/src -p iOS -r -r -r app/src/requirement For the **web** (`Emscripten`) target there is no `SERIOUS_PYTHON_APP`; the app and its `__pypackages__` are zipped into the `app/app.zip` asset instead — make sure it's added to `pubspec.yaml`. +On **iOS**, also set `SERIOUS_PYTHON_BUNDLE_ID` to your app's bundle identifier: + +``` +export SERIOUS_PYTHON_BUNDLE_ID=com.example.myapp +``` + +Each Python C-extension ships as its own embedded framework, and this namespaces +their `CFBundleIdentifier`s under your app (`com.example.myapp.-ssl`) instead of a +shared `org.python.*` default that would be identical in every app built with +serious_python — matching what CPython's own iOS support does. A framework's bundle +identifier also becomes its code signing identifier, which Apple's App Store scan +appears to key on for the third-party SDK requirements. Set it in **both** places +`SERIOUS_PYTHON_SITE_PACKAGES` is set (the `package` command and the later `flutter +build`), since the CocoaPods `prepare_command` re-runs the darwin sync. If unset — +or if the value isn't a valid bundle identifier — the `org.python.*` defaults are +kept and a warning is printed. `flet build` sets this for you. + ## Python app structure By default, embedded Python program is run in a separate thread, to avoid UI blocking. Your Flutter app is not supposed to directly call Python functions or modules, but instead it should communicate via some API provided by a Python app, such as: REST API, sockets, SQLite database, files, etc. diff --git a/src/serious_python_darwin/CHANGELOG.md b/src/serious_python_darwin/CHANGELOG.md index 77a756a0..453fda59 100644 --- a/src/serious_python_darwin/CHANGELOG.md +++ b/src/serious_python_darwin/CHANGELOG.md @@ -1,7 +1,8 @@ ## 4.4.2 +* **iOS: framework bundle identifiers are now namespaced under the host app.** Every Python C-extension ships as its own embedded framework, and each carried a fixed `org.python.` `CFBundleIdentifier` — `org.python.ssl`, `org.python.hashlib`, … — byte-identical in every app ever built with serious_python. Set `SERIOUS_PYTHON_BUNDLE_ID` to the app's bundle identifier and they become `com.example.myapp.-ssl` instead, matching what CPython's own iOS support does when it converts a `.so` into a framework. This matters because a framework's bundle identifier also becomes its **code signing identifier**, which is the one field that survives the `codesign -f` Xcode applies at embed and again at `exportArchive`. Unset, or set to something that isn't a valid bundle identifier, leaves the old defaults in place and prints a warning. `flet build` supplies it from your app's configured bundle id (flet-dev/flet#6731). * **iOS: generated frameworks now carry Xcode's build-provenance keys in their `Info.plist`.** Every framework built from a `lib-dynload` `.so` — `_ssl`, `_hashlib`, and the rest — previously shipped ten hand-written keys and not a single `DT*` key, so to Apple's static analysis the bundle didn't look like anything Xcode had produced. They now carry the same key set Xcode stamps into a real framework (`BuildMachineOSBuild`, `DTCompiler`, `DTPlatformBuild`, `DTPlatformName`, `DTPlatformVersion`, `DTSDKBuild`, `DTSDKName`, `DTXcode`, `DTXcodeBuild`, `UIDeviceFamily`, and `UIRequiredDeviceCapabilities` on the device slice), resolved from the SDK the release was built with. The simulator slice is also labelled correctly now — it previously claimed `CFBundleSupportedPlatforms=iPhoneOS`. See flet-dev/python-build#36. -* This is the next hypothesis under test for the `ITMS-91065: Missing signature` App Store rejection in flet-dev/flet#6724, **not a confirmed fix**. Two other candidates were ruled out experimentally: a signature applied at the source can't matter (re-signing the way `exportArchive` does destroys the certificate chain, team identifier and timestamp, leaving only the identifier string, which is derived from `CFBundleIdentifier` for an unsigned framework anyway), and the privacy manifest can't be the differentiator (a third-party OpenSSL xcframework the App Store accepts ships an empty stub, and our frameworks already carry a manifest — Apple emits no `ITMS-91061` alongside the rejection). The check only fires for new apps, or updates that newly add a listed SDK, so existing published apps are unaffected either way. +* Both changes above are hypotheses under test for the `ITMS-91065: Missing signature` App Store rejection in flet-dev/flet#6724, **neither is a confirmed fix**. The shared bundle identifier is the stronger of the two: it accounts for every outcome we could find (BeeWare namespaces under the app and has no reports; a third-party OpenSSL xcframework with its own vendor identifier is reported to pass; our `org.python.*` and one other project's independent identifier both draw the rejection). Two further candidates were ruled out experimentally: a signature applied at the source can't matter (re-signing the way `exportArchive` does destroys the certificate chain, team identifier and timestamp, leaving only the identifier string, which is derived from `CFBundleIdentifier` for an unsigned framework anyway), and the privacy manifest can't be the differentiator (a third-party OpenSSL xcframework the App Store accepts ships an empty stub, and our frameworks already carry a manifest — Apple emits no `ITMS-91061` alongside the rejection). The check only fires for new apps, or updates that newly add a listed SDK, so existing published apps are unaffected either way. * Re-pins the bundled python-build snapshot to **20260727**. No versions moved: Python (**3.12.13 / 3.13.14 / 3.14.6**), Pyodide, and `dart_bridge` **1.6.1** are unchanged from 20260726, and the `Info.plist` change above is the only functional difference on any platform. ## 4.4.1 diff --git a/src/serious_python_darwin/darwin/sync_site_packages.sh b/src/serious_python_darwin/darwin/sync_site_packages.sh index 48c931da..f1fe0207 100755 --- a/src/serious_python_darwin/darwin/sync_site_packages.sh +++ b/src/serious_python_darwin/darwin/sync_site_packages.sh @@ -49,6 +49,16 @@ if [[ -n "$SERIOUS_PYTHON_SITE_PACKAGES" && -d "$SERIOUS_PYTHON_SITE_PACKAGES" ] done done + # Namespace the frameworks' CFBundleIdentifiers under the host app's + # bundle id (see xcframework_utils.sh). Runs before the reconcile pass + # below, whose ad-hoc re-sign reseals the modified Info.plists. Skipped + # when the caller doesn't supply a bundle id, leaving the org.python.* + # defaults in place. + if [[ -n "${SERIOUS_PYTHON_BUNDLE_ID:-}" ]]; then + echo "Namespacing framework bundle identifiers under $SERIOUS_PYTHON_BUNDLE_ID" + rewrite_framework_bundle_ids "$dist/site-xcframeworks" "$SERIOUS_PYTHON_BUNDLE_ID" || exit 1 + fi + # After every .so/.dylib is framework-ized, reconcile the Mach-O # install names so the interdependent @rpath refs point at the new # framework paths (serious-python #223). Without this, dyld cannot diff --git a/src/serious_python_darwin/darwin/xcframework_utils.sh b/src/serious_python_darwin/darwin/xcframework_utils.sh index 2973c825..6ea03a0d 100644 --- a/src/serious_python_darwin/darwin/xcframework_utils.sh +++ b/src/serious_python_darwin/darwin/xcframework_utils.sh @@ -106,6 +106,60 @@ create_xcframework_from_dylibs() { rm -rf "${dylib_tmp_dir}" >/dev/null } +# Namespace every generated framework's CFBundleIdentifier under the host app's +# bundle id, mirroring what CPython's own iOS support does when it converts a +# .so into a framework (Platforms/Apple/testbed/Python.xcframework/build/utils.sh): +# +# FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr "_" "-") +# +# The frameworks are built long before the app's bundle id is known -- python-build +# has no idea what app its stdlib will end up in -- so they carry a placeholder +# `org.python.` until this pass. That placeholder is byte-identical in every +# app that ships serious_python, and it also becomes the framework's CODE SIGNING +# identifier, which is the one field that survives Xcode's `codesign -f` at embed +# and again at exportArchive. A globally-shared identifier on a framework Apple +# fingerprints as a listed third-party SDK is the leading explanation for the +# ITMS-91065 rejection in flet-dev/flet#6724. +# +# The leading hyphen this produces for underscore-prefixed modules (`_ssl` -> +# `.-ssl`) is deliberate: it is what keeps `_ssl` distinct from `ssl`, and it +# is the exact form CPython and BeeWare ship. +# +# Must run BEFORE reconcile_framework_install_names, whose ad-hoc re-sign reseals +# the modified Info.plists. The stdlib xcframeworks copied in from python-build are +# unsigned at this point, so editing their plists invalidates nothing. +rewrite_framework_bundle_ids() { + local xcframeworks_dir=$1 + local bundle_id=${2%.} + + # CFBundleIdentifier allows only [A-Za-z0-9.-], and every dot-separated + # component must be non-empty. The bundle id comes from the app's pyproject / + # CLI, so validate rather than emit a plist that fails much later at export + # with an error naming a framework the developer has never heard of. + if ! printf '%s' "$bundle_id" | grep -Eq '^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*$'; then + echo "rewrite_framework_bundle_ids: '$bundle_id' is not a valid bundle identifier;" \ + "leaving framework identifiers at their org.python.* defaults" >&2 + return 0 + fi + + local xcf fw component identifier plist err + for xcf in "$xcframeworks_dir"/*.xcframework; do + [ -d "$xcf" ] || continue + fw=$(basename "$xcf" .xcframework) + # Module names come from whatever wheels the app depends on, so anything + # outside the allowed set becomes a hyphen. + component=$(printf '%s' "$fw" | tr '_' '-' | sed 's/[^A-Za-z0-9.-]/-/g') + identifier="$bundle_id.$component" + for plist in "$xcf"/*/"$fw.framework/Info.plist"; do + [ -f "$plist" ] || continue + if ! err=$(plutil -replace CFBundleIdentifier -string "$identifier" "$plist" 2>&1); then + echo "rewrite_framework_bundle_ids: plutil failed for $plist: $err" >&2 + return 1 + fi + done + done +} + # Reconcile install names across the newly-created site-package frameworks. # # create_xcframework_from_dylibs renames each lib to a framework named by its From 074328325d50fdf7dc66257e47626457f04c2fad Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 27 Jul 2026 11:50:49 -0700 Subject: [PATCH 3/4] iOS: namespace Python.framework's bundle id too Python.xcframework is staged straight out of dist/xcframeworks by stage_spm.sh and vendored from there by the podspec, so it never passes through site-xcframeworks and kept a shared `org.python.python` -- identical in every app, the same class of identifier the previous commit fixed for the extension frameworks. Verified inert at runtime before renaming: `org.python.python` appears in no shipped binary, and neither Python.framework nor App.framework contains a CFBundleGetBundleWithIdentifier / bundleWithIdentifier lookup. serious_python sets PYTHONHOME explicitly and resolves extensions through the .fwork/.origin path mechanism, not bundle identifiers. rewrite_framework_bundle_ids takes an optional skip list so dart_bridge keeps `dev.flet.dartbridge` -- a vendor-owned reverse-DNS identifier is the shape we're aiming for, not the problem we're fixing. Confirmed against a real build (flet playground abc1.xcarchive): 93 of 98 frameworks were already namespaced, no duplicate identifiers; this covers Python.framework, leaving only Flutter's own three (App, Flutter, objective_c) and dart_bridge. --- src/serious_python_darwin/CHANGELOG.md | 2 +- src/serious_python_darwin/darwin/sync_site_packages.sh | 8 ++++++++ src/serious_python_darwin/darwin/xcframework_utils.sh | 7 +++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/serious_python_darwin/CHANGELOG.md b/src/serious_python_darwin/CHANGELOG.md index 453fda59..3d13f548 100644 --- a/src/serious_python_darwin/CHANGELOG.md +++ b/src/serious_python_darwin/CHANGELOG.md @@ -1,6 +1,6 @@ ## 4.4.2 -* **iOS: framework bundle identifiers are now namespaced under the host app.** Every Python C-extension ships as its own embedded framework, and each carried a fixed `org.python.` `CFBundleIdentifier` — `org.python.ssl`, `org.python.hashlib`, … — byte-identical in every app ever built with serious_python. Set `SERIOUS_PYTHON_BUNDLE_ID` to the app's bundle identifier and they become `com.example.myapp.-ssl` instead, matching what CPython's own iOS support does when it converts a `.so` into a framework. This matters because a framework's bundle identifier also becomes its **code signing identifier**, which is the one field that survives the `codesign -f` Xcode applies at embed and again at `exportArchive`. Unset, or set to something that isn't a valid bundle identifier, leaves the old defaults in place and prints a warning. `flet build` supplies it from your app's configured bundle id (flet-dev/flet#6731). +* **iOS: framework bundle identifiers are now namespaced under the host app.** Every Python C-extension ships as its own embedded framework, and each carried a fixed `org.python.` `CFBundleIdentifier` — `org.python.ssl`, `org.python.hashlib`, … — byte-identical in every app ever built with serious_python. Set `SERIOUS_PYTHON_BUNDLE_ID` to the app's bundle identifier and they become `com.example.myapp.-ssl` instead, matching what CPython's own iOS support does when it converts a `.so` into a framework. This matters because a framework's bundle identifier also becomes its **code signing identifier**, which is the one field that survives the `codesign -f` Xcode applies at embed and again at `exportArchive`. `Python.framework` is covered too — it was `org.python.python` in every app; nothing resolves it by identifier (the string appears in no shipped binary and there are no `CFBundleGetBundleWithIdentifier` lookups), so the rename is inert at runtime. `dart_bridge.framework` is deliberately left at `dev.flet.dartbridge`, which is already a vendor-owned identifier rather than a shared placeholder. Unset, or set to something that isn't a valid bundle identifier, leaves the old defaults in place and prints a warning. `flet build` supplies it from your app's configured bundle id (flet-dev/flet#6731). * **iOS: generated frameworks now carry Xcode's build-provenance keys in their `Info.plist`.** Every framework built from a `lib-dynload` `.so` — `_ssl`, `_hashlib`, and the rest — previously shipped ten hand-written keys and not a single `DT*` key, so to Apple's static analysis the bundle didn't look like anything Xcode had produced. They now carry the same key set Xcode stamps into a real framework (`BuildMachineOSBuild`, `DTCompiler`, `DTPlatformBuild`, `DTPlatformName`, `DTPlatformVersion`, `DTSDKBuild`, `DTSDKName`, `DTXcode`, `DTXcodeBuild`, `UIDeviceFamily`, and `UIRequiredDeviceCapabilities` on the device slice), resolved from the SDK the release was built with. The simulator slice is also labelled correctly now — it previously claimed `CFBundleSupportedPlatforms=iPhoneOS`. See flet-dev/python-build#36. * Both changes above are hypotheses under test for the `ITMS-91065: Missing signature` App Store rejection in flet-dev/flet#6724, **neither is a confirmed fix**. The shared bundle identifier is the stronger of the two: it accounts for every outcome we could find (BeeWare namespaces under the app and has no reports; a third-party OpenSSL xcframework with its own vendor identifier is reported to pass; our `org.python.*` and one other project's independent identifier both draw the rejection). Two further candidates were ruled out experimentally: a signature applied at the source can't matter (re-signing the way `exportArchive` does destroys the certificate chain, team identifier and timestamp, leaving only the identifier string, which is derived from `CFBundleIdentifier` for an unsigned framework anyway), and the privacy manifest can't be the differentiator (a third-party OpenSSL xcframework the App Store accepts ships an empty stub, and our frameworks already carry a manifest — Apple emits no `ITMS-91061` alongside the rejection). The check only fires for new apps, or updates that newly add a listed SDK, so existing published apps are unaffected either way. * Re-pins the bundled python-build snapshot to **20260727**. No versions moved: Python (**3.12.13 / 3.13.14 / 3.14.6**), Pyodide, and `dart_bridge` **1.6.1** are unchanged from 20260726, and the `Info.plist` change above is the only functional difference on any platform. diff --git a/src/serious_python_darwin/darwin/sync_site_packages.sh b/src/serious_python_darwin/darwin/sync_site_packages.sh index f1fe0207..d08bad36 100755 --- a/src/serious_python_darwin/darwin/sync_site_packages.sh +++ b/src/serious_python_darwin/darwin/sync_site_packages.sh @@ -57,6 +57,14 @@ if [[ -n "$SERIOUS_PYTHON_SITE_PACKAGES" && -d "$SERIOUS_PYTHON_SITE_PACKAGES" ] if [[ -n "${SERIOUS_PYTHON_BUNDLE_ID:-}" ]]; then echo "Namespacing framework bundle identifiers under $SERIOUS_PYTHON_BUNDLE_ID" rewrite_framework_bundle_ids "$dist/site-xcframeworks" "$SERIOUS_PYTHON_BUNDLE_ID" || exit 1 + # Python.xcframework is staged straight out of dist/xcframeworks by + # stage_spm.sh (and referenced there by the podspec), so it never + # passes through site-xcframeworks above and kept a shared + # `org.python.python`. Nothing resolves it by identifier -- the string + # appears in no binary and there are no CFBundleGetBundleWithIdentifier + # lookups -- so renaming it is inert at runtime. dart_bridge is left + # alone: `dev.flet.dartbridge` is already a vendor-owned identifier. + rewrite_framework_bundle_ids "$dist/xcframeworks" "$SERIOUS_PYTHON_BUNDLE_ID" "dart_bridge" || exit 1 fi # After every .so/.dylib is framework-ized, reconcile the Mach-O diff --git a/src/serious_python_darwin/darwin/xcframework_utils.sh b/src/serious_python_darwin/darwin/xcframework_utils.sh index 6ea03a0d..dd5ea203 100644 --- a/src/serious_python_darwin/darwin/xcframework_utils.sh +++ b/src/serious_python_darwin/darwin/xcframework_utils.sh @@ -128,9 +128,15 @@ create_xcframework_from_dylibs() { # Must run BEFORE reconcile_framework_install_names, whose ad-hoc re-sign reseals # the modified Info.plists. The stdlib xcframeworks copied in from python-build are # unsigned at this point, so editing their plists invalidates nothing. +# +# $3 is an optional space-separated list of framework names to leave alone -- +# used for artifacts that already carry a vendor-owned reverse-DNS identifier +# (dart_bridge is `dev.flet.dartbridge`), which is the shape we are aiming for +# rather than the problem we are fixing. rewrite_framework_bundle_ids() { local xcframeworks_dir=$1 local bundle_id=${2%.} + local skip=" ${3:-} " # CFBundleIdentifier allows only [A-Za-z0-9.-], and every dot-separated # component must be non-empty. The bundle id comes from the app's pyproject / @@ -146,6 +152,7 @@ rewrite_framework_bundle_ids() { for xcf in "$xcframeworks_dir"/*.xcframework; do [ -d "$xcf" ] || continue fw=$(basename "$xcf" .xcframework) + case "$skip" in *" $fw "*) continue ;; esac # Module names come from whatever wheels the app depends on, so anything # outside the allowed set becomes a hyphen. component=$(printf '%s' "$fw" | tr '_' '-' | sed 's/[^A-Za-z0-9.-]/-/g') From cdc19022cb3fe04c0edcf6510a4c50bd81250178 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 27 Jul 2026 12:08:37 -0700 Subject: [PATCH 4/4] examples: pin jni to 1.0.0 (1.0.1 breaks aarch64 Linux builds) jni 1.0.1, published 2026-07-27T01:10Z, added global_jni_env.c, which passes a `va_list` where a `void *` is expected. On x86_64 va_list is an array type that decays to a pointer so it compiles; on aarch64 it is a struct, so it is a hard error and `flutter build linux` fails with 19 of them. Tracked upstream at dart-lang/native#3498. This surfaced on the release PR rather than as a scheduled failure because the examples depend on the serious_python packages by path: bumping their version invalidates the committed pubspec.lock, pub re-resolves the whole graph, and picks up the newest transitive versions. Any PR touching package versions after 2026-07-27T01:10Z would have hit it. jni itself arrives via path_provider -> path_provider_android -> jni_flutter -> jni. jni_flutter 1.0.1 declares `jni: ^1.0.0`, so 1.0.0 satisfies it -- the override narrows within the allowed range rather than overriding a constraint. Applied to all three examples; only bridge_example runs in CI, but the other two break identically for anyone building them on an ARM64 Linux host. The flask_example / run_example locks also pick up the current path-dependency version (4.0.0 -> 4.4.1); they were simply stale. --- .../example/bridge_example/pubspec.yaml | 9 +++++++++ .../example/flask_example/pubspec.lock | 14 +++++++------- .../example/flask_example/pubspec.yaml | 9 +++++++++ .../example/run_example/pubspec.lock | 14 +++++++------- .../example/run_example/pubspec.yaml | 9 +++++++++ 5 files changed, 41 insertions(+), 14 deletions(-) diff --git a/src/serious_python/example/bridge_example/pubspec.yaml b/src/serious_python/example/bridge_example/pubspec.yaml index 23133af7..fd4e23bb 100644 --- a/src/serious_python/example/bridge_example/pubspec.yaml +++ b/src/serious_python/example/bridge_example/pubspec.yaml @@ -24,5 +24,14 @@ dev_dependencies: sdk: flutter flutter_lints: ^2.0.0 +# jni arrives transitively (path_provider -> path_provider_android -> +# jni_flutter -> jni). 1.0.1 added global_jni_env.c, which passes a `va_list` +# where a `void *` is expected: fine on x86_64, where va_list is an array type +# that decays to a pointer, but a hard error on aarch64, where it's a struct. +# That breaks `flutter build linux` on ARM64 hosts. Tracked upstream at +# dart-lang/native#3498 — drop this override once a fixed jni is released. +dependency_overrides: + jni: 1.0.0 + flutter: uses-material-design: true diff --git a/src/serious_python/example/flask_example/pubspec.lock b/src/serious_python/example/flask_example/pubspec.lock index a0c8b512..921d732a 100644 --- a/src/serious_python/example/flask_example/pubspec.lock +++ b/src/serious_python/example/flask_example/pubspec.lock @@ -156,7 +156,7 @@ packages: source: hosted version: "4.1.2" jni: - dependency: transitive + dependency: "direct overridden" description: name: jni sha256: c2230682d5bc2362c1c9e8d3c7f406d9cbba23ab3f2e203a025dd47e0fb2e68f @@ -361,42 +361,42 @@ packages: path: "../.." relative: true source: path - version: "4.0.0" + version: "4.4.1" serious_python_android: dependency: transitive description: path: "../../../serious_python_android" relative: true source: path - version: "4.0.0" + version: "4.4.1" serious_python_darwin: dependency: transitive description: path: "../../../serious_python_darwin" relative: true source: path - version: "4.0.0" + version: "4.4.1" serious_python_linux: dependency: transitive description: path: "../../../serious_python_linux" relative: true source: path - version: "4.0.0" + version: "4.4.1" serious_python_platform_interface: dependency: transitive description: path: "../../../serious_python_platform_interface" relative: true source: path - version: "4.0.0" + version: "4.4.1" serious_python_windows: dependency: transitive description: path: "../../../serious_python_windows" relative: true source: path - version: "4.0.0" + version: "4.4.1" shelf: dependency: transitive description: diff --git a/src/serious_python/example/flask_example/pubspec.yaml b/src/serious_python/example/flask_example/pubspec.yaml index 47da6963..23df2097 100644 --- a/src/serious_python/example/flask_example/pubspec.yaml +++ b/src/serious_python/example/flask_example/pubspec.yaml @@ -56,6 +56,15 @@ dev_dependencies: # following page: https://dart.dev/tools/pub/pubspec # The following section is specific to Flutter packages. +# jni arrives transitively (path_provider -> path_provider_android -> +# jni_flutter -> jni). 1.0.1 added global_jni_env.c, which passes a `va_list` +# where a `void *` is expected: fine on x86_64, where va_list is an array type +# that decays to a pointer, but a hard error on aarch64, where it's a struct. +# That breaks `flutter build linux` on ARM64 hosts. Tracked upstream at +# dart-lang/native#3498 — drop this override once a fixed jni is released. +dependency_overrides: + jni: 1.0.0 + flutter: # The following line ensures that the Material Icons font is diff --git a/src/serious_python/example/run_example/pubspec.lock b/src/serious_python/example/run_example/pubspec.lock index 6d374be4..21ba23f7 100644 --- a/src/serious_python/example/run_example/pubspec.lock +++ b/src/serious_python/example/run_example/pubspec.lock @@ -171,7 +171,7 @@ packages: source: sdk version: "0.0.0" jni: - dependency: transitive + dependency: "direct overridden" description: name: jni sha256: c2230682d5bc2362c1c9e8d3c7f406d9cbba23ab3f2e203a025dd47e0fb2e68f @@ -384,42 +384,42 @@ packages: path: "../.." relative: true source: path - version: "4.0.0" + version: "4.4.1" serious_python_android: dependency: transitive description: path: "../../../serious_python_android" relative: true source: path - version: "4.0.0" + version: "4.4.1" serious_python_darwin: dependency: transitive description: path: "../../../serious_python_darwin" relative: true source: path - version: "4.0.0" + version: "4.4.1" serious_python_linux: dependency: transitive description: path: "../../../serious_python_linux" relative: true source: path - version: "4.0.0" + version: "4.4.1" serious_python_platform_interface: dependency: transitive description: path: "../../../serious_python_platform_interface" relative: true source: path - version: "4.0.0" + version: "4.4.1" serious_python_windows: dependency: transitive description: path: "../../../serious_python_windows" relative: true source: path - version: "4.0.0" + version: "4.4.1" shelf: dependency: transitive description: diff --git a/src/serious_python/example/run_example/pubspec.yaml b/src/serious_python/example/run_example/pubspec.yaml index 3035fb7e..5e3c3c30 100644 --- a/src/serious_python/example/run_example/pubspec.yaml +++ b/src/serious_python/example/run_example/pubspec.yaml @@ -58,6 +58,15 @@ dev_dependencies: # following page: https://dart.dev/tools/pub/pubspec # The following section is specific to Flutter packages. +# jni arrives transitively (path_provider -> path_provider_android -> +# jni_flutter -> jni). 1.0.1 added global_jni_env.c, which passes a `va_list` +# where a `void *` is expected: fine on x86_64, where va_list is an array type +# that decays to a pointer, but a hard error on aarch64, where it's a struct. +# That breaks `flutter build linux` on ARM64 hosts. Tracked upstream at +# dart-lang/native#3498 — drop this override once a fixed jni is released. +dependency_overrides: + jni: 1.0.0 + flutter: # The following line ensures that the Material Icons font is