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,304 @@
1
+ """Device and simulator discovery for ``pn devices`` / ``pn run --device``.
2
+
3
+ Enumerates the three kinds of targets a PythonNative app can run on:
4
+
5
+ - **Android devices and emulators** via ``adb devices -l``.
6
+ - **iOS Simulators** via ``xcrun simctl list devices --json``.
7
+ - **Physical iOS devices** via ``xcrun devicectl list devices`` (Xcode
8
+ 15+; earlier Xcodes simply report no physical devices).
9
+
10
+ The shell-outs are isolated in ``list_*`` functions; the parsers are
11
+ pure functions over captured text/JSON so they can be unit tested
12
+ without any toolchain installed.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import json
18
+ import re
19
+ import subprocess
20
+ import tempfile
21
+ from dataclasses import asdict, dataclass
22
+ from pathlib import Path
23
+ from typing import Any, Dict, List, Optional
24
+
25
+ __all__ = [
26
+ "Device",
27
+ "list_devices",
28
+ "list_android_devices",
29
+ "list_ios_simulators",
30
+ "list_ios_physical_devices",
31
+ "find_device",
32
+ "parse_adb_devices",
33
+ "parse_simctl_devices",
34
+ "parse_devicectl_devices",
35
+ ]
36
+
37
+
38
+ @dataclass
39
+ class Device:
40
+ """One runnable target.
41
+
42
+ Attributes:
43
+ platform: ``"android"`` or ``"ios"``.
44
+ kind: ``"device"``, ``"emulator"``, or ``"simulator"``.
45
+ identifier: The stable id used to target the device (adb
46
+ serial, simulator UDID, or devicectl identifier).
47
+ name: Human-readable model or simulator name.
48
+ os_version: OS version string when known.
49
+ state: ``"booted"``, ``"connected"``, ``"shutdown"``, or
50
+ ``"offline"``.
51
+ """
52
+
53
+ platform: str
54
+ kind: str
55
+ identifier: str
56
+ name: str
57
+ os_version: str = ""
58
+ state: str = ""
59
+
60
+ @property
61
+ def is_ready(self) -> bool:
62
+ """Whether the target can be used right now (or booted on demand)."""
63
+ if self.kind == "simulator":
64
+ return self.state in ("booted", "shutdown")
65
+ return self.state in ("booted", "connected")
66
+
67
+ def to_dict(self) -> Dict[str, Any]:
68
+ """Return a JSON-serializable view of this target.
69
+
70
+ Derived from the dataclass fields so a field added later is
71
+ carried into the payload automatically, plus the computed
72
+ ``is_ready`` that consumers would otherwise have to re-derive
73
+ from ``kind`` and ``state``.
74
+
75
+ Returns:
76
+ The declared fields in declaration order, then ``is_ready``.
77
+ """
78
+ return {**asdict(self), "is_ready": self.is_ready}
79
+
80
+ def format(self) -> str:
81
+ """Return one aligned listing row for the CLI."""
82
+ os_part = f" ({self.os_version})" if self.os_version else ""
83
+ return f" {self.identifier:<40} {self.kind:<10} {self.state:<10} {self.name}{os_part}"
84
+
85
+
86
+ # ======================================================================
87
+ # Android (adb)
88
+ # ======================================================================
89
+
90
+
91
+ def parse_adb_devices(output: str) -> List[Device]:
92
+ """Parse ``adb devices -l`` output into devices.
93
+
94
+ Args:
95
+ output: Raw stdout from ``adb devices -l``.
96
+
97
+ Returns:
98
+ Android devices and emulators (offline entries included, with
99
+ ``state="offline"``).
100
+ """
101
+ devices: List[Device] = []
102
+ for line in output.splitlines():
103
+ line = line.strip()
104
+ if not line or line.startswith("List of devices"):
105
+ continue
106
+ parts = line.split()
107
+ if len(parts) < 2:
108
+ continue
109
+ serial, state = parts[0], parts[1]
110
+ if state == "unauthorized":
111
+ state = "offline"
112
+ elif state == "device":
113
+ state = "connected"
114
+ model = ""
115
+ for token in parts[2:]:
116
+ if token.startswith("model:"):
117
+ model = token[len("model:") :].replace("_", " ")
118
+ kind = "emulator" if serial.startswith("emulator-") else "device"
119
+ devices.append(
120
+ Device(
121
+ platform="android",
122
+ kind=kind,
123
+ identifier=serial,
124
+ name=model or serial,
125
+ state=state if state in ("connected", "offline") else "offline",
126
+ )
127
+ )
128
+ return devices
129
+
130
+
131
+ def list_android_devices() -> List[Device]:
132
+ """Return connected Android devices/emulators (empty without adb)."""
133
+ try:
134
+ result = subprocess.run(["adb", "devices", "-l"], check=False, capture_output=True, text=True)
135
+ except FileNotFoundError:
136
+ return []
137
+ return parse_adb_devices(result.stdout or "")
138
+
139
+
140
+ # ======================================================================
141
+ # iOS Simulators (simctl)
142
+ # ======================================================================
143
+
144
+ _RUNTIME_RE = re.compile(r"iOS[.-](\d+)[.-](\d+)")
145
+
146
+
147
+ def parse_simctl_devices(data: Dict[str, Any]) -> List[Device]:
148
+ """Parse ``simctl list devices --json`` payload into devices.
149
+
150
+ Args:
151
+ data: The decoded JSON payload.
152
+
153
+ Returns:
154
+ Available iOS Simulators (unavailable ones are skipped).
155
+ """
156
+ devices: List[Device] = []
157
+ for runtime, entries in (data.get("devices") or {}).items():
158
+ match = _RUNTIME_RE.search(runtime)
159
+ os_version = f"iOS {match.group(1)}.{match.group(2)}" if match else ""
160
+ if "iOS" not in runtime:
161
+ continue
162
+ for entry in entries or []:
163
+ if not entry.get("isAvailable") or not entry.get("udid"):
164
+ continue
165
+ devices.append(
166
+ Device(
167
+ platform="ios",
168
+ kind="simulator",
169
+ identifier=str(entry["udid"]),
170
+ name=str(entry.get("name") or entry["udid"]),
171
+ os_version=os_version,
172
+ state="booted" if entry.get("state") == "Booted" else "shutdown",
173
+ )
174
+ )
175
+ return devices
176
+
177
+
178
+ def list_ios_simulators() -> List[Device]:
179
+ """Return available iOS Simulators (empty without Xcode)."""
180
+ try:
181
+ result = subprocess.run(
182
+ ["xcrun", "simctl", "list", "devices", "available", "--json"],
183
+ check=False,
184
+ capture_output=True,
185
+ text=True,
186
+ )
187
+ except FileNotFoundError:
188
+ return []
189
+ try:
190
+ data = json.loads(result.stdout or "{}")
191
+ except json.JSONDecodeError:
192
+ return []
193
+ return parse_simctl_devices(data)
194
+
195
+
196
+ # ======================================================================
197
+ # Physical iOS devices (devicectl)
198
+ # ======================================================================
199
+
200
+
201
+ def parse_devicectl_devices(data: Dict[str, Any]) -> List[Device]:
202
+ """Parse ``devicectl list devices --json-output`` payload into devices.
203
+
204
+ Args:
205
+ data: The decoded JSON payload.
206
+
207
+ Returns:
208
+ Physical iOS devices.
209
+ """
210
+ devices: List[Device] = []
211
+ for entry in (data.get("result") or {}).get("devices") or []:
212
+ props = entry.get("deviceProperties") or {}
213
+ hardware = entry.get("hardwareProperties") or {}
214
+ connection = entry.get("connectionProperties") or {}
215
+ identifier = str(entry.get("identifier") or "")
216
+ if not identifier:
217
+ continue
218
+ state = "connected" if connection.get("tunnelState") in ("connected", "available") else "offline"
219
+ os_version = props.get("osVersionNumber") or ""
220
+ devices.append(
221
+ Device(
222
+ platform="ios",
223
+ kind="device",
224
+ identifier=identifier,
225
+ name=str(props.get("name") or hardware.get("marketingName") or identifier),
226
+ os_version=f"iOS {os_version}" if os_version else "",
227
+ state=state,
228
+ )
229
+ )
230
+ return devices
231
+
232
+
233
+ def list_ios_physical_devices() -> List[Device]:
234
+ """Return physical iOS devices (empty without Xcode 15+)."""
235
+ with tempfile.TemporaryDirectory() as tmp:
236
+ out_path = Path(tmp) / "devices.json"
237
+ try:
238
+ subprocess.run(
239
+ ["xcrun", "devicectl", "list", "devices", "--json-output", str(out_path)],
240
+ check=False,
241
+ capture_output=True,
242
+ text=True,
243
+ timeout=30,
244
+ )
245
+ except (FileNotFoundError, subprocess.TimeoutExpired):
246
+ return []
247
+ if not out_path.is_file():
248
+ return []
249
+ try:
250
+ data = json.loads(out_path.read_text(encoding="utf-8"))
251
+ except (json.JSONDecodeError, OSError):
252
+ return []
253
+ return parse_devicectl_devices(data)
254
+
255
+
256
+ # ======================================================================
257
+ # Aggregation / selection
258
+ # ======================================================================
259
+
260
+
261
+ def list_devices(platform: Optional[str] = None) -> List[Device]:
262
+ """Return all known targets, optionally restricted to one platform.
263
+
264
+ Args:
265
+ platform: ``"android"``, ``"ios"``, or ``None`` for both.
266
+
267
+ Returns:
268
+ Devices ordered: Android first, then physical iOS devices, then
269
+ simulators (booted simulators before shutdown ones).
270
+ """
271
+ devices: List[Device] = []
272
+ if platform in (None, "android"):
273
+ devices.extend(list_android_devices())
274
+ if platform in (None, "ios"):
275
+ devices.extend(list_ios_physical_devices())
276
+ simulators = list_ios_simulators()
277
+ simulators.sort(key=lambda dev: (dev.state != "booted", dev.name))
278
+ devices.extend(simulators)
279
+ return devices
280
+
281
+
282
+ def find_device(devices: List[Device], query: str) -> Optional[Device]:
283
+ """Resolve ``query`` against ``devices`` (exact id, then name match).
284
+
285
+ Args:
286
+ devices: Candidate devices.
287
+ query: An identifier, exact name, or case-insensitive name
288
+ substring.
289
+
290
+ Returns:
291
+ The best match, or ``None``. Ready devices win ties.
292
+ """
293
+ ready_first = sorted(devices, key=lambda dev: not dev.is_ready)
294
+ for device in ready_first:
295
+ if device.identifier == query:
296
+ return device
297
+ lowered = query.lower()
298
+ for device in ready_first:
299
+ if device.name.lower() == lowered:
300
+ return device
301
+ for device in ready_first:
302
+ if lowered in device.name.lower():
303
+ return device
304
+ return None
@@ -0,0 +1,283 @@
1
+ """Environment diagnostics for ``pn doctor``.
2
+
3
+ Inspects the local toolchain and the project's ``pythonnative.toml`` and
4
+ reports what's ready and what's missing for building on each platform,
5
+ analogous to ``flutter doctor`` / ``npx react-native doctor``. The checks
6
+ are deliberately read-only and fast; they shell out only to ask tools for
7
+ their versions.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import shutil
13
+ import subprocess
14
+ import sys
15
+ from dataclasses import dataclass
16
+ from pathlib import Path
17
+ from typing import List, Optional
18
+
19
+ from . import icons
20
+ from .config import SUPPORTED_PYTHON_VERSIONS, AppConfig, ConfigError
21
+ from .runtime_assets import PINNED_ASSETS
22
+
23
+ OK = "ok"
24
+ WARN = "warn"
25
+ ERROR = "error"
26
+ INFO = "info"
27
+
28
+ _SYMBOLS = {OK: "[ok]", WARN: "[!]", ERROR: "[x]", INFO: "[i]"}
29
+
30
+
31
+ @dataclass
32
+ class CheckResult:
33
+ """The outcome of a single diagnostic check.
34
+
35
+ Attributes:
36
+ name: Short label for the thing checked.
37
+ level: One of ``"ok"``, ``"warn"``, ``"error"``, ``"info"``.
38
+ detail: Human-readable detail / remediation hint.
39
+ """
40
+
41
+ name: str
42
+ level: str
43
+ detail: str = ""
44
+
45
+ def format(self) -> str:
46
+ """Return a single aligned line for terminal output."""
47
+ symbol = _SYMBOLS.get(self.level, "[?]")
48
+ suffix = f": {self.detail}" if self.detail else ""
49
+ return f" {symbol} {self.name}{suffix}"
50
+
51
+
52
+ def _which_version(tool: str, version_args: List[str]) -> Optional[str]:
53
+ path = shutil.which(tool)
54
+ if not path:
55
+ return None
56
+ try:
57
+ out = subprocess.run([tool, *version_args], capture_output=True, text=True, timeout=20)
58
+ except Exception:
59
+ return path
60
+ text = (out.stdout or out.stderr or "").strip().splitlines()
61
+ return text[0] if text else path
62
+
63
+
64
+ def build_python_for(python_version: str) -> Optional[str]:
65
+ """Locate an interpreter matching ``python_version`` (e.g. ``python3.13``) on ``PATH``.
66
+
67
+ Args:
68
+ python_version: CPython ``major.minor``.
69
+
70
+ Returns:
71
+ The interpreter path, or ``None`` when none is installed.
72
+ """
73
+ host = f"{sys.version_info.major}.{sys.version_info.minor}"
74
+ if host == python_version:
75
+ return sys.executable
76
+ return shutil.which(f"python{python_version}")
77
+
78
+
79
+ def check_common(config: Optional[AppConfig] = None) -> List[CheckResult]:
80
+ """Run platform-agnostic checks (interpreters, requirements, optional dependencies).
81
+
82
+ Args:
83
+ config: The loaded app config, or ``None`` if unavailable.
84
+
85
+ Returns:
86
+ Check results for the host and build Pythons, the declared
87
+ requirements, and optional dependencies.
88
+ """
89
+ results: List[CheckResult] = []
90
+ py_version = f"{sys.version_info.major}.{sys.version_info.minor}"
91
+ if py_version in SUPPORTED_PYTHON_VERSIONS:
92
+ results.append(CheckResult("Host Python", OK, f"{sys.version.split()[0]}"))
93
+ else:
94
+ results.append(
95
+ CheckResult(
96
+ "Host Python",
97
+ WARN,
98
+ f"{py_version} (PythonNative targets {', '.join(SUPPORTED_PYTHON_VERSIONS)})",
99
+ )
100
+ )
101
+
102
+ if config is not None:
103
+ # pip cross-resolves iOS wheels from any interpreter, but Chaquopy
104
+ # needs a matching python3.X on the build machine whenever there
105
+ # are requirements, and release bytecode is version-specific.
106
+ build_python = build_python_for(config.python_version)
107
+ label = f"Build Python {config.python_version} (matches app.python_version)"
108
+ if build_python:
109
+ results.append(CheckResult(label, OK, build_python))
110
+ else:
111
+ level = WARN if config.requirements else INFO
112
+ results.append(
113
+ CheckResult(
114
+ label,
115
+ level,
116
+ f"python{config.python_version} not found on PATH; Android builds with [requirements] and "
117
+ "release bytecode compilation need it (e.g. uv python install "
118
+ f"{config.python_version})",
119
+ )
120
+ )
121
+ if config.requirements:
122
+ count = len(config.requirements)
123
+ results.append(
124
+ CheckResult(
125
+ "Requirements",
126
+ INFO,
127
+ f"{count} package(s) declared; run 'pn deps' to check device wheel availability",
128
+ )
129
+ )
130
+
131
+ if icons.pillow_available():
132
+ results.append(CheckResult("Pillow (icon/splash generation)", OK))
133
+ else:
134
+ results.append(
135
+ CheckResult(
136
+ "Pillow (icon/splash generation)",
137
+ WARN,
138
+ "not installed; run: pip install 'pythonnative[build]'",
139
+ )
140
+ )
141
+ return results
142
+
143
+
144
+ def check_android(config: Optional[AppConfig]) -> List[CheckResult]:
145
+ """Run Android toolchain and signing checks.
146
+
147
+ Args:
148
+ config: The loaded app config, or ``None`` if unavailable.
149
+
150
+ Returns:
151
+ Android-specific check results.
152
+ """
153
+ results: List[CheckResult] = []
154
+ adb = _which_version("adb", ["--version"])
155
+ results.append(CheckResult("adb (Android platform-tools)", OK if adb else WARN, adb or "not found on PATH"))
156
+
157
+ java_home = shutil.which("java")
158
+ import os
159
+
160
+ if os.environ.get("JAVA_HOME") or java_home:
161
+ results.append(CheckResult("Java (JDK 17 recommended)", OK, os.environ.get("JAVA_HOME") or java_home or ""))
162
+ else:
163
+ results.append(CheckResult("Java (JDK 17 recommended)", WARN, "JAVA_HOME not set and 'java' not on PATH"))
164
+
165
+ if config is not None:
166
+ signing = config.android.signing
167
+ if signing.is_configured:
168
+ keystore = config.resolve_path(signing.keystore) if signing.keystore else None
169
+ if keystore and keystore.is_file():
170
+ results.append(CheckResult("Android release keystore", OK, str(keystore)))
171
+ else:
172
+ results.append(CheckResult("Android release keystore", ERROR, f"not found: {keystore}"))
173
+ missing = [env for env in (signing.store_password_env, signing.key_password_env) if not os.environ.get(env)]
174
+ if missing:
175
+ results.append(CheckResult("Android signing passwords", WARN, f"unset env: {', '.join(missing)}"))
176
+ else:
177
+ results.append(
178
+ CheckResult(
179
+ "Android release signing",
180
+ INFO,
181
+ "not configured; release builds will be unsigned (set [android.signing])",
182
+ )
183
+ )
184
+ return results
185
+
186
+
187
+ def check_ios(config: Optional[AppConfig]) -> List[CheckResult]:
188
+ """Run iOS toolchain and signing checks.
189
+
190
+ Args:
191
+ config: The loaded app config, or ``None`` if unavailable.
192
+
193
+ Returns:
194
+ iOS-specific check results.
195
+ """
196
+ results: List[CheckResult] = []
197
+ if sys.platform != "darwin":
198
+ results.append(CheckResult("macOS (required for iOS)", ERROR, f"this is {sys.platform}; iOS builds need macOS"))
199
+ return results
200
+
201
+ xcodebuild = _which_version("xcodebuild", ["-version"])
202
+ results.append(CheckResult("Xcode (xcodebuild)", OK if xcodebuild else ERROR, xcodebuild or "not found on PATH"))
203
+ simctl = shutil.which("xcrun")
204
+ results.append(CheckResult("xcrun simctl (Simulators)", OK if simctl else WARN, simctl or "not found on PATH"))
205
+
206
+ if config is not None:
207
+ if config.python_version not in PINNED_ASSETS:
208
+ supported = ", ".join(sorted(PINNED_ASSETS))
209
+ results.append(
210
+ CheckResult(
211
+ "iOS embedded Python",
212
+ WARN,
213
+ f"app.python_version={config.python_version}; pinned iOS builds exist for {supported}",
214
+ )
215
+ )
216
+ if config.ios.development_team:
217
+ results.append(CheckResult("iOS development team", OK, config.ios.development_team))
218
+ else:
219
+ results.append(
220
+ CheckResult(
221
+ "iOS development team",
222
+ INFO,
223
+ "not set; required for device builds (set [ios].development_team)",
224
+ )
225
+ )
226
+ return results
227
+
228
+
229
+ def check_config(project_root: Path) -> tuple[Optional[AppConfig], List[CheckResult]]:
230
+ """Load and validate the project config, returning it with a result.
231
+
232
+ Args:
233
+ project_root: Directory expected to contain ``pythonnative.toml``.
234
+
235
+ Returns:
236
+ A tuple of the loaded config (or ``None``) and the check results.
237
+ """
238
+ results: List[CheckResult] = []
239
+ try:
240
+ config = AppConfig.load(project_root)
241
+ except ConfigError as exc:
242
+ results.append(CheckResult("pythonnative.toml", ERROR, str(exc).splitlines()[0]))
243
+ return None, results
244
+ results.append(CheckResult("pythonnative.toml", OK, f"{config.app_id} (v{config.version})"))
245
+ return config, results
246
+
247
+
248
+ def run_doctor(project_root: Path, *, platform: Optional[str] = None) -> List[CheckResult]:
249
+ """Run all diagnostics for ``project_root``.
250
+
251
+ Args:
252
+ project_root: The project directory.
253
+ platform: Restrict checks to ``"android"`` or ``"ios"``; ``None``
254
+ checks both.
255
+
256
+ Returns:
257
+ All check results in display order.
258
+ """
259
+ config, results = check_config(project_root)
260
+ results.extend(check_common(config))
261
+ if platform in (None, "android"):
262
+ results.extend(check_android(config))
263
+ if platform in (None, "ios"):
264
+ results.extend(check_ios(config))
265
+ return results
266
+
267
+
268
+ def worst_level(results: List[CheckResult]) -> str:
269
+ """Return the most severe level among ``results``.
270
+
271
+ Args:
272
+ results: The diagnostic results.
273
+
274
+ Returns:
275
+ ``"error"`` if any error, else ``"warn"`` if any warning, else
276
+ ``"ok"``.
277
+ """
278
+ levels = {result.level for result in results}
279
+ if ERROR in levels:
280
+ return ERROR
281
+ if WARN in levels:
282
+ return WARN
283
+ return OK