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,519 @@
1
+ """Native plugin staging for ``pn build``.
2
+
3
+ A PyPI package can ship Swift and Kotlin next to its Python facade. It
4
+ declares an entry point in the ``pythonnative.plugins`` group whose
5
+ value is an importable package directory containing:
6
+
7
+ ```text
8
+ my_blur/native/
9
+ pn_plugin.json
10
+ ios/BlurViewManager.swift
11
+ android/com/example/myblur/BlurViewManager.kt
12
+ ```
13
+
14
+ ``pn_plugin.json`` names the registration entry on each platform:
15
+
16
+ ```json
17
+ {"ios": {"entry": "MyBlurPlugin"}, "android": {"entry": "com.example.myblur.MyBlurPlugin"}}
18
+ ```
19
+
20
+ At build time this module copies each plugin's sources into the staged
21
+ native project (``PythonNativeKit/Sources/PythonNativeKit/Plugins/<name>``
22
+ on iOS, ``pythonnative/src/main/java`` on Android) and regenerates the
23
+ registration file that calls every entry's ``register``. SwiftPM and
24
+ Gradle compile whatever lands under those roots, so no project-file
25
+ surgery is needed.
26
+
27
+ Projects can also bundle plugins that aren't installed as distributions
28
+ by listing directories under ``[plugins].paths`` in ``pythonnative.toml``;
29
+ each path must contain the same ``pn_plugin.json`` layout.
30
+ """
31
+
32
+ from __future__ import annotations
33
+
34
+ import json
35
+ import re
36
+ import shutil
37
+ from dataclasses import dataclass, field
38
+ from importlib import import_module
39
+ from pathlib import Path
40
+ from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence
41
+
42
+ __all__ = [
43
+ "ENTRY_POINT_GROUP",
44
+ "MANIFEST_NAME",
45
+ "NativePlugin",
46
+ "PluginError",
47
+ "discover_plugins",
48
+ "load_plugin",
49
+ "stage_android_plugins",
50
+ "stage_ios_plugins",
51
+ ]
52
+
53
+ ENTRY_POINT_GROUP = "pythonnative.plugins"
54
+ """Entry-point group whose values name plugin directories (as packages)."""
55
+
56
+ MANIFEST_NAME = "pn_plugin.json"
57
+ """File name of the per-plugin manifest."""
58
+
59
+ Logger = Callable[[str], None]
60
+
61
+ _IOS_PLUGIN_DIR = ("PythonNativeKit", "Sources", "PythonNativeKit", "Plugins")
62
+ _IOS_REGISTRATION = (*_IOS_PLUGIN_DIR, "PNPluginRegistration.swift")
63
+ _ANDROID_SOURCE_ROOT = ("pythonnative", "src", "main", "java")
64
+ _ANDROID_REGISTRATION = (*_ANDROID_SOURCE_ROOT, "com", "pythonnative", "runtime", "plugins", "GeneratedPlugins.kt")
65
+
66
+ _SWIFT_IDENT = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
67
+ _KOTLIN_FQN = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)+$")
68
+
69
+
70
+ class PluginError(RuntimeError):
71
+ """A plugin manifest or source layout is invalid."""
72
+
73
+
74
+ @dataclass(frozen=True)
75
+ class NativePlugin:
76
+ """A discovered native plugin.
77
+
78
+ Attributes:
79
+ name: Stable identifier (the entry-point name or the directory
80
+ name for path plugins). Used for the iOS subfolder.
81
+ root: Directory containing ``pn_plugin.json``.
82
+ ios_entry: Swift type conforming to ``PNPlugin``, or ``None``
83
+ when the plugin has no iOS side.
84
+ android_entry: Fully qualified Kotlin object implementing
85
+ ``PNPlugin``, or ``None`` when the plugin has no Android side.
86
+ ios_sources: Swift files under ``ios/``.
87
+ android_sources: Kotlin files under ``android/`` (relative
88
+ paths are preserved so packages land in the right folders).
89
+ """
90
+
91
+ name: str
92
+ root: Path
93
+ ios_entry: Optional[str] = None
94
+ android_entry: Optional[str] = None
95
+ ios_sources: Sequence[Path] = field(default_factory=tuple)
96
+ android_sources: Sequence[Path] = field(default_factory=tuple)
97
+ ios_resources: Sequence[Path] = field(default_factory=tuple)
98
+ android_resources: Sequence[Path] = field(default_factory=tuple)
99
+ android_assets: Sequence[Path] = field(default_factory=tuple)
100
+ contracts: Optional[Path] = None
101
+
102
+ @property
103
+ def has_ios(self) -> bool:
104
+ """Whether this plugin contributes Swift code."""
105
+ return self.ios_entry is not None
106
+
107
+ @property
108
+ def has_android(self) -> bool:
109
+ """Whether this plugin contributes Kotlin code."""
110
+ return self.android_entry is not None
111
+
112
+
113
+ # ======================================================================
114
+ # Discovery
115
+ # ======================================================================
116
+
117
+
118
+ def load_plugin(root: Path, *, name: Optional[str] = None) -> NativePlugin:
119
+ """Read ``pn_plugin.json`` under ``root`` and collect its sources.
120
+
121
+ Args:
122
+ root: Directory containing the manifest.
123
+ name: Identifier override (defaults to the directory name).
124
+
125
+ Returns:
126
+ The parsed plugin.
127
+
128
+ Raises:
129
+ PluginError: If the manifest is missing or malformed, or an
130
+ entry is declared without any matching sources.
131
+ """
132
+ root = Path(root).resolve()
133
+ manifest_path = root / MANIFEST_NAME
134
+ if not manifest_path.is_file():
135
+ raise PluginError(f"{root} has no {MANIFEST_NAME}.")
136
+ try:
137
+ manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
138
+ except (OSError, ValueError) as exc:
139
+ raise PluginError(f"Could not parse {manifest_path}: {exc}") from exc
140
+ if not isinstance(manifest, dict):
141
+ raise PluginError(f"{manifest_path} must contain a JSON object.")
142
+
143
+ plugin_name = name or manifest.get("name") or root.name
144
+ if not _SWIFT_IDENT.match(str(plugin_name).replace("-", "_")):
145
+ raise PluginError(f"Plugin name {plugin_name!r} must be an identifier.")
146
+ plugin_name = str(plugin_name).replace("-", "_")
147
+
148
+ ios_entry = _entry(manifest, "ios", manifest_path)
149
+ android_entry = _entry(manifest, "android", manifest_path)
150
+ if ios_entry is None and android_entry is None:
151
+ raise PluginError(f"{manifest_path} declares neither an 'ios' nor an 'android' entry.")
152
+
153
+ ios_sources: List[Path] = []
154
+ android_sources: List[Path] = []
155
+ if ios_entry is not None:
156
+ if not _SWIFT_IDENT.match(ios_entry):
157
+ raise PluginError(f"{manifest_path}: ios.entry {ios_entry!r} is not a Swift type name.")
158
+ ios_sources = sorted(p.relative_to(root) for p in (root / "ios").rglob("*.swift") if p.is_file())
159
+ if not ios_sources:
160
+ raise PluginError(f"{root} declares an iOS entry but ios/ contains no .swift files.")
161
+ if android_entry is not None:
162
+ if not _KOTLIN_FQN.match(android_entry):
163
+ raise PluginError(f"{manifest_path}: android.entry {android_entry!r} must be a fully qualified name.")
164
+ android_sources = sorted(
165
+ p.relative_to(root) for p in (root / "android").rglob("*") if p.is_file() and p.suffix in (".kt", ".java")
166
+ )
167
+ if not android_sources:
168
+ raise PluginError(f"{root} declares an Android entry but android/ contains no .kt or .java files.")
169
+
170
+ def resources(platform: str, key: str) -> tuple[Path, ...]:
171
+ section = manifest.get(platform, {})
172
+ patterns = section.get(key, []) if isinstance(section, dict) else []
173
+ if not isinstance(patterns, list) or not all(isinstance(item, str) for item in patterns):
174
+ raise PluginError(f"{manifest_path}: {platform}.{key} must be a list of relative globs")
175
+ selected: set[Path] = set()
176
+ for pattern in patterns:
177
+ if Path(pattern).is_absolute() or ".." in Path(pattern).parts:
178
+ raise PluginError(f"Resource pattern escapes the plugin: {pattern}")
179
+ for resource in root.glob(pattern):
180
+ if resource.is_file():
181
+ if not resource.resolve().is_relative_to(root):
182
+ raise PluginError(f"Resource symlink escapes the plugin: {resource}")
183
+ selected.add(resource.relative_to(root))
184
+ return tuple(sorted(selected))
185
+
186
+ contract_path = Path(manifest["contracts"]) if "contracts" in manifest else None
187
+ if contract_path is not None and (
188
+ not (root / contract_path).is_file() or not (root / contract_path).resolve().is_relative_to(root)
189
+ ):
190
+ raise PluginError(f"{manifest_path}: contracts must name a file inside the plugin")
191
+ return NativePlugin(
192
+ name=plugin_name,
193
+ root=root,
194
+ ios_entry=ios_entry,
195
+ android_entry=android_entry,
196
+ ios_sources=tuple(ios_sources),
197
+ android_sources=tuple(android_sources),
198
+ ios_resources=resources("ios", "resources"),
199
+ android_resources=resources("android", "resources"),
200
+ android_assets=resources("android", "assets"),
201
+ contracts=contract_path,
202
+ )
203
+
204
+
205
+ def _entry(manifest: Dict[str, object], platform: str, manifest_path: Path) -> Optional[str]:
206
+ section = manifest.get(platform)
207
+ if section is None:
208
+ return None
209
+ if not isinstance(section, dict) or not isinstance(section.get("entry"), str):
210
+ raise PluginError(f"{manifest_path}: '{platform}' must be an object with a string 'entry'.")
211
+ return str(section["entry"])
212
+
213
+
214
+ def discover_plugins(
215
+ *,
216
+ extra_paths: Iterable[Path] = (),
217
+ include_environment: bool = True,
218
+ log: Optional[Logger] = None,
219
+ ) -> List[NativePlugin]:
220
+ """Find every plugin visible to this interpreter.
221
+
222
+ Args:
223
+ extra_paths: Plugin directories to include in addition to the
224
+ ``pythonnative.plugins`` entry points (from
225
+ ``[plugins].paths`` in the project config).
226
+ include_environment: Inspect this interpreter's installed entry points.
227
+ Mobile builds pass ``False`` and inspect the target wheels instead.
228
+ log: Optional progress logger; broken entry points are reported
229
+ here and skipped rather than failing the build.
230
+
231
+ Returns:
232
+ Plugins in a stable order (entry points sorted by name, then
233
+ explicit paths in the order given).
234
+ """
235
+ emit: Logger = log or (lambda _message: None)
236
+ plugins: List[NativePlugin] = []
237
+ seen: set = set()
238
+
239
+ for ep_name, target in sorted(_entry_points().items() if include_environment else []):
240
+ try:
241
+ root = _resolve_entry_point(target)
242
+ plugin = load_plugin(root, name=ep_name)
243
+ except (PluginError, ImportError, AttributeError) as exc:
244
+ emit(f"Skipping native plugin {ep_name!r}: {exc}")
245
+ continue
246
+ if plugin.name in seen:
247
+ emit(f"Skipping duplicate native plugin {plugin.name!r}.")
248
+ continue
249
+ seen.add(plugin.name)
250
+ plugins.append(plugin)
251
+
252
+ for path in extra_paths:
253
+ plugin = load_plugin(Path(path))
254
+ if plugin.name in seen:
255
+ raise PluginError(f"Native plugin {plugin.name!r} is declared twice.")
256
+ seen.add(plugin.name)
257
+ plugins.append(plugin)
258
+
259
+ return plugins
260
+
261
+
262
+ def _entry_points() -> Dict[str, str]:
263
+ try:
264
+ from importlib.metadata import entry_points
265
+ except ImportError: # pragma: no cover - Python < 3.8
266
+ return {}
267
+ try:
268
+ eps = entry_points()
269
+ except Exception:
270
+ return {}
271
+ selected = []
272
+ if hasattr(eps, "select"):
273
+ selected = list(eps.select(group=ENTRY_POINT_GROUP))
274
+ else: # pragma: no cover - importlib.metadata < 3.10
275
+ getter = getattr(eps, "get", None)
276
+ if callable(getter):
277
+ selected = list(getter(ENTRY_POINT_GROUP, []))
278
+ return {ep.name: ep.value for ep in selected}
279
+
280
+
281
+ def _resolve_entry_point(target: str) -> Path:
282
+ """Turn ``"pkg.subpkg"`` (or ``"pkg.subpkg:attr"``) into a directory."""
283
+ module_name, _, attr = target.partition(":")
284
+ module = import_module(module_name.strip())
285
+ if attr:
286
+ value = getattr(module, attr.strip())
287
+ return Path(str(value))
288
+ location = getattr(module, "__file__", None)
289
+ if location is None:
290
+ raise PluginError(f"{module_name!r} is a namespace package; point the entry point at a regular package.")
291
+ return Path(location).resolve().parent
292
+
293
+
294
+ # ======================================================================
295
+ # Staging
296
+ # ======================================================================
297
+
298
+
299
+ def stage_ios_plugins(project_dir: Path, plugins: Sequence[NativePlugin], *, log: Optional[Logger] = None) -> None:
300
+ """Copy Swift sources into ``PythonNativeKit`` and write the registration file.
301
+
302
+ Args:
303
+ project_dir: The staged ``ios_template`` directory.
304
+ plugins: Plugins to bundle; ones without an iOS side are skipped.
305
+ log: Optional progress logger.
306
+ """
307
+ emit: Logger = log or (lambda _message: None)
308
+ plugins_dir = Path(project_dir).joinpath(*_IOS_PLUGIN_DIR)
309
+ registration = Path(project_dir).joinpath(*_IOS_REGISTRATION)
310
+ plugins_dir.mkdir(parents=True, exist_ok=True)
311
+
312
+ # Wipe previous plugin folders so removed plugins don't linger in an
313
+ # incremental build directory; the registration file is regenerated.
314
+ for child in plugins_dir.iterdir():
315
+ if child.is_dir():
316
+ shutil.rmtree(child)
317
+
318
+ bundled = [p for p in plugins if p.has_ios]
319
+ for plugin in bundled:
320
+ target = plugins_dir / plugin.name
321
+ for rel in plugin.ios_sources:
322
+ dest = target / Path(*rel.parts[1:]) # strip the leading "ios/"
323
+ dest.parent.mkdir(parents=True, exist_ok=True)
324
+ shutil.copy2(plugin.root / rel, dest)
325
+ resource_root = Path(project_dir) / "PythonNativeKit/Sources/PythonNativeKit/PluginResources"
326
+ if resource_root.exists():
327
+ shutil.rmtree(resource_root)
328
+ for plugin in bundled:
329
+ for rel in plugin.ios_resources:
330
+ target = resource_root / plugin.name / rel
331
+ target.parent.mkdir(parents=True, exist_ok=True)
332
+ shutil.copy2(plugin.root / rel, target)
333
+ package = Path(project_dir) / "PythonNativeKit/Package.swift"
334
+ if package.exists():
335
+ contents = package.read_text(encoding="utf-8")
336
+ contents = contents.replace(', resources: [.process("PluginResources")]', "")
337
+ if resource_root.exists():
338
+ contents = contents.replace(
339
+ 'path: "Sources/PythonNativeKit"',
340
+ 'path: "Sources/PythonNativeKit", resources: [.process("PluginResources")]',
341
+ )
342
+ package.write_text(contents, encoding="utf-8")
343
+ registration.write_text(_render_swift_registration(bundled), encoding="utf-8")
344
+ if bundled:
345
+ emit(
346
+ f"Bundled {len(bundled)} native plugin(s) into PythonNativeKit: " + ", ".join(p.name for p in bundled) + "."
347
+ )
348
+
349
+
350
+ def stage_android_plugins(project_dir: Path, plugins: Sequence[NativePlugin], *, log: Optional[Logger] = None) -> None:
351
+ """Copy Kotlin sources into the ``pythonnative`` module and write the registration file.
352
+
353
+ Args:
354
+ project_dir: The staged ``android_template`` directory.
355
+ plugins: Plugins to bundle; ones without an Android side are skipped.
356
+ log: Optional progress logger.
357
+ """
358
+ emit: Logger = log or (lambda _message: None)
359
+ source_root = Path(project_dir).joinpath(*_ANDROID_SOURCE_ROOT)
360
+ registration = Path(project_dir).joinpath(*_ANDROID_REGISTRATION)
361
+ source_root.mkdir(parents=True, exist_ok=True)
362
+ managed_manifest = Path(project_dir) / ".pn-plugin-files.json"
363
+ if managed_manifest.exists():
364
+ for relative in json.loads(managed_manifest.read_text(encoding="utf-8")):
365
+ old = Path(project_dir) / relative
366
+ if old.resolve().is_relative_to(Path(project_dir).resolve()) and old.is_file():
367
+ old.unlink()
368
+ managed = []
369
+
370
+ bundled = [p for p in plugins if p.has_android]
371
+ for plugin in bundled:
372
+ for rel in plugin.android_sources:
373
+ dest = source_root / Path(*rel.parts[1:]) # strip the leading "android/"
374
+ if dest.exists() and dest.read_bytes() != (plugin.root / rel).read_bytes():
375
+ raise PluginError(f"Conflicting plugin source: {dest}")
376
+ dest.parent.mkdir(parents=True, exist_ok=True)
377
+ shutil.copy2(plugin.root / rel, dest)
378
+ managed.append(str(dest.relative_to(project_dir)))
379
+ native_root = Path(project_dir) / "pythonnative/src/main"
380
+ for plugin in bundled:
381
+ for kind, resources in (("res", plugin.android_resources), ("assets", plugin.android_assets)):
382
+ for rel in resources:
383
+ parts = rel.parts
384
+ # Android resource type directories (drawable, values, and so on) stay intact.
385
+ index = parts.index(kind) + 1 if kind in parts else max(0, len(parts) - 2)
386
+ target = native_root / kind / Path(*parts[index:])
387
+ if kind == "assets":
388
+ target = native_root / kind / plugin.name / Path(*parts[index:])
389
+ if target.exists() and target.read_bytes() != (plugin.root / rel).read_bytes():
390
+ raise PluginError(f"Conflicting plugin resource: {target}")
391
+ target.parent.mkdir(parents=True, exist_ok=True)
392
+ shutil.copy2(plugin.root / rel, target)
393
+ managed.append(str(target.relative_to(project_dir)))
394
+ registration.parent.mkdir(parents=True, exist_ok=True)
395
+ registration.write_text(_render_kotlin_registration(bundled), encoding="utf-8")
396
+ managed_manifest.write_text(json.dumps(sorted(managed)) + "\n", encoding="utf-8")
397
+ if bundled:
398
+ emit(
399
+ f"Bundled {len(bundled)} native plugin(s) into the pythonnative module: "
400
+ + ", ".join(p.name for p in bundled)
401
+ + "."
402
+ )
403
+
404
+
405
+ def _render_swift_registration(plugins: Sequence[NativePlugin]) -> str:
406
+ calls = "".join(f" {p.ios_entry}.register(into: registry)\n" for p in plugins)
407
+ body = f" {{\n{calls} }}" if calls else " {}"
408
+ return (
409
+ "// Generated by `pn build`. Do not edit: the PythonNative build tool\n"
410
+ "// overwrites this file with one `register(into:)` call per bundled plugin.\n"
411
+ "\n"
412
+ "/// Registration hook for plugins copied into `Sources/Plugins` by `pn build`.\n"
413
+ "public enum PNGeneratedPlugins {\n"
414
+ " /// Register every generated plugin into `registry`.\n"
415
+ f" public static func registerAll(into registry: PNRegistry){body}\n"
416
+ "}\n"
417
+ )
418
+
419
+
420
+ def _render_kotlin_registration(plugins: Sequence[NativePlugin]) -> str:
421
+ calls = "".join(f" {p.android_entry}.register(registry)\n" for p in plugins)
422
+ if calls:
423
+ method = f" fun registerAll(registry: PNRegistry) {{\n{calls} }}\n"
424
+ else:
425
+ method = ' @Suppress("UNUSED_PARAMETER")\n fun registerAll(registry: PNRegistry) {}\n'
426
+ return (
427
+ "package com.pythonnative.runtime.plugins\n"
428
+ "\n"
429
+ "import com.pythonnative.runtime.bridge.PNRegistry\n"
430
+ "\n"
431
+ "/**\n"
432
+ " * Plugin registrations generated by `pn build`.\n"
433
+ " *\n"
434
+ " * The build tool overwrites this file with one `register` call per\n"
435
+ " * plugin entry declared in the project's `pythonnative.plugins` entry\n"
436
+ " * points. Nothing else may depend on its content beyond [registerAll].\n"
437
+ " */\n"
438
+ "object GeneratedPlugins {\n"
439
+ " /** Register every generated plugin with `registry`. */\n"
440
+ f"{method}"
441
+ "}\n"
442
+ )
443
+
444
+
445
+ def discover_target_plugins(config: Any, targets: Sequence[Any], runner: Any) -> List[NativePlugin]:
446
+ """Discover native source manifests from the wheels selected for each device.
447
+
448
+ Target wheels are inspected as data. Their Python code isn't imported into
449
+ the build interpreter, so binary extensions and host-only installations
450
+ don't affect native plugin discovery.
451
+ """
452
+ import hashlib
453
+ import zipfile
454
+
455
+ from . import deps
456
+ from .lockfile import read, target_key
457
+
458
+ if not config.requirements:
459
+ return []
460
+ selected: Dict[str, NativePlugin] = {}
461
+ fingerprints: Dict[str, str] = {}
462
+ lock = read(config)
463
+ for target in targets:
464
+ cache = config.project_root / "build" / "plugin_wheels" / target_key(target).replace(":", "_")
465
+ cache.mkdir(parents=True, exist_ok=True)
466
+ if lock is not None:
467
+ entry = lock["targets"].get(target_key(target))
468
+ if entry is None:
469
+ raise PluginError(f"pn.lock has no {target.label}; run 'pn deps --lock'.")
470
+ urls = [package["url"] + "#sha256=" + package["sha256"] for package in entry["packages"]]
471
+ else:
472
+ resolution = deps.resolve(config, target, runner=runner)
473
+ if not resolution.ok:
474
+ raise PluginError(f"Cannot resolve plugins for {target.label}: {resolution.error}")
475
+ urls = [package.url for package in resolution.packages]
476
+ if urls:
477
+ command = deps.pip_base_args(config, target)
478
+ command[3] = "download"
479
+ result = runner.run([*command, "--no-deps", "--dest", str(cache), *urls], capture=True)
480
+ if not result.ok:
481
+ raise PluginError(f"Could not download plugin metadata for {target.label}: {result.stderr}")
482
+ # Only inspect wheels in this resolution, excluding removed cached packages.
483
+ names = {url.split("#", 1)[0].rsplit("/", 1)[-1] for url in urls}
484
+ for wheel in sorted(cache.glob("*.whl")):
485
+ if wheel.name not in names:
486
+ continue
487
+ with zipfile.ZipFile(wheel) as archive:
488
+ manifests = [name for name in archive.namelist() if name.endswith("/" + MANIFEST_NAME)]
489
+ if not manifests:
490
+ continue
491
+ digest = hashlib.sha256(wheel.read_bytes()).hexdigest()
492
+ destination = cache / "extracted" / digest
493
+ if not destination.exists():
494
+ for info in archive.infolist():
495
+ if Path(info.filename).is_absolute() or ".." in Path(info.filename).parts:
496
+ raise PluginError(f"Unsafe wheel path: {info.filename}")
497
+ destination.mkdir(parents=True)
498
+ archive.extractall(destination)
499
+ for manifest_path in manifests:
500
+ plugin = load_plugin((destination / manifest_path).parent)
501
+ fingerprint = hashlib.sha256()
502
+ fingerprint.update((plugin.root / MANIFEST_NAME).read_bytes())
503
+ if plugin.contracts is not None:
504
+ fingerprint.update((plugin.root / plugin.contracts).read_bytes())
505
+ for rel in [
506
+ *plugin.ios_sources,
507
+ *plugin.android_sources,
508
+ *plugin.ios_resources,
509
+ *plugin.android_resources,
510
+ *plugin.android_assets,
511
+ ]:
512
+ fingerprint.update(str(rel).encode())
513
+ fingerprint.update((plugin.root / rel).read_bytes())
514
+ value = fingerprint.hexdigest()
515
+ if plugin.name in fingerprints and fingerprints[plugin.name] != value:
516
+ raise PluginError(f"Plugin {plugin.name} has different native source across target wheels")
517
+ fingerprints[plugin.name] = value
518
+ selected[plugin.name] = plugin
519
+ return [selected[name] for name in sorted(selected)]