pythonnative 0.40.0__cp314-cp314-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.cp314-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,149 @@
1
+ """Screen definitions and the typed options a screen accepts."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ from typing import Any, Callable, Dict, Literal, Mapping, Optional, TypedDict, Union
7
+
8
+ if sys.version_info >= (3, 11):
9
+ from typing import Unpack
10
+ else: # pragma: no cover
11
+ from typing_extensions import Unpack
12
+
13
+ from ..element import Element
14
+
15
+ __all__ = ["HeaderSlot", "ScreenDef", "ScreenOptions", "Unpack"]
16
+
17
+ HeaderSlot = Union[Element, Callable[[], Optional[Element]], None]
18
+ """An element (or zero-arg factory) rendered into a header slot."""
19
+
20
+
21
+ class ScreenOptions(TypedDict, total=False):
22
+ """Per-screen options accepted by ``Screen(...)`` and ``nav.set_options(...)``.
23
+
24
+ All keys are optional. Navigators ignore keys they don't use; the
25
+ native host applies the header keys it can (see the platform notes
26
+ on each key).
27
+
28
+ Attributes:
29
+ title: Screen title. Stack navigators show it in the native
30
+ navigation bar; tab and drawer navigators use it as the item
31
+ label.
32
+ header_shown: Whether the native navigation bar is visible for
33
+ this screen (default ``True``).
34
+ header_large_title: iOS: use a large title that collapses on
35
+ scroll. Ignored elsewhere.
36
+ header_back_title: iOS: label of the back button shown on the
37
+ *next* screen when it navigates back to this one. Ignored
38
+ elsewhere.
39
+ header_back_visible: Whether the back button is shown
40
+ (default ``True``).
41
+ header_left: Element (or factory) rendered at the leading edge
42
+ of the navigation bar. Rendered by PythonNative into the bar
43
+ on iOS; ignored on Android and in the browser preview today.
44
+ header_right: Element (or factory) rendered at the trailing edge
45
+ of the navigation bar (same platform notes as
46
+ ``header_left``).
47
+ header_tint_color: Color of the bar's buttons and back chevron.
48
+ header_style: Style dict for the bar itself; ``background_color``
49
+ is honored on every platform that draws a bar.
50
+ header_title_style: Style dict for the title label
51
+ (``color``, ``font_size``, ``bold``).
52
+ presentation: ``"card"`` (default) pushes; ``"modal"`` presents
53
+ the screen as a sheet on iOS and as a full-screen push
54
+ elsewhere.
55
+ gesture_enabled: Whether the interactive back gesture (iOS
56
+ swipe) can pop this screen (default ``True``).
57
+ animation: Transition to use when the screen is pushed:
58
+ ``"default"``, ``"none"``, ``"fade"``, ``"slide_from_right"``,
59
+ ``"slide_from_bottom"``. Only ``"none"`` versus animated is
60
+ distinguished on iOS and Android today.
61
+ tab_bar_icon: Native system icon identifier for tab items. A
62
+ string is used on every platform; a dict like
63
+ ``{"ios": "house.fill", "android": "ic_menu_home"}`` selects
64
+ per platform (SF Symbols on iOS, ``android.R.drawable.<name>``
65
+ on Android).
66
+ tab_bar_badge: Badge text or count shown on the tab item.
67
+ tab_bar_label: Label used for the tab item when it should differ
68
+ from ``title``.
69
+ lazy: Tab and drawer navigators only: mount the screen the first
70
+ time it's focused (default ``True``) instead of at
71
+ navigator mount.
72
+ unmount_on_blur: Tab and drawer navigators only: unmount the
73
+ screen when it loses focus instead of keeping it alive
74
+ hidden (default ``False``).
75
+ """
76
+
77
+ title: str
78
+ header_shown: bool
79
+ header_large_title: bool
80
+ header_back_title: str
81
+ header_back_visible: bool
82
+ header_left: HeaderSlot
83
+ header_right: HeaderSlot
84
+ header_tint_color: str
85
+ header_style: Dict[str, Any]
86
+ header_title_style: Dict[str, Any]
87
+ presentation: Literal["card", "modal"]
88
+ gesture_enabled: bool
89
+ animation: Literal["default", "none", "fade", "slide_from_right", "slide_from_bottom"]
90
+ tab_bar_icon: Union[str, Dict[str, str]]
91
+ tab_bar_badge: Union[str, int]
92
+ tab_bar_label: str
93
+ lazy: bool
94
+ unmount_on_blur: bool
95
+
96
+
97
+ class ScreenDef:
98
+ """Configuration for one screen inside a navigator.
99
+
100
+ Created by ``Navigator.Screen(name, component, **options)``.
101
+
102
+ Attributes:
103
+ name: Route name used by ``nav.navigate(name)``.
104
+ component: The ``@component`` rendered when this screen is
105
+ active. Receives no props; read params with
106
+ [`use_route`][pythonnative.use_route].
107
+ options: Static [`ScreenOptions`][pythonnative.ScreenOptions]
108
+ for the screen. May be a callable ``(route) -> options`` to
109
+ derive options from the route's params.
110
+ initial_params: Params merged under any params supplied by
111
+ ``navigate`` when this screen is first shown.
112
+ """
113
+
114
+ __slots__ = ("name", "component", "options", "initial_params")
115
+
116
+ def __init__(
117
+ self,
118
+ name: str,
119
+ component: Callable[[], Any],
120
+ *,
121
+ options: Union[ScreenOptions, Callable[[Any], ScreenOptions], None] = None,
122
+ initial_params: Optional[Mapping[str, Any]] = None,
123
+ **option_kwargs: Unpack[ScreenOptions],
124
+ ) -> None:
125
+ if not name or not isinstance(name, str):
126
+ raise TypeError("Screen name must be a non-empty string")
127
+ if not callable(component):
128
+ raise TypeError(f"Screen {name!r}: component must be a @component, got {component!r}")
129
+ self.name = name
130
+ self.component = component
131
+ if callable(options):
132
+ if option_kwargs:
133
+ raise TypeError("Pass either a callable `options` or keyword options, not both")
134
+ self.options: Union[ScreenOptions, Callable[[Any], ScreenOptions]] = options
135
+ else:
136
+ merged: Dict[str, Any] = dict(options or {})
137
+ merged.update(option_kwargs)
138
+ self.options = merged # type: ignore[assignment]
139
+ self.initial_params: Dict[str, Any] = dict(initial_params or {})
140
+
141
+ def resolve_options(self, route: Any) -> Dict[str, Any]:
142
+ """Return the static options, evaluating a callable ``options`` for ``route``."""
143
+ opts = self.options
144
+ if callable(opts):
145
+ return dict(opts(route) or {})
146
+ return dict(opts)
147
+
148
+ def __repr__(self) -> str:
149
+ return f"Screen({self.name!r})"
@@ -0,0 +1,269 @@
1
+ """Immutable navigation state and the pure operations on it.
2
+
3
+ A navigator's state is a [`NavigationState`][pythonnative.navigation.NavigationState]:
4
+ an ordered tuple of [`Route`][pythonnative.navigation.Route] entries
5
+ plus the index of the active one. Stack navigators treat the tuple as
6
+ a history (the active route is always the last); tab and drawer
7
+ navigators keep one route per screen and move the index.
8
+
9
+ Every operation returns a new state, so navigators can store the state
10
+ in ``use_state`` and diff it like any other value. States serialize to
11
+ plain dicts (``to_dict`` / ``from_dict``) so a native host can hand a
12
+ pushed screen the full history it belongs to.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import itertools
18
+ from typing import Any, Dict, Iterable, Iterator, Mapping, Optional, Sequence, Tuple, cast
19
+
20
+ __all__ = ["NavigationState", "Route", "RouteParams"]
21
+
22
+ type RouteParams = Mapping[str, Any]
23
+ """Bound for the ``P`` type parameter of [`Route`][pythonnative.navigation.Route].
24
+
25
+ Declare a screen's params as a ``TypedDict`` and read them with
26
+ ``use_route(MyParams)`` for a fully typed ``route.params``.
27
+ """
28
+
29
+ _route_keys = itertools.count(1)
30
+
31
+
32
+ def _new_key(name: str) -> str:
33
+ return f"{name}-{next(_route_keys)}"
34
+
35
+
36
+ class Route[P: RouteParams = Dict[str, Any]]:
37
+ """One entry in a navigator's state.
38
+
39
+ ``Route`` is generic in its params type. Bare ``Route`` is
40
+ ``Route[dict[str, Any]]``; pass a ``TypedDict`` to
41
+ [`use_route`][pythonnative.use_route] to get ``Route[MyParams]``
42
+ with a typed ``params`` attribute:
43
+
44
+ ```python
45
+ class DetailParams(TypedDict):
46
+ id: int
47
+
48
+ route = pn.use_route(DetailParams)
49
+ route.params["id"] # int
50
+ ```
51
+
52
+ Attributes:
53
+ name: The screen name this route renders.
54
+ params: Parameters passed to the screen (read with
55
+ [`use_route`][pythonnative.use_route]). Always a plain
56
+ ``dict`` at runtime.
57
+ key: Stable identity for this particular visit to the screen,
58
+ unique per process. Two pushes of the same screen have
59
+ different keys, so their component state never mixes.
60
+ state: Seed state for a navigator rendered by this screen
61
+ (set by ``navigate("Tabs", screen="Profile")`` and by deep
62
+ links). ``None`` for ordinary screens.
63
+ """
64
+
65
+ __slots__ = ("name", "params", "key", "state")
66
+
67
+ def __init__(
68
+ self,
69
+ name: str,
70
+ params: Optional[Mapping[str, Any]] = None,
71
+ key: Optional[str] = None,
72
+ state: Optional["NavigationState"] = None,
73
+ ) -> None:
74
+ self.name = name
75
+ self.params: P = cast(P, dict(params or {}))
76
+ self.key = key or _new_key(name)
77
+ self.state = state
78
+
79
+ def with_params(self, params: Mapping[str, Any], *, merge: bool = True) -> "Route[P]":
80
+ """Return a copy carrying ``params`` (merged over the current ones by default)."""
81
+ merged = {**self.params, **params} if merge else dict(params)
82
+ return Route(self.name, merged, key=self.key, state=self.state)
83
+
84
+ def with_state(self, state: Optional["NavigationState"]) -> "Route[P]":
85
+ """Return a copy carrying a nested navigator seed ``state``."""
86
+ return Route(self.name, self.params, key=self.key, state=state)
87
+
88
+ def to_dict(self) -> Dict[str, Any]:
89
+ """Serialize to a plain dict (``name``, ``params``, ``key``, and ``state`` when nested)."""
90
+ out: Dict[str, Any] = {"name": self.name, "params": dict(self.params), "key": self.key}
91
+ if self.state is not None:
92
+ out["state"] = self.state.to_dict()
93
+ return out
94
+
95
+ @classmethod
96
+ def from_dict(cls, data: Mapping[str, Any]) -> "Route[Dict[str, Any]]":
97
+ """Rebuild a route from ``to_dict`` output, keeping its ``key`` so component state carries over."""
98
+ params = data.get("params")
99
+ nested = data.get("state")
100
+ return Route(
101
+ str(data["name"]),
102
+ params if isinstance(params, Mapping) else {},
103
+ key=data.get("key"),
104
+ state=NavigationState.from_dict(nested) if isinstance(nested, Mapping) and nested.get("routes") else None,
105
+ )
106
+
107
+ def __eq__(self, other: object) -> bool:
108
+ if not isinstance(other, Route):
109
+ return NotImplemented
110
+ return (
111
+ self.name == other.name
112
+ and self.params == other.params
113
+ and self.key == other.key
114
+ and self.state == other.state
115
+ )
116
+
117
+ def __hash__(self) -> int:
118
+ return hash(self.key)
119
+
120
+ def __repr__(self) -> str:
121
+ return f"Route({self.name!r}, params={self.params!r}, key={self.key!r})"
122
+
123
+
124
+ class NavigationState:
125
+ """Immutable ordered routes plus the active index.
126
+
127
+ Attributes:
128
+ routes: The routes, oldest first.
129
+ index: Position of the active route in ``routes``.
130
+ """
131
+
132
+ __slots__ = ("routes", "index")
133
+
134
+ def __init__(self, routes: Iterable[Route], index: Optional[int] = None) -> None:
135
+ self.routes: Tuple[Route, ...] = tuple(routes)
136
+ if not self.routes:
137
+ raise ValueError("NavigationState needs at least one route")
138
+ self.index = len(self.routes) - 1 if index is None else index
139
+ if not 0 <= self.index < len(self.routes):
140
+ raise IndexError(f"index {self.index} out of range for {len(self.routes)} route(s)")
141
+
142
+ # ------------------------------------------------------------------
143
+ # Introspection
144
+ # ------------------------------------------------------------------
145
+
146
+ @property
147
+ def current(self) -> Route:
148
+ """The active route."""
149
+ return self.routes[self.index]
150
+
151
+ @property
152
+ def can_go_back(self) -> bool:
153
+ """Whether a route precedes the active one."""
154
+ return self.index > 0
155
+
156
+ def find(self, name: str) -> Optional[int]:
157
+ """Index of the most recent route named ``name``, or ``None``."""
158
+ for i in range(len(self.routes) - 1, -1, -1):
159
+ if self.routes[i].name == name:
160
+ return i
161
+ return None
162
+
163
+ def __len__(self) -> int:
164
+ return len(self.routes)
165
+
166
+ def __iter__(self) -> Iterator[Route]:
167
+ return iter(self.routes)
168
+
169
+ def __eq__(self, other: object) -> bool:
170
+ if not isinstance(other, NavigationState):
171
+ return NotImplemented
172
+ return self.routes == other.routes and self.index == other.index
173
+
174
+ def __repr__(self) -> str:
175
+ names = [r.name for r in self.routes]
176
+ return f"NavigationState({names!r}, index={self.index})"
177
+
178
+ # ------------------------------------------------------------------
179
+ # Stack operations (history semantics)
180
+ # ------------------------------------------------------------------
181
+
182
+ def push(
183
+ self, name: str, params: Optional[Mapping[str, Any]] = None, state: Optional["NavigationState"] = None
184
+ ) -> "NavigationState":
185
+ """Append a new route and make it active (drops any forward entries)."""
186
+ kept = self.routes[: self.index + 1]
187
+ return NavigationState(kept + (Route(name, params, state=state),))
188
+
189
+ def pop(self, count: int = 1) -> "NavigationState":
190
+ """Remove up to ``count`` routes from the end (never below one)."""
191
+ keep = max(1, len(self.routes) - max(0, count))
192
+ return NavigationState(self.routes[:keep])
193
+
194
+ def pop_to_top(self) -> "NavigationState":
195
+ """Keep only the first route."""
196
+ return NavigationState(self.routes[:1])
197
+
198
+ def pop_to(
199
+ self, name: str, params: Optional[Mapping[str, Any]] = None, state: Optional["NavigationState"] = None
200
+ ) -> "NavigationState":
201
+ """Pop back to the most recent route named ``name``, merging ``params`` into it."""
202
+ idx = self.find(name)
203
+ if idx is None:
204
+ raise KeyError(name)
205
+ routes = list(self.routes[: idx + 1])
206
+ if params:
207
+ routes[-1] = routes[-1].with_params(params)
208
+ if state is not None:
209
+ routes[-1] = routes[-1].with_state(state)
210
+ return NavigationState(routes)
211
+
212
+ def replace(
213
+ self, name: str, params: Optional[Mapping[str, Any]] = None, state: Optional["NavigationState"] = None
214
+ ) -> "NavigationState":
215
+ """Swap the active route for a fresh one (new key, so state resets)."""
216
+ routes = list(self.routes)
217
+ routes[self.index] = Route(name, params, state=state)
218
+ return NavigationState(routes, self.index)
219
+
220
+ def navigate(
221
+ self, name: str, params: Optional[Mapping[str, Any]] = None, state: Optional["NavigationState"] = None
222
+ ) -> "NavigationState":
223
+ """Go to ``name``: pop back to it if it's in the history, else push it."""
224
+ if self.find(name) is not None:
225
+ return self.pop_to(name, params, state)
226
+ return self.push(name, params, state)
227
+
228
+ # ------------------------------------------------------------------
229
+ # Tab / drawer operations (one route per screen)
230
+ # ------------------------------------------------------------------
231
+
232
+ def jump_to(
233
+ self, name: str, params: Optional[Mapping[str, Any]] = None, state: Optional["NavigationState"] = None
234
+ ) -> "NavigationState":
235
+ """Activate the route named ``name`` in place, merging ``params``."""
236
+ idx = self.find(name)
237
+ if idx is None:
238
+ raise KeyError(name)
239
+ routes = list(self.routes)
240
+ if params:
241
+ routes[idx] = routes[idx].with_params(params)
242
+ if state is not None:
243
+ routes[idx] = routes[idx].with_state(state)
244
+ return NavigationState(routes, idx)
245
+
246
+ # ------------------------------------------------------------------
247
+ # Shared
248
+ # ------------------------------------------------------------------
249
+
250
+ def set_params(self, params: Mapping[str, Any]) -> "NavigationState":
251
+ """Merge ``params`` into the active route."""
252
+ routes = list(self.routes)
253
+ routes[self.index] = routes[self.index].with_params(params)
254
+ return NavigationState(routes, self.index)
255
+
256
+ def reset(self, routes: Sequence[Route], index: Optional[int] = None) -> "NavigationState":
257
+ """Return a state holding exactly ``routes`` with ``index`` active (the last route by default)."""
258
+ return NavigationState(routes, index)
259
+
260
+ def to_dict(self) -> Dict[str, Any]:
261
+ """Serialize to a plain dict with ``routes`` (each via ``Route.to_dict``) and ``index``."""
262
+ return {"routes": [r.to_dict() for r in self.routes], "index": self.index}
263
+
264
+ @classmethod
265
+ def from_dict(cls, data: Mapping[str, Any]) -> "NavigationState":
266
+ """Rebuild a state from ``to_dict`` output; a missing ``index`` activates the last route."""
267
+ routes = [Route.from_dict(r) for r in data.get("routes", [])]
268
+ index = data.get("index")
269
+ return cls(routes, int(index) if isinstance(index, int) else None)
pythonnative/net.py ADDED
@@ -0,0 +1,248 @@
1
+ """Async HTTP client (``pn.fetch``).
2
+
3
+ A small, dependency-free coroutine wrapper around
4
+ :mod:`urllib.request`. Operates on bytes internally and exposes a
5
+ :class:`Response` with `text()`, `json()`, and `bytes` accessors.
6
+
7
+ The implementation is deliberately minimal; it covers the
8
+ "call a JSON API" path that's overwhelmingly the use case for mobile
9
+ apps. For streaming, multipart uploads, or HTTP/2, integrate
10
+ ``httpx`` / ``aiohttp`` directly; this module won't try to compete.
11
+
12
+ Example:
13
+ ```python
14
+ import pythonnative as pn
15
+
16
+
17
+ async def load_user(user_id):
18
+ resp = await pn.fetch(
19
+ f"https://api.example.com/users/{user_id}",
20
+ headers={"Accept": "application/json"},
21
+ )
22
+ resp.raise_for_status()
23
+ return resp.json()
24
+ ```
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import asyncio
30
+ import json
31
+ import ssl
32
+ import urllib.error
33
+ import urllib.parse
34
+ import urllib.request
35
+ from dataclasses import dataclass, field
36
+ from typing import Any, Dict, Mapping, Optional, Union
37
+
38
+ # A package-level SSL context lets callers (or tests) override TLS
39
+ # verification globally without monkeypatching the stdlib. Defaults
40
+ # to the system trust store.
41
+ _default_ssl_context: Optional[ssl.SSLContext] = None
42
+
43
+
44
+ def set_default_ssl_context(context: Optional[ssl.SSLContext]) -> None:
45
+ """Override the SSL context used by [`fetch`][pythonnative.fetch].
46
+
47
+ ``None`` (the default) means ``urllib`` builds a context from
48
+ the system trust store. Tests can pass an unverified context.
49
+ """
50
+ global _default_ssl_context
51
+ _default_ssl_context = context
52
+
53
+
54
+ # ======================================================================
55
+ # Response object
56
+ # ======================================================================
57
+
58
+
59
+ @dataclass
60
+ class Response:
61
+ """The result of a [`fetch`][pythonnative.fetch] call.
62
+
63
+ Attributes:
64
+ status: HTTP status code (e.g. ``200``).
65
+ url: Final URL after any redirects.
66
+ headers: Response headers, case-insensitive.
67
+ content: Raw response body.
68
+ """
69
+
70
+ status: int
71
+ url: str
72
+ headers: Mapping[str, str] = field(default_factory=dict)
73
+ content: bytes = b""
74
+
75
+ @property
76
+ def ok(self) -> bool:
77
+ """``True`` if the status is in the 2xx range."""
78
+ return 200 <= self.status < 300
79
+
80
+ def __repr__(self) -> str:
81
+ cls = type(self).__name__
82
+ return f"{cls}(status={self.status}, url={self.url!r}, ok={self.ok}, content={len(self.content)} bytes)"
83
+
84
+ def text(self, encoding: Optional[str] = None) -> str:
85
+ """Decode ``content`` to ``str``.
86
+
87
+ Args:
88
+ encoding: Optional override; defaults to the
89
+ ``charset`` parameter of ``Content-Type`` (or UTF-8).
90
+ """
91
+ enc = encoding or _charset_from_headers(self.headers) or "utf-8"
92
+ try:
93
+ return self.content.decode(enc)
94
+ except (UnicodeDecodeError, LookupError):
95
+ return self.content.decode("utf-8", errors="replace")
96
+
97
+ def json(self) -> Any:
98
+ """Parse the response body as JSON.
99
+
100
+ Raises:
101
+ json.JSONDecodeError: If the body isn't valid JSON.
102
+ """
103
+ return json.loads(self.text())
104
+
105
+ def raise_for_status(self) -> None:
106
+ """Raise [`HTTPError`][pythonnative.net.HTTPError] for non-2xx responses."""
107
+ if not self.ok:
108
+ raise HTTPError(self.status, self.url, self.text())
109
+
110
+
111
+ class HTTPError(Exception):
112
+ """Raised by [`Response.raise_for_status`][pythonnative.net.Response.raise_for_status]."""
113
+
114
+ def __init__(self, status: int, url: str, body: str) -> None:
115
+ super().__init__(f"HTTP {status} from {url}: {body[:200]}")
116
+ self.status = status
117
+ self.url = url
118
+ self.body = body
119
+
120
+
121
+ def _charset_from_headers(headers: Mapping[str, str]) -> Optional[str]:
122
+ ctype = headers.get("Content-Type") or headers.get("content-type")
123
+ if not ctype:
124
+ return None
125
+ for part in ctype.split(";"):
126
+ part = part.strip()
127
+ if part.lower().startswith("charset="):
128
+ return part[len("charset=") :].strip().strip('"')
129
+ return None
130
+
131
+
132
+ # ======================================================================
133
+ # Public fetch coroutine
134
+ # ======================================================================
135
+
136
+
137
+ async def fetch(
138
+ url: str,
139
+ *,
140
+ method: str = "GET",
141
+ headers: Optional[Mapping[str, str]] = None,
142
+ body: Union[bytes, str, Mapping[str, Any], None] = None,
143
+ params: Optional[Mapping[str, Any]] = None,
144
+ timeout: float = 30.0,
145
+ ) -> Response:
146
+ """Make an HTTP request and return a [`Response`][pythonnative.net.Response].
147
+
148
+ Args:
149
+ url: Target URL. Relative URLs are not supported.
150
+ method: HTTP method (``"GET"``, ``"POST"``, ``"PUT"`` …).
151
+ headers: Optional request headers.
152
+ body: Request body. ``bytes`` are sent as-is; ``str`` is
153
+ UTF-8 encoded; ``dict`` is JSON-encoded with a
154
+ ``Content-Type: application/json`` header added (unless
155
+ already supplied).
156
+ params: Optional mapping of query-string parameters appended
157
+ to ``url`` (urlencoded).
158
+ timeout: Seconds to wait for the response (excluding the
159
+ time spent on DNS / connect).
160
+
161
+ Returns:
162
+ A [`Response`][pythonnative.net.Response].
163
+
164
+ Raises:
165
+ TimeoutError: If the request doesn't complete within
166
+ ``timeout`` seconds.
167
+ OSError: For network errors (DNS failure, connection refused,
168
+ etc.), re-raised from ``urllib``.
169
+
170
+ Example:
171
+ ```python
172
+ resp = await pn.fetch(
173
+ "https://api.example.com/posts",
174
+ method="POST",
175
+ body={"title": "Hello"},
176
+ )
177
+ resp.raise_for_status()
178
+ ```
179
+ """
180
+ request = _build_request(url=url, method=method, headers=headers, body=body, params=params)
181
+ return await asyncio.to_thread(_dispatch_request, request, timeout)
182
+
183
+
184
+ def _build_request(
185
+ *,
186
+ url: str,
187
+ method: str,
188
+ headers: Optional[Mapping[str, str]],
189
+ body: Union[bytes, str, Mapping[str, Any], None],
190
+ params: Optional[Mapping[str, Any]],
191
+ ) -> urllib.request.Request:
192
+ if params:
193
+ sep = "&" if "?" in url else "?"
194
+ url = url + sep + urllib.parse.urlencode(params, doseq=True)
195
+
196
+ header_dict: Dict[str, str] = dict(headers or {})
197
+ payload: Optional[bytes] = None
198
+ if body is None:
199
+ pass
200
+ elif isinstance(body, (bytes, bytearray)):
201
+ payload = bytes(body)
202
+ elif isinstance(body, str):
203
+ payload = body.encode("utf-8")
204
+ elif isinstance(body, Mapping):
205
+ payload = json.dumps(body, default=str).encode("utf-8")
206
+ header_dict.setdefault("Content-Type", "application/json")
207
+ else:
208
+ raise TypeError(f"Unsupported body type: {type(body)!r}")
209
+
210
+ return urllib.request.Request(
211
+ url=url,
212
+ data=payload,
213
+ method=method.upper(),
214
+ headers=header_dict,
215
+ )
216
+
217
+
218
+ def _dispatch_request(request: urllib.request.Request, timeout: float) -> Response:
219
+ context = _default_ssl_context
220
+ try:
221
+ with urllib.request.urlopen(request, timeout=timeout, context=context) as resp:
222
+ content = resp.read()
223
+ return Response(
224
+ status=resp.status,
225
+ url=resp.geturl(),
226
+ headers={k: v for k, v in resp.headers.items()},
227
+ content=content,
228
+ )
229
+ except urllib.error.HTTPError as exc:
230
+ # HTTPError is itself a response object; propagate the body
231
+ # so callers can inspect it before deciding to raise.
232
+ body = exc.read() if hasattr(exc, "read") else b""
233
+ return Response(
234
+ status=exc.code,
235
+ url=getattr(exc, "url", request.full_url),
236
+ headers={k: v for k, v in (exc.headers or {}).items()},
237
+ content=body,
238
+ )
239
+ except urllib.error.URLError as exc:
240
+ # Convert urllib's TimeoutError wrapper into a plain TimeoutError
241
+ # so callers get a recognisable exception type.
242
+ reason = getattr(exc, "reason", exc)
243
+ if isinstance(reason, TimeoutError):
244
+ raise reason
245
+ raise OSError(str(reason)) from exc
246
+
247
+
248
+ __all__ = ["fetch", "Response", "HTTPError", "set_default_ssl_context"]