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,773 @@
1
+ """Typed model and loader for ``pythonnative.toml``.
2
+
3
+ Every PythonNative project is described by a single ``pythonnative.toml``
4
+ at its root. This module parses and validates that file into a typed
5
+ [`AppConfig`][pythonnative.project.config.AppConfig] tree that the rest
6
+ of the build system consumes. Keeping all parsing/validation here means
7
+ the configurators ([`ios`][pythonnative.project.ios],
8
+ [`android`][pythonnative.project.android]) and the
9
+ [`builder`][pythonnative.project.builder] can assume a fully-validated,
10
+ defaulted config object.
11
+
12
+ The canonical schema:
13
+
14
+ ```toml
15
+ [app]
16
+ id = "com.example.myapp" # reverse-DNS app/bundle id (required)
17
+ name = "myapp" # short project name (required)
18
+ display_name = "My App" # home-screen label (defaults to name)
19
+ version = "1.0.0" # marketing version
20
+ build = 1 # integer build number
21
+ python_version = "3.13" # embedded CPython version (3.13 or 3.14)
22
+ orientation = "portrait" # portrait | landscape | all
23
+ entry_point = "app/main.py" # module whose `App` is mounted
24
+ url_schemes = ["myapp"] # custom deep-link URL schemes
25
+
26
+ [permissions] # see pythonnative.project.permissions
27
+ camera = "Scan receipts."
28
+ notifications = true
29
+
30
+ [assets]
31
+ icon = "assets/icon.png" # 1024x1024 source icon
32
+ splash = "assets/splash.png" # splash/launch image
33
+
34
+ [requirements]
35
+ packages = ["humanize", "httpx"]
36
+ # Extra package indexes searched after PyPI and the platform wheel
37
+ # repositories (BeeWare for iOS, Chaquopy for Android).
38
+ extra_index_urls = ["https://wheels.example.com/simple"]
39
+
40
+ [plugins]
41
+ paths = ["native/my_plugin"] # local native plugins (pn_plugin.json + ios/ + android/)
42
+
43
+ [ios]
44
+ deployment_target = "13.0"
45
+ development_team = "ABCDE12345"
46
+ bundle_id = "com.example.myapp" # optional override of app.id
47
+
48
+ [ios.signing]
49
+ export_method = "app-store" # development | ad-hoc | app-store | enterprise
50
+ provisioning_profile = "My App Distribution"
51
+
52
+ [android]
53
+ min_sdk = 24
54
+ target_sdk = 34
55
+ abi_filters = ["arm64-v8a", "x86_64"] # the default and the full set (64-bit only)
56
+
57
+ [android.signing]
58
+ keystore = "release.keystore"
59
+ key_alias = "myapp"
60
+ ```
61
+ """
62
+
63
+ from __future__ import annotations
64
+
65
+ import re
66
+ import tomllib as _toml
67
+ from dataclasses import dataclass, field
68
+ from pathlib import Path
69
+ from typing import Any, Dict, List, Mapping, Optional
70
+
71
+ from . import permissions as _permissions
72
+
73
+ CONFIG_FILENAME = "pythonnative.toml"
74
+ """The fixed config filename looked up at the project root."""
75
+
76
+ SUPPORTED_PYTHON_VERSIONS = ("3.13", "3.14")
77
+ """CPython versions accepted in ``app.python_version``.
78
+
79
+ Every listed version has a pinned, checksum-verified iOS runtime (see
80
+ ``runtime_assets.PINNED_ASSETS``) and is supported by Chaquopy on
81
+ Android. The floor is 3.13 because that is the first CPython with
82
+ official iOS (PEP 730) and Android (PEP 738) platform support, which is
83
+ what lets binary wheels from PyPI install on both platforms.
84
+ """
85
+
86
+ DEFAULT_PYTHON_VERSION = "3.13"
87
+ """The ``app.python_version`` used when the config leaves it out.
88
+
89
+ 3.13 rather than 3.14 because binary wheel coverage (BeeWare's iOS
90
+ index, Chaquopy's Android index, PyPI itself) is far wider for 3.13.
91
+ """
92
+
93
+ _URL_RE = re.compile(r"^https?://\S+$")
94
+
95
+ VALID_ORIENTATIONS = ("portrait", "landscape", "all")
96
+ """Accepted values for ``app.orientation``."""
97
+
98
+ MIN_IOS_DEPLOYMENT_TARGET = "13.0"
99
+ """Lowest ``[ios].deployment_target`` accepted.
100
+
101
+ BeeWare's CPython builds and every iOS wheel on PyPI target iOS 13.0
102
+ (the ``ios_13_0_*`` platform tags), so lower values can't be satisfied.
103
+ """
104
+
105
+ MIN_ANDROID_SDK = 24
106
+ """Lowest ``[android].min_sdk`` accepted (Chaquopy 17 requirement)."""
107
+
108
+ VALID_ANDROID_ABIS = ("arm64-v8a", "x86_64")
109
+ """Android ABIs the embedded runtime supports.
110
+
111
+ CPython 3.12+ on Chaquopy ships 64-bit only, and PEP 738 Android wheels
112
+ on PyPI are published for these two ABIs.
113
+ """
114
+
115
+ _IOS_VERSION_RE = re.compile(r"^\d+(\.\d+){1,2}$")
116
+
117
+
118
+ def _version_tuple(version: str) -> tuple:
119
+ return tuple(int(part) for part in version.split("."))
120
+
121
+
122
+ VALID_IOS_EXPORT_METHODS = ("development", "ad-hoc", "app-store", "enterprise")
123
+ """Accepted values for ``[ios.signing].export_method``."""
124
+
125
+ # Reserved Java keywords can't appear as Android package segments.
126
+ _JAVA_KEYWORDS = frozenset("""
127
+ abstract assert boolean break byte case catch char class const continue default do double else enum
128
+ extends final finally float for goto if implements import instanceof int interface long native new
129
+ package private protected public return short static strictfp super switch synchronized this throw
130
+ throws transient try void volatile while true false null
131
+ """.split())
132
+
133
+ _APP_ID_SEGMENT = re.compile(r"^[a-z][a-z0-9_]*$")
134
+ _VERSION_RE = re.compile(r"^\d+(\.\d+){0,3}$")
135
+ _URL_SCHEME_RE = re.compile(r"^[a-zA-Z][a-zA-Z0-9+.-]*$")
136
+
137
+
138
+ class ConfigError(Exception):
139
+ """Raised when ``pythonnative.toml`` is missing, malformed, or invalid.
140
+
141
+ The message is intended to be shown directly to the user by the CLI,
142
+ so it should be specific and actionable.
143
+ """
144
+
145
+
146
+ # ======================================================================
147
+ # Sub-models
148
+ # ======================================================================
149
+
150
+
151
+ @dataclass
152
+ class IOSSigning:
153
+ """iOS code-signing / export configuration (``[ios.signing]``).
154
+
155
+ Attributes:
156
+ export_method: How the archive is exported, one of
157
+ ``development``, ``ad-hoc``, ``app-store``, ``enterprise``.
158
+ provisioning_profile: Optional provisioning profile name or UUID
159
+ for manual signing.
160
+ """
161
+
162
+ export_method: str = "development"
163
+ provisioning_profile: Optional[str] = None
164
+
165
+
166
+ @dataclass
167
+ class IOSConfig:
168
+ """iOS-specific settings (``[ios]``).
169
+
170
+ Attributes:
171
+ deployment_target: Minimum iOS version (e.g., ``"13.0"``).
172
+ development_team: Apple Developer Team ID used for signing.
173
+ bundle_id: Optional override of ``app.id`` for the iOS bundle
174
+ identifier.
175
+ extra_info_plist: Arbitrary additional ``Info.plist`` key/values
176
+ merged verbatim into the generated plist.
177
+ signing: Nested [`IOSSigning`][pythonnative.project.config.IOSSigning].
178
+ """
179
+
180
+ deployment_target: str = "13.0"
181
+ development_team: Optional[str] = None
182
+ bundle_id: Optional[str] = None
183
+ extra_info_plist: Dict[str, Any] = field(default_factory=dict)
184
+ signing: IOSSigning = field(default_factory=IOSSigning)
185
+
186
+
187
+ @dataclass
188
+ class AndroidSigning:
189
+ """Android release signing configuration (``[android.signing]``).
190
+
191
+ Passwords are never stored in the config; they're read from the
192
+ environment at build time (see ``store_password_env`` /
193
+ ``key_password_env``).
194
+
195
+ Attributes:
196
+ keystore: Path to the release keystore, relative to the project
197
+ root.
198
+ key_alias: Key alias within the keystore.
199
+ store_password_env: Environment variable holding the keystore
200
+ password.
201
+ key_password_env: Environment variable holding the key password.
202
+ """
203
+
204
+ keystore: Optional[str] = None
205
+ key_alias: Optional[str] = None
206
+ store_password_env: str = "PN_ANDROID_KEYSTORE_PASSWORD"
207
+ key_password_env: str = "PN_ANDROID_KEY_PASSWORD"
208
+
209
+ @property
210
+ def is_configured(self) -> bool:
211
+ """Whether both a keystore path and key alias are present."""
212
+ return bool(self.keystore and self.key_alias)
213
+
214
+
215
+ @dataclass
216
+ class AndroidConfig:
217
+ """Android-specific settings (``[android]``).
218
+
219
+ Attributes:
220
+ min_sdk: Minimum supported Android API level.
221
+ target_sdk: Target Android API level.
222
+ compile_sdk: SDK level the project compiles against.
223
+ application_id: Optional override of ``app.id`` for the Android
224
+ application id (and package).
225
+ abi_filters: Native ABIs to include (e.g., ``"arm64-v8a"``).
226
+ permissions: Extra raw Android permission strings appended to the
227
+ ones derived from ``[permissions]``.
228
+ signing: Nested
229
+ [`AndroidSigning`][pythonnative.project.config.AndroidSigning].
230
+ """
231
+
232
+ min_sdk: int = 24
233
+ target_sdk: int = 34
234
+ compile_sdk: int = 34
235
+ application_id: Optional[str] = None
236
+ # arm64 devices plus x86_64 emulators: the only ABIs CPython 3.12+
237
+ # on Chaquopy (and PEP 738 wheels on PyPI) are built for.
238
+ abi_filters: List[str] = field(default_factory=lambda: list(VALID_ANDROID_ABIS))
239
+ permissions: List[str] = field(default_factory=list)
240
+ signing: AndroidSigning = field(default_factory=AndroidSigning)
241
+
242
+
243
+ # ======================================================================
244
+ # Root model
245
+ # ======================================================================
246
+
247
+
248
+ @dataclass
249
+ class AppConfig:
250
+ """A fully-parsed, validated ``pythonnative.toml``.
251
+
252
+ Use [`load`][pythonnative.project.config.AppConfig.load] to read it
253
+ from a project directory, or
254
+ [`from_dict`][pythonnative.project.config.AppConfig.from_dict] to
255
+ build one from an already-parsed mapping (e.g., in tests).
256
+
257
+ Attributes:
258
+ app_id: Reverse-DNS identifier (``app.id``); the default bundle
259
+ id / application id for both platforms.
260
+ name: Short project name (``app.name``).
261
+ display_name: Home-screen label (``app.display_name``).
262
+ version: Marketing version string (``app.version``).
263
+ build: Integer build number (``app.build``).
264
+ python_version: Embedded CPython version (``app.python_version``).
265
+ orientation: ``"portrait"``, ``"landscape"``, or ``"all"``.
266
+ entry_point: Path to the entry module (``app.entry_point``).
267
+ url_schemes: Custom deep-link URL schemes the app handles
268
+ (``app.url_schemes``); wired into ``CFBundleURLTypes`` on
269
+ iOS and a ``VIEW`` intent filter on Android.
270
+ permissions: The declared capability map (``[permissions]``).
271
+ icon: Optional source icon path (``[assets].icon``).
272
+ splash: Optional splash image path (``[assets].splash``).
273
+ requirements: Third-party pip packages (``[requirements].packages``).
274
+ extra_index_urls: Additional package indexes
275
+ (``[requirements].extra_index_urls``) searched after PyPI and
276
+ the platform wheel repositories.
277
+ plugin_paths: Project-local native plugin directories
278
+ (``[plugins].paths``); installed packages contribute plugins
279
+ through the ``pythonnative.plugins`` entry point group instead.
280
+ ios: Nested [`IOSConfig`][pythonnative.project.config.IOSConfig].
281
+ android: Nested
282
+ [`AndroidConfig`][pythonnative.project.config.AndroidConfig].
283
+ project_root: Absolute path to the directory containing the config.
284
+ """
285
+
286
+ app_id: str
287
+ name: str
288
+ display_name: str
289
+ version: str = "1.0.0"
290
+ build: int = 1
291
+ python_version: str = DEFAULT_PYTHON_VERSION
292
+ orientation: str = "portrait"
293
+ entry_point: str = "app/main.py"
294
+ url_schemes: List[str] = field(default_factory=list)
295
+ permissions: Dict[str, _permissions.PermissionValue] = field(default_factory=dict)
296
+ icon: Optional[str] = None
297
+ splash: Optional[str] = None
298
+ requirements: List[str] = field(default_factory=list)
299
+ extra_index_urls: List[str] = field(default_factory=list)
300
+ plugin_paths: List[str] = field(default_factory=list)
301
+ ios: IOSConfig = field(default_factory=IOSConfig)
302
+ android: AndroidConfig = field(default_factory=AndroidConfig)
303
+ project_root: Path = field(default_factory=Path.cwd)
304
+
305
+ # -- Derived values -------------------------------------------------
306
+
307
+ @property
308
+ def application_id(self) -> str:
309
+ """The Android application id (``[android].application_id`` or ``app.id``)."""
310
+ return self.android.application_id or self.app_id
311
+
312
+ @property
313
+ def bundle_id(self) -> str:
314
+ """The iOS bundle identifier (``[ios].bundle_id`` or ``app.id``)."""
315
+ return self.ios.bundle_id or self.app_id
316
+
317
+ @property
318
+ def entry_module(self) -> str:
319
+ """The dotted import path for ``entry_point`` (e.g., ``"app.main"``)."""
320
+ return entrypoint_to_module(self.entry_point)
321
+
322
+ @property
323
+ def android_package_path(self) -> str:
324
+ """The Android source directory path for ``application_id``.
325
+
326
+ ``"com.example.myapp"`` → ``"com/example/myapp"``.
327
+ """
328
+ return self.application_id.replace(".", "/")
329
+
330
+ def resolve_path(self, relative: str) -> Path:
331
+ """Resolve a config-relative path against the project root.
332
+
333
+ Args:
334
+ relative: A path string from the config (e.g., an icon path).
335
+
336
+ Returns:
337
+ An absolute [`Path`][pathlib.Path].
338
+ """
339
+ candidate = Path(relative)
340
+ if candidate.is_absolute():
341
+ return candidate
342
+ return (self.project_root / candidate).resolve()
343
+
344
+ def resolved_permissions(self) -> _permissions.ResolvedPermissions:
345
+ """Resolve declared capabilities into native permission artifacts.
346
+
347
+ Returns:
348
+ A
349
+ [`ResolvedPermissions`][pythonnative.project.permissions.ResolvedPermissions]
350
+ combining ``[permissions]`` with any extra
351
+ ``[android].permissions``.
352
+ """
353
+ return _permissions.resolve_permissions(
354
+ self.permissions,
355
+ extra_android_permissions=self.android.permissions,
356
+ )
357
+
358
+ # -- Construction ---------------------------------------------------
359
+
360
+ @classmethod
361
+ def load(cls, project_root: Optional[Path] = None) -> "AppConfig":
362
+ """Load and validate ``pythonnative.toml`` from a project directory.
363
+
364
+ Args:
365
+ project_root: Directory containing the config. Defaults to the
366
+ current working directory.
367
+
368
+ Returns:
369
+ A validated [`AppConfig`][pythonnative.project.config.AppConfig].
370
+
371
+ Raises:
372
+ ConfigError: If the file is missing, isn't valid TOML, or
373
+ fails validation.
374
+ """
375
+ root = Path(project_root).resolve() if project_root is not None else Path.cwd()
376
+ config_path = root / CONFIG_FILENAME
377
+ if not config_path.is_file():
378
+ raise ConfigError(f"No {CONFIG_FILENAME} found in {root}.")
379
+ try:
380
+ with open(config_path, "rb") as handle:
381
+ data = _toml.load(handle)
382
+ except _toml.TOMLDecodeError as exc:
383
+ raise ConfigError(f"Could not parse {CONFIG_FILENAME}: {exc}") from exc
384
+ return cls.from_dict(data, project_root=root)
385
+
386
+ @classmethod
387
+ def from_dict(cls, data: Mapping[str, Any], *, project_root: Optional[Path] = None) -> "AppConfig":
388
+ """Build an [`AppConfig`][pythonnative.project.config.AppConfig] from a mapping.
389
+
390
+ Args:
391
+ data: A parsed TOML mapping (top-level tables: ``app``,
392
+ ``permissions``, ``assets``, ``requirements``, ``plugins``,
393
+ ``ios``, ``android``).
394
+ project_root: Directory the config came from, used to resolve
395
+ relative paths.
396
+
397
+ Returns:
398
+ A validated config.
399
+
400
+ Raises:
401
+ ConfigError: On any structural or value validation failure.
402
+ """
403
+ root = Path(project_root).resolve() if project_root is not None else Path.cwd()
404
+ app = _expect_table(data, "app")
405
+
406
+ config = cls(
407
+ app_id=_require_str(app, "id", "app"),
408
+ name=_require_str(app, "name", "app"),
409
+ display_name=_opt_str(app, "display_name") or _require_str(app, "name", "app"),
410
+ version=_opt_str(app, "version") or "1.0.0",
411
+ build=_opt_int(app, "build", default=1),
412
+ python_version=_opt_str(app, "python_version") or DEFAULT_PYTHON_VERSION,
413
+ orientation=(_opt_str(app, "orientation") or "portrait").lower(),
414
+ entry_point=_opt_str(app, "entry_point") or "app/main.py",
415
+ url_schemes=_opt_str_list(app, "url_schemes"),
416
+ permissions=dict(_expect_table(data, "permissions", optional=True)),
417
+ ios=_parse_ios(_expect_table(data, "ios", optional=True)),
418
+ android=_parse_android(_expect_table(data, "android", optional=True)),
419
+ project_root=root,
420
+ )
421
+
422
+ assets = _expect_table(data, "assets", optional=True)
423
+ config.icon = _opt_str(assets, "icon")
424
+ config.splash = _opt_str(assets, "splash")
425
+
426
+ requirements = _expect_table(data, "requirements", optional=True)
427
+ config.requirements = _opt_str_list(requirements, "packages")
428
+ config.extra_index_urls = _opt_str_list(requirements, "extra_index_urls")
429
+
430
+ plugins = _expect_table(data, "plugins", optional=True)
431
+ config.plugin_paths = _opt_str_list(plugins, "paths")
432
+
433
+ config.validate()
434
+ return config
435
+
436
+ # -- Validation -----------------------------------------------------
437
+
438
+ def validate(self) -> None:
439
+ """Validate all fields, raising [`ConfigError`][pythonnative.project.config.ConfigError] on first problem."""
440
+ _validate_app_id(self.app_id)
441
+ if self.android.application_id:
442
+ _validate_app_id(self.android.application_id)
443
+ if self.ios.bundle_id:
444
+ _validate_app_id(self.ios.bundle_id, allow_hyphen=True)
445
+
446
+ if not self.name.strip():
447
+ raise ConfigError("app.name must not be empty.")
448
+
449
+ if not _VERSION_RE.match(self.version):
450
+ raise ConfigError(f"app.version {self.version!r} must be a dotted number like '1.0.0'.")
451
+
452
+ if self.build < 1:
453
+ raise ConfigError("app.build must be a positive integer.")
454
+
455
+ if self.python_version not in SUPPORTED_PYTHON_VERSIONS:
456
+ supported = ", ".join(SUPPORTED_PYTHON_VERSIONS)
457
+ raise ConfigError(
458
+ f"app.python_version {self.python_version!r} is unsupported (choose one of: {supported})."
459
+ )
460
+
461
+ if self.orientation not in VALID_ORIENTATIONS:
462
+ valid = ", ".join(VALID_ORIENTATIONS)
463
+ raise ConfigError(f"app.orientation {self.orientation!r} is invalid (choose one of: {valid}).")
464
+
465
+ if not self.entry_point.strip():
466
+ raise ConfigError("app.entry_point must not be empty.")
467
+
468
+ for scheme in self.url_schemes:
469
+ if not _URL_SCHEME_RE.match(scheme):
470
+ raise ConfigError(
471
+ f"app.url_schemes entry {scheme!r} is invalid; schemes must start with a letter "
472
+ "and contain only letters, digits, '+', '-', and '.'."
473
+ )
474
+
475
+ unknown = _permissions.unknown_capabilities(self.permissions.keys())
476
+ if unknown:
477
+ known = ", ".join(sorted(_permissions.CAPABILITIES))
478
+ raise ConfigError("Unknown permission(s): " + ", ".join(unknown) + f".\nValid capabilities: {known}")
479
+
480
+ _validate_requirements(self.requirements)
481
+ for url in self.extra_index_urls:
482
+ if not _URL_RE.match(url):
483
+ raise ConfigError(
484
+ f"[requirements].extra_index_urls entry {url!r} is invalid; use a full http(s) URL "
485
+ "to a PEP 503 simple index (e.g. 'https://wheels.example.com/simple')."
486
+ )
487
+
488
+ if self.ios.signing.export_method not in VALID_IOS_EXPORT_METHODS:
489
+ valid = ", ".join(VALID_IOS_EXPORT_METHODS)
490
+ raise ConfigError(
491
+ f"[ios.signing].export_method {self.ios.signing.export_method!r} is invalid (choose one of: {valid})."
492
+ )
493
+ if not _IOS_VERSION_RE.match(self.ios.deployment_target):
494
+ raise ConfigError(
495
+ f"[ios].deployment_target {self.ios.deployment_target!r} must look like '13.0' or '16.4'."
496
+ )
497
+ if _version_tuple(self.ios.deployment_target) < _version_tuple(MIN_IOS_DEPLOYMENT_TARGET):
498
+ raise ConfigError(
499
+ f"[ios].deployment_target must be at least {MIN_IOS_DEPLOYMENT_TARGET} "
500
+ "(the embedded CPython runtime and iOS binary wheels target that floor)."
501
+ )
502
+
503
+ if self.android.min_sdk < MIN_ANDROID_SDK:
504
+ raise ConfigError(f"[android].min_sdk must be at least {MIN_ANDROID_SDK} (Chaquopy requirement).")
505
+ if self.android.target_sdk < self.android.min_sdk:
506
+ raise ConfigError("[android].target_sdk must be >= min_sdk.")
507
+ for abi in self.android.abi_filters:
508
+ if abi not in VALID_ANDROID_ABIS:
509
+ valid = ", ".join(VALID_ANDROID_ABIS)
510
+ raise ConfigError(f"[android].abi_filters entry {abi!r} is invalid (choose from: {valid}).")
511
+
512
+
513
+ # ======================================================================
514
+ # Parsing helpers
515
+ # ======================================================================
516
+
517
+
518
+ def _parse_ios(table: Mapping[str, Any]) -> IOSConfig:
519
+ signing_table = _expect_table(table, "signing", optional=True, parent="ios")
520
+ extra = table.get("extra_info_plist") or {}
521
+ if extra and not isinstance(extra, dict):
522
+ raise ConfigError("[ios].extra_info_plist must be a table.")
523
+ return IOSConfig(
524
+ deployment_target=_opt_str(table, "deployment_target") or "13.0",
525
+ development_team=_opt_str(table, "development_team"),
526
+ bundle_id=_opt_str(table, "bundle_id"),
527
+ extra_info_plist=dict(extra),
528
+ signing=IOSSigning(
529
+ export_method=(_opt_str(signing_table, "export_method") or "development").lower(),
530
+ provisioning_profile=_opt_str(signing_table, "provisioning_profile"),
531
+ ),
532
+ )
533
+
534
+
535
+ def _parse_android(table: Mapping[str, Any]) -> AndroidConfig:
536
+ signing_table = _expect_table(table, "signing", optional=True, parent="android")
537
+ cfg = AndroidConfig(
538
+ min_sdk=_opt_int(table, "min_sdk", default=24),
539
+ target_sdk=_opt_int(table, "target_sdk", default=34),
540
+ compile_sdk=_opt_int(table, "compile_sdk", default=34),
541
+ application_id=_opt_str(table, "application_id"),
542
+ permissions=_opt_str_list(table, "permissions"),
543
+ signing=AndroidSigning(
544
+ keystore=_opt_str(signing_table, "keystore"),
545
+ key_alias=_opt_str(signing_table, "key_alias"),
546
+ store_password_env=_opt_str(signing_table, "store_password_env") or "PN_ANDROID_KEYSTORE_PASSWORD",
547
+ key_password_env=_opt_str(signing_table, "key_password_env") or "PN_ANDROID_KEY_PASSWORD",
548
+ ),
549
+ )
550
+ abis = _opt_str_list(table, "abi_filters")
551
+ if abis:
552
+ cfg.abi_filters = abis
553
+ return cfg
554
+
555
+
556
+ def _expect_table(
557
+ data: Mapping[str, Any], key: str, *, optional: bool = False, parent: Optional[str] = None
558
+ ) -> Mapping[str, Any]:
559
+ label = f"[{parent}.{key}]" if parent else f"[{key}]"
560
+ if key not in data:
561
+ if optional:
562
+ return {}
563
+ raise ConfigError(f"Missing required {label} table in {CONFIG_FILENAME}.")
564
+ value = data[key]
565
+ if not isinstance(value, Mapping):
566
+ raise ConfigError(f"{label} must be a table.")
567
+ return value
568
+
569
+
570
+ def _require_str(table: Mapping[str, Any], key: str, parent: str) -> str:
571
+ if key not in table:
572
+ raise ConfigError(f"Missing required '{key}' in [{parent}] ({CONFIG_FILENAME}).")
573
+ value = table[key]
574
+ if not isinstance(value, str) or not value.strip():
575
+ raise ConfigError(f"[{parent}].{key} must be a non-empty string.")
576
+ return value.strip()
577
+
578
+
579
+ def _opt_str(table: Mapping[str, Any], key: str) -> Optional[str]:
580
+ if key not in table:
581
+ return None
582
+ value = table[key]
583
+ if value is None:
584
+ return None
585
+ if not isinstance(value, str):
586
+ raise ConfigError(f"'{key}' must be a string (got {type(value).__name__}).")
587
+ return value.strip() or None
588
+
589
+
590
+ def _opt_int(table: Mapping[str, Any], key: str, *, default: int) -> int:
591
+ if key not in table:
592
+ return default
593
+ value = table[key]
594
+ if isinstance(value, bool) or not isinstance(value, int):
595
+ raise ConfigError(f"'{key}' must be an integer (got {type(value).__name__}).")
596
+ return value
597
+
598
+
599
+ def _opt_str_list(table: Mapping[str, Any], key: str) -> List[str]:
600
+ if key not in table:
601
+ return []
602
+ value = table[key]
603
+ if not isinstance(value, list) or not all(isinstance(item, str) for item in value):
604
+ raise ConfigError(f"'{key}' must be a list of strings.")
605
+ return [item.strip() for item in value if item.strip()]
606
+
607
+
608
+ def _validate_app_id(app_id: str, *, allow_hyphen: bool = False) -> None:
609
+ segments = app_id.split(".")
610
+ if len(segments) < 2:
611
+ raise ConfigError(f"app id {app_id!r} must be reverse-DNS with at least two segments (e.g. 'com.example.app').")
612
+ for segment in segments:
613
+ normalized = segment.replace("-", "_") if allow_hyphen else segment
614
+ if not _APP_ID_SEGMENT.match(normalized):
615
+ raise ConfigError(
616
+ f"app id segment {segment!r} is invalid; use lowercase letters, digits and underscores, "
617
+ "and start each segment with a letter."
618
+ )
619
+ if normalized in _JAVA_KEYWORDS:
620
+ raise ConfigError(f"app id segment {segment!r} is a reserved word and cannot be used.")
621
+
622
+
623
+ def _validate_requirements(requirements: List[str]) -> None:
624
+ for spec in requirements:
625
+ pkg = re.split(r"[\[><=!;~ ]", spec, maxsplit=1)[0].strip()
626
+ if pkg.lower().replace("-", "_") == "pythonnative":
627
+ raise ConfigError(
628
+ "Do not list 'pythonnative' in [requirements].packages; the pn CLI bundles it automatically."
629
+ )
630
+
631
+
632
+ # ======================================================================
633
+ # Misc utilities
634
+ # ======================================================================
635
+
636
+
637
+ def entrypoint_to_module(entry_point: str) -> str:
638
+ """Convert an ``entry_point`` path into an importable module path.
639
+
640
+ ``"app/main.py"`` → ``"app.main"``. Returns ``"app.main"`` for empty
641
+ or unusable input so callers always have a sane default.
642
+
643
+ Args:
644
+ entry_point: A path like ``"app/main.py"``.
645
+
646
+ Returns:
647
+ A dotted module path.
648
+ """
649
+ normalized = (entry_point or "").strip().replace("\\", "/")
650
+ if normalized.endswith(".py"):
651
+ normalized = normalized[:-3]
652
+ normalized = normalized.strip("/").replace("/", ".")
653
+ return normalized or "app.main"
654
+
655
+
656
+ # TOML v1.0.0 basic strings must escape the quotation mark, the backslash,
657
+ # and every control character except tab: U+0000-U+0008, U+000A-U+001F, and
658
+ # U+007F. Tab is legal raw, and U+000B has no compact escape, so anything
659
+ # without one falls through to \uXXXX.
660
+ _TOML_COMPACT_ESCAPES = {
661
+ "\\": "\\\\",
662
+ '"': '\\"',
663
+ "\b": "\\b",
664
+ "\f": "\\f",
665
+ "\n": "\\n",
666
+ "\r": "\\r",
667
+ }
668
+
669
+
670
+ def _toml_escape(value: str) -> str:
671
+ """Escape ``value`` for use inside a TOML basic string.
672
+
673
+ Applied at the render boundary rather than relying on the caller,
674
+ since this module is public API and reachable without ``pn init``'s
675
+ name validation in front of it.
676
+
677
+ Args:
678
+ value: The raw string to embed between double quotes.
679
+
680
+ Returns:
681
+ The escaped text, without the surrounding quotes.
682
+ """
683
+ out = []
684
+ for char in value:
685
+ escaped = _TOML_COMPACT_ESCAPES.get(char)
686
+ if escaped is not None:
687
+ out.append(escaped)
688
+ elif char != "\t" and (char < "\x20" or char == "\x7f"):
689
+ out.append(f"\\u{ord(char):04X}")
690
+ else:
691
+ out.append(char)
692
+ return "".join(out)
693
+
694
+
695
+ def render_default_toml(*, name: str, app_id: str, python_version: str = DEFAULT_PYTHON_VERSION) -> str:
696
+ """Render a starter ``pythonnative.toml`` for ``pn init``.
697
+
698
+ Every interpolated value is escaped for a TOML basic string, so a
699
+ name containing a quote, a backslash, or a control character still
700
+ produces a parseable file.
701
+
702
+ Args:
703
+ name: Project name.
704
+ app_id: Reverse-DNS app identifier.
705
+ python_version: Embedded CPython version.
706
+
707
+ Returns:
708
+ The TOML file contents as a string, with commented-out examples
709
+ for the optional tables.
710
+ """
711
+ display = name.replace("_", " ").replace("-", " ").strip().title() or name
712
+ name = _toml_escape(name)
713
+ display = _toml_escape(display)
714
+ app_id = _toml_escape(app_id)
715
+ python_version = _toml_escape(python_version)
716
+ return f"""# PythonNative project configuration.
717
+ # Docs: https://pythonnative.com/guides/configuration/
718
+
719
+ [app]
720
+ id = "{app_id}"
721
+ name = "{name}"
722
+ display_name = "{display}"
723
+ version = "1.0.0"
724
+ build = 1
725
+ python_version = "{python_version}"
726
+ orientation = "portrait" # portrait | landscape | all
727
+ entry_point = "app/main.py"
728
+ # Custom URL schemes for deep links (e.g. "{name}" opens {name}://...).
729
+ # url_schemes = ["{name}"]
730
+
731
+ # Declare the device capabilities your app needs. A string becomes the
732
+ # iOS permission prompt text; `true` uses a sensible default.
733
+ # See: https://pythonnative.com/guides/permissions/
734
+ [permissions]
735
+ # camera = "Scan receipts with your camera."
736
+ # location_when_in_use = "Show nearby stores."
737
+ # notifications = true
738
+ # face_id = "Unlock the app with Face ID."
739
+
740
+ # App icon and splash. Provide a 1024x1024 PNG icon; it is resized for
741
+ # every density/idiom automatically.
742
+ [assets]
743
+ # icon = "assets/icon.png"
744
+ # splash = "assets/splash.png"
745
+
746
+ # Third-party pip packages bundled into the app. Pure-Python packages
747
+ # always work; packages with C extensions need a wheel for each device
748
+ # platform. Run `pn deps` to see what resolves before you build.
749
+ # Do NOT list "pythonnative". Docs: https://pythonnative.com/guides/pypi-packages/
750
+ [requirements]
751
+ packages = []
752
+ # extra_index_urls = ["https://wheels.example.com/simple"]
753
+
754
+ [ios]
755
+ deployment_target = "{MIN_IOS_DEPLOYMENT_TARGET}"
756
+ # development_team = "ABCDE12345"
757
+ # bundle_id = "{app_id}"
758
+
759
+ [ios.signing]
760
+ export_method = "development" # development | ad-hoc | app-store | enterprise
761
+ # provisioning_profile = "My App Distribution"
762
+
763
+ [android]
764
+ min_sdk = {MIN_ANDROID_SDK}
765
+ target_sdk = 34
766
+ # arm64 devices + x86_64 emulators. These are the only ABIs the embedded
767
+ # CPython (3.12+) and Android wheels on PyPI ship for.
768
+ # abi_filters = ["arm64-v8a", "x86_64"]
769
+
770
+ [android.signing]
771
+ # keystore = "release.keystore"
772
+ # key_alias = "{name}"
773
+ """