pythonnative 0.40.0__cp313-cp313-win_amd64.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 (418) hide show
  1. pythonnative/__init__.py +411 -0
  2. pythonnative/_ios_log.py +92 -0
  3. pythonnative/_yoga.cp313-win_amd64.pyd +0 -0
  4. pythonnative/alerts.py +236 -0
  5. pythonnative/animated.py +1687 -0
  6. pythonnative/animation_graph.py +78 -0
  7. pythonnative/appearance.py +124 -0
  8. pythonnative/bootstrap.py +100 -0
  9. pythonnative/bridge/__init__.py +302 -0
  10. pythonnative/bridge/android.py +78 -0
  11. pythonnative/bridge/codec.py +170 -0
  12. pythonnative/bridge/commits.py +114 -0
  13. pythonnative/bridge/fake.py +243 -0
  14. pythonnative/bridge/ios.py +138 -0
  15. pythonnative/bridge/web.py +489 -0
  16. pythonnative/cli/__init__.py +7 -0
  17. pythonnative/cli/pn.py +1214 -0
  18. pythonnative/component.py +271 -0
  19. pythonnative/components/__init__.py +111 -0
  20. pythonnative/components/_base.py +166 -0
  21. pythonnative/components/controls.py +521 -0
  22. pythonnative/components/layout.py +518 -0
  23. pythonnative/components/lists.py +785 -0
  24. pythonnative/components/media.py +204 -0
  25. pythonnative/components/overlays.py +107 -0
  26. pythonnative/components/pressable.py +252 -0
  27. pythonnative/components/structural.py +167 -0
  28. pythonnative/components/text.py +291 -0
  29. pythonnative/devclient.py +824 -0
  30. pythonnative/devserver/__init__.py +34 -0
  31. pythonnative/devserver/server.py +718 -0
  32. pythonnative/devserver/static/animation_graph.js +82 -0
  33. pythonnative/devserver/static/bridge.js +147 -0
  34. pythonnative/devserver/static/colors.js +70 -0
  35. pythonnative/devserver/static/host.js +552 -0
  36. pythonnative/devserver/static/index.html +60 -0
  37. pythonnative/devserver/static/layout.js +85 -0
  38. pythonnative/devserver/static/preview.css +415 -0
  39. pythonnative/devserver/static/renderer.js +1994 -0
  40. pythonnative/devserver/static/shell.js +345 -0
  41. pythonnative/devserver/static/yoga/LICENSE +21 -0
  42. pythonnative/devserver/static/yoga/binaries/yoga-wasm-base64-esm.js +75 -0
  43. pythonnative/devserver/static/yoga/src/generated/YGEnums.d.ts +189 -0
  44. pythonnative/devserver/static/yoga/src/generated/YGEnums.js +211 -0
  45. pythonnative/devserver/static/yoga/src/generated/YGEnums.js.map +1 -0
  46. pythonnative/devserver/static/yoga/src/index.d.ts +12 -0
  47. pythonnative/devserver/static/yoga/src/index.js +16 -0
  48. pythonnative/devserver/static/yoga/src/index.js.map +1 -0
  49. pythonnative/devserver/static/yoga/src/load.d.ts +11 -0
  50. pythonnative/devserver/static/yoga/src/load.js +17 -0
  51. pythonnative/devserver/static/yoga/src/load.js.map +1 -0
  52. pythonnative/devserver/static/yoga/src/wrapAssembly.d.ts +155 -0
  53. pythonnative/devserver/static/yoga/src/wrapAssembly.js +125 -0
  54. pythonnative/devserver/static/yoga/src/wrapAssembly.js.map +1 -0
  55. pythonnative/devserver/watcher.py +267 -0
  56. pythonnative/devserver/ws.py +421 -0
  57. pythonnative/diagnostics.py +241 -0
  58. pythonnative/element.py +159 -0
  59. pythonnative/equality.py +19 -0
  60. pythonnative/events.py +231 -0
  61. pythonnative/gestures.py +1333 -0
  62. pythonnative/hooks.py +1621 -0
  63. pythonnative/hosts/__init__.py +63 -0
  64. pythonnative/hosts/base.py +596 -0
  65. pythonnative/hosts/native.py +302 -0
  66. pythonnative/hot_reload.py +585 -0
  67. pythonnative/layout.py +328 -0
  68. pythonnative/mutations.py +142 -0
  69. pythonnative/native/android/build.gradle +52 -0
  70. pythonnative/native/android/gradle.properties +3 -0
  71. pythonnative/native/android/settings.gradle +12 -0
  72. pythonnative/native/android/src/main/AndroidManifest.xml +11 -0
  73. pythonnative/native/android/src/main/cpp/CMakeLists.txt +9 -0
  74. pythonnative/native/android/src/main/cpp/yoga_jni.cpp +52 -0
  75. pythonnative/native/android/src/main/java/com/pythonnative/generated/NativeModules.kt +5 -0
  76. pythonnative/native/android/src/main/java/com/pythonnative/generated/NativeProps.kt +3705 -0
  77. pythonnative/native/android/src/main/java/com/pythonnative/generated/PNContracts.kt +129 -0
  78. pythonnative/native/android/src/main/java/com/pythonnative/runtime/PNBridge.kt +261 -0
  79. pythonnative/native/android/src/main/java/com/pythonnative/runtime/PythonHost.kt +20 -0
  80. pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/AnimationGraph.kt +108 -0
  81. pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/AnimationSpecs.kt +141 -0
  82. pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/PNAnimator.kt +257 -0
  83. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/CommitState.kt +106 -0
  84. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/JsonUtil.kt +175 -0
  85. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/MainThread.kt +40 -0
  86. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNLog.kt +33 -0
  87. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNRegistry.kt +82 -0
  88. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNTransaction.kt +80 -0
  89. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/TransactionApplier.kt +117 -0
  90. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/ViewRegistry.kt +74 -0
  91. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/BuiltinComponents.kt +37 -0
  92. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ButtonManager.kt +32 -0
  93. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ComponentManager.kt +213 -0
  94. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ContainerManagers.kt +69 -0
  95. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ControlManagers.kt +186 -0
  96. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/DatePickerManager.kt +123 -0
  97. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ImageLoader.kt +141 -0
  98. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ImageManager.kt +162 -0
  99. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ModalManager.kt +134 -0
  100. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PNColor.kt +201 -0
  101. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PickerManager.kt +86 -0
  102. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PortalManager.kt +55 -0
  103. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PressableManager.kt +122 -0
  104. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ScreenStackManager.kt +110 -0
  105. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ScrollViewManager.kt +214 -0
  106. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/SegmentedControlManager.kt +113 -0
  107. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/StatusBarManager.kt +52 -0
  108. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TabBarManager.kt +131 -0
  109. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TextInputManager.kt +250 -0
  110. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TextManager.kt +233 -0
  111. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ViewStyler.kt +355 -0
  112. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/VirtualListManager.kt +159 -0
  113. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/WebViewManager.kt +149 -0
  114. pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureArbiter.kt +233 -0
  115. pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureCoordinator.kt +127 -0
  116. pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureRecognizers.kt +527 -0
  117. pythonnative/native/android/src/main/java/com/pythonnative/runtime/layout/NativeLayout.kt +145 -0
  118. pythonnative/native/android/src/main/java/com/pythonnative/runtime/layout/YogaNode.kt +29 -0
  119. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/BuiltinModules.kt +73 -0
  120. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/DeviceModules.kt +316 -0
  121. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/HostModule.kt +61 -0
  122. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/MediaModules.kt +200 -0
  123. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/NativeModule.kt +158 -0
  124. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/NotificationsModule.kt +100 -0
  125. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/PermissionsModule.kt +99 -0
  126. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/StorageModules.kt +89 -0
  127. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/SystemModules.kt +178 -0
  128. pythonnative/native/android/src/main/java/com/pythonnative/runtime/plugins/GeneratedPlugins.kt +16 -0
  129. pythonnative/native/android/src/main/java/com/pythonnative/runtime/screens/PNScreenFragment.kt +188 -0
  130. pythonnative/native/android/src/main/java/com/pythonnative/runtime/screens/ScreenRegistry.kt +80 -0
  131. pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNAccessibilityDelegate.kt +48 -0
  132. pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNBorderDrawable.kt +76 -0
  133. pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNEditText.kt +18 -0
  134. pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNFrameLayout.kt +49 -0
  135. pythonnative/native/android/src/test/java/com/pythonnative/runtime/AnimationSpecsTest.kt +75 -0
  136. pythonnative/native/android/src/test/java/com/pythonnative/runtime/GestureArbiterTest.kt +197 -0
  137. pythonnative/native/android/src/test/java/com/pythonnative/runtime/PNColorTest.kt +47 -0
  138. pythonnative/native/android/src/test/java/com/pythonnative/runtime/PNTransactionTest.kt +78 -0
  139. pythonnative/native/android/src/test/java/com/pythonnative/runtime/PromiseTest.kt +89 -0
  140. pythonnative/native/ios/Package.swift +23 -0
  141. pythonnative/native/ios/Sources/PythonNativeKit/Animation/PNAnimationGraph.swift +199 -0
  142. pythonnative/native/ios/Sources/PythonNativeKit/Animation/PNAnimator.swift +312 -0
  143. pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNBridge.swift +255 -0
  144. pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNCommit.swift +104 -0
  145. pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNRegistry.swift +142 -0
  146. pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNTransaction.swift +166 -0
  147. pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNViewRegistry.swift +53 -0
  148. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNButtonManagers.swift +207 -0
  149. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNColor.swift +173 -0
  150. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNComponentManager.swift +183 -0
  151. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNContainerView.swift +56 -0
  152. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNControlManagers.swift +281 -0
  153. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNImageManager.swift +222 -0
  154. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNOverlayManagers.swift +243 -0
  155. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNPressableManager.swift +114 -0
  156. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNScreenStackManager.swift +107 -0
  157. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNScrollViewManager.swift +197 -0
  158. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTabBarManager.swift +106 -0
  159. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTextInputManager.swift +297 -0
  160. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTextManager.swift +230 -0
  161. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTransform.swift +82 -0
  162. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewManager.swift +86 -0
  163. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewState.swift +87 -0
  164. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewStyler.swift +340 -0
  165. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNVirtualListManager.swift +155 -0
  166. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNWebViewManager.swift +124 -0
  167. pythonnative/native/ios/Sources/PythonNativeKit/Generated/NativeModules.swift +1 -0
  168. pythonnative/native/ios/Sources/PythonNativeKit/Generated/NativeProps.swift +3775 -0
  169. pythonnative/native/ios/Sources/PythonNativeKit/Generated/PNContracts.swift +46 -0
  170. pythonnative/native/ios/Sources/PythonNativeKit/Gestures/PNGestureCoordinator.swift +260 -0
  171. pythonnative/native/ios/Sources/PythonNativeKit/Layout/PNLayout.swift +173 -0
  172. pythonnative/native/ios/Sources/PythonNativeKit/Modules/AlertModule.swift +92 -0
  173. pythonnative/native/ios/Sources/PythonNativeKit/Modules/HostModule.swift +80 -0
  174. pythonnative/native/ios/Sources/PythonNativeKit/Modules/LifecycleModules.swift +174 -0
  175. pythonnative/native/ios/Sources/PythonNativeKit/Modules/MediaModules.swift +177 -0
  176. pythonnative/native/ios/Sources/PythonNativeKit/Modules/PNNativeModule.swift +168 -0
  177. pythonnative/native/ios/Sources/PythonNativeKit/Modules/PermissionModules.swift +266 -0
  178. pythonnative/native/ios/Sources/PythonNativeKit/Modules/SystemModules.swift +251 -0
  179. pythonnative/native/ios/Sources/PythonNativeKit/Plugins/PNPluginRegistration.swift +8 -0
  180. pythonnative/native/ios/Sources/PythonNativeKit/Screens/PNScreenRegistry.swift +46 -0
  181. pythonnative/native/ios/Sources/PythonNativeKit/Screens/PNViewController.swift +237 -0
  182. pythonnative/native/ios/Sources/PythonNativeKit/Support/PNCompat.swift +46 -0
  183. pythonnative/native/ios/Sources/PythonNativeKit/Support/PNJSON.swift +173 -0
  184. pythonnative/native/ios/Sources/PythonNativeKit/Support/PNLog.swift +47 -0
  185. pythonnative/native/ios/Sources/PythonNativeKit/Support/PNWindow.swift +51 -0
  186. pythonnative/native/ios/Tests/PythonNativeKitTests/PNAnimationGraphTests.swift +26 -0
  187. pythonnative/native/ios/Tests/PythonNativeKitTests/PNColorTests.swift +50 -0
  188. pythonnative/native/ios/Tests/PythonNativeKitTests/PNCommitTests.swift +86 -0
  189. pythonnative/native/ios/Tests/PythonNativeKitTests/PNGestureTests.swift +101 -0
  190. pythonnative/native/ios/Tests/PythonNativeKitTests/PNManagerTests.swift +126 -0
  191. pythonnative/native/ios/Tests/PythonNativeKitTests/PNModuleTests.swift +111 -0
  192. pythonnative/native/ios/Tests/PythonNativeKitTests/PNTransactionTests.swift +91 -0
  193. pythonnative/native/yoga/LICENSE +21 -0
  194. pythonnative/native/yoga/Package.swift +9 -0
  195. pythonnative/native/yoga/VENDORED.md +2 -0
  196. pythonnative/native/yoga/include/PNStyle.h +10 -0
  197. pythonnative/native/yoga/include/module.modulemap +5 -0
  198. pythonnative/native/yoga/include/yoga/YGConfig.h +158 -0
  199. pythonnative/native/yoga/include/yoga/YGEnums.h +142 -0
  200. pythonnative/native/yoga/include/yoga/YGMacros.h +99 -0
  201. pythonnative/native/yoga/include/yoga/YGNode.h +304 -0
  202. pythonnative/native/yoga/include/yoga/YGNodeLayout.h +35 -0
  203. pythonnative/native/yoga/include/yoga/YGNodeStyle.h +130 -0
  204. pythonnative/native/yoga/include/yoga/YGPixelGrid.h +29 -0
  205. pythonnative/native/yoga/include/yoga/YGValue.h +84 -0
  206. pythonnative/native/yoga/include/yoga/Yoga.h +21 -0
  207. pythonnative/native/yoga/python.cpp +16 -0
  208. pythonnative/native/yoga/style.cpp +272 -0
  209. pythonnative/native/yoga/yoga/CMakeLists.txt +41 -0
  210. pythonnative/native/yoga/yoga/YGConfig.cpp +92 -0
  211. pythonnative/native/yoga/yoga/YGConfig.h +158 -0
  212. pythonnative/native/yoga/yoga/YGEnums.cpp +262 -0
  213. pythonnative/native/yoga/yoga/YGEnums.h +142 -0
  214. pythonnative/native/yoga/yoga/YGMacros.h +99 -0
  215. pythonnative/native/yoga/yoga/YGNode.cpp +366 -0
  216. pythonnative/native/yoga/yoga/YGNode.h +304 -0
  217. pythonnative/native/yoga/yoga/YGNodeLayout.cpp +92 -0
  218. pythonnative/native/yoga/yoga/YGNodeLayout.h +35 -0
  219. pythonnative/native/yoga/yoga/YGNodeStyle.cpp +405 -0
  220. pythonnative/native/yoga/yoga/YGNodeStyle.h +130 -0
  221. pythonnative/native/yoga/yoga/YGPixelGrid.cpp +22 -0
  222. pythonnative/native/yoga/yoga/YGPixelGrid.h +29 -0
  223. pythonnative/native/yoga/yoga/YGValue.cpp +20 -0
  224. pythonnative/native/yoga/yoga/YGValue.h +84 -0
  225. pythonnative/native/yoga/yoga/Yoga.h +21 -0
  226. pythonnative/native/yoga/yoga/algorithm/AbsoluteLayout.cpp +563 -0
  227. pythonnative/native/yoga/yoga/algorithm/AbsoluteLayout.h +41 -0
  228. pythonnative/native/yoga/yoga/algorithm/Align.h +72 -0
  229. pythonnative/native/yoga/yoga/algorithm/Baseline.cpp +78 -0
  230. pythonnative/native/yoga/yoga/algorithm/Baseline.h +21 -0
  231. pythonnative/native/yoga/yoga/algorithm/BoundAxis.h +79 -0
  232. pythonnative/native/yoga/yoga/algorithm/Cache.cpp +121 -0
  233. pythonnative/native/yoga/yoga/algorithm/Cache.h +30 -0
  234. pythonnative/native/yoga/yoga/algorithm/CalculateLayout.cpp +2428 -0
  235. pythonnative/native/yoga/yoga/algorithm/CalculateLayout.h +38 -0
  236. pythonnative/native/yoga/yoga/algorithm/FlexDirection.h +120 -0
  237. pythonnative/native/yoga/yoga/algorithm/FlexLine.cpp +124 -0
  238. pythonnative/native/yoga/yoga/algorithm/FlexLine.h +75 -0
  239. pythonnative/native/yoga/yoga/algorithm/PixelGrid.cpp +132 -0
  240. pythonnative/native/yoga/yoga/algorithm/PixelGrid.h +29 -0
  241. pythonnative/native/yoga/yoga/algorithm/SizingMode.h +73 -0
  242. pythonnative/native/yoga/yoga/algorithm/TrailingPosition.h +44 -0
  243. pythonnative/native/yoga/yoga/config/Config.cpp +135 -0
  244. pythonnative/native/yoga/yoga/config/Config.h +92 -0
  245. pythonnative/native/yoga/yoga/debug/AssertFatal.cpp +53 -0
  246. pythonnative/native/yoga/yoga/debug/AssertFatal.h +29 -0
  247. pythonnative/native/yoga/yoga/debug/Log.cpp +107 -0
  248. pythonnative/native/yoga/yoga/debug/Log.h +34 -0
  249. pythonnative/native/yoga/yoga/enums/Align.h +47 -0
  250. pythonnative/native/yoga/yoga/enums/BoxSizing.h +40 -0
  251. pythonnative/native/yoga/yoga/enums/Dimension.h +40 -0
  252. pythonnative/native/yoga/yoga/enums/Direction.h +41 -0
  253. pythonnative/native/yoga/yoga/enums/Display.h +41 -0
  254. pythonnative/native/yoga/yoga/enums/Edge.h +47 -0
  255. pythonnative/native/yoga/yoga/enums/Errata.h +41 -0
  256. pythonnative/native/yoga/yoga/enums/ExperimentalFeature.h +39 -0
  257. pythonnative/native/yoga/yoga/enums/FlexDirection.h +42 -0
  258. pythonnative/native/yoga/yoga/enums/Gutter.h +41 -0
  259. pythonnative/native/yoga/yoga/enums/Justify.h +44 -0
  260. pythonnative/native/yoga/yoga/enums/LogLevel.h +44 -0
  261. pythonnative/native/yoga/yoga/enums/MeasureMode.h +41 -0
  262. pythonnative/native/yoga/yoga/enums/NodeType.h +40 -0
  263. pythonnative/native/yoga/yoga/enums/Overflow.h +41 -0
  264. pythonnative/native/yoga/yoga/enums/PhysicalEdge.h +21 -0
  265. pythonnative/native/yoga/yoga/enums/PositionType.h +41 -0
  266. pythonnative/native/yoga/yoga/enums/Unit.h +42 -0
  267. pythonnative/native/yoga/yoga/enums/Wrap.h +41 -0
  268. pythonnative/native/yoga/yoga/enums/YogaEnums.h +85 -0
  269. pythonnative/native/yoga/yoga/event/event.cpp +87 -0
  270. pythonnative/native/yoga/yoga/event/event.h +130 -0
  271. pythonnative/native/yoga/yoga/module.modulemap +21 -0
  272. pythonnative/native/yoga/yoga/node/CachedMeasurement.h +53 -0
  273. pythonnative/native/yoga/yoga/node/LayoutResults.cpp +48 -0
  274. pythonnative/native/yoga/yoga/node/LayoutResults.h +122 -0
  275. pythonnative/native/yoga/yoga/node/LayoutableChildren.h +148 -0
  276. pythonnative/native/yoga/yoga/node/Node.cpp +443 -0
  277. pythonnative/native/yoga/yoga/node/Node.h +337 -0
  278. pythonnative/native/yoga/yoga/numeric/Comparison.h +81 -0
  279. pythonnative/native/yoga/yoga/numeric/FloatOptional.h +93 -0
  280. pythonnative/native/yoga/yoga/style/SmallValueBuffer.h +133 -0
  281. pythonnative/native/yoga/yoga/style/Style.h +757 -0
  282. pythonnative/native/yoga/yoga/style/StyleLength.h +107 -0
  283. pythonnative/native/yoga/yoga/style/StyleValueHandle.h +98 -0
  284. pythonnative/native/yoga/yoga/style/StyleValuePool.h +127 -0
  285. pythonnative/native_modules/__init__.py +124 -0
  286. pythonnative/native_modules/app_state.py +99 -0
  287. pythonnative/native_modules/battery.py +75 -0
  288. pythonnative/native_modules/biometrics.py +42 -0
  289. pythonnative/native_modules/camera.py +65 -0
  290. pythonnative/native_modules/clipboard.py +46 -0
  291. pythonnative/native_modules/fallback.py +324 -0
  292. pythonnative/native_modules/file_system.py +145 -0
  293. pythonnative/native_modules/haptics.py +75 -0
  294. pythonnative/native_modules/linking.py +123 -0
  295. pythonnative/native_modules/location.py +75 -0
  296. pythonnative/native_modules/net_info.py +112 -0
  297. pythonnative/native_modules/notifications.py +129 -0
  298. pythonnative/native_modules/permissions.py +108 -0
  299. pythonnative/native_modules/registry.py +526 -0
  300. pythonnative/native_modules/secure_store.py +52 -0
  301. pythonnative/native_modules/share.py +51 -0
  302. pythonnative/native_views/__init__.py +375 -0
  303. pythonnative/native_views/base.py +316 -0
  304. pythonnative/native_views/bridge_backend.py +277 -0
  305. pythonnative/navigation/__init__.py +103 -0
  306. pythonnative/navigation/container.py +154 -0
  307. pythonnative/navigation/handle.py +540 -0
  308. pythonnative/navigation/hooks.py +133 -0
  309. pythonnative/navigation/host.py +58 -0
  310. pythonnative/navigation/linking.py +200 -0
  311. pythonnative/navigation/navigators.py +659 -0
  312. pythonnative/navigation/screen.py +149 -0
  313. pythonnative/navigation/state.py +269 -0
  314. pythonnative/net.py +248 -0
  315. pythonnative/platform.py +166 -0
  316. pythonnative/platform_metrics.py +252 -0
  317. pythonnative/preview.py +287 -0
  318. pythonnative/profiling.py +101 -0
  319. pythonnative/project/__init__.py +68 -0
  320. pythonnative/project/android.py +557 -0
  321. pythonnative/project/builder.py +817 -0
  322. pythonnative/project/config.py +773 -0
  323. pythonnative/project/deps.py +719 -0
  324. pythonnative/project/devices.py +304 -0
  325. pythonnative/project/doctor.py +283 -0
  326. pythonnative/project/fingerprint.py +171 -0
  327. pythonnative/project/icons.py +247 -0
  328. pythonnative/project/ios.py +325 -0
  329. pythonnative/project/lockfile.py +100 -0
  330. pythonnative/project/permissions.py +361 -0
  331. pythonnative/project/plugins.py +519 -0
  332. pythonnative/project/runtime_assets.py +183 -0
  333. pythonnative/py.typed +0 -0
  334. pythonnative/query.py +153 -0
  335. pythonnative/reconciler/__init__.py +29 -0
  336. pythonnative/reconciler/boundaries.py +375 -0
  337. pythonnative/reconciler/children.py +88 -0
  338. pythonnative/reconciler/core.py +1165 -0
  339. pythonnative/reconciler/layout_pass.py +393 -0
  340. pythonnative/reconciler/vnode.py +266 -0
  341. pythonnative/refresh.py +55 -0
  342. pythonnative/runtime.py +306 -0
  343. pythonnative/scheduler.py +159 -0
  344. pythonnative/sdk/__init__.py +153 -0
  345. pythonnative/sdk/_components.py +427 -0
  346. pythonnative/sdk/builtins.py +91 -0
  347. pythonnative/sdk/codegen.py +149 -0
  348. pythonnative/sdk/module_codegen.py +184 -0
  349. pythonnative/sdk/schema.py +224 -0
  350. pythonnative/sdk/templates/contracts.kt +46 -0
  351. pythonnative/sdk/templates/contracts.swift +46 -0
  352. pythonnative/storage.py +184 -0
  353. pythonnative/style.py +841 -0
  354. pythonnative/suspense.py +183 -0
  355. pythonnative/templates/android_template/app/build.gradle +75 -0
  356. pythonnative/templates/android_template/app/proguard-rules.pro +21 -0
  357. pythonnative/templates/android_template/app/src/androidTest/java/com/pythonnative/android_template/ExampleInstrumentedTest.kt +24 -0
  358. pythonnative/templates/android_template/app/src/debug/AndroidManifest.xml +7 -0
  359. pythonnative/templates/android_template/app/src/main/AndroidManifest.xml +32 -0
  360. pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +130 -0
  361. pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt +13 -0
  362. pythonnative/templates/android_template/app/src/main/res/drawable/ic_launcher_background.xml +170 -0
  363. pythonnative/templates/android_template/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +30 -0
  364. pythonnative/templates/android_template/app/src/main/res/layout/activity_main.xml +10 -0
  365. pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +6 -0
  366. pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +6 -0
  367. pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
  368. pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
  369. pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
  370. pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
  371. pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
  372. pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
  373. pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
  374. pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
  375. pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
  376. pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
  377. pythonnative/templates/android_template/app/src/main/res/navigation/nav_graph.xml +22 -0
  378. pythonnative/templates/android_template/app/src/main/res/values/colors.xml +5 -0
  379. pythonnative/templates/android_template/app/src/main/res/values/strings.xml +5 -0
  380. pythonnative/templates/android_template/app/src/main/res/values/themes.xml +9 -0
  381. pythonnative/templates/android_template/app/src/main/res/values-night/themes.xml +7 -0
  382. pythonnative/templates/android_template/app/src/main/res/xml/backup_rules.xml +13 -0
  383. pythonnative/templates/android_template/app/src/main/res/xml/data_extraction_rules.xml +19 -0
  384. pythonnative/templates/android_template/app/src/test/java/com/pythonnative/android_template/ExampleUnitTest.kt +17 -0
  385. pythonnative/templates/android_template/build.gradle +10 -0
  386. pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.jar +0 -0
  387. pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties +6 -0
  388. pythonnative/templates/android_template/gradle.properties +23 -0
  389. pythonnative/templates/android_template/gradlew +185 -0
  390. pythonnative/templates/android_template/gradlew.bat +89 -0
  391. pythonnative/templates/android_template/settings.gradle +17 -0
  392. pythonnative/templates/ios_template/ios_template/AppDelegate.swift +51 -0
  393. pythonnative/templates/ios_template/ios_template/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
  394. pythonnative/templates/ios_template/ios_template/Assets.xcassets/AppIcon.appiconset/Contents.json +13 -0
  395. pythonnative/templates/ios_template/ios_template/Assets.xcassets/Contents.json +6 -0
  396. pythonnative/templates/ios_template/ios_template/Base.lproj/LaunchScreen.storyboard +25 -0
  397. pythonnative/templates/ios_template/ios_template/BridgingHeader.h +14 -0
  398. pythonnative/templates/ios_template/ios_template/Info.plist +28 -0
  399. pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +390 -0
  400. pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +61 -0
  401. pythonnative/templates/ios_template/ios_template/ViewController.swift +23 -0
  402. pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +745 -0
  403. pythonnative/templates/ios_template/ios_template.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  404. pythonnative/templates/ios_template/ios_templateTests/ios_templateTests.swift +36 -0
  405. pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITests.swift +41 -0
  406. pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITestsLaunchTests.swift +32 -0
  407. pythonnative/testing/__init__.py +53 -0
  408. pythonnative/testing/backend.py +276 -0
  409. pythonnative/testing/harness.py +369 -0
  410. pythonnative/utils.py +145 -0
  411. pythonnative-0.40.0.dist-info/DELVEWHEEL +2 -0
  412. pythonnative-0.40.0.dist-info/METADATA +150 -0
  413. pythonnative-0.40.0.dist-info/RECORD +418 -0
  414. pythonnative-0.40.0.dist-info/WHEEL +5 -0
  415. pythonnative-0.40.0.dist-info/entry_points.txt +2 -0
  416. pythonnative-0.40.0.dist-info/licenses/LICENSE +21 -0
  417. pythonnative-0.40.0.dist-info/top_level.txt +1 -0
  418. pythonnative.libs/msvcp140-a4c2229bdc2a2a630acdc095b4d86008.dll +0 -0
@@ -0,0 +1,817 @@
1
+ """Build orchestration for PythonNative projects.
2
+
3
+ The [`Builder`][pythonnative.project.builder.Builder] ties the pieces
4
+ together: it stages the bundled native template, runs the platform
5
+ [`configurators`][pythonnative.project.android], stages the Python
6
+ sources and (on iOS) the embedded CPython runtime into the native
7
+ project, and invokes the native toolchains (Gradle / Xcode).
8
+
9
+ On iOS, the staged project is self-contained: ``Python.xcframework`` is
10
+ linked at build time and an Xcode build phase installs the standard
11
+ library and app code into the bundle, so ``xcodebuild`` output is
12
+ directly runnable and archivable with no post-build patching.
13
+ Third-party packages are resolved per destination (see
14
+ [`deps`][pythonnative.project.deps]) into ``app_packages.iphoneos`` and
15
+ ``app_packages.iphonesimulator``; the build phase installs the slice
16
+ matching the SDK being built.
17
+
18
+ All shell-outs go through a small
19
+ [`CommandRunner`][pythonnative.project.builder.CommandRunner] abstraction
20
+ so the orchestration logic can be unit tested with a recording fake
21
+ instead of a real device toolchain. The default
22
+ [`SubprocessRunner`][pythonnative.project.builder.SubprocessRunner] simply
23
+ delegates to :mod:`subprocess`.
24
+
25
+ Device interaction that genuinely needs a device (booting simulators,
26
+ installing, launching, streaming logs, hot reload) lives in the CLI; the
27
+ builder stops at producing installable/archivable artifacts.
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ import compileall
33
+ import json
34
+ import os
35
+ import platform as platform_module
36
+ import shutil
37
+ import subprocess
38
+ import sys
39
+ from dataclasses import dataclass, field
40
+ from importlib import resources
41
+ from pathlib import Path
42
+ from typing import Callable, List, Optional, Sequence, Union
43
+
44
+ from . import android as android_config
45
+ from . import deps, runtime_assets
46
+ from . import ios as ios_config
47
+ from . import plugins as native_plugins
48
+ from .android import AndroidLayout
49
+ from .config import AppConfig
50
+ from .ios import IOSLayout
51
+
52
+ Logger = Callable[[str], None]
53
+
54
+
55
+ class BuildError(Exception):
56
+ """Raised when a native build step fails.
57
+
58
+ Carries a user-facing message; the CLI prints it and exits non-zero.
59
+ """
60
+
61
+
62
+ # ======================================================================
63
+ # Command runner abstraction
64
+ # ======================================================================
65
+
66
+
67
+ @dataclass
68
+ class CommandResult:
69
+ """The outcome of a single command invocation.
70
+
71
+ Attributes:
72
+ returncode: Process exit status.
73
+ stdout: Captured standard output (empty unless captured).
74
+ stderr: Captured standard error (empty unless captured).
75
+ """
76
+
77
+ returncode: int
78
+ stdout: str = ""
79
+ stderr: str = ""
80
+
81
+ @property
82
+ def ok(self) -> bool:
83
+ """Whether the command exited successfully (return code 0)."""
84
+ return self.returncode == 0
85
+
86
+
87
+ class CommandRunner:
88
+ """Protocol for running external commands.
89
+
90
+ Implementations execute a command and return a
91
+ [`CommandResult`][pythonnative.project.builder.CommandResult]. Tests
92
+ provide a recording fake; production uses
93
+ [`SubprocessRunner`][pythonnative.project.builder.SubprocessRunner].
94
+ """
95
+
96
+ def run(
97
+ self,
98
+ args: Sequence[str],
99
+ *,
100
+ cwd: Optional[Path] = None,
101
+ env: Optional[dict] = None,
102
+ capture: bool = False,
103
+ ) -> CommandResult:
104
+ """Run a command.
105
+
106
+ Args:
107
+ args: The command and its arguments.
108
+ cwd: Working directory, or ``None`` for the current one.
109
+ env: Full environment mapping, or ``None`` to inherit.
110
+ capture: Whether to capture and return stdout/stderr.
111
+
112
+ Returns:
113
+ A [`CommandResult`][pythonnative.project.builder.CommandResult].
114
+ """
115
+ raise NotImplementedError
116
+
117
+
118
+ class SubprocessRunner(CommandRunner):
119
+ """A [`CommandRunner`][pythonnative.project.builder.CommandRunner] backed by :mod:`subprocess`."""
120
+
121
+ def run(
122
+ self,
123
+ args: Sequence[str],
124
+ *,
125
+ cwd: Optional[Path] = None,
126
+ env: Optional[dict] = None,
127
+ capture: bool = False,
128
+ ) -> CommandResult:
129
+ """Execute ``args`` with :func:`subprocess.run`."""
130
+ completed = subprocess.run(
131
+ list(args),
132
+ cwd=str(cwd) if cwd else None,
133
+ env=env,
134
+ capture_output=capture,
135
+ text=True,
136
+ )
137
+ return CommandResult(
138
+ returncode=completed.returncode,
139
+ stdout=completed.stdout or "" if capture else "",
140
+ stderr=completed.stderr or "" if capture else "",
141
+ )
142
+
143
+
144
+ # ======================================================================
145
+ # Result/state dataclasses
146
+ # ======================================================================
147
+
148
+
149
+ @dataclass
150
+ class PreparedProject:
151
+ """A staged, configured project ready to build.
152
+
153
+ Attributes:
154
+ platform: ``"android"`` or ``"ios"``.
155
+ build_dir: The ``build/<platform>`` directory.
156
+ project_dir: The staged native project directory.
157
+ app_id: The resolved application id / bundle id for the platform.
158
+ android: Android layout (when ``platform == "android"``).
159
+ ios: iOS layout (when ``platform == "ios"``).
160
+ """
161
+
162
+ platform: str
163
+ build_dir: Path
164
+ project_dir: Path
165
+ app_id: str
166
+ android: Optional[AndroidLayout] = None
167
+ ios: Optional[IOSLayout] = None
168
+
169
+
170
+ @dataclass
171
+ class BuildArtifacts:
172
+ """Paths to artifacts produced by a release/standalone build.
173
+
174
+ Attributes:
175
+ paths: Output artifact paths (APK/AAB/IPA/.app).
176
+ """
177
+
178
+ paths: List[Path] = field(default_factory=list)
179
+
180
+
181
+ # ======================================================================
182
+ # Template staging
183
+ # ======================================================================
184
+
185
+ _TEMPLATE_NAMES = {"android": "android_template", "ios": "ios_template"}
186
+
187
+ # A source checkout accumulates Gradle, SwiftPM, and Xcode outputs next to
188
+ # the native library sources; a staged project must start from a clean tree.
189
+ _TEMPLATE_IGNORE = shutil.ignore_patterns(
190
+ "build",
191
+ ".build",
192
+ ".gradle",
193
+ ".cxx",
194
+ ".kotlin",
195
+ ".swiftpm",
196
+ "xcuserdata",
197
+ "DerivedData",
198
+ "__pycache__",
199
+ "*.pyc",
200
+ ".DS_Store",
201
+ )
202
+
203
+
204
+ def template_name(platform: str) -> str:
205
+ """The bundled template directory name for ``platform``.
206
+
207
+ Raises:
208
+ BuildError: For an unknown platform.
209
+ """
210
+ try:
211
+ return _TEMPLATE_NAMES[platform]
212
+ except KeyError:
213
+ raise BuildError(f"Unknown platform: {platform!r} (expected 'android' or 'ios').") from None
214
+
215
+
216
+ def template_source(platform: str) -> Path:
217
+ """Where the bundled template for ``platform`` lives in this installation."""
218
+ return Path(__file__).resolve().parents[1] / "templates" / template_name(platform)
219
+
220
+
221
+ def stage_template(template_name: str, destination: Path) -> Path:
222
+ """Copy a bundled native template into ``destination``.
223
+
224
+ Resolution order mirrors the historical CLI: a local source checkout
225
+ first (so dev edits take effect immediately), then installed package
226
+ data via :mod:`importlib.resources`.
227
+
228
+ Args:
229
+ template_name: ``"android_template"`` or ``"ios_template"``.
230
+ destination: Parent directory; the template lands at
231
+ ``destination/<template_name>``.
232
+
233
+ Returns:
234
+ The path to the staged template directory.
235
+
236
+ Raises:
237
+ BuildError: If no bundled copy can be located.
238
+ """
239
+ dest_path = destination / template_name
240
+ destination.mkdir(parents=True, exist_ok=True)
241
+
242
+ # Dev-first: local source package templates.
243
+ local = Path(__file__).resolve().parents[1] / "templates" / template_name
244
+ if local.is_dir():
245
+ shutil.copytree(local, dest_path, dirs_exist_ok=True, ignore=_TEMPLATE_IGNORE)
246
+ _stage_runtime(template_name, dest_path)
247
+ return dest_path
248
+
249
+ try:
250
+ candidate = resources.files("pythonnative").joinpath("templates").joinpath(template_name)
251
+ with resources.as_file(candidate) as resolved:
252
+ if Path(resolved).is_dir():
253
+ shutil.copytree(resolved, dest_path, dirs_exist_ok=True, ignore=_TEMPLATE_IGNORE)
254
+ _stage_runtime(template_name, dest_path)
255
+ return dest_path
256
+ except (ModuleNotFoundError, FileNotFoundError, OSError):
257
+ pass
258
+
259
+ raise BuildError(
260
+ f"Could not find bundled template {template_name!r}. Reinstall pythonnative or run from a source checkout."
261
+ )
262
+
263
+
264
+ def _stage_runtime(name: str, destination: Path) -> None:
265
+ platform = name.removesuffix("_template")
266
+ source = Path(__file__).resolve().parents[1] / "native"
267
+ runtime_name = "PythonNativeKit" if platform == "ios" else "pythonnative"
268
+ managed = "Sources/PythonNativeKit" if platform == "ios" else "src/main/java/com/pythonnative/runtime"
269
+ staged_sources = destination / runtime_name / managed
270
+ if staged_sources.exists():
271
+ for path in staged_sources.rglob("*"):
272
+ rel = path.relative_to(staged_sources)
273
+ if path.is_file() and rel.parts[0] not in {"Plugins", "PluginResources", "Generated", "plugins"}:
274
+ if not (source / platform / managed / rel).exists():
275
+ path.unlink()
276
+ shutil.copytree(source / platform, destination / runtime_name, dirs_exist_ok=True, ignore=_TEMPLATE_IGNORE)
277
+ shutil.copytree(source / "yoga", destination / "yoga", dirs_exist_ok=True, ignore=_TEMPLATE_IGNORE)
278
+ from ..sdk.codegen import generate
279
+
280
+ generated = destination / "contracts"
281
+ generate(generated)
282
+ if platform == "ios":
283
+ output = destination / runtime_name / "Sources/PythonNativeKit/Generated"
284
+ extension = "swift"
285
+ else:
286
+ output = destination / runtime_name / "src/main/java/com/pythonnative/generated"
287
+ extension = "kt"
288
+ output.mkdir(parents=True, exist_ok=True)
289
+ for name in ("PNContracts", "NativeProps", "NativeModules"):
290
+ shutil.copy2(generated / f"{name}.{extension}", output / f"{name}.{extension}")
291
+
292
+
293
+ # ======================================================================
294
+ # The builder
295
+ # ======================================================================
296
+
297
+
298
+ class Builder:
299
+ """Stages, configures, and builds a PythonNative project.
300
+
301
+ Args:
302
+ config: The validated app configuration.
303
+ runner: Command runner (defaults to
304
+ [`SubprocessRunner`][pythonnative.project.builder.SubprocessRunner]).
305
+ log: Progress logger (defaults to :func:`print`).
306
+ build_root: Override for the ``build/`` directory.
307
+ dev_lib_root: Override for the ``pythonnative`` package directory
308
+ to bundle (defaults to the running package).
309
+ """
310
+
311
+ def __init__(
312
+ self,
313
+ config: AppConfig,
314
+ *,
315
+ runner: Optional[CommandRunner] = None,
316
+ log: Optional[Logger] = None,
317
+ build_root: Optional[Path] = None,
318
+ dev_lib_root: Optional[Path] = None,
319
+ ) -> None:
320
+ self.config = config
321
+ self.runner = runner or SubprocessRunner()
322
+ self.log: Logger = log or print
323
+ self.build_root = build_root or (config.project_root / "build")
324
+ # The currently-running pythonnative package is bundled into the app
325
+ # (works for both a source checkout and a pip install).
326
+ self.dev_lib_root = dev_lib_root or Path(__file__).resolve().parents[1]
327
+
328
+ # -- Preparation ----------------------------------------------------
329
+
330
+ def prepare(
331
+ self,
332
+ platform: str,
333
+ *,
334
+ release: bool = False,
335
+ ios_sdks: Sequence[str] = deps.IOS_SDKS,
336
+ ) -> PreparedProject:
337
+ """Stage and configure the native project for ``platform``.
338
+
339
+ Args:
340
+ platform: ``"android"`` or ``"ios"``.
341
+ release: Prepare for a release/store build: byte-compile
342
+ the staged Python code and drop the ``.py`` sources from
343
+ the bundle (on Android through Chaquopy's ``pyc.src``).
344
+ Debug builds keep the sources so tracebacks show code and
345
+ the on-device dev client can see what it already runs.
346
+ ios_sdks: Which iOS SDKs to resolve third-party packages
347
+ for: ``"iphoneos"`` (devices, archives) and/or
348
+ ``"iphonesimulator"``. Each becomes an
349
+ ``app_packages.<sdk>`` slice in the staged project.
350
+ Defaults to both; the CLI narrows it to the destination
351
+ it is about to build so a package that only has a
352
+ Simulator wheel doesn't block a Simulator run.
353
+
354
+ Returns:
355
+ A [`PreparedProject`][pythonnative.project.builder.PreparedProject].
356
+
357
+ Raises:
358
+ BuildError: For an unknown platform, a staging failure, or a
359
+ requirement with no wheel for a requested iOS SDK.
360
+ """
361
+ if platform not in _TEMPLATE_NAMES:
362
+ raise BuildError(f"Unknown platform: {platform!r} (expected 'android' or 'ios').")
363
+
364
+ build_dir = self.build_root / platform
365
+ build_dir.mkdir(parents=True, exist_ok=True)
366
+ project_dir = stage_template(_TEMPLATE_NAMES[platform], build_dir)
367
+ targets = (
368
+ deps.android_targets(self.config) if platform == "android" else deps.ios_targets(self.config, sdks=ios_sdks)
369
+ )
370
+ plugins = self._discover_plugins(targets)
371
+
372
+ if platform == "android":
373
+ layout = android_config.configure(
374
+ project_dir,
375
+ self.config,
376
+ dev_lib_root=self.dev_lib_root,
377
+ release=release,
378
+ log=self.log,
379
+ )
380
+ native_plugins.stage_android_plugins(project_dir, plugins, log=self.log)
381
+ self._stage_contracts(project_dir, platform, plugins, [layout.python_root / "pythonnative"])
382
+ return PreparedProject(
383
+ platform=platform,
384
+ build_dir=build_dir,
385
+ project_dir=project_dir,
386
+ app_id=layout.application_id,
387
+ android=layout,
388
+ )
389
+
390
+ ios_layout = ios_config.configure(project_dir, self.config, log=self.log)
391
+ native_plugins.stage_ios_plugins(project_dir, plugins, log=self.log)
392
+ self._stage_ios_python(project_dir, release=release, sdks=ios_sdks)
393
+ self._stage_contracts(
394
+ project_dir,
395
+ platform,
396
+ plugins,
397
+ [project_dir / target.slice_name / "pythonnative" for target in targets],
398
+ )
399
+ self._link_ios_runtime(project_dir)
400
+ return PreparedProject(
401
+ platform=platform,
402
+ build_dir=build_dir,
403
+ project_dir=project_dir,
404
+ app_id=ios_layout.bundle_id,
405
+ ios=ios_layout,
406
+ )
407
+
408
+ def _stage_contracts(
409
+ self,
410
+ project_dir: Path,
411
+ platform: str,
412
+ plugins: Sequence[native_plugins.NativePlugin],
413
+ python_roots: Sequence[Path],
414
+ ) -> None:
415
+ from ..sdk import schema
416
+ from ..sdk.codegen import generate
417
+
418
+ components, modules = dict(schema.COMPONENTS), dict(schema.MODULES)
419
+ try:
420
+ for plugin in plugins:
421
+ if plugin.contracts is not None:
422
+ schema.load_manifest(json.loads((plugin.root / plugin.contracts).read_text(encoding="utf-8")))
423
+ generated = project_dir / "contracts"
424
+ generate(generated)
425
+ output = project_dir / (
426
+ "PythonNativeKit/Sources/PythonNativeKit/Generated"
427
+ if platform == "ios"
428
+ else "pythonnative/src/main/java/com/pythonnative/generated"
429
+ )
430
+ extension = "swift" if platform == "ios" else "kt"
431
+ for name in ("PNContracts", "NativeProps", "NativeModules"):
432
+ shutil.copy2(generated / f"{name}.{extension}", output / f"{name}.{extension}")
433
+ for root in python_roots:
434
+ root.mkdir(parents=True, exist_ok=True)
435
+ shutil.copy2(generated / "schema.json", root / "_native_contracts.json")
436
+ finally:
437
+ schema.COMPONENTS.clear()
438
+ schema.COMPONENTS.update(components)
439
+ schema.MODULES.clear()
440
+ schema.MODULES.update(modules)
441
+
442
+ def _discover_plugins(self, targets: Sequence[deps.Target] = ()) -> List[native_plugins.NativePlugin]:
443
+ """Collect native plugins from entry points and ``[plugins].paths``.
444
+
445
+ Raises:
446
+ BuildError: If a project-local plugin directory is invalid.
447
+ """
448
+ try:
449
+ installed = native_plugins.discover_target_plugins(self.config, targets, self.runner)
450
+ local = native_plugins.discover_plugins(
451
+ include_environment=False,
452
+ extra_paths=[self.config.resolve_path(p) for p in self.config.plugin_paths],
453
+ log=self.log,
454
+ )
455
+ names = {plugin.name for plugin in installed}
456
+ if any(plugin.name in names for plugin in local):
457
+ raise native_plugins.PluginError("A plugin appears in both target requirements and [plugins].paths")
458
+ return installed + local
459
+ except native_plugins.PluginError as exc:
460
+ raise BuildError(f"Invalid native plugin: {exc}") from exc
461
+
462
+ def _stage_ios_python(self, project_dir: Path, *, release: bool, sdks: Sequence[str]) -> None:
463
+ """Stage ``app/`` and one ``app_packages.<sdk>/`` slice per SDK.
464
+
465
+ ``app/`` is a bundle resource. The package slices are not: the
466
+ "Install Python runtime" build phase copies the slice matching
467
+ ``EFFECTIVE_PLATFORM_NAME`` into the bundle as ``app_packages``
468
+ and converts any binary modules inside it into signed
469
+ frameworks. Every slice carries the ``pythonnative`` package
470
+ (pure Python) plus the project's requirements resolved for that
471
+ SDK's wheel tags.
472
+ """
473
+ app_dir = project_dir / "app"
474
+ if app_dir.exists():
475
+ shutil.rmtree(app_dir)
476
+ for stale in project_dir.glob("app_packages*"):
477
+ if stale.is_dir():
478
+ shutil.rmtree(stale)
479
+ else:
480
+ stale.unlink()
481
+
482
+ app_src = self.config.project_root / "app"
483
+ if not app_src.is_dir():
484
+ raise BuildError(f"No app/ directory found at {app_src}; nothing to bundle.")
485
+ shutil.copytree(app_src, app_dir)
486
+
487
+ slices: List[Path] = []
488
+ for target in deps.ios_targets(self.config, sdks=sdks):
489
+ packages_dir = project_dir / target.slice_name
490
+ packages_dir.mkdir(parents=True, exist_ok=True)
491
+ slices.append(packages_dir)
492
+
493
+ # The framework has no Python-side native dependencies (the
494
+ # bridge into PythonNativeKit is ctypes), so it is copied
495
+ # verbatim into every slice.
496
+ if self.dev_lib_root.is_dir():
497
+ shutil.copytree(
498
+ self.dev_lib_root,
499
+ packages_dir / "pythonnative",
500
+ dirs_exist_ok=True,
501
+ ignore=android_config.LIB_IGNORE,
502
+ )
503
+
504
+ if self.config.requirements:
505
+ self.log(f"Resolving {len(self.config.requirements)} requirement(s) for {target.label}...")
506
+ try:
507
+ deps.install(self.config, target, packages_dir, runner=self.runner)
508
+ except deps.DependencyError as exc:
509
+ raise BuildError(str(exc)) from exc
510
+
511
+ if release:
512
+ self._compile_ios_bytecode(app_dir, *slices)
513
+
514
+ def _compile_ios_bytecode(self, *roots: Path) -> None:
515
+ """Byte-compile staged Python code and drop the ``.py`` sources.
516
+
517
+ Ships ``.pyc`` only: smaller bundles, faster cold start, and no
518
+ plain-text source in the store artifact. Requires the host
519
+ interpreter to match ``app.python_version``, since bytecode is
520
+ version-specific.
521
+ """
522
+ host_version = f"{sys.version_info[0]}.{sys.version_info[1]}"
523
+ if host_version != self.config.python_version:
524
+ self.log(
525
+ f"Skipping bytecode compilation: the build host runs Python {host_version} "
526
+ f"but app.python_version is {self.config.python_version}, and bytecode is "
527
+ "version-specific. The app will ship .py sources. Run pn with Python "
528
+ f"{self.config.python_version} to ship bytecode."
529
+ )
530
+ return
531
+ for root in roots:
532
+ # legacy=True writes module.pyc next to module.py (no
533
+ # __pycache__), which the embedded interpreter imports
534
+ # directly once the .py files are gone.
535
+ if not compileall.compile_dir(str(root), quiet=1, legacy=True, optimize=1):
536
+ raise BuildError(f"Byte-compiling {root} failed; fix the syntax error above.")
537
+ for source in root.rglob("*.py"):
538
+ source.unlink()
539
+ self.log("Compiled Python sources to bytecode for release.")
540
+
541
+ def _link_ios_runtime(self, project_dir: Path) -> None:
542
+ """Make ``Python.xcframework`` available at the project root.
543
+
544
+ A symlink into the shared runtime cache is preferred (the
545
+ framework is hundreds of megabytes); staging falls back to a
546
+ copy when symlinks are unavailable.
547
+ """
548
+ runtime = self._ios_runtime()
549
+ link = project_dir / "Python.xcframework"
550
+ if link.is_symlink() or link.is_file():
551
+ link.unlink()
552
+ elif link.is_dir():
553
+ shutil.rmtree(link)
554
+ try:
555
+ link.symlink_to(runtime.xcframework_dir, target_is_directory=True)
556
+ except OSError:
557
+ shutil.copytree(runtime.xcframework_dir, link)
558
+ self.log(f"Linked Python.xcframework (Python {self.config.python_version}).")
559
+
560
+ # -- Android builds -------------------------------------------------
561
+
562
+ def install_android_debug(self, prepared: PreparedProject) -> None:
563
+ """Build and install the debug APK on a connected device/emulator.
564
+
565
+ Args:
566
+ prepared: A prepared Android project.
567
+
568
+ Raises:
569
+ BuildError: If the Gradle build fails.
570
+ """
571
+ self._gradlew(prepared, ["installDebug"])
572
+
573
+ def android_debug_apk(self, prepared: PreparedProject) -> Optional[Path]:
574
+ """The debug APK produced by the last Gradle build, if it exists."""
575
+ outputs = prepared.project_dir / "app" / "build" / "outputs"
576
+ candidates = _existing(outputs.rglob("*-debug.apk"))
577
+ return candidates[0] if candidates else None
578
+
579
+ def build_android(self, prepared: PreparedProject, *, debug: bool = False) -> BuildArtifacts:
580
+ """Assemble standalone Android artifacts (APK + AAB).
581
+
582
+ Args:
583
+ prepared: A prepared Android project.
584
+ debug: Build the debug variant instead of release.
585
+
586
+ Returns:
587
+ The produced artifact paths.
588
+
589
+ Raises:
590
+ BuildError: If the Gradle build fails.
591
+ """
592
+ if debug:
593
+ self._gradlew(prepared, ["assembleDebug"])
594
+ outputs = prepared.project_dir / "app" / "build" / "outputs"
595
+ return BuildArtifacts(paths=_existing(outputs.rglob("*-debug.apk")))
596
+
597
+ self._gradlew(prepared, ["assembleRelease", "bundleRelease"])
598
+ outputs = prepared.project_dir / "app" / "build" / "outputs"
599
+ candidates = list(outputs.rglob("*-release.apk")) + list(outputs.rglob("*-release.aab"))
600
+ if not config_has_android_signing(self.config):
601
+ candidates += list(outputs.rglob("*-release-unsigned.apk"))
602
+ return BuildArtifacts(paths=_existing(candidates))
603
+
604
+ def _gradlew(self, prepared: PreparedProject, tasks: Sequence[str]) -> None:
605
+ gradlew = prepared.project_dir / "gradlew"
606
+ if gradlew.exists():
607
+ os.chmod(gradlew, 0o755)
608
+ env = self._android_env()
609
+ result = self.runner.run(["./gradlew", *tasks], cwd=prepared.project_dir, env=env)
610
+ if not result.ok:
611
+ raise BuildError(f"Gradle build failed ({' '.join(tasks)}). See output above.")
612
+
613
+ def _android_env(self) -> dict:
614
+ env = dict(os.environ)
615
+ if sys.platform == "darwin" and not env.get("JAVA_HOME"):
616
+ try:
617
+ jdk = subprocess.check_output(["brew", "--prefix", "openjdk@17"], text=True).strip()
618
+ if jdk:
619
+ env["JAVA_HOME"] = jdk
620
+ except Exception:
621
+ pass
622
+ return env
623
+
624
+ # -- iOS builds -----------------------------------------------------
625
+
626
+ def build_ios_simulator(self, prepared: PreparedProject) -> Path:
627
+ """Build the iOS app for the simulator.
628
+
629
+ The Xcode build produces a complete, runnable ``.app``: the
630
+ Python framework is linked and the "Install Python runtime"
631
+ build phase embeds the standard library and staged sources.
632
+
633
+ Args:
634
+ prepared: A prepared iOS project.
635
+
636
+ Returns:
637
+ Path to the built ``.app``.
638
+
639
+ Raises:
640
+ BuildError: If the build fails.
641
+ """
642
+ derived = prepared.project_dir / "build"
643
+ settings = ios_config.build_settings(self.config)
644
+ # The simulator runs on the host CPU, and BeeWare's runtime
645
+ # install script requires a single-architecture build (its
646
+ # stdlib layout is per-arch), so pin ARCHS to the host.
647
+ host_arch = "arm64" if platform_module.machine() == "arm64" else "x86_64"
648
+ settings.append(f"ARCHS={host_arch}")
649
+ result = self.runner.run(
650
+ [
651
+ "xcodebuild",
652
+ "-project",
653
+ ios_config.PROJECT_FILE,
654
+ "-scheme",
655
+ ios_config.PROJECT_NAME,
656
+ "-configuration",
657
+ "Debug",
658
+ "-destination",
659
+ "generic/platform=iOS Simulator",
660
+ "-derivedDataPath",
661
+ str(derived),
662
+ "build",
663
+ *settings,
664
+ ],
665
+ cwd=prepared.project_dir,
666
+ )
667
+ if not result.ok:
668
+ raise BuildError("xcodebuild (simulator) failed. See output above.")
669
+
670
+ app_path = derived / "Build" / "Products" / "Debug-iphonesimulator" / ios_config.APP_BUNDLE_NAME
671
+ if not app_path.is_dir():
672
+ raise BuildError(f"Built app not found at {app_path}.")
673
+ return app_path
674
+
675
+ def build_ios_device(self, prepared: PreparedProject) -> Path:
676
+ """Build a signed Debug app for a physical iOS device.
677
+
678
+ Requires ``[ios].development_team`` for automatic signing.
679
+
680
+ Args:
681
+ prepared: A prepared iOS project.
682
+
683
+ Returns:
684
+ Path to the built ``.app``.
685
+
686
+ Raises:
687
+ BuildError: If the build fails or signing is not set up.
688
+ """
689
+ if not self.config.ios.development_team:
690
+ raise BuildError(
691
+ "Running on a physical iOS device requires code signing. "
692
+ "Set [ios].development_team in pythonnative.toml."
693
+ )
694
+ derived = prepared.project_dir / "build"
695
+ settings = ios_config.build_settings(self.config)
696
+ settings.append("-allowProvisioningUpdates")
697
+ result = self.runner.run(
698
+ [
699
+ "xcodebuild",
700
+ "-project",
701
+ ios_config.PROJECT_FILE,
702
+ "-scheme",
703
+ ios_config.PROJECT_NAME,
704
+ "-configuration",
705
+ "Debug",
706
+ "-destination",
707
+ "generic/platform=iOS",
708
+ "-derivedDataPath",
709
+ str(derived),
710
+ "build",
711
+ *settings,
712
+ ],
713
+ cwd=prepared.project_dir,
714
+ )
715
+ if not result.ok:
716
+ raise BuildError("xcodebuild (device) failed. See output above.")
717
+
718
+ app_path = derived / "Build" / "Products" / "Debug-iphoneos" / ios_config.APP_BUNDLE_NAME
719
+ if not app_path.is_dir():
720
+ raise BuildError(f"Built app not found at {app_path}.")
721
+ return app_path
722
+
723
+ def build_ios_archive(self, prepared: PreparedProject, *, upload: bool = False) -> BuildArtifacts:
724
+ """Archive the iOS app and export a signed IPA (or upload it).
725
+
726
+ Args:
727
+ prepared: A prepared iOS project (use ``release=True``).
728
+ upload: Upload directly to App Store Connect instead of
729
+ exporting a local ``.ipa`` (requires
730
+ ``export_method = "app-store"`` and stored ASC
731
+ credentials in Xcode).
732
+
733
+ Returns:
734
+ The produced ``.ipa`` (and ``.xcarchive``) paths.
735
+
736
+ Raises:
737
+ BuildError: If archiving or export fails.
738
+ """
739
+ archive_path = prepared.build_dir / "ios_template.xcarchive"
740
+ settings = ios_config.build_settings(self.config, for_archive=True)
741
+ result = self.runner.run(
742
+ [
743
+ "xcodebuild",
744
+ "-project",
745
+ ios_config.PROJECT_FILE,
746
+ "-scheme",
747
+ ios_config.PROJECT_NAME,
748
+ "-configuration",
749
+ "Release",
750
+ "-destination",
751
+ "generic/platform=iOS",
752
+ "-archivePath",
753
+ str(archive_path),
754
+ "-allowProvisioningUpdates",
755
+ "archive",
756
+ *settings,
757
+ ],
758
+ cwd=prepared.project_dir,
759
+ )
760
+ if not result.ok:
761
+ raise BuildError("xcodebuild archive failed. See output above.")
762
+
763
+ export_dir = prepared.build_dir / "export"
764
+ options = ios_config.write_export_options(
765
+ self.config, prepared.build_dir / "exportOptions.plist", upload=upload
766
+ )
767
+ result = self.runner.run(
768
+ [
769
+ "xcodebuild",
770
+ "-exportArchive",
771
+ "-archivePath",
772
+ str(archive_path),
773
+ "-exportOptionsPlist",
774
+ str(options),
775
+ "-exportPath",
776
+ str(export_dir),
777
+ "-allowProvisioningUpdates",
778
+ ],
779
+ cwd=prepared.project_dir,
780
+ )
781
+ if not result.ok:
782
+ raise BuildError("xcodebuild -exportArchive failed. Check signing settings in [ios.signing].")
783
+
784
+ return BuildArtifacts(paths=_existing(list(export_dir.rglob("*.ipa")) + [archive_path]))
785
+
786
+ def _ios_runtime(self) -> runtime_assets.IOSRuntime:
787
+ cache = self.build_root / "ios_runtime"
788
+ try:
789
+ return runtime_assets.prepare_ios_runtime(cache, self.config.python_version, log=self.log)
790
+ except RuntimeError as exc:
791
+ raise BuildError(str(exc)) from exc
792
+
793
+
794
+ # ======================================================================
795
+ # Helpers
796
+ # ======================================================================
797
+
798
+
799
+ def config_has_android_signing(config: AppConfig) -> bool:
800
+ """Return whether the config defines an Android release signing key.
801
+
802
+ Args:
803
+ config: The app configuration.
804
+
805
+ Returns:
806
+ ``True`` if a keystore and alias are configured.
807
+ """
808
+ return config.android.signing.is_configured
809
+
810
+
811
+ def _existing(paths: Union[Sequence[Path], object]) -> List[Path]:
812
+ result: List[Path] = []
813
+ for path in paths: # type: ignore[union-attr]
814
+ candidate = Path(path)
815
+ if candidate.exists() and candidate not in result:
816
+ result.append(candidate)
817
+ return result