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,585 @@
1
+ """Device-side module reloading for Fast Refresh.
2
+
3
+ The dev server (``pythonnative.devserver``) watches the project's
4
+ ``app/`` directory and tells every connected dev client which files
5
+ changed. This module is the client's other half: it re-executes the
6
+ changed modules with ``importlib`` and refreshes every mounted screen.
7
+
8
+ Two strategies share the surface:
9
+
10
+ - **Fast Refresh** (default): after reloading the changed modules the
11
+ reconciler tree is walked and every component function whose module
12
+ was reloaded is swapped in place. Hook state, navigation state, and
13
+ even scroll positions survive because the underlying ``VNode``
14
+ objects are reused; the next render simply calls the new function
15
+ bodies through the old slots.
16
+ - **Full remount**: when the in-place swap fails (e.g. the new module
17
+ raised at import time, or a render exception bubbled out while
18
+ running the new function), the host falls back to building a
19
+ brand-new reconciler tree. State is lost but the app keeps running.
20
+
21
+ [`apply_reload`][pythonnative.hot_reload.apply_reload] is the single
22
+ entry point: it reloads once per process (several screens may be
23
+ mounted) and then refreshes each live host.
24
+
25
+ On device, sources arrive in a writable **overlay** directory that
26
+ shadows the app bundle (see
27
+ [`configure_dev_environment`][pythonnative.hot_reload.configure_dev_environment]);
28
+ under ``pn preview`` the project directory itself is on ``sys.path``
29
+ and there is no overlay.
30
+ """
31
+
32
+ from __future__ import annotations
33
+
34
+ import importlib
35
+ import importlib.util
36
+ import os
37
+ import sys
38
+ import threading
39
+ from dataclasses import dataclass, field
40
+ from typing import Any, Dict, Iterable, List, Optional, Sequence, Set
41
+
42
+ __all__ = [
43
+ "DEV_ROOT_DIR",
44
+ "ModuleReloader",
45
+ "ReloadResult",
46
+ "apply_reload",
47
+ "configure_dev_environment",
48
+ "overlay_root",
49
+ ]
50
+
51
+ DEV_ROOT_DIR = "pythonnative_dev"
52
+ """Name of the writable on-device directory that shadows bundled app code."""
53
+
54
+ _OVERLAY_ENV = "PYTHONNATIVE_HOT_RELOAD_ROOT"
55
+
56
+
57
+ def configure_dev_environment(writable_root: str, server_url: Optional[str] = None) -> str:
58
+ """Create and prioritize the writable source overlay.
59
+
60
+ The returned directory is inserted at the front of `sys.path`, so a
61
+ synced `app/main.py` shadows the copy bundled into the native
62
+ application. Debug templates call this before importing user code.
63
+
64
+ Args:
65
+ writable_root: Platform data directory that the app can write to
66
+ (Android `filesDir`, iOS `Documents`, or a test directory).
67
+ server_url: The dev server this launch should connect to, when
68
+ the launcher passed one (``pn run`` does, through a launch
69
+ environment variable on iOS and an intent extra on
70
+ Android). It is exported as ``PN_DEV_SERVER`` so the dev
71
+ client picks it up; a remembered server is used otherwise.
72
+
73
+ Returns:
74
+ Absolute path to the overlay root.
75
+ """
76
+ dev_root = os.path.abspath(os.path.join(writable_root, DEV_ROOT_DIR))
77
+ os.makedirs(os.path.join(dev_root, "app"), exist_ok=True)
78
+ if dev_root in sys.path:
79
+ sys.path.remove(dev_root)
80
+ sys.path.insert(0, dev_root)
81
+ os.environ[_OVERLAY_ENV] = dev_root
82
+ if server_url:
83
+ os.environ["PN_DEV_SERVER"] = str(server_url)
84
+ return dev_root
85
+
86
+
87
+ def overlay_root() -> Optional[str]:
88
+ """The overlay directory configured for this process, if any."""
89
+ return os.environ.get(_OVERLAY_ENV) or None
90
+
91
+
92
+ def _overlay_module_path(module_name: str) -> Optional[str]:
93
+ dev_root = overlay_root()
94
+ if not dev_root:
95
+ return None
96
+
97
+ rel_parts = module_name.split(".")
98
+ module_path = os.path.join(dev_root, *rel_parts) + ".py"
99
+ if os.path.exists(module_path):
100
+ return module_path
101
+
102
+ package_path = os.path.join(dev_root, *rel_parts, "__init__.py")
103
+ if os.path.exists(package_path):
104
+ return package_path
105
+
106
+ return None
107
+
108
+
109
+ # ======================================================================
110
+ # Module reloader
111
+ # ======================================================================
112
+
113
+
114
+ class ModuleReloader:
115
+ """Reload changed Python modules and rewrite mounted trees to match.
116
+
117
+ All methods are static; the class is a namespace. The tree-rewrite
118
+ helpers (``build_replacement_map``, ``swap_components_in_tree``,
119
+ ``refresh_in_place``) are what make Fast Refresh state-preserving.
120
+ """
121
+
122
+ _reload_lock = threading.Lock()
123
+
124
+ @staticmethod
125
+ def reload_module(module_name: str) -> bool:
126
+ """Reload a single module by its dotted name.
127
+
128
+ Args:
129
+ module_name: Dotted module name (e.g., `"app.main"`).
130
+
131
+ Returns:
132
+ `True` if the module imported successfully from the current
133
+ `sys.path`; `False` otherwise (the previous module object is
134
+ restored so the app keeps running).
135
+ """
136
+ previous = sys.modules.get(module_name)
137
+ try:
138
+ importlib.invalidate_caches()
139
+ overlay_path = _overlay_module_path(module_name)
140
+ if overlay_path is not None:
141
+ spec = importlib.util.spec_from_file_location(module_name, overlay_path)
142
+ if spec is None or spec.loader is None:
143
+ return False
144
+ module = importlib.util.module_from_spec(spec)
145
+ sys.modules[module_name] = module
146
+ spec.loader.exec_module(module)
147
+ else:
148
+ sys.modules.pop(module_name, None)
149
+ importlib.import_module(module_name)
150
+ return True
151
+ except Exception:
152
+ if previous is not None:
153
+ sys.modules[module_name] = previous
154
+ else:
155
+ sys.modules.pop(module_name, None)
156
+ return False
157
+
158
+ @staticmethod
159
+ def reload_modules(module_names: Sequence[str]) -> List[str]:
160
+ """Reload ``module_names`` in order, returning the names that succeeded."""
161
+ importlib.invalidate_caches()
162
+ reloaded: List[str] = []
163
+ seen: set[str] = set()
164
+ with ModuleReloader._reload_lock:
165
+ for module_name in module_names:
166
+ if not module_name or module_name in seen:
167
+ continue
168
+ seen.add(module_name)
169
+ if ModuleReloader.reload_module(module_name):
170
+ reloaded.append(module_name)
171
+ return reloaded
172
+
173
+ @staticmethod
174
+ def reload_module_strict(module_name: str) -> None:
175
+ """Reload one module, propagating the import error instead of swallowing it.
176
+
177
+ Used by the dev client so a syntax error in a saved file shows
178
+ up in the RedBox and the terminal rather than as a silent
179
+ "nothing reloaded".
180
+ """
181
+ previous = sys.modules.get(module_name)
182
+ try:
183
+ importlib.invalidate_caches()
184
+ overlay_path = _overlay_module_path(module_name)
185
+ if overlay_path is not None:
186
+ spec = importlib.util.spec_from_file_location(module_name, overlay_path)
187
+ if spec is None or spec.loader is None:
188
+ raise ImportError(f"cannot load {module_name} from {overlay_path}")
189
+ module = importlib.util.module_from_spec(spec)
190
+ sys.modules[module_name] = module
191
+ spec.loader.exec_module(module)
192
+ else:
193
+ sys.modules.pop(module_name, None)
194
+ importlib.import_module(module_name)
195
+ except Exception:
196
+ if previous is not None:
197
+ sys.modules[module_name] = previous
198
+ else:
199
+ sys.modules.pop(module_name, None)
200
+ raise
201
+
202
+ @staticmethod
203
+ def expand_reload_targets(changed_modules: Sequence[str], component_path: str) -> List[str]:
204
+ """Expand a set of changed modules into the full reload order.
205
+
206
+ When a user edits ``app/screens/home.py``, only that module is
207
+ reported. But the entry-point module ``app.main`` has bindings
208
+ like ``from app.screens.home import HomeScreen`` that need to be
209
+ re-evaluated against the freshly-loaded ``app.screens.home``;
210
+ likewise other user-app modules may carry transitive bindings
211
+ (e.g. through a shared ``app/theme.py``) that go stale if only
212
+ the changed file is reloaded.
213
+
214
+ The order is:
215
+
216
+ 1. Explicitly changed modules first (in the order given), so
217
+ their fresh source replaces the cached version in
218
+ ``sys.modules`` before any dependent modules re-execute.
219
+ 2. All other currently-imported modules under the entry-point's
220
+ top-level package, deepest first. The depth heuristic biases
221
+ toward leaves so re-executing a screen file picks up the
222
+ newest shared utilities before the file that imports it does.
223
+ 3. The entry-point module itself, last, so its
224
+ ``from ... import`` bindings rebind against everything that
225
+ was refreshed in steps 1 and 2.
226
+
227
+ Modules outside the entry-point's top-level package
228
+ (``pythonnative.*``, stdlib, third-party) are never included;
229
+ framework code is not reloaded.
230
+
231
+ Args:
232
+ changed_modules: Modules reported as changed (dotted form).
233
+ component_path: The host's entry-point identifier, either a
234
+ module path (``"app.main"``) or a dotted attribute path
235
+ (``"app.main.RootScreen"``).
236
+
237
+ Returns:
238
+ The ordered list of modules to feed to
239
+ [`reload_modules`][pythonnative.hot_reload.ModuleReloader.reload_modules].
240
+ """
241
+ entry_module: Optional[str] = None
242
+ if component_path in sys.modules:
243
+ entry_module = component_path
244
+ elif "." in component_path:
245
+ parent = component_path.rsplit(".", 1)[0]
246
+ if parent in sys.modules:
247
+ entry_module = parent
248
+
249
+ app_prefix: Optional[str] = None
250
+ if entry_module:
251
+ app_prefix = entry_module.split(".")[0]
252
+ else:
253
+ for m in changed_modules:
254
+ if m:
255
+ app_prefix = m.split(".")[0]
256
+ break
257
+
258
+ app_modules: Set[str] = set()
259
+ if app_prefix:
260
+ for name in list(sys.modules):
261
+ if name == app_prefix or name.startswith(app_prefix + "."):
262
+ app_modules.add(name)
263
+
264
+ ordered: List[str] = []
265
+ seen: Set[str] = set()
266
+ for m in changed_modules:
267
+ if m and m not in seen:
268
+ ordered.append(m)
269
+ seen.add(m)
270
+
271
+ others = [m for m in app_modules if m not in seen and m != entry_module]
272
+ others.sort(key=lambda m: (-m.count("."), m))
273
+ for m in others:
274
+ ordered.append(m)
275
+ seen.add(m)
276
+
277
+ if entry_module:
278
+ if entry_module in seen:
279
+ ordered.remove(entry_module)
280
+ ordered.append(entry_module)
281
+
282
+ return ordered
283
+
284
+ @staticmethod
285
+ def file_to_module(file_path: str, base_dir: str = "") -> Optional[str]:
286
+ """Convert a file path to a dotted module name.
287
+
288
+ Args:
289
+ file_path: Path to a `.py` file (absolute or relative).
290
+ base_dir: Base directory that names should be relative to.
291
+ If empty, `file_path` is treated as already relative.
292
+
293
+ Returns:
294
+ The dotted module name (e.g., `"app.screens.home"`), or
295
+ `None` for an empty path.
296
+ """
297
+ rel = os.path.relpath(file_path, base_dir) if base_dir else file_path
298
+ rel = rel.replace("\\", os.sep).replace("/", os.sep).lstrip(os.sep)
299
+ if rel.endswith(".py"):
300
+ rel = rel[:-3]
301
+ parts = rel.replace(os.sep, ".").split(".")
302
+ if parts[-1] == "__init__":
303
+ parts = parts[:-1]
304
+ return ".".join(parts) if parts else None
305
+
306
+ @staticmethod
307
+ def modules_from_files(file_paths: Sequence[str], base_dir: str = "") -> List[str]:
308
+ """Convert Python source paths to importable module names."""
309
+ modules: List[str] = []
310
+ for file_path in file_paths:
311
+ module = ModuleReloader.file_to_module(file_path, base_dir=base_dir)
312
+ if module is not None:
313
+ modules.append(module)
314
+ return modules
315
+
316
+ @staticmethod
317
+ def find_replacement_function(old_fn: Any) -> Optional[Any]:
318
+ """Locate a function's post-reload counterpart by qualname.
319
+
320
+ [`Component`][pythonnative.Component] objects forward
321
+ ``__module__`` / ``__qualname__`` from the render function they
322
+ wrap, so the reconciler's stored ``element.type`` carries the
323
+ information needed to re-resolve after a module reload.
324
+
325
+ Args:
326
+ old_fn: The function captured in an
327
+ [`Element`][pythonnative.Element]'s ``type`` slot.
328
+
329
+ Returns:
330
+ The reloaded module's matching function, ``None`` if no
331
+ replacement was found, or the original function itself
332
+ when the module has not been reloaded (so callers can
333
+ skip the swap).
334
+ """
335
+ module_name = getattr(old_fn, "__module__", None)
336
+ qualname = getattr(old_fn, "__qualname__", None) or getattr(old_fn, "__name__", None)
337
+ if not module_name or not qualname:
338
+ return None
339
+ if "<locals>" in qualname:
340
+ return None # nested functions are not addressable from the module surface
341
+
342
+ module = sys.modules.get(module_name)
343
+ if module is None:
344
+ return None
345
+
346
+ obj: Any = module
347
+ for part in qualname.split("."):
348
+ obj = getattr(obj, part, None)
349
+ if obj is None:
350
+ return None
351
+
352
+ if obj is old_fn:
353
+ return None
354
+ return obj
355
+
356
+ @staticmethod
357
+ def build_replacement_map(reconciler: Any, reloaded_modules: Iterable[str]) -> Dict[Any, Any]:
358
+ """Compute ``{old_function: new_function}`` for one tree.
359
+
360
+ The reconciler's stored tree references the *pre-reload*
361
+ component functions through ``VNode.element.type``. This
362
+ method walks the tree, collects every callable type whose
363
+ ``__module__`` was just reloaded, and asks
364
+ [`find_replacement_function`][pythonnative.hot_reload.ModuleReloader.find_replacement_function]
365
+ for its successor.
366
+
367
+ Args:
368
+ reconciler: The reconciler whose mounted ``root`` should be inspected.
369
+ reloaded_modules: Set of module names that were just
370
+ reloaded (only callables from these modules are
371
+ considered).
372
+
373
+ Returns:
374
+ A mapping suitable for passing to
375
+ [`swap_components_in_tree`][pythonnative.hot_reload.ModuleReloader.swap_components_in_tree].
376
+ """
377
+ modules: Set[str] = {m for m in reloaded_modules if m}
378
+ if not modules or reconciler is None or getattr(reconciler, "root", None) is None:
379
+ return {}
380
+
381
+ seen: Set[int] = set()
382
+ mapping: Dict[Any, Any] = {}
383
+
384
+ def visit(vnode: Any) -> None:
385
+ if vnode is None:
386
+ return
387
+ elem = getattr(vnode, "element", None)
388
+ if elem is not None and callable(elem.type):
389
+ fn = elem.type
390
+ fn_id = id(fn)
391
+ if fn_id not in seen:
392
+ seen.add(fn_id)
393
+ if getattr(fn, "__module__", None) in modules:
394
+ replacement = ModuleReloader.find_replacement_function(fn)
395
+ if replacement is not None and replacement is not fn:
396
+ mapping[fn] = replacement
397
+ for child in getattr(vnode, "children", []) or []:
398
+ visit(child)
399
+
400
+ visit(reconciler.root)
401
+ return mapping
402
+
403
+ @staticmethod
404
+ def swap_components_in_tree(reconciler: Any, replacement_map: Dict[Any, Any]) -> int:
405
+ """Apply a ``{old: new}`` map to every node in the reconciler tree.
406
+
407
+ Mutates ``vnode.element.type`` directly so the NEXT diff sees
408
+ identical types and reuses VNodes (preserving hook state).
409
+ The element lists stored on ``vnode.rendered`` are rewritten
410
+ too because the reconciler reads from them when comparing keys
411
+ across renders.
412
+
413
+ Returns:
414
+ The number of element type references that were rewritten.
415
+ """
416
+ if not replacement_map or reconciler is None or getattr(reconciler, "root", None) is None:
417
+ return 0
418
+
419
+ rewrites = 0
420
+
421
+ def rewrite_element_tree(element: Any) -> None:
422
+ nonlocal rewrites
423
+ if element is None:
424
+ return
425
+ new_type = replacement_map.get(element.type)
426
+ if new_type is not None:
427
+ element.type = new_type
428
+ rewrites += 1
429
+ for child in element.children or []:
430
+ rewrite_element_tree(child)
431
+
432
+ def visit(vnode: Any) -> None:
433
+ if vnode is None:
434
+ return
435
+ if getattr(vnode, "element", None) is not None:
436
+ rewrite_element_tree(vnode.element)
437
+ rendered = getattr(vnode, "rendered", None)
438
+ if isinstance(rendered, (list, tuple)):
439
+ for rendered_el in rendered:
440
+ rewrite_element_tree(rendered_el)
441
+ elif rendered is not None:
442
+ rewrite_element_tree(rendered)
443
+ for child in getattr(vnode, "children", []) or []:
444
+ visit(child)
445
+
446
+ visit(reconciler.root)
447
+ return rewrites
448
+
449
+ @staticmethod
450
+ def refresh_in_place(reconciler: Any, reloaded_modules: Iterable[str]) -> bool:
451
+ """Try a state-preserving Fast Refresh for one reconciler.
452
+
453
+ Returns:
454
+ ``True`` if any component function was replaced (callers
455
+ should then trigger a re-render). ``False`` means the
456
+ tree already references the latest functions (or has no
457
+ nodes from the reloaded modules at all).
458
+ """
459
+ replacement_map = ModuleReloader.build_replacement_map(reconciler, reloaded_modules)
460
+ if not replacement_map:
461
+ return False
462
+ compatible = {
463
+ old: new
464
+ for old, new in replacement_map.items()
465
+ if getattr(old, "refresh_signature", None) is not None
466
+ and old.refresh_signature == getattr(new, "refresh_signature", None)
467
+ }
468
+ ModuleReloader.swap_components_in_tree(reconciler, compatible)
469
+ # Incompatible types remain old in the mounted tree. The next ordinary
470
+ # diff remounts those instances when their parent renders the new type.
471
+ return True
472
+
473
+
474
+ # ======================================================================
475
+ # Process-wide reload
476
+ # ======================================================================
477
+
478
+
479
+ @dataclass
480
+ class ReloadResult:
481
+ """What [`apply_reload`][pythonnative.hot_reload.apply_reload] did.
482
+
483
+ Attributes:
484
+ requested: Modules the caller reported as changed.
485
+ reloaded: Modules actually re-executed (in reload order).
486
+ mode: ``"fast_refresh"`` when every host refreshed in place,
487
+ ``"remount"`` when at least one fell back to a full remount,
488
+ ``"error"`` when a host hit an exception (shown in its
489
+ RedBox), or ``"none"`` when nothing could be reloaded.
490
+ error: The import error text when a changed module failed to
491
+ execute (the previous module stays in ``sys.modules``).
492
+ hosts: Number of hosts refreshed.
493
+ """
494
+
495
+ requested: List[str] = field(default_factory=list)
496
+ reloaded: List[str] = field(default_factory=list)
497
+ mode: str = "none"
498
+ error: Optional[str] = None
499
+ hosts: int = 0
500
+
501
+
502
+ def apply_reload(changed_modules: Sequence[str], hosts: Optional[Sequence[Any]] = None) -> ReloadResult:
503
+ """Reload ``changed_modules`` once and refresh every mounted screen.
504
+
505
+ Args:
506
+ changed_modules: Dotted module names whose source changed.
507
+ hosts: Screen hosts to refresh; defaults to every live host on
508
+ the current platform (``pythonnative.hosts.live_hosts``).
509
+
510
+ Returns:
511
+ A [`ReloadResult`][pythonnative.hot_reload.ReloadResult].
512
+ """
513
+ import traceback
514
+
515
+ from . import diagnostics
516
+
517
+ if hosts is None:
518
+ from .hosts import live_hosts
519
+
520
+ hosts = list(live_hosts())
521
+ result = ReloadResult(requested=[m for m in changed_modules if m])
522
+ if not result.requested or not hosts:
523
+ # Nothing changed, or nothing is mounted yet (the files landed
524
+ # before the first screen, and the first import will read them).
525
+ return result
526
+ entry = hosts[0].component_path
527
+ # A changed module that was never imported needs no re-execution;
528
+ # re-running the entry module (always last) imports it if it's used.
529
+ imported = [m for m in result.requested if m in sys.modules]
530
+ # Instances of application classes can live in memo or ref slots. Replacing
531
+ # their defining module requires new instances, even if hooks didn't move.
532
+ from .component import Component
533
+
534
+ remount = any(
535
+ (isinstance(value, type) or callable(value))
536
+ and not isinstance(value, Component)
537
+ and getattr(value, "__module__", None) == name
538
+ and not key.startswith(("_", "use_"))
539
+ for name in imported
540
+ for key, value in vars(sys.modules[name]).items()
541
+ )
542
+ targets = ModuleReloader.expand_reload_targets(imported, entry)
543
+
544
+ # Changed modules are reloaded strictly so a syntax error is reported
545
+ # with its traceback; the dependents (which didn't change) reload
546
+ # leniently, since a failure there is almost always caused by the
547
+ # same error and would only repeat it.
548
+ reloaded: List[str] = []
549
+ for name in targets:
550
+ if name in imported:
551
+ try:
552
+ ModuleReloader.reload_module_strict(name)
553
+ except Exception as exc:
554
+ result.error = traceback.format_exc()
555
+ for host in hosts:
556
+ if diagnostics.is_dev():
557
+ host.show_redbox(exc, phase=f"reloading {name}")
558
+ result.mode = "error"
559
+ return result
560
+ reloaded.append(name)
561
+ elif ModuleReloader.reload_module(name):
562
+ reloaded.append(name)
563
+ result.reloaded = reloaded
564
+ if not reloaded:
565
+ return result
566
+
567
+ modes: List[str] = []
568
+ for host in hosts:
569
+ try:
570
+ modes.append(host.refresh(reloaded, force_remount=True) if remount else host.refresh(reloaded))
571
+ except Exception as exc:
572
+ result.error = traceback.format_exc()
573
+ if diagnostics.is_dev():
574
+ host.show_redbox(exc, phase="hot reload")
575
+ modes.append("error")
576
+ result.hosts = len(hosts)
577
+ if "error" in modes:
578
+ result.mode = "error"
579
+ elif "remount" in modes:
580
+ result.mode = "remount"
581
+ elif "fast_refresh" in modes:
582
+ result.mode = "fast_refresh"
583
+ else:
584
+ result.mode = "fast_refresh"
585
+ return result