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,719 @@
1
+ """Target-aware resolution of ``[requirements].packages``.
2
+
3
+ A PythonNative app runs on an embedded CPython, so its third-party
4
+ packages have to be resolved for the *device*, not for the machine
5
+ running ``pn``. Pure-Python wheels (``py3-none-any``) work everywhere,
6
+ but anything with a compiled extension needs a wheel built for the
7
+ exact platform triple the app will run on:
8
+
9
+ - iOS (PEP 730): ``ios_13_0_arm64_iphoneos`` for devices and
10
+ ``ios_13_0_arm64_iphonesimulator`` / ``ios_13_0_x86_64_iphonesimulator``
11
+ for the Simulator, published on PyPI and on BeeWare's index.
12
+ - Android (PEP 738): ``android_24_arm64_v8a`` / ``android_24_x86_64``,
13
+ published on PyPI and on Chaquopy's index.
14
+
15
+ This module models those targets ([`Target`][pythonnative.project.deps.Target]),
16
+ asks pip to resolve the project's requirements against each of them
17
+ without installing anything ([`resolve`][pythonnative.project.deps.resolve]),
18
+ and installs the resolved set into a per-target directory for the
19
+ iOS build ([`install`][pythonnative.project.deps.install]). ``pip``
20
+ does the actual work through its ``--platform`` / ``--python-version``
21
+ / ``--only-binary=:all:`` cross-resolution mode, so the host
22
+ interpreter's version and platform never leak into the app.
23
+
24
+ On Android the authoritative install is done by Chaquopy inside the
25
+ Gradle build (with the same ``--only-binary`` rule); resolving here is
26
+ a fast preview that catches missing wheels before a multi-minute build.
27
+
28
+ All pip invocations go through the builder's
29
+ [`CommandRunner`][pythonnative.project.builder.CommandRunner] so the
30
+ logic is unit testable with a recording fake.
31
+ """
32
+
33
+ from __future__ import annotations
34
+
35
+ import json
36
+ import re
37
+ import sys
38
+ from dataclasses import dataclass, field
39
+ from pathlib import Path
40
+ from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Sequence, Tuple
41
+ from urllib.parse import urlparse
42
+
43
+ from .config import AppConfig
44
+
45
+ if TYPE_CHECKING:
46
+ from .builder import CommandRunner
47
+
48
+ BEEWARE_INDEX_URL = "https://pypi.anaconda.org/beeware/simple"
49
+ """BeeWare's wheel index: iOS binary wheels for the CPython versions Python-Apple-support ships."""
50
+
51
+ CHAQUOPY_INDEX_URL = "https://chaquo.com/pypi-13.1/"
52
+ """Chaquopy's wheel index: Android binary wheels built for its embedded CPython."""
53
+
54
+ MIN_IOS_WHEEL_VERSION = (12, 0)
55
+ """Oldest iOS version a PEP 730 wheel can be tagged with (CPython's own floor)."""
56
+
57
+ MIN_ANDROID_WHEEL_API = 16
58
+ """Oldest Android API level a PEP 738 wheel can be tagged with."""
59
+
60
+ IOS_SDKS = ("iphoneos", "iphonesimulator")
61
+ """The two iOS SDKs a build can target; each gets its own ``app_packages`` slice."""
62
+
63
+ ANDROID_ABI_TAGS: Dict[str, str] = {"arm64-v8a": "arm64_v8a", "x86_64": "x86_64"}
64
+ """Gradle ABI name -> wheel platform tag component."""
65
+
66
+
67
+ # ======================================================================
68
+ # Targets
69
+ # ======================================================================
70
+
71
+
72
+ @dataclass(frozen=True)
73
+ class Target:
74
+ """One device platform to resolve packages for.
75
+
76
+ Attributes:
77
+ platform: ``"ios"`` or ``"android"``.
78
+ python_version: CPython ``major.minor`` the app embeds.
79
+ arch: CPU architecture (``"arm64"`` or ``"x86_64"``).
80
+ sdk: iOS SDK (``"iphoneos"`` / ``"iphonesimulator"``); empty on
81
+ Android.
82
+ os_version: Minimum OS version the app declares: the iOS
83
+ deployment target (``"13.0"``) or the Android API level as a
84
+ string (``"24"``).
85
+ """
86
+
87
+ platform: str
88
+ python_version: str
89
+ arch: str
90
+ sdk: str = ""
91
+ os_version: str = "13.0"
92
+
93
+ @property
94
+ def label(self) -> str:
95
+ """Short human-readable name, e.g. ``"iOS Simulator (arm64)"``."""
96
+ if self.platform == "ios":
97
+ kind = "iOS device" if self.sdk == "iphoneos" else "iOS Simulator"
98
+ return f"{kind} ({self.arch}, iOS {self.os_version}+)"
99
+ return f"Android {self.abi} (API {self.os_version}+)"
100
+
101
+ @property
102
+ def abi(self) -> str:
103
+ """The Gradle ABI name for an Android target (``"arm64-v8a"``)."""
104
+ for abi, tag in ANDROID_ABI_TAGS.items():
105
+ if tag == self.arch or abi == self.arch:
106
+ return abi
107
+ return self.arch
108
+
109
+ @property
110
+ def slice_name(self) -> str:
111
+ """The staged packages folder name for an iOS target (``app_packages.iphoneos``)."""
112
+ return f"app_packages.{self.sdk}"
113
+
114
+ @property
115
+ def platform_tags(self) -> List[str]:
116
+ """Every wheel platform tag this target accepts, newest first.
117
+
118
+ A wheel is tagged with the *minimum* OS version it supports, so
119
+ a target on iOS 13.0 also accepts wheels tagged for 12.x.
120
+ Mirrors ``packaging.tags.ios_platforms`` /
121
+ ``android_platforms`` so the list stays in step with what pip
122
+ itself would compute on the device.
123
+ """
124
+ if self.platform == "ios":
125
+ return list(_ios_platform_tags(self.os_version, f"{self.arch}_{self.sdk}"))
126
+ return list(_android_platform_tags(int(self.os_version), ANDROID_ABI_TAGS.get(self.arch, self.arch)))
127
+
128
+ @property
129
+ def index_urls(self) -> List[str]:
130
+ """Extra indexes searched after PyPI: the platform's binary wheel repository."""
131
+ return [BEEWARE_INDEX_URL] if self.platform == "ios" else [CHAQUOPY_INDEX_URL]
132
+
133
+ @property
134
+ def abi_tag(self) -> str:
135
+ """The CPython ABI tag (``cp313``)."""
136
+ return "cp" + self.python_version.replace(".", "")
137
+
138
+
139
+ def _ios_platform_tags(version: str, multiarch: str) -> Iterable[str]:
140
+ major, minor = _parse_version(version)
141
+ for maj in range(major, MIN_IOS_WHEEL_VERSION[0] - 1, -1):
142
+ top = minor if maj == major else 9
143
+ bottom = MIN_IOS_WHEEL_VERSION[1] if maj == MIN_IOS_WHEEL_VERSION[0] else 0
144
+ for mnr in range(top, bottom - 1, -1):
145
+ yield f"ios_{maj}_{mnr}_{multiarch}"
146
+
147
+
148
+ def _android_platform_tags(api_level: int, abi: str) -> Iterable[str]:
149
+ for level in range(api_level, MIN_ANDROID_WHEEL_API - 1, -1):
150
+ yield f"android_{level}_{abi}"
151
+
152
+
153
+ def _parse_version(version: str) -> Tuple[int, int]:
154
+ parts = version.split(".")
155
+ major = int(parts[0])
156
+ minor = int(parts[1]) if len(parts) > 1 else 0
157
+ return major, minor
158
+
159
+
160
+ def host_simulator_arch() -> str:
161
+ """The iOS Simulator architecture matching this Mac (``arm64`` or ``x86_64``)."""
162
+ import platform as platform_module
163
+
164
+ return "arm64" if platform_module.machine() == "arm64" else "x86_64"
165
+
166
+
167
+ def ios_targets(
168
+ config: AppConfig, *, sdks: Sequence[str] = IOS_SDKS, simulator_arch: Optional[str] = None
169
+ ) -> List[Target]:
170
+ """Build the iOS targets for ``config``.
171
+
172
+ Args:
173
+ config: The validated app configuration.
174
+ sdks: Which SDKs to include (default both).
175
+ simulator_arch: Architecture for the Simulator target; defaults
176
+ to the host Mac's, which is also what the builder pins
177
+ ``ARCHS`` to.
178
+
179
+ Returns:
180
+ One [`Target`][pythonnative.project.deps.Target] per SDK.
181
+ """
182
+ targets: List[Target] = []
183
+ for sdk in sdks:
184
+ if sdk not in IOS_SDKS:
185
+ raise ValueError(f"Unknown iOS SDK {sdk!r} (expected one of {IOS_SDKS}).")
186
+ arch = "arm64" if sdk == "iphoneos" else (simulator_arch or host_simulator_arch())
187
+ targets.append(
188
+ Target(
189
+ platform="ios",
190
+ python_version=config.python_version,
191
+ arch=arch,
192
+ sdk=sdk,
193
+ os_version=config.ios.deployment_target,
194
+ )
195
+ )
196
+ return targets
197
+
198
+
199
+ def android_targets(config: AppConfig) -> List[Target]:
200
+ """Build one Android target per ``[android].abi_filters`` entry."""
201
+ return [
202
+ Target(
203
+ platform="android",
204
+ python_version=config.python_version,
205
+ arch=ANDROID_ABI_TAGS.get(abi, abi),
206
+ os_version=str(config.android.min_sdk),
207
+ )
208
+ for abi in config.android.abi_filters
209
+ ]
210
+
211
+
212
+ def targets_for(
213
+ config: AppConfig, platform: Optional[str] = None, *, all_simulator_archs: bool = False
214
+ ) -> List[Target]:
215
+ """Targets for a platform, optionally including both Mac architectures for portable locks."""
216
+ targets: List[Target] = []
217
+ if platform in (None, "ios"):
218
+ if all_simulator_archs:
219
+ targets.extend(ios_targets(config, simulator_arch="arm64"))
220
+ targets.extend(ios_targets(config, sdks=("iphonesimulator",), simulator_arch="x86_64"))
221
+ else:
222
+ targets.extend(ios_targets(config))
223
+ if platform in (None, "android"):
224
+ targets.extend(android_targets(config))
225
+ return targets
226
+
227
+
228
+ # ======================================================================
229
+ # pip invocations
230
+ # ======================================================================
231
+
232
+
233
+ def pip_base_args(config: AppConfig, target: Target, *, python: Optional[str] = None) -> List[str]:
234
+ """The pip arguments that select ``target`` for cross-platform resolution.
235
+
236
+ Args:
237
+ config: The app configuration (for ``extra_index_urls``).
238
+ target: The device target.
239
+ python: Interpreter to run pip with; defaults to the current one.
240
+
241
+ Returns:
242
+ ``[python, "-m", "pip", "install", <selection flags...>]`` with
243
+ no requirements appended.
244
+ """
245
+ args = [
246
+ python or sys.executable,
247
+ "-m",
248
+ "pip",
249
+ "install",
250
+ "--disable-pip-version-check",
251
+ # Cross-resolution: pip refuses to build from source for a foreign
252
+ # platform, which is also exactly what an app store build needs.
253
+ "--only-binary=:all:",
254
+ "--implementation",
255
+ "cp",
256
+ "--python-version",
257
+ target.python_version,
258
+ "--abi",
259
+ target.abi_tag,
260
+ ]
261
+ for tag in target.platform_tags:
262
+ args.extend(["--platform", tag])
263
+ for url in [*target.index_urls, *config.extra_index_urls]:
264
+ args.extend(["--extra-index-url", url])
265
+ return args
266
+
267
+
268
+ def report_args(config: AppConfig, target: Target, *, python: Optional[str] = None) -> List[str]:
269
+ """Pip arguments that resolve ``config.requirements`` for ``target`` without installing.
270
+
271
+ The JSON report (``--report -``) lists every wheel pip *would*
272
+ install, which is what [`resolve`][pythonnative.project.deps.resolve]
273
+ parses.
274
+ """
275
+ return [
276
+ *pip_base_args(config, target, python=python),
277
+ "--dry-run",
278
+ "--ignore-installed",
279
+ "--quiet",
280
+ "--report",
281
+ "-",
282
+ *config.requirements,
283
+ ]
284
+
285
+
286
+ def install_args(config: AppConfig, target: Target, dest: Path, *, python: Optional[str] = None) -> List[str]:
287
+ """Pip arguments that install ``config.requirements`` for ``target`` into ``dest``."""
288
+ return [
289
+ *pip_base_args(config, target, python=python),
290
+ "--target",
291
+ str(dest),
292
+ "--upgrade",
293
+ *config.requirements,
294
+ ]
295
+
296
+
297
+ # ======================================================================
298
+ # Results
299
+ # ======================================================================
300
+
301
+
302
+ @dataclass
303
+ class ResolvedPackage:
304
+ """One wheel pip selected for a target.
305
+
306
+ Attributes:
307
+ name: Distribution name as reported by pip.
308
+ version: Selected version.
309
+ filename: The wheel filename (its tags say what it is built for).
310
+ url: Where the wheel comes from.
311
+ """
312
+
313
+ name: str
314
+ version: str
315
+ filename: str
316
+ url: str = ""
317
+ sha256: str = ""
318
+ latest: str = ""
319
+ """The version an unconstrained desktop resolution picks, when it is newer than ``version``.
320
+
321
+ Set by [`mark_downgrades`][pythonnative.project.deps.mark_downgrades].
322
+ A non-empty value means pip walked back to an older release because
323
+ the newer ones have no wheel for this target.
324
+ """
325
+
326
+ @property
327
+ def downgraded(self) -> bool:
328
+ """Whether this target got an older release than the desktop would."""
329
+ return bool(self.latest)
330
+
331
+ @property
332
+ def is_pure(self) -> bool:
333
+ """Whether this is a platform-independent (``none-any``) wheel."""
334
+ return self.filename.endswith("-none-any.whl")
335
+
336
+ @property
337
+ def platform_tag(self) -> str:
338
+ """The wheel's platform tag (``"any"`` for pure-Python wheels)."""
339
+ stem = self.filename[:-4] if self.filename.endswith(".whl") else self.filename
340
+ return stem.rsplit("-", 1)[-1]
341
+
342
+ @property
343
+ def index_host(self) -> str:
344
+ """Host name of the index the wheel is served from (``"files.pythonhosted.org"`` for PyPI)."""
345
+ return urlparse(self.url).netloc if self.url else ""
346
+
347
+ @property
348
+ def source_label(self) -> str:
349
+ """Friendly source name: ``PyPI``, ``BeeWare``, ``Chaquopy``, or the index host."""
350
+ host = self.index_host
351
+ if host.endswith("pythonhosted.org") or host.endswith("pypi.org"):
352
+ return "PyPI"
353
+ if host == "pypi.anaconda.org" and "/beeware/" in self.url:
354
+ return "BeeWare"
355
+ if host == "chaquo.com":
356
+ return "Chaquopy"
357
+ return host or "local"
358
+
359
+
360
+ @dataclass
361
+ class Resolution:
362
+ """The outcome of resolving the requirements for one target.
363
+
364
+ Attributes:
365
+ target: The target that was resolved.
366
+ packages: Wheels pip selected (empty when ``error`` is set or
367
+ there are no requirements).
368
+ error: pip's failure explanation when resolution failed, else
369
+ ``None``.
370
+ missing: Requirement names pip could not satisfy, parsed from
371
+ ``error`` (best effort).
372
+ """
373
+
374
+ target: Target
375
+ packages: List[ResolvedPackage] = field(default_factory=list)
376
+ error: Optional[str] = None
377
+ missing: List[str] = field(default_factory=list)
378
+
379
+ @property
380
+ def ok(self) -> bool:
381
+ """Whether every requirement resolved to a wheel."""
382
+ return self.error is None
383
+
384
+ @property
385
+ def binary_packages(self) -> List[ResolvedPackage]:
386
+ """The platform-specific wheels in ``packages``."""
387
+ return [pkg for pkg in self.packages if not pkg.is_pure]
388
+
389
+ @property
390
+ def downgraded_packages(self) -> List[ResolvedPackage]:
391
+ """Packages that resolved to an older release than the desktop would get."""
392
+ return [pkg for pkg in self.packages if pkg.downgraded]
393
+
394
+ def to_dict(self) -> Dict[str, Any]:
395
+ """Serialize for ``pn deps --json`` and the compatibility matrix job."""
396
+ return {
397
+ "platform": self.target.platform,
398
+ "label": self.target.label,
399
+ "python_version": self.target.python_version,
400
+ "arch": self.target.arch,
401
+ "sdk": self.target.sdk,
402
+ "ok": self.ok,
403
+ "error": self.error,
404
+ "missing": list(self.missing),
405
+ "packages": [
406
+ {
407
+ "name": pkg.name,
408
+ "version": pkg.version,
409
+ "filename": pkg.filename,
410
+ "pure": pkg.is_pure,
411
+ "platform_tag": pkg.platform_tag,
412
+ "source": pkg.source_label,
413
+ "url": pkg.url,
414
+ "latest": pkg.latest or None,
415
+ }
416
+ for pkg in self.packages
417
+ ],
418
+ }
419
+
420
+
421
+ class DependencyError(Exception):
422
+ """Raised when requirements can't be resolved or installed for a target.
423
+
424
+ The message is user-facing: it names the target, the unsatisfiable
425
+ requirement(s), and what to do about it.
426
+ """
427
+
428
+
429
+ # ======================================================================
430
+ # Resolving
431
+ # ======================================================================
432
+
433
+ _NO_MATCH_RE = re.compile(r"No matching distribution found for (?P<req>[^\s]+)")
434
+ _NO_VERSION_RE = re.compile(r"Could not find a version that satisfies the requirement (?P<req>[^\s]+)")
435
+
436
+
437
+ def parse_report(report_json: str, target: Target) -> Resolution:
438
+ """Turn a pip ``--report`` JSON document into a [`Resolution`][pythonnative.project.deps.Resolution]."""
439
+ data = json.loads(report_json or "{}")
440
+ packages: List[ResolvedPackage] = []
441
+ for item in data.get("install", []):
442
+ metadata = item.get("metadata", {})
443
+ download = item.get("download_info", {}) or {}
444
+ url = str(download.get("url", ""))
445
+ filename = url.rsplit("/", 1)[-1] if url else ""
446
+ packages.append(
447
+ ResolvedPackage(
448
+ name=str(metadata.get("name", "?")),
449
+ version=str(metadata.get("version", "?")),
450
+ filename=filename,
451
+ url=url,
452
+ sha256=str(download.get("archive_info", {}).get("hashes", {}).get("sha256", "")),
453
+ )
454
+ )
455
+ packages.sort(key=lambda pkg: pkg.name.lower())
456
+ return Resolution(target=target, packages=packages)
457
+
458
+
459
+ def parse_failure(stderr: str) -> Tuple[str, List[str]]:
460
+ """Extract a one-paragraph explanation and the unsatisfied requirement names from pip's stderr."""
461
+ text = (stderr or "").strip()
462
+ missing: List[str] = []
463
+ for match in _NO_MATCH_RE.finditer(text):
464
+ req = match.group("req").strip(".,")
465
+ if req not in missing:
466
+ missing.append(req)
467
+ if not missing:
468
+ for match in _NO_VERSION_RE.finditer(text):
469
+ req = match.group("req").strip(".,")
470
+ if req not in missing:
471
+ missing.append(req)
472
+ # pip prints the useful sentence(s) as ERROR: lines; everything else is noise.
473
+ lines = [line for line in text.splitlines() if line.startswith("ERROR:")]
474
+ summary = "\n".join(lines) if lines else (text.splitlines()[-1] if text else "pip failed without output")
475
+ return summary, missing
476
+
477
+
478
+ def resolve(config: AppConfig, target: Target, *, runner: CommandRunner, python: Optional[str] = None) -> Resolution:
479
+ """Resolve ``config.requirements`` for ``target`` without installing.
480
+
481
+ Args:
482
+ config: The validated app configuration.
483
+ target: The device target.
484
+ runner: A [`CommandRunner`][pythonnative.project.builder.CommandRunner].
485
+ python: Interpreter to run pip with (defaults to the current one).
486
+
487
+ Returns:
488
+ A [`Resolution`][pythonnative.project.deps.Resolution]; check
489
+ ``.ok`` rather than expecting an exception, so callers can
490
+ report every target at once.
491
+ """
492
+ if not config.requirements:
493
+ return Resolution(target=target)
494
+ result = runner.run(report_args(config, target, python=python), capture=True)
495
+ if not result.ok:
496
+ summary, missing = parse_failure(result.stderr)
497
+ return Resolution(target=target, error=summary, missing=missing)
498
+ try:
499
+ return parse_report(result.stdout, target)
500
+ except (ValueError, TypeError) as exc:
501
+ return Resolution(target=target, error=f"Could not parse pip's report: {exc}")
502
+
503
+
504
+ def resolve_all(
505
+ config: AppConfig,
506
+ targets: Sequence[Target],
507
+ *,
508
+ runner: CommandRunner,
509
+ python: Optional[str] = None,
510
+ detect_downgrades: bool = True,
511
+ ) -> List[Resolution]:
512
+ """[`resolve`][pythonnative.project.deps.resolve] every target in order.
513
+
514
+ Args:
515
+ config: The validated app configuration.
516
+ targets: Targets to resolve for.
517
+ runner: A [`CommandRunner`][pythonnative.project.builder.CommandRunner].
518
+ python: Interpreter to run pip with (defaults to the current one).
519
+ detect_downgrades: Also resolve once without any platform
520
+ constraint and mark packages that got an older release on a
521
+ target than that unconstrained resolution picked (see
522
+ [`mark_downgrades`][pythonnative.project.deps.mark_downgrades]).
523
+ One extra pip call; skipped when there are no requirements.
524
+ """
525
+ resolutions = [resolve(config, target, runner=runner, python=python) for target in targets]
526
+ if detect_downgrades and config.requirements and any(res.ok and res.packages for res in resolutions):
527
+ reference = resolve_reference(config, runner=runner, python=python)
528
+ if reference is not None:
529
+ mark_downgrades(resolutions, reference)
530
+ return resolutions
531
+
532
+
533
+ def reference_args(config: AppConfig, *, python: Optional[str] = None) -> List[str]:
534
+ """Pip arguments for an unconstrained dry-run resolution of ``config.requirements``.
535
+
536
+ No platform or ABI flags and source distributions allowed, so pip
537
+ picks the newest release it would give a desktop. Only the versions
538
+ matter; nothing is downloaded beyond metadata.
539
+ """
540
+ return [
541
+ python or sys.executable,
542
+ "-m",
543
+ "pip",
544
+ "install",
545
+ "--disable-pip-version-check",
546
+ "--dry-run",
547
+ "--ignore-installed",
548
+ "--quiet",
549
+ "--report",
550
+ "-",
551
+ *[arg for url in config.extra_index_urls for arg in ("--extra-index-url", url)],
552
+ *config.requirements,
553
+ ]
554
+
555
+
556
+ def resolve_reference(
557
+ config: AppConfig, *, runner: CommandRunner, python: Optional[str] = None
558
+ ) -> Optional[Dict[str, str]]:
559
+ """Resolve the requirements with no target constraint; return ``{name: version}``.
560
+
561
+ Returns ``None`` when pip can't resolve them at all (the per-target
562
+ errors already say why) or the report can't be parsed.
563
+ """
564
+ if not config.requirements:
565
+ return {}
566
+ result = runner.run(reference_args(config, python=python), capture=True)
567
+ if not result.ok:
568
+ return None
569
+ try:
570
+ data = json.loads(result.stdout or "{}")
571
+ except ValueError:
572
+ return None
573
+ versions: Dict[str, str] = {}
574
+ for item in data.get("install", []):
575
+ metadata = item.get("metadata", {})
576
+ versions[_canonical(str(metadata.get("name", "")))] = str(metadata.get("version", ""))
577
+ return versions
578
+
579
+
580
+ def mark_downgrades(resolutions: Sequence[Resolution], reference: Dict[str, str]) -> None:
581
+ """Set ``latest`` on every resolved package that is older than in ``reference``.
582
+
583
+ pip's cross-resolution silently walks back through a package's
584
+ release history until it finds a version with a usable wheel for
585
+ the target. That is the right call for a build (it works), but it
586
+ is a surprise for the developer who gets a two-year-old API on the
587
+ device and the current one in their tests. Marking the gap lets
588
+ ``pn deps`` say so.
589
+
590
+ Args:
591
+ resolutions: Per-target resolutions to annotate in place.
592
+ reference: ``{canonical name: version}`` from an unconstrained
593
+ resolution (see
594
+ [`resolve_reference`][pythonnative.project.deps.resolve_reference]).
595
+ """
596
+ for res in resolutions:
597
+ for pkg in res.packages:
598
+ latest = reference.get(_canonical(pkg.name))
599
+ if latest and _version_key(latest) > _version_key(pkg.version):
600
+ pkg.latest = latest
601
+
602
+
603
+ def _canonical(name: str) -> str:
604
+ return re.sub(r"[-_.]+", "-", name).lower()
605
+
606
+
607
+ def _version_key(version: str) -> Tuple[int, ...]:
608
+ """Order versions by their numeric release segment, enough to detect a walk-back.
609
+
610
+ Pre/post/dev suffixes are ignored on purpose: a ``2.5.2`` versus
611
+ ``2.5.2.post1`` gap is not the kind of downgrade worth a warning.
612
+ """
613
+ match = re.match(r"^\d+(?:\.\d+)*", version)
614
+ return tuple(int(part) for part in match.group(0).split(".")) if match else ()
615
+
616
+
617
+ def install(
618
+ config: AppConfig,
619
+ target: Target,
620
+ dest: Path,
621
+ *,
622
+ runner: CommandRunner,
623
+ python: Optional[str] = None,
624
+ ) -> None:
625
+ """Install ``config.requirements`` for ``target`` into ``dest``.
626
+
627
+ Args:
628
+ config: The validated app configuration.
629
+ target: The device target (an iOS one; Android installs happen in
630
+ Gradle).
631
+ dest: The ``app_packages.<sdk>`` directory to populate.
632
+ runner: A [`CommandRunner`][pythonnative.project.builder.CommandRunner].
633
+ python: Interpreter to run pip with (defaults to the current one).
634
+
635
+ Raises:
636
+ DependencyError: When pip can't satisfy the requirements for the
637
+ target, with the unsatisfiable requirement(s) named.
638
+ """
639
+ if not config.requirements:
640
+ return
641
+ dest.mkdir(parents=True, exist_ok=True)
642
+ from .lockfile import requirements
643
+
644
+ locked = requirements(config, [target], direct=True)
645
+ command = install_args(config, target, dest, python=python)
646
+ if locked is not None:
647
+ locked_path = dest.parent / f"requirements.{target.slice_name}.lock"
648
+ locked_path.write_text(locked, encoding="utf-8")
649
+ command = command[: -len(config.requirements)] + ["--no-deps", "-r", str(locked_path)]
650
+ result = runner.run(command, capture=True)
651
+ if result.ok:
652
+ return
653
+ summary, missing = parse_failure(result.stderr)
654
+ raise DependencyError(explain_failure(target, summary, missing))
655
+
656
+
657
+ def explain_failure(target: Target, summary: str, missing: Sequence[str]) -> str:
658
+ """Compose the user-facing message for a failed resolution."""
659
+ names = ", ".join(missing) if missing else "one or more requirements"
660
+ lines = [f"Could not resolve {names} for {target.label} (Python {target.python_version})."]
661
+ if summary:
662
+ lines.append(summary)
663
+ if target.platform == "ios":
664
+ lines.append(
665
+ "iOS needs a wheel tagged for the device (ios_*_arm64_iphoneos) and, for 'pn run ios', the "
666
+ "Simulator (ios_*_iphonesimulator). Pure-Python packages always work; for a C extension, check "
667
+ f"{BEEWARE_INDEX_URL} or ask upstream for iOS wheels (PEP 730)."
668
+ )
669
+ else:
670
+ lines.append(
671
+ "Android needs a wheel tagged android_*_arm64_v8a / android_*_x86_64 (PEP 738). Pure-Python "
672
+ f"packages always work; for a C extension, check {CHAQUOPY_INDEX_URL} or PyPI's Android wheels."
673
+ )
674
+ lines.append("Run 'pn deps' for a per-target report; see https://pythonnative.com/guides/pypi-packages/.")
675
+ return "\n".join(lines)
676
+
677
+
678
+ # ======================================================================
679
+ # Reporting
680
+ # ======================================================================
681
+
682
+
683
+ def format_report(resolutions: Sequence[Resolution], *, requirements: Sequence[str]) -> str:
684
+ """Render resolutions as the terminal report ``pn deps`` prints."""
685
+ if not requirements:
686
+ return "No [requirements].packages declared in pythonnative.toml; nothing to resolve."
687
+ out: List[str] = []
688
+ for res in resolutions:
689
+ header = res.target.label
690
+ if res.target.platform == "android":
691
+ header += " (preview; Chaquopy resolves again inside the Gradle build)"
692
+ out.append(header)
693
+ if not res.ok:
694
+ out.append(" [x] " + (res.error or "resolution failed").replace("\n", "\n "))
695
+ out.append("")
696
+ continue
697
+ width = max((len(f"{pkg.name} {pkg.version}") for pkg in res.packages), default=0)
698
+ for pkg in res.packages:
699
+ name = f"{pkg.name} {pkg.version}".ljust(width)
700
+ kind = "pure Python" if pkg.is_pure else f"binary wheel {pkg.platform_tag} ({pkg.source_label})"
701
+ marker = "[!!]" if pkg.downgraded else "[ok]"
702
+ out.append(f" {marker} {name} {kind}")
703
+ if pkg.downgraded:
704
+ out.append(
705
+ f" older than the desktop resolution ({pkg.latest}): newer releases have no wheel here"
706
+ )
707
+ out.append("")
708
+ failed = [res for res in resolutions if not res.ok]
709
+ downgraded = sorted({pkg.name for res in resolutions for pkg in res.downgraded_packages}, key=str.lower)
710
+ if downgraded:
711
+ out.append(
712
+ f"Older release selected for: {', '.join(downgraded)} ([!!] above). Pin a version in "
713
+ "[requirements].packages if the API difference matters."
714
+ )
715
+ if failed:
716
+ out.append(f"{len(failed)} of {len(resolutions)} targets cannot be satisfied. See the [x] lines above.")
717
+ else:
718
+ out.append(f"All {len(resolutions)} targets resolved.")
719
+ return "\n".join(out)