pythonnative 0.35.0__py3-none-any.whl → 0.37.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. pythonnative/__init__.py +6 -4
  2. pythonnative/_ios_log.py +1 -1
  3. pythonnative/alerts.py +34 -88
  4. pythonnative/bootstrap.py +90 -0
  5. pythonnative/bridge/__init__.py +306 -0
  6. pythonnative/bridge/android.py +78 -0
  7. pythonnative/bridge/codec.py +164 -0
  8. pythonnative/bridge/fake.py +236 -0
  9. pythonnative/bridge/ios.py +138 -0
  10. pythonnative/cli/pn.py +24 -6
  11. pythonnative/components/layout.py +65 -3
  12. pythonnative/gestures.py +13 -10
  13. pythonnative/hooks.py +41 -26
  14. pythonnative/hosts/__init__.py +13 -36
  15. pythonnative/hosts/native.py +348 -0
  16. pythonnative/images.py +6 -5
  17. pythonnative/mutations.py +21 -0
  18. pythonnative/native_modules/__init__.py +30 -29
  19. pythonnative/native_modules/app_state.py +10 -6
  20. pythonnative/native_modules/battery.py +20 -84
  21. pythonnative/native_modules/biometrics.py +11 -124
  22. pythonnative/native_modules/camera.py +15 -291
  23. pythonnative/native_modules/clipboard.py +14 -109
  24. pythonnative/native_modules/desktop.py +322 -0
  25. pythonnative/native_modules/file_system.py +15 -21
  26. pythonnative/native_modules/haptics.py +19 -129
  27. pythonnative/native_modules/linking.py +24 -137
  28. pythonnative/native_modules/location.py +28 -180
  29. pythonnative/native_modules/net_info.py +25 -135
  30. pythonnative/native_modules/notifications.py +36 -257
  31. pythonnative/native_modules/permissions.py +20 -184
  32. pythonnative/native_modules/registry.py +513 -0
  33. pythonnative/native_modules/secure_store.py +18 -155
  34. pythonnative/native_modules/share.py +8 -113
  35. pythonnative/native_views/__init__.py +67 -86
  36. pythonnative/native_views/bridge_backend.py +264 -0
  37. pythonnative/platform.py +19 -15
  38. pythonnative/platform_metrics.py +3 -3
  39. pythonnative/project/android.py +0 -3
  40. pythonnative/project/builder.py +38 -16
  41. pythonnative/project/config.py +12 -2
  42. pythonnative/project/plugins.py +366 -0
  43. pythonnative/py.typed +0 -0
  44. pythonnative/runtime.py +35 -203
  45. pythonnative/sdk/__init__.py +48 -33
  46. pythonnative/sdk/_components.py +57 -76
  47. pythonnative/storage.py +18 -243
  48. pythonnative/templates/android_template/app/build.gradle +6 -9
  49. pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +34 -166
  50. pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt +13 -151
  51. pythonnative/templates/android_template/pythonnative/build.gradle +48 -0
  52. pythonnative/templates/android_template/pythonnative/src/main/AndroidManifest.xml +11 -0
  53. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/PNBridge.kt +214 -0
  54. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/PythonHost.kt +20 -0
  55. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/animation/AnimationSpecs.kt +141 -0
  56. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/animation/PNAnimator.kt +249 -0
  57. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/JsonUtil.kt +175 -0
  58. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/MainThread.kt +32 -0
  59. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/PNLog.kt +33 -0
  60. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/PNRegistry.kt +82 -0
  61. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/PNTransaction.kt +80 -0
  62. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/TransactionApplier.kt +115 -0
  63. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/ViewRegistry.kt +74 -0
  64. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/BuiltinComponents.kt +35 -0
  65. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ButtonManager.kt +33 -0
  66. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ComponentManager.kt +213 -0
  67. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ContainerManagers.kt +69 -0
  68. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ControlManagers.kt +186 -0
  69. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/DatePickerManager.kt +123 -0
  70. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ImageLoader.kt +141 -0
  71. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ImageManager.kt +156 -0
  72. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ModalManager.kt +134 -0
  73. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PNColor.kt +201 -0
  74. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PickerManager.kt +87 -0
  75. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PortalManager.kt +55 -0
  76. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PressableManager.kt +122 -0
  77. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ScrollViewManager.kt +214 -0
  78. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/SegmentedControlManager.kt +113 -0
  79. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/StatusBarManager.kt +52 -0
  80. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/TabBarManager.kt +131 -0
  81. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/TextInputManager.kt +245 -0
  82. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/TextManager.kt +233 -0
  83. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ViewStyler.kt +355 -0
  84. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/VirtualListManager.kt +179 -0
  85. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/WebViewManager.kt +149 -0
  86. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/gestures/GestureArbiter.kt +233 -0
  87. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/gestures/GestureCoordinator.kt +127 -0
  88. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/gestures/GestureRecognizers.kt +527 -0
  89. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/BuiltinModules.kt +73 -0
  90. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/DeviceModules.kt +316 -0
  91. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/HostModule.kt +97 -0
  92. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/MediaModules.kt +200 -0
  93. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/NativeModule.kt +135 -0
  94. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/NotificationsModule.kt +100 -0
  95. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/PermissionsModule.kt +88 -0
  96. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/StorageModules.kt +89 -0
  97. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/SystemModules.kt +178 -0
  98. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/plugins/GeneratedPlugins.kt +16 -0
  99. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/screens/Navigator.kt +116 -0
  100. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/screens/PNScreenFragment.kt +223 -0
  101. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/screens/ScreenRegistry.kt +80 -0
  102. pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNAccessibilityDelegate.kt +5 -4
  103. pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNBorderDrawable.kt +12 -3
  104. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/views/PNEditText.kt +18 -0
  105. pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNFrameLayout.kt +9 -10
  106. pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNVirtualListView.java +9 -12
  107. pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/AnimationSpecsTest.kt +75 -0
  108. pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/GestureArbiterTest.kt +197 -0
  109. pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/PNColorTest.kt +47 -0
  110. pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/PNTransactionTest.kt +78 -0
  111. pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/PromiseTest.kt +75 -0
  112. pythonnative/templates/android_template/settings.gradle +1 -0
  113. pythonnative/templates/ios_template/PythonNativeKit/Package.swift +21 -0
  114. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Animation/PNAnimator.swift +301 -0
  115. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNBridge.swift +191 -0
  116. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNRegistry.swift +140 -0
  117. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNTransaction.swift +165 -0
  118. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNViewRegistry.swift +53 -0
  119. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNButtonManagers.swift +206 -0
  120. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNColor.swift +173 -0
  121. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNComponentManager.swift +183 -0
  122. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNContainerView.swift +56 -0
  123. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNControlManagers.swift +281 -0
  124. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNImageManager.swift +221 -0
  125. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNOverlayManagers.swift +243 -0
  126. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNPressableManager.swift +114 -0
  127. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNScrollViewManager.swift +197 -0
  128. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTabBarManager.swift +106 -0
  129. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTextInputManager.swift +289 -0
  130. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTextManager.swift +230 -0
  131. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTransform.swift +82 -0
  132. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNViewManager.swift +86 -0
  133. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNViewState.swift +87 -0
  134. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNViewStyler.swift +340 -0
  135. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNVirtualListManager.swift +257 -0
  136. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNWebViewManager.swift +124 -0
  137. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Gestures/PNGestureCoordinator.swift +260 -0
  138. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/AlertModule.swift +92 -0
  139. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/HostModule.swift +158 -0
  140. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/LifecycleModules.swift +174 -0
  141. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/MediaModules.swift +177 -0
  142. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/PNNativeModule.swift +125 -0
  143. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/PermissionModules.swift +251 -0
  144. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/SystemModules.swift +251 -0
  145. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Plugins/PNPluginRegistration.swift +8 -0
  146. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Screens/PNScreenRegistry.swift +46 -0
  147. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Screens/PNViewController.swift +251 -0
  148. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNCompat.swift +46 -0
  149. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNJSON.swift +173 -0
  150. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNLog.swift +47 -0
  151. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNWindow.swift +51 -0
  152. pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNColorTests.swift +50 -0
  153. pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNGestureTests.swift +101 -0
  154. pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNManagerTests.swift +124 -0
  155. pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNModuleTests.swift +98 -0
  156. pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNTransactionTests.swift +91 -0
  157. pythonnative/templates/ios_template/ios_template/AppDelegate.swift +8 -50
  158. pythonnative/templates/ios_template/ios_template/Info.plist +4 -4
  159. pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +38 -46
  160. pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +11 -16
  161. pythonnative/templates/ios_template/ios_template/ViewController.swift +12 -142
  162. pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +47 -1
  163. pythonnative/utils.py +4 -82
  164. pythonnative/virtual_rows.py +3 -3
  165. {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/METADATA +3 -5
  166. pythonnative-0.37.0.dist-info/RECORD +263 -0
  167. pythonnative/hosts/android.py +0 -269
  168. pythonnative/hosts/ios.py +0 -404
  169. pythonnative/native_views/android.py +0 -3702
  170. pythonnative/native_views/ios.py +0 -5253
  171. pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/Navigator.kt +0 -43
  172. pythonnative-0.35.0.dist-info/RECORD +0 -155
  173. {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/WHEEL +0 -0
  174. {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/entry_points.txt +0 -0
  175. {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/licenses/LICENSE +0 -0
  176. {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/top_level.txt +0 -0
@@ -3,9 +3,11 @@
3
3
  // ios_template
4
4
  //
5
5
  // Creates the window programmatically and forwards scene-level events
6
- // (deep links, foreground/background transitions) to the Python side.
6
+ // (deep links, foreground/background transitions) to the Swift native
7
+ // modules, which relay them to Python over the bridge.
7
8
  //
8
9
 
10
+ import PythonNativeKit
9
11
  import UIKit
10
12
 
11
13
  class SceneDelegate: UIResponder, UIWindowSceneDelegate {
@@ -18,10 +20,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
18
20
  ) {
19
21
  guard let windowScene = (scene as? UIWindowScene) else { return }
20
22
  // A cold start from a deep link delivers the URL here, before
21
- // Python is running; PythonRuntime buffers it and flushes after
22
- // startup so Linking.get_initial_url() sees it.
23
+ // Python is running; LinkingModule buffers it until the bridge
24
+ // callback is registered so Linking.get_initial_url() sees it.
23
25
  for context in connectionOptions.urlContexts {
24
- PythonRuntime.shared.deliverURL(context.url.absoluteString)
26
+ LinkingModule.deliver(url: context.url.absoluteString)
25
27
  }
26
28
  let window = UIWindow(windowScene: windowScene)
27
29
  let root = ViewController()
@@ -33,33 +35,26 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
33
35
 
34
36
  func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
35
37
  for context in URLContexts {
36
- PythonRuntime.shared.deliverURL(context.url.absoluteString)
38
+ LinkingModule.deliver(url: context.url.absoluteString)
37
39
  }
38
40
  }
39
41
 
40
42
  // MARK: - AppState forwarding
41
43
 
42
- private func dispatchAppState(_ state: String) {
43
- guard PythonRuntime.shared.started else { return }
44
- PythonRuntime.shared.notify(
45
- module: "pythonnative.native_modules.app_state", function: "dispatch_app_state", state
46
- )
47
- }
48
-
49
44
  func sceneDidBecomeActive(_ scene: UIScene) {
50
- dispatchAppState("active")
45
+ AppStateModule.dispatch("active")
51
46
  }
52
47
 
53
48
  func sceneWillResignActive(_ scene: UIScene) {
54
- dispatchAppState("inactive")
49
+ AppStateModule.dispatch("inactive")
55
50
  }
56
51
 
57
52
  func sceneWillEnterForeground(_ scene: UIScene) {
58
- dispatchAppState("inactive")
53
+ AppStateModule.dispatch("inactive")
59
54
  }
60
55
 
61
56
  func sceneDidEnterBackground(_ scene: UIScene) {
62
- dispatchAppState("background")
57
+ AppStateModule.dispatch("background")
63
58
  }
64
59
 
65
60
  func sceneDidDisconnect(_ scene: UIScene) {}
@@ -2,158 +2,28 @@
2
2
  // ViewController.swift
3
3
  // ios_template
4
4
  //
5
- // Hosts one PythonNative screen. Python is initialized through
6
- // PythonRuntime (linked CPython, no PythonKit); a bootstrap failure
7
- // shows a full-screen error report instead of a silent fallback.
5
+ // Hosts one PythonNative screen. All screen plumbing lives in
6
+ // PythonNativeKit's PNViewController; this subclass only starts the
7
+ // embedded interpreter (showing a bootstrap error on failure) and
8
+ // points dev builds at the hot-reload overlay.
8
9
  //
9
10
 
11
+ import PythonNativeKit
10
12
  import UIKit
11
13
 
12
- class ViewController: UIViewController {
13
- // Optional keys for dynamic screen navigation. Push navigation sets
14
- // these before presenting the controller; nil means the app's entry
15
- // module (Info.plist `PNEntryModule`, default "app.main").
16
- @objc dynamic var requestedScreenPath: String? = nil
17
- @objc dynamic var requestedScreenArgsJSON: String? = nil
18
-
19
- private var screen: PyRef? = nil
20
- #if DEBUG
21
- private var hotReloadTimer: Timer? = nil
22
- #endif
23
-
24
- override func viewDidLoad() {
25
- super.viewDidLoad()
26
- view.backgroundColor = .systemBackground
27
-
14
+ final class ViewController: PNViewController {
15
+ override func prepareRuntime() -> Bool {
28
16
  do {
29
17
  try PythonRuntime.shared.ensureStarted()
30
18
  } catch {
31
19
  showBootstrapError("Python failed to start.\n\n\(error)")
32
- return
20
+ return false
33
21
  }
34
-
35
- // PythonNative's convention is "import the module and grab its
36
- // top-level `App` attribute". The entry module comes from
37
- // `app.entry_point` in pythonnative.toml (written to Info.plist as
38
- // `PNEntryModule` by `pn build`); push navigation overrides it via
39
- // `requestedScreenPath`.
40
- let entryModule = Bundle.main.object(forInfoDictionaryKey: "PNEntryModule") as? String
41
- let screenPath: String = requestedScreenPath ?? entryModule ?? "app.main"
42
- let addr = UInt(bitPattern: Unmanaged.passUnretained(self).toOpaque())
43
- do {
44
- let screen: PyRef
45
- if let argsJSON = requestedScreenArgsJSON {
46
- screen = try PythonRuntime.shared.call(
47
- module: "pythonnative.hosts", function: "create_screen", screenPath, addr, argsJSON
48
- )
49
- } else {
50
- screen = try PythonRuntime.shared.call(
51
- module: "pythonnative.hosts", function: "create_screen", screenPath, addr
52
- )
53
- }
54
- self.screen = screen
55
- #if DEBUG
56
- let devRoot = "\(NSHomeDirectory())/Documents/pythonnative_dev"
57
- try screen.call("enable_hot_reload", "\(devRoot)/reload.json", devRoot)
58
- #endif
59
- try screen.call("on_create")
60
- #if DEBUG
61
- startHotReloadPolling()
62
- #endif
63
- } catch {
64
- showBootstrapError("The PythonNative screen failed to mount.\n\n\(error)")
65
- }
66
- }
67
-
68
- // MARK: - Lifecycle forwarding
69
-
70
- private func forwardLifecycle(_ event: String) {
71
- guard screen != nil else { return }
72
- let addr = UInt(bitPattern: Unmanaged.passUnretained(self).toOpaque())
73
- PythonRuntime.shared.notify(
74
- module: "pythonnative.hosts", function: "forward_lifecycle", addr, event
75
- )
76
- }
77
-
78
- override func viewWillAppear(_ animated: Bool) {
79
- super.viewWillAppear(animated)
80
- forwardLifecycle("on_start")
81
- }
82
-
83
- override func viewDidLayoutSubviews() {
84
- super.viewDidLayoutSubviews()
85
- // The root view's safeAreaInsets are only valid after iOS has
86
- // positioned the view in its window; forward every layout pass
87
- // to Python so the reconciler can re-run layout against the
88
- // correct viewport (initial mount, rotation, multitasking, etc.).
89
- forwardLifecycle("on_layout")
90
- }
91
-
92
- override func viewDidAppear(_ animated: Bool) {
93
- super.viewDidAppear(animated)
94
- forwardLifecycle("on_resume")
95
- }
96
-
97
- override func viewWillDisappear(_ animated: Bool) {
98
- super.viewWillDisappear(animated)
99
- forwardLifecycle("on_pause")
100
- }
101
-
102
- override func viewDidDisappear(_ animated: Bool) {
103
- super.viewDidDisappear(animated)
104
- forwardLifecycle("on_stop")
105
- }
106
-
107
- override func encodeRestorableState(with coder: NSCoder) {
108
- super.encodeRestorableState(with: coder)
109
- forwardLifecycle("on_save_instance_state")
110
- }
111
-
112
- override func decodeRestorableState(with coder: NSCoder) {
113
- super.decodeRestorableState(with: coder)
114
- forwardLifecycle("on_restore_instance_state")
115
- }
116
-
117
- deinit {
118
22
  #if DEBUG
119
- hotReloadTimer?.invalidate()
120
- #endif
121
- if screen != nil {
122
- let addr = UInt(bitPattern: Unmanaged.passUnretained(self).toOpaque())
123
- PythonRuntime.shared.notify(
124
- module: "pythonnative.hosts", function: "forward_lifecycle", addr, "on_destroy"
125
- )
23
+ if devRoot == nil {
24
+ devRoot = "\(NSHomeDirectory())/Documents/pythonnative_dev"
126
25
  }
127
- }
128
-
129
- // MARK: - Hot reload (dev builds only)
130
-
131
- #if DEBUG
132
- private func startHotReloadPolling() {
133
- hotReloadTimer?.invalidate()
134
- hotReloadTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { [weak self] _ in
135
- guard let screen = self?.screen else { return }
136
- do {
137
- try screen.call("hot_reload_tick")
138
- } catch {
139
- NSLog("[PN] hot_reload_tick failed: \(error)")
140
- }
141
- }
142
- }
143
- #endif
144
-
145
- // MARK: - Bootstrap error UI
146
-
147
- private func showBootstrapError(_ message: String) {
148
- NSLog("[PN] %@", message)
149
- let text = UITextView(frame: view.bounds)
150
- text.autoresizingMask = [.flexibleWidth, .flexibleHeight]
151
- text.isEditable = false
152
- text.backgroundColor = UIColor(red: 0.75, green: 0.10, blue: 0.10, alpha: 1.0)
153
- text.textColor = .white
154
- text.font = UIFont.monospacedSystemFont(ofSize: 13, weight: .regular)
155
- text.textContainerInset = UIEdgeInsets(top: 60, left: 16, bottom: 40, right: 16)
156
- text.text = "PythonNative could not start\n\n\(message)"
157
- view.addSubview(text)
26
+ #endif
27
+ return true
158
28
  }
159
29
  }
@@ -3,7 +3,7 @@
3
3
  archiveVersion = 1;
4
4
  classes = {
5
5
  };
6
- objectVersion = 56;
6
+ objectVersion = 60;
7
7
  objects = {
8
8
 
9
9
  /* Begin PBXBuildFile section */
@@ -20,6 +20,7 @@
20
20
  AA0000000000000000000005 /* Python.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = AA0000000000000000000003 /* Python.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
21
21
  AA0000000000000000000007 /* app in Resources */ = {isa = PBXBuildFile; fileRef = AA0000000000000000000006 /* app */; };
22
22
  AA0000000000000000000009 /* app_packages in Resources */ = {isa = PBXBuildFile; fileRef = AA0000000000000000000008 /* app_packages */; };
23
+ AA000000000000000000000D /* PythonNativeKit in Frameworks */ = {isa = PBXBuildFile; productRef = AA000000000000000000000C /* PythonNativeKit */; };
23
24
  /* End PBXBuildFile section */
24
25
 
25
26
  /* Begin PBXContainerItemProxy section */
@@ -65,6 +66,7 @@
65
66
  buildActionMask = 2147483647;
66
67
  files = (
67
68
  AA0000000000000000000004 /* Python.xcframework in Frameworks */,
69
+ AA000000000000000000000D /* PythonNativeKit in Frameworks */,
68
70
  );
69
71
  runOnlyForDeploymentPostprocessing = 0;
70
72
  };
@@ -158,6 +160,9 @@
158
160
  dependencies = (
159
161
  );
160
162
  name = ios_template;
163
+ packageProductDependencies = (
164
+ AA000000000000000000000C /* PythonNativeKit */,
165
+ );
161
166
  productName = ios_template;
162
167
  productReference = 146EBB7E2A40CCC900E4A2B3 /* ios_template.app */;
163
168
  productType = "com.apple.product-type.application";
@@ -230,6 +235,9 @@
230
235
  Base,
231
236
  );
232
237
  mainGroup = 146EBB752A40CCC900E4A2B3;
238
+ packageReferences = (
239
+ AA000000000000000000000E /* XCLocalSwiftPackageReference "PythonNativeKit" */,
240
+ );
233
241
  productRefGroup = 146EBB7F2A40CCC900E4A2B3 /* Products */;
234
242
  projectDirPath = "";
235
243
  projectRoot = "";
@@ -504,6 +512,18 @@
504
512
  "@executable_path/Frameworks",
505
513
  );
506
514
  MARKETING_VERSION = 1.0;
515
+ OTHER_LDFLAGS = (
516
+ "$(inherited)",
517
+ "-Wl,-exported_symbol,_pn_bridge_apply",
518
+ "-Wl,-exported_symbol,_pn_bridge_measure",
519
+ "-Wl,-exported_symbol,_pn_bridge_command",
520
+ "-Wl,-exported_symbol,_pn_bridge_animate",
521
+ "-Wl,-exported_symbol,_pn_bridge_call",
522
+ "-Wl,-exported_symbol,_pn_bridge_free",
523
+ "-Wl,-exported_symbol,_pn_bridge_set_callback",
524
+ "-Wl,-exported_symbol,_pn_bridge_protocol_version",
525
+ "-Wl,-exported_symbol,_main",
526
+ );
507
527
  PRODUCT_BUNDLE_IDENTIFIER = "com.pythonnative.ios-template";
508
528
  PRODUCT_NAME = "$(TARGET_NAME)";
509
529
  SWIFT_EMIT_LOC_STRINGS = YES;
@@ -542,6 +562,18 @@
542
562
  "@executable_path/Frameworks",
543
563
  );
544
564
  MARKETING_VERSION = 1.0;
565
+ OTHER_LDFLAGS = (
566
+ "$(inherited)",
567
+ "-Wl,-exported_symbol,_pn_bridge_apply",
568
+ "-Wl,-exported_symbol,_pn_bridge_measure",
569
+ "-Wl,-exported_symbol,_pn_bridge_command",
570
+ "-Wl,-exported_symbol,_pn_bridge_animate",
571
+ "-Wl,-exported_symbol,_pn_bridge_call",
572
+ "-Wl,-exported_symbol,_pn_bridge_free",
573
+ "-Wl,-exported_symbol,_pn_bridge_set_callback",
574
+ "-Wl,-exported_symbol,_pn_bridge_protocol_version",
575
+ "-Wl,-exported_symbol,_main",
576
+ );
545
577
  PRODUCT_BUNDLE_IDENTIFIER = "com.pythonnative.ios-template";
546
578
  PRODUCT_NAME = "$(TARGET_NAME)";
547
579
  SWIFT_EMIT_LOC_STRINGS = YES;
@@ -668,6 +700,20 @@
668
700
  defaultConfigurationName = Release;
669
701
  };
670
702
  /* End XCConfigurationList section */
703
+
704
+ /* Begin XCLocalSwiftPackageReference section */
705
+ AA000000000000000000000E /* XCLocalSwiftPackageReference "PythonNativeKit" */ = {
706
+ isa = XCLocalSwiftPackageReference;
707
+ relativePath = PythonNativeKit;
708
+ };
709
+ /* End XCLocalSwiftPackageReference section */
710
+
711
+ /* Begin XCSwiftPackageProductDependency section */
712
+ AA000000000000000000000C /* PythonNativeKit */ = {
713
+ isa = XCSwiftPackageProductDependency;
714
+ productName = PythonNativeKit;
715
+ };
716
+ /* End XCSwiftPackageProductDependency section */
671
717
  };
672
718
  rootObject = 146EBB762A40CCC900E4A2B3 /* Project object */;
673
719
  }
pythonnative/utils.py CHANGED
@@ -6,19 +6,13 @@ results are cached the first time
6
6
  [`IS_ANDROID`][pythonnative.utils.IS_ANDROID] and
7
7
  [`IS_IOS`][pythonnative.utils.IS_IOS] are read.
8
8
 
9
- The Android variants also expose a small global registry for the
10
- current `Activity` / `Context` and the screen's fragment container view;
11
- both are populated by the bundled Android template before any
12
- PythonNative code runs.
13
-
14
9
  Attributes:
15
10
  IS_ANDROID: `True` when running inside an Android process (either
16
11
  because `ANDROID_*` env vars are present or because Chaquopy's
17
12
  `java` module imports successfully).
18
13
  IS_IOS: `True` when running inside an iOS app bundle (signaled by
19
14
  `PN_PLATFORM=ios`, `sys.platform == "ios"`, or a Simulator
20
- `HOME` path). Importing `rubicon-objc` alone is intentionally
21
- not enough to trigger this flag.
15
+ `HOME` path).
22
16
  IS_DESKTOP: `True` when running the desktop preview backend
23
17
  (signaled by `PN_PLATFORM=desktop`, set by ``pn preview``).
24
18
  This drives the Tkinter native-view registry so a PythonNative
@@ -27,7 +21,7 @@ Attributes:
27
21
 
28
22
  import os
29
23
  import sys
30
- from typing import Any, Optional
24
+ from typing import Optional
31
25
 
32
26
  # ======================================================================
33
27
  # Platform detection
@@ -65,10 +59,8 @@ def _detect_ios() -> bool:
65
59
  - `/CoreSimulator/Devices/` in `$HOME` (iOS Simulator fallback if
66
60
  the template signal is missing for some reason).
67
61
 
68
- Crucially, having `rubicon-objc` importable is *not* enough:
69
- developers frequently install it on macOS via the `[ios]` extra,
70
- and treating that as iOS would cause subtle side effects
71
- (e.g., stdout redirection) on desktop machines.
62
+ Running on macOS is never enough on its own: the desktop preview
63
+ and unit tests run there and must not be mistaken for iOS.
72
64
  """
73
65
  if os.environ.get("PN_PLATFORM") == "ios":
74
66
  return True
@@ -148,73 +140,3 @@ Set by ``pn preview`` via ``PN_PLATFORM=desktop``. Mutually exclusive
148
140
  with `IS_ANDROID` / `IS_IOS`. Off-device unit tests leave this
149
141
  ``False`` and inject a mock registry instead.
150
142
  """
151
-
152
- # ======================================================================
153
- # Android context management
154
- # ======================================================================
155
-
156
- _android_context: Any = None
157
- _android_fragment_container: Any = None
158
-
159
-
160
- def set_android_context(context: Any) -> None:
161
- """Record the current Android `Activity`/`Context`.
162
-
163
- Called by the bundled Android template before any view is created;
164
- most app code should not call this directly.
165
-
166
- Args:
167
- context: A Java `android.content.Context` (typically the
168
- current `Activity`).
169
- """
170
- global _android_context
171
- _android_context = context
172
-
173
-
174
- def set_android_fragment_container(container_view: Any) -> None:
175
- """Record the current `Fragment` root container `ViewGroup`.
176
-
177
- Args:
178
- container_view: A Java `android.view.ViewGroup` that holds the
179
- screen's view tree.
180
- """
181
- global _android_fragment_container
182
- _android_fragment_container = container_view
183
-
184
-
185
- def get_android_context() -> Any:
186
- """Return the current Android `Activity`/`Context`.
187
-
188
- Returns:
189
- The most recently recorded Android context.
190
-
191
- Raises:
192
- RuntimeError: If called on a non-Android platform, or before
193
- the template has registered a context.
194
- """
195
- if not IS_ANDROID:
196
- raise RuntimeError("get_android_context() called on non-Android platform")
197
- if _android_context is None:
198
- raise RuntimeError(
199
- "Android context not set. Ensure the screen host is initialized from an Activity before constructing views."
200
- )
201
- return _android_context
202
-
203
-
204
- def get_android_fragment_container() -> Any:
205
- """Return the current `Fragment` container `ViewGroup`.
206
-
207
- Returns:
208
- The most recently recorded fragment container view.
209
-
210
- Raises:
211
- RuntimeError: If called on a non-Android platform, or before
212
- `ScreenFragment` has registered its container.
213
- """
214
- if not IS_ANDROID:
215
- raise RuntimeError("get_android_fragment_container() called on non-Android platform")
216
- if _android_fragment_container is None:
217
- raise RuntimeError(
218
- "Android fragment container not set. Ensure ScreenFragment has been created before set_root_view."
219
- )
220
- return _android_fragment_container
@@ -90,9 +90,9 @@ class RowSubtree:
90
90
  class RowHostPool:
91
91
  """Per-list bookkeeping of row subtrees keyed by native container.
92
92
 
93
- Platform handlers create one pool per virtualized list view. Keys
94
- are platform-stable container identities (Java identity hash on
95
- Android, ``contentView`` pointer on iOS).
93
+ The view backend creates one pool per virtualized list view. Keys
94
+ are the container ids native sends with ``on_bind_row`` (stable for
95
+ the life of a recycled cell).
96
96
  """
97
97
 
98
98
  def __init__(self) -> None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythonnative
3
- Version: 0.35.0
3
+ Version: 0.37.0
4
4
  Summary: Cross-platform native UI toolkit for Android and iOS
5
5
  Author: Owen Carey
6
6
  License: MIT License
@@ -44,8 +44,6 @@ License-File: LICENSE
44
44
  Requires-Dist: requests>=2.31.0
45
45
  Requires-Dist: tomli>=2.0; python_version < "3.11"
46
46
  Requires-Dist: typing_extensions>=4.4; python_version < "3.11"
47
- Provides-Extra: ios
48
- Requires-Dist: rubicon-objc<0.5.0,>=0.4.6; extra == "ios"
49
47
  Provides-Extra: build
50
48
  Requires-Dist: Pillow>=10.0; extra == "build"
51
49
  Dynamic: license-file
@@ -78,7 +76,7 @@ Dynamic: license-file
78
76
 
79
77
  ## Overview
80
78
 
81
- PythonNative is a cross-platform toolkit for building native Android and iOS apps in Python. It provides a **declarative, React-like component model** with hooks and automatic reconciliation, powered by Chaquopy on Android and rubicon-objc on iOS. Write function components with `use_state`, `use_effect`, and friends, just like React, and let PythonNative handle creating and updating native views.
79
+ PythonNative is a cross-platform toolkit for building native Android and iOS apps in Python. It provides a **declarative, React-like component model** with hooks and automatic reconciliation on top of a **native rendering core** written in Swift and Kotlin. Write function components with `use_state`, `use_effect`, and friends, just like React, and let PythonNative handle creating and updating native views.
82
80
 
83
81
  ## Features
84
82
 
@@ -94,7 +92,7 @@ PythonNative is a cross-platform toolkit for building native Android and iOS app
94
92
  - **Native-driven animations:** The `Animated` API (timing / spring / decay / loop / stagger, awaitable or fire-and-forget) hands animations to Core Animation and `ViewPropertyAnimator` whenever possible, so no Python code runs per frame; a pure-Python ticker covers the rest. `interpolate`, arithmetic operators on animated nodes, `Animated.event` scroll binding, and `diff_clamp` cover the scroll-driven patterns (collapsing headers, parallax) that define native feel.
95
93
  - **Native gesture system:** Attach `Tap`, `LongPress`, `Pan`, `Swipe`, `Fling`, `Pinch`, and `Rotation` recognizers to any view via the `gestures=` prop, backed by `UIGestureRecognizer` on iOS and a unit-testable pure-Python arbiter on Android and desktop. Compose them with `Race`, `Exclusive`, and `Simultaneous` for cross-gesture arbitration (single vs. double tap, drag vs. long press).
96
94
  - **Virtualized lists:** `FlatList` / `SectionList` window their rows in Python over the platform scroll view: uniform, exact, or measured variable heights, grids, headers/footers, infinite scroll, and an imperative scroll controller, identical on every platform.
97
- - **Direct native bindings:** Python calls platform APIs directly through Chaquopy and rubicon-objc, with no JavaScript bridge.
95
+ - **Native rendering core:** each commit is one serialized transaction applied by Swift and Kotlin component managers, the same shape as React Native's Fabric. Device APIs are native modules registered by name (TurboModules-style), callable from thin Python facades.
98
96
  - **Custom-component SDK:** Wrap any platform widget as a first-class element with type-checked props via `pythonnative.sdk` (`Props`, `@native_component`, `element_factory`). Plugins distributed on PyPI auto-register through the `pythonnative.handlers` entry-point group.
99
97
  - **CLI scaffolding:** `pn init` creates a ready-to-run project; `pn run android` and `pn run ios` build and launch your app.
100
98
  - **Instant desktop preview:** `pn preview` renders your app in a native desktop window via Tkinter with Fast Refresh on every save: iterate on layout, state, and navigation in milliseconds without booting a simulator or device. The reconciler, hooks, layout engine, and navigation are the same code that ships to the phone.