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,241 @@
1
+ """Developer diagnostics: dev mode, warnings, and error reporting.
2
+
3
+ PythonNative distinguishes **dev mode** (the `pn preview` window, `pn
4
+ run` with hot reload, or any process with ``PN_DEV=1``) from production.
5
+ Dev mode turns on:
6
+
7
+ - **Validation warnings** ([`warn`][pythonnative.diagnostics.warn] /
8
+ [`warn_once`][pythonnative.diagnostics.warn_once]): unknown style
9
+ keys, duplicate list keys, and similar mistakes are printed once with
10
+ a suggestion instead of failing silently.
11
+ - **Hook-order checking**: calling hooks conditionally corrupts slot
12
+ state; in dev mode the mismatch raises a
13
+ [`HookOrderError`][pythonnative.diagnostics.HookOrderError]
14
+ immediately instead of cross-wiring state.
15
+ - **The RedBox**: uncaught errors from render, effects, and event
16
+ handlers are routed to the screen host, which presents a full-screen
17
+ error overlay (see `pythonnative.hosts`) instead of crashing or
18
+ swallowing the traceback.
19
+
20
+ In production none of this runs: validation is skipped, hook-order
21
+ checks are skipped, and errors propagate exactly as raised.
22
+
23
+ This module has no dependencies on the rest of PythonNative, so any
24
+ module (hooks, reconciler, events) may import it freely.
25
+ """
26
+
27
+ import os
28
+ import sys
29
+ import threading
30
+ import traceback
31
+ from collections import deque
32
+ from typing import Any, Callable, Deque, List, Optional, Set, Tuple
33
+
34
+ __all__ = [
35
+ "HookOrderError",
36
+ "set_dev_mode",
37
+ "is_dev",
38
+ "warn",
39
+ "warn_once",
40
+ "swallowed",
41
+ "get_warnings",
42
+ "clear_warnings",
43
+ "set_error_reporter",
44
+ "report_error",
45
+ ]
46
+
47
+
48
+ class HookOrderError(RuntimeError):
49
+ """Raised in dev mode when hooks are called in a different order than the previous render.
50
+
51
+ Hooks map to state slots by call order, so calling them inside
52
+ conditionals or loops (or returning early between hook calls)
53
+ silently cross-wires state in production. Dev mode detects the
54
+ mismatch and raises this error with the offending component and
55
+ slot so the bug is caught at the source.
56
+ """
57
+
58
+
59
+ # ======================================================================
60
+ # Dev mode
61
+ # ======================================================================
62
+
63
+ # Tri-state: ``None`` means "not explicitly set, consult the environment".
64
+ _dev_mode: Optional[bool] = None
65
+
66
+
67
+ def set_dev_mode(enabled: bool) -> None:
68
+ """Explicitly enable or disable dev mode for this process.
69
+
70
+ Called automatically by ``pn preview`` and by the screen host's
71
+ ``enable_hot_reload`` (which the device templates invoke on debug
72
+ builds). An explicit call wins over the ``PN_DEV`` environment
73
+ variable.
74
+
75
+ Args:
76
+ enabled: ``True`` to turn on dev diagnostics.
77
+ """
78
+ global _dev_mode
79
+ _dev_mode = bool(enabled)
80
+
81
+
82
+ def is_dev() -> bool:
83
+ """Return whether dev diagnostics are active.
84
+
85
+ Resolution order: an explicit
86
+ [`set_dev_mode`][pythonnative.diagnostics.set_dev_mode] call, then
87
+ the ``PN_DEV`` environment variable, then ``False``.
88
+ """
89
+ if _dev_mode is not None:
90
+ return _dev_mode
91
+ return os.environ.get("PN_DEV", "").lower() in {"1", "true", "yes", "on"}
92
+
93
+
94
+ # ======================================================================
95
+ # Warnings (LogBox-lite)
96
+ # ======================================================================
97
+
98
+ _MAX_WARNINGS = 200
99
+
100
+ _warn_lock = threading.Lock()
101
+ _warned_keys: Set[str] = set()
102
+ _warnings: Deque[str] = deque(maxlen=_MAX_WARNINGS)
103
+
104
+
105
+ def warn(message: str) -> None:
106
+ """Print a dev warning and record it in the warning log.
107
+
108
+ No-op in production. Warnings are prefixed with ``[PN] WARN`` on
109
+ stderr and retained (most recent 200) for inspection via
110
+ [`get_warnings`][pythonnative.diagnostics.get_warnings].
111
+
112
+ Args:
113
+ message: Human-readable description of the problem, ideally
114
+ with a suggestion for the fix.
115
+ """
116
+ if not is_dev():
117
+ return
118
+ with _warn_lock:
119
+ _warnings.append(message)
120
+ try:
121
+ print(f"[PN] WARN: {message}", file=sys.stderr, flush=True)
122
+ except Exception:
123
+ pass
124
+
125
+
126
+ def warn_once(message: str, key: Optional[str] = None) -> None:
127
+ """Like [`warn`][pythonnative.diagnostics.warn], but at most once per ``key``.
128
+
129
+ Use for per-render validation (style keys, list keys) so a warning
130
+ fires once instead of sixty times a second.
131
+
132
+ Args:
133
+ message: The warning message.
134
+ key: Dedupe key, e.g. ``"style:Text:font_siez"``. Defaults to
135
+ the message itself.
136
+ """
137
+ if not is_dev():
138
+ return
139
+ dedupe = key if key is not None else message
140
+ with _warn_lock:
141
+ if dedupe in _warned_keys:
142
+ return
143
+ _warned_keys.add(dedupe)
144
+ warn(message)
145
+
146
+
147
+ def swallowed(context: str) -> None:
148
+ """Surface a suppressed native-backend exception (dev mode only).
149
+
150
+ The native view/module backends intentionally degrade gracefully in
151
+ production: a failed style application or a missing OS API must not
152
+ crash the app. Call this from the ``except`` block instead of
153
+ ``pass`` so that, in dev mode, each suppression is reported once per
154
+ call site instead of disappearing.
155
+
156
+ Args:
157
+ context: Where the suppression happened, e.g.
158
+ ``"ios.TextHandler._apply"``.
159
+ """
160
+ if not is_dev():
161
+ return
162
+ exc = sys.exc_info()[1]
163
+ if exc is None:
164
+ return
165
+ frame = sys._getframe(1)
166
+ location = f"{os.path.basename(frame.f_code.co_filename)}:{frame.f_lineno}"
167
+ warn_once(
168
+ f"{context} suppressed {type(exc).__name__}: {exc} ({location})",
169
+ key=f"swallowed:{context}:{location}",
170
+ )
171
+
172
+
173
+ def get_warnings() -> List[str]:
174
+ """Return a snapshot of the recorded warnings (oldest first)."""
175
+ with _warn_lock:
176
+ return list(_warnings)
177
+
178
+
179
+ def clear_warnings() -> None:
180
+ """Drop all recorded warnings and dedupe keys (test helper)."""
181
+ with _warn_lock:
182
+ _warnings.clear()
183
+ _warned_keys.clear()
184
+
185
+
186
+ # ======================================================================
187
+ # Error reporting (RedBox routing)
188
+ # ======================================================================
189
+ #
190
+ # Screen hosts register themselves as error reporters. When an error
191
+ # escapes user code in a context that would otherwise be swallowed
192
+ # (event handlers) or crash the process (async tasks), dev mode routes
193
+ # it to the most recently registered reporter, which shows the RedBox.
194
+ # Reporters form a stack: the top entry is the most recently created
195
+ # (and therefore frontmost) screen.
196
+
197
+ _reporter_lock = threading.Lock()
198
+ _reporters: List[Tuple[int, Callable[[BaseException, str], None]]] = []
199
+
200
+
201
+ def set_error_reporter(owner: Any, reporter: Optional[Callable[[BaseException, str], None]]) -> None:
202
+ """Register or unregister ``owner``'s RedBox reporter.
203
+
204
+ Args:
205
+ owner: Any object identifying the registration (a screen
206
+ host); keyed by ``id(owner)``.
207
+ reporter: ``reporter(exc, phase)`` callable, or ``None`` to
208
+ unregister the owner's reporter.
209
+ """
210
+ key = id(owner)
211
+ with _reporter_lock:
212
+ _reporters[:] = [(k, r) for (k, r) in _reporters if k != key]
213
+ if reporter is not None:
214
+ _reporters.append((key, reporter))
215
+
216
+
217
+ def report_error(exc: BaseException, phase: str = "runtime") -> bool:
218
+ """Route ``exc`` to the active RedBox reporter (dev mode only).
219
+
220
+ Args:
221
+ exc: The exception to display.
222
+ phase: Where it came from: ``"render"``, ``"effect"``,
223
+ ``"event"``, or ``"async"``.
224
+
225
+ Returns:
226
+ ``True`` when a reporter accepted the error, ``False`` when no
227
+ reporter is registered (or dev mode is off), in which case the
228
+ caller should fall back to its default behavior.
229
+ """
230
+ if not is_dev():
231
+ return False
232
+ with _reporter_lock:
233
+ reporter = _reporters[-1][1] if _reporters else None
234
+ if reporter is None:
235
+ return False
236
+ try:
237
+ reporter(exc, phase)
238
+ return True
239
+ except Exception:
240
+ traceback.print_exc()
241
+ return False
@@ -0,0 +1,159 @@
1
+ """Lightweight element descriptors for the virtual view tree.
2
+
3
+ An [`Element`][pythonnative.Element] is an immutable description of a UI
4
+ node, analogous to a React element. It captures a type, a props dict,
5
+ and an ordered list of children without creating any native platform
6
+ objects. The reconciler consumes these trees to determine what native
7
+ views must be created, updated, or removed.
8
+
9
+ An element's ``type`` is one of three things:
10
+
11
+ - a ``str`` naming a **native view** (``"Text"``, ``"View"``, ...),
12
+ - a [`Component`][pythonnative.Component] produced by
13
+ [`@component`][pythonnative.component.component], or
14
+ - a **structural type**: one of the singletons defined here
15
+ ([`FRAGMENT`][pythonnative.element.FRAGMENT],
16
+ [`ERROR_BOUNDARY`][pythonnative.element.ERROR_BOUNDARY],
17
+ [`SUSPENSE`][pythonnative.element.SUSPENSE]) or a
18
+ [`Context`][pythonnative.Context] (whose elements are providers).
19
+
20
+ Structural types are real objects rather than magic strings so the
21
+ reconciler can dispatch on them with identity checks and no user
22
+ element can collide with them.
23
+
24
+ Elements are produced by built-in factories such as
25
+ [`Text`][pythonnative.Text], [`Button`][pythonnative.Button], and
26
+ [`Column`][pythonnative.Column], or by calling components.
27
+
28
+ Example:
29
+ ```python
30
+ from pythonnative import Element
31
+
32
+ node = Element("Text", {"text": "Hello"}, [])
33
+ ```
34
+ """
35
+
36
+ from __future__ import annotations
37
+
38
+ from typing import Any, Dict, Iterable, List, Optional, Union
39
+
40
+ __all__ = [
41
+ "ERROR_BOUNDARY",
42
+ "FRAGMENT",
43
+ "SUSPENSE",
44
+ "Element",
45
+ "Node",
46
+ "StructuralType",
47
+ "type_label",
48
+ ]
49
+
50
+
51
+ class StructuralType:
52
+ """Identity object naming a reconciler-owned element kind.
53
+
54
+ Instances are singletons compared by identity; ``repr`` shows the
55
+ kind for debugging (``<Fragment>``).
56
+ """
57
+
58
+ __slots__ = ("name",)
59
+
60
+ def __init__(self, name: str) -> None:
61
+ self.name = name
62
+
63
+ def __repr__(self) -> str:
64
+ return f"<{self.name}>"
65
+
66
+
67
+ FRAGMENT = StructuralType("Fragment")
68
+ """Type of [`Fragment`][pythonnative.Fragment] elements: a transparent group."""
69
+
70
+ ERROR_BOUNDARY = StructuralType("ErrorBoundary")
71
+ """Type of [`ErrorBoundary`][pythonnative.ErrorBoundary] elements."""
72
+
73
+ SUSPENSE = StructuralType("Suspense")
74
+ """Type of [`Suspense`][pythonnative.Suspense] elements."""
75
+
76
+
77
+ class Element:
78
+ """Immutable description of a single UI node.
79
+
80
+ Built-in elements use a string ``type`` (``"Text"``, ``"Button"``,
81
+ ``"Column"``, etc.); components use the
82
+ [`Component`][pythonnative.Component] object itself as ``type``;
83
+ structural elements use a [`StructuralType`][pythonnative.element.StructuralType]
84
+ or a [`Context`][pythonnative.Context]. The reconciler dispatches on
85
+ this distinction when mounting the tree.
86
+
87
+ Attributes:
88
+ type: The element kind (see the module docstring).
89
+ props: Dict of properties passed to the native handler or the
90
+ component function.
91
+ children: Ordered list of child nodes. ``None`` and ``False``
92
+ entries are permitted and dropped during reconciliation, so
93
+ conditional children (``cond and Text(...)``) need no special
94
+ casing. Components receive these as their ``*children``.
95
+ key: Optional stable identity used by the reconciler when
96
+ diffing keyed lists. Two elements with the same ``type`` and
97
+ ``key`` are treated as the same logical node across renders.
98
+ """
99
+
100
+ __slots__ = ("type", "props", "children", "key")
101
+
102
+ def __init__(
103
+ self,
104
+ type_: Any,
105
+ props: Optional[Dict[str, Any]] = None,
106
+ children: Optional[Iterable[Any]] = None,
107
+ key: Optional[str] = None,
108
+ ) -> None:
109
+ self.type = type_
110
+ self.props: Dict[str, Any] = props if props is not None else {}
111
+ self.children: List[Any] = list(children) if children is not None else []
112
+ self.key = key
113
+
114
+ def __repr__(self) -> str:
115
+ return f"Element({type_label(self.type)!r}, props={sorted(self.props)}, children={len(self.children)})"
116
+
117
+ def __eq__(self, other: object) -> bool:
118
+ if not isinstance(other, Element):
119
+ return NotImplemented
120
+ return (
121
+ self.type == other.type
122
+ and self.props == other.props
123
+ and self.children == other.children
124
+ and self.key == other.key
125
+ )
126
+
127
+ def __ne__(self, other: object) -> bool:
128
+ result = self.__eq__(other)
129
+ if result is NotImplemented:
130
+ return result
131
+ return not result
132
+
133
+ __hash__ = None # type: ignore[assignment,unused-ignore]
134
+
135
+ def with_key(self, key: Optional[str]) -> "Element":
136
+ """Return a copy of this element carrying ``key``.
137
+
138
+ Handy when a factory result needs a key after the fact, for
139
+ example while building a list comprehension over elements
140
+ produced by a helper that doesn't take ``key``.
141
+ """
142
+ return Element(self.type, self.props, self.children, key=key)
143
+
144
+
145
+ Node = Union[Element, None, bool, Iterable[Any]]
146
+ """Anything a component may render: an element, ``None`` / ``False``
147
+ for "nothing", or a (possibly nested) iterable of nodes."""
148
+
149
+
150
+ def type_label(type_obj: Any) -> str:
151
+ """Return a human-readable name for an element type (for messages)."""
152
+ if isinstance(type_obj, str):
153
+ return type_obj
154
+ if isinstance(type_obj, StructuralType):
155
+ return type_obj.name
156
+ name = getattr(type_obj, "display_name", None) or getattr(type_obj, "__name__", None)
157
+ if name:
158
+ return str(name)
159
+ return repr(type_obj)
@@ -0,0 +1,19 @@
1
+ """Equality rules for immutable application snapshots.
2
+
3
+ Identity is equal. Scalar boolean equality is respected. Objects whose
4
+ comparison returns a nonboolean result (such as arrays) require a new
5
+ identity to signal a change. In-place mutations don't signal updates.
6
+ """
7
+
8
+ from typing import Any
9
+
10
+
11
+ def equal(left: Any, right: Any) -> bool:
12
+ """Compare values without coercing array expressions to booleans."""
13
+ if left is right:
14
+ return True
15
+ try:
16
+ result = left == right
17
+ return result if isinstance(result, bool) else False
18
+ except Exception:
19
+ return False
pythonnative/events.py ADDED
@@ -0,0 +1,231 @@
1
+ """Tag-based event routing between native views and Python callbacks.
2
+
3
+ Before the batched-commit overhaul, every event prop (``on_press``,
4
+ ``on_change``, …) was wired by storing the Python callable on (or next
5
+ to) the native view, and every re-render re-pushed fresh closures across
6
+ the bridge. This module replaces that with a single dispatch channel:
7
+
8
+ - The reconciler strips callable props out of the payload sent to
9
+ native handlers and registers them here, keyed by ``(tag, name)``.
10
+ - Handlers wire their platform listener **once** at view creation; the
11
+ listener calls [`dispatch_event`][pythonnative.events.dispatch_event]
12
+ with the view's tag and the event name.
13
+ - Re-renders only mutate this Python-side registry; no native call is
14
+ made when just a callback identity changes.
15
+
16
+ The set of event names present on an element is forwarded to handlers
17
+ under the [`EVENTS_PROP`][pythonnative.events.EVENTS_PROP] key (a
18
+ ``frozenset``), so handlers that wire expensive listeners (scroll
19
+ delegates, gesture recognizers) can do so conditionally. Dispatching an
20
+ event nobody listens to is a cheap dict miss.
21
+ """
22
+
23
+ import threading
24
+ from typing import Any, Callable, Dict, FrozenSet, Optional, Tuple
25
+
26
+ EVENTS_PROP = "_pn_events"
27
+ """Prop key carrying the ``frozenset`` of event names wired on an element."""
28
+
29
+ GESTURES_PROP = "gestures"
30
+ """Prop key carrying gesture descriptors (see ``pythonnative.gestures``)."""
31
+
32
+ # Prop dicts that may carry nested callables, mapped to the event name
33
+ # each nested key is hoisted to.
34
+ _NESTED_EVENT_PROPS: Dict[str, Dict[str, str]] = {
35
+ "refresh_control": {"on_refresh": "on_refresh"},
36
+ }
37
+
38
+
39
+ class EventRegistry:
40
+ """Process-wide map of ``(tag, event name) -> Python callback``.
41
+
42
+ Thread-safe: native backends may dispatch from the platform UI
43
+ thread while the reconciler updates registrations from the render
44
+ thread.
45
+ """
46
+
47
+ def __init__(self) -> None:
48
+ self._lock = threading.Lock()
49
+ self._scopes: Dict[int, Any] = {}
50
+ self._callbacks: Dict[int, Dict[str, Callable[..., Any]]] = {}
51
+
52
+ def set_events(self, tag: int, events: Dict[str, Callable[..., Any]]) -> None:
53
+ """Replace every registration for ``tag`` with ``events``."""
54
+ with self._lock:
55
+ if events:
56
+ self._callbacks[tag] = dict(events)
57
+ else:
58
+ self._callbacks.pop(tag, None)
59
+
60
+ def set_scope(self, tag: int, scope: Any) -> None:
61
+ """Associate callbacks with the component that owns this view."""
62
+ self._scopes[tag] = scope
63
+
64
+ def invoke(self, tag: int, name: str, *args: Any) -> Any:
65
+ """Invoke a sync or async handler with component cancellation."""
66
+ from .runtime import invoke
67
+
68
+ callback = self.get(tag, name)
69
+ if callback is not None:
70
+ return invoke(callback, *args, scope=self._scopes.get(tag))
71
+ return None
72
+
73
+ def clear(self, tag: int) -> None:
74
+ """Drop every registration for ``tag`` (called on view destroy)."""
75
+ with self._lock:
76
+ self._callbacks.pop(tag, None)
77
+ self._scopes.pop(tag, None)
78
+
79
+ def get(self, tag: int, name: str) -> Optional[Callable[..., Any]]:
80
+ """Return the callback for ``(tag, name)``, or ``None``."""
81
+ with self._lock:
82
+ bucket = self._callbacks.get(tag)
83
+ if bucket is None:
84
+ return None
85
+ return bucket.get(name)
86
+
87
+ def has(self, tag: int, name: str) -> bool:
88
+ """Return whether a callback is registered for ``(tag, name)``."""
89
+ return self.get(tag, name) is not None
90
+
91
+ def dispatch(self, tag: int, name: str, *args: Any) -> bool:
92
+ """Invoke the callback for ``(tag, name)`` with ``args``.
93
+
94
+ Returns:
95
+ ``True`` when a callback existed and was invoked (even if
96
+ it raised: exceptions never propagate into the platform's
97
+ UI thread; in dev mode they are routed to the RedBox via
98
+ [`report_error`][pythonnative.diagnostics.report_error],
99
+ otherwise the traceback is printed), ``False`` when nothing
100
+ is registered.
101
+ """
102
+ callback = self.get(tag, name)
103
+ if callback is None:
104
+ return False
105
+ try:
106
+ self.invoke(tag, name, *args)
107
+ except Exception as exc:
108
+ from . import diagnostics
109
+
110
+ if not diagnostics.report_error(exc, phase=f"event {name!r}"):
111
+ import traceback
112
+
113
+ traceback.print_exc()
114
+ return True
115
+
116
+ def reset(self) -> None:
117
+ """Drop every registration (test helper)."""
118
+ with self._lock:
119
+ self._callbacks.clear()
120
+ self._scopes.clear()
121
+
122
+
123
+ _registry = EventRegistry()
124
+
125
+
126
+ def get_event_registry() -> EventRegistry:
127
+ """Return the process-wide [`EventRegistry`][pythonnative.events.EventRegistry]."""
128
+ return _registry
129
+
130
+
131
+ def dispatch_event(tag: int, name: str, *args: Any) -> bool:
132
+ """Dispatch an event from a native view into Python.
133
+
134
+ This is the single entry point platform handlers call when a
135
+ native listener fires.
136
+
137
+ Args:
138
+ tag: The view's reconciler-assigned tag.
139
+ name: Event name, the original prop name (``"on_press"``,
140
+ ``"on_change"``, …) or a gesture channel (``"gesture:0"``).
141
+ *args: Positional arguments forwarded to the user callback,
142
+ preserving each prop's documented signature.
143
+
144
+ Returns:
145
+ Whether a callback was registered for ``(tag, name)``.
146
+ """
147
+ return _registry.dispatch(tag, name, *args)
148
+
149
+
150
+ # ======================================================================
151
+ # Prop splitting
152
+ # ======================================================================
153
+
154
+
155
+ def extract_events(props: Dict[str, Any]) -> Tuple[Dict[str, Any], Dict[str, Callable[..., Any]]]:
156
+ """Split ``props`` into native-safe props and Python event callbacks.
157
+
158
+ Rules:
159
+
160
+ - Top-level callables named ``on_*`` become events under their prop
161
+ name and are removed from the native payload.
162
+ - ``refresh_control`` dicts have their nested ``on_refresh``
163
+ hoisted to the ``"on_refresh"`` event; the remaining keys
164
+ (``refreshing``, ``tint_color``) stay in the payload.
165
+ - ``gestures`` lists of gesture descriptors are serialized to plain
166
+ dicts (handlers wire recognizers from them) while their callbacks
167
+ are folded into per-gesture ``"gesture:<i>"`` routers.
168
+ - The resulting payload carries ``_pn_events`` (a frozenset of the
169
+ event names present), so handlers can wire listeners
170
+ conditionally and the prop differ can detect listener
171
+ addition/removal without comparing closures.
172
+
173
+ Args:
174
+ props: Raw element props (already stripped of reconciler-owned
175
+ keys).
176
+
177
+ Returns:
178
+ ``(clean_props, events)`` where ``clean_props`` contains no
179
+ callables and ``events`` maps event names to callbacks.
180
+ """
181
+ clean: Dict[str, Any] = {}
182
+ events: Dict[str, Callable[..., Any]] = {}
183
+
184
+ for key, value in props.items():
185
+ if key.startswith("on_") and callable(value):
186
+ events[key] = value
187
+ continue
188
+ nested_spec = _NESTED_EVENT_PROPS.get(key)
189
+ if nested_spec is not None and isinstance(value, dict):
190
+ remainder: Dict[str, Any] = {}
191
+ for nested_key, nested_value in value.items():
192
+ event_name = nested_spec.get(nested_key)
193
+ if event_name is not None and callable(nested_value):
194
+ events[event_name] = nested_value
195
+ else:
196
+ remainder[nested_key] = nested_value
197
+ clean[key] = remainder
198
+ continue
199
+ if key == GESTURES_PROP and value:
200
+ from .gestures import serialize_gestures
201
+
202
+ specs, gesture_events = serialize_gestures(value)
203
+ clean[key] = specs
204
+ events.update(gesture_events)
205
+ continue
206
+ clean[key] = value
207
+
208
+ if events:
209
+ clean[EVENTS_PROP] = frozenset(events)
210
+ return clean, events
211
+
212
+
213
+ def event_names(props: Dict[str, Any]) -> FrozenSet[str]:
214
+ """Return the event-name set a handler should consult for ``props``."""
215
+ names = props.get(EVENTS_PROP)
216
+ if isinstance(names, frozenset):
217
+ return names
218
+ if isinstance(names, (set, list, tuple)):
219
+ return frozenset(names)
220
+ return frozenset()
221
+
222
+
223
+ __all__ = [
224
+ "EVENTS_PROP",
225
+ "GESTURES_PROP",
226
+ "EventRegistry",
227
+ "get_event_registry",
228
+ "dispatch_event",
229
+ "extract_events",
230
+ "event_names",
231
+ ]