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,237 @@
1
+ import UIKit
2
+
3
+ /// Hosts one PythonNative screen.
4
+ ///
5
+ /// Lifecycle is forwarded to Python as `callback("host", screenId,
6
+ /// event, payload)`: `create` (viewDidLoad), `start`, `layout`,
7
+ /// `resume`, `pause`, `stop`, `destroy`, `save_state`, and
8
+ /// `restore_state`. Python attaches its root view with
9
+ /// `Host.attach_root`; the controller keeps that view below the top
10
+ /// safe-area inset and full-bleed at the bottom. Fast Refresh needs no
11
+ /// help from here: the Python dev client reloads modules and refreshes
12
+ /// the mounted screens itself.
13
+ open class PNViewController: UIViewController {
14
+ /// Dotted path of the screen component (`nil` = the app entry module).
15
+ public var requestedScreenPath: String?
16
+ /// JSON-encoded screen args, or `nil`.
17
+ public var requestedScreenArgsJSON: String?
18
+ /// State JSON to hand to Python right after `create` (from a prior `saveState`).
19
+ public var restoredStateJSON: String?
20
+ public var cachedStateJSON: String?
21
+
22
+ /// The id Python uses to address this screen.
23
+ public private(set) var screenId: Int64 = 0
24
+ /// Whether `create` has been sent (and not refused by a bootstrap error).
25
+ public private(set) var isScreenCreated = false
26
+
27
+ private(set) var rootView: UIView?
28
+ private var lastLayoutPayload: String?
29
+
30
+ /// The application host can instantiate its controller subclass dynamically.
31
+ public required override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
32
+ super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
33
+ screenId = PNScreenRegistry.shared.register(self)
34
+ }
35
+
36
+ public required init?(coder: NSCoder) {
37
+ super.init(coder: coder)
38
+ screenId = PNScreenRegistry.shared.register(self)
39
+ }
40
+
41
+ deinit {
42
+ if isScreenCreated {
43
+ PNBridge.shared.callPython(kind: "host", tag: screenId, name: "destroy", payload: "{}")
44
+ }
45
+ PNScreenRegistry.shared.unregister(screenId)
46
+ }
47
+
48
+ // MARK: - Subclass hooks
49
+
50
+ /// Called from `viewDidLoad` before the screen is created. Return
51
+ /// `false` (after showing an error) to skip creating the screen.
52
+ open func prepareRuntime() -> Bool {
53
+ true
54
+ }
55
+
56
+ /// The screen path used when `requestedScreenPath` is `nil`.
57
+ open var defaultScreenPath: String {
58
+ (Bundle.main.object(forInfoDictionaryKey: "PNEntryModule") as? String) ?? "app.main"
59
+ }
60
+
61
+ // MARK: - Lifecycle
62
+
63
+ open override func viewDidLoad() {
64
+ super.viewDidLoad()
65
+ view.backgroundColor = .systemBackground
66
+ guard prepareRuntime() else { return }
67
+ guard PNBridge.shared.hasCallback else {
68
+ showBootstrapError("The Python bridge callback is not registered.\n\nImport pythonnative.bridge after starting the interpreter.")
69
+ return
70
+ }
71
+ isScreenCreated = true
72
+ var payload: [String: Any] = [
73
+ "path": requestedScreenPath ?? defaultScreenPath,
74
+ "args": requestedScreenArgsJSON ?? NSNull(),
75
+ ]
76
+ if let restored = restoredStateJSON {
77
+ payload["restored_state"] = restored
78
+ }
79
+ PNBridge.shared.callPython(kind: "host", tag: screenId, name: "create", payload: PNJSON.encode(payload))
80
+ if let restored = restoredStateJSON {
81
+ PNBridge.shared.callPython(kind: "host", tag: screenId, name: "restore_state", payload: restored)
82
+ }
83
+ }
84
+
85
+ open override func viewWillAppear(_ animated: Bool) {
86
+ super.viewWillAppear(animated)
87
+ forward("start")
88
+ }
89
+
90
+ open override func viewDidLayoutSubviews() {
91
+ super.viewDidLayoutSubviews()
92
+ syncRootFrame()
93
+ let payload = viewportJSON()
94
+ // Every pass forwards (rotation, multitasking, keyboard), but a
95
+ // pass that changed nothing about the viewport is skipped.
96
+ if payload != lastLayoutPayload {
97
+ lastLayoutPayload = payload
98
+ forward("layout", payload)
99
+ }
100
+ }
101
+
102
+ open override func viewDidAppear(_ animated: Bool) {
103
+ super.viewDidAppear(animated)
104
+ syncRootFrame()
105
+ forward("resume", viewportJSON())
106
+ }
107
+
108
+ open override func viewWillDisappear(_ animated: Bool) {
109
+ super.viewWillDisappear(animated)
110
+ forward("pause")
111
+ }
112
+
113
+ open override func viewDidDisappear(_ animated: Bool) {
114
+ super.viewDidDisappear(animated)
115
+ forward("stop")
116
+ }
117
+
118
+ open override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
119
+ super.traitCollectionDidChange(previousTraitCollection)
120
+ if previousTraitCollection?.userInterfaceStyle != traitCollection.userInterfaceStyle {
121
+ lastLayoutPayload = nil
122
+ view.setNeedsLayout()
123
+ }
124
+ }
125
+
126
+ /// Ask Python for the screen's serialized state (`save_state`).
127
+ @discardableResult
128
+ public func saveState() -> String? {
129
+ guard isScreenCreated else { return nil }
130
+ PNBridge.shared.callPython(kind: "host", tag: screenId, name: "save_state", payload: "{}")
131
+ return cachedStateJSON
132
+ }
133
+
134
+ /// Hand previously saved state back to Python (`restore_state`).
135
+ public func restoreState(_ json: String) {
136
+ guard isScreenCreated else {
137
+ restoredStateJSON = json
138
+ return
139
+ }
140
+ PNBridge.shared.callPython(kind: "host", tag: screenId, name: "restore_state", payload: json)
141
+ }
142
+
143
+ open override var prefersStatusBarHidden: Bool { PNStatusBarState.hidden }
144
+ open override var preferredStatusBarStyle: UIStatusBarStyle { PNStatusBarState.style }
145
+ open override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { PNStatusBarState.animation }
146
+
147
+ private func forward(_ event: String, _ payload: String = "{}") {
148
+ guard isScreenCreated else { return }
149
+ PNBridge.shared.callPython(kind: "host", tag: screenId, name: event, payload: payload)
150
+ }
151
+
152
+ // MARK: - Root view
153
+
154
+ /// Attach Python's root view (`Host.attach_root`).
155
+ func attachRoot(_ root: UIView) {
156
+ if rootView !== root {
157
+ rootView?.removeFromSuperview()
158
+ }
159
+ rootView = root
160
+ root.translatesAutoresizingMaskIntoConstraints = true
161
+ root.autoresizingMask = [.flexibleWidth, .flexibleHeight]
162
+ view.addSubview(root)
163
+ syncRootFrame()
164
+ }
165
+
166
+ /// Detach the root view (`Host.detach_root`).
167
+ func detachRoot(_ root: UIView) {
168
+ if root.superview === view {
169
+ root.removeFromSuperview()
170
+ }
171
+ if rootView === root {
172
+ rootView = nil
173
+ }
174
+ }
175
+
176
+ /// The frame the root occupies: below the top inset, full-bleed at
177
+ /// the bottom so a tab bar can reach the home indicator.
178
+ public var rootFrame: CGRect {
179
+ let bounds = view.bounds
180
+ let insets = view.safeAreaInsets
181
+ let w = max(0, bounds.width - insets.left - insets.right)
182
+ let h = max(0, bounds.height - insets.top)
183
+ if w > 0, h > 0 {
184
+ return CGRect(x: insets.left, y: insets.top, width: w, height: h)
185
+ }
186
+ return bounds
187
+ }
188
+
189
+ private func syncRootFrame() {
190
+ guard let root = rootView else { return }
191
+ let frame = rootFrame
192
+ if root.frame != frame {
193
+ root.frame = frame
194
+ }
195
+ }
196
+
197
+ /// The `{"width","height","insets","color_scheme"}` payload shared by
198
+ /// `layout`, `resume`, and `Host.viewport`.
199
+ public func viewport() -> [String: Any] {
200
+ var frame = rootFrame
201
+ if frame.width <= 0 || frame.height <= 0 {
202
+ frame = PNWindow.screenBounds()
203
+ }
204
+ let insets = view.safeAreaInsets
205
+ return [
206
+ "width": Double(frame.width),
207
+ "height": Double(frame.height),
208
+ "insets": [
209
+ "top": 0.0,
210
+ "left": Double(insets.left),
211
+ "bottom": Double(insets.bottom),
212
+ "right": Double(insets.right),
213
+ ],
214
+ "color_scheme": PNWindow.colorScheme(for: view),
215
+ ]
216
+ }
217
+
218
+ func viewportJSON() -> String {
219
+ PNJSON.encode(viewport())
220
+ }
221
+
222
+ // MARK: - Bootstrap error UI
223
+
224
+ /// Replace the screen with a full-bleed error report.
225
+ public func showBootstrapError(_ message: String) {
226
+ PNLog.screens.error("\(message)")
227
+ let text = UITextView(frame: view.bounds)
228
+ text.autoresizingMask = [.flexibleWidth, .flexibleHeight]
229
+ text.isEditable = false
230
+ text.backgroundColor = UIColor(red: 0.75, green: 0.10, blue: 0.10, alpha: 1.0)
231
+ text.textColor = .white
232
+ text.font = UIFont.monospacedSystemFont(ofSize: 13, weight: .regular)
233
+ text.textContainerInset = UIEdgeInsets(top: 60, left: 16, bottom: 40, right: 16)
234
+ text.text = "PythonNative could not start\n\n\(message)"
235
+ view.addSubview(text)
236
+ }
237
+ }
@@ -0,0 +1,46 @@
1
+ import CoreLocation
2
+ import Photos
3
+ import UIKit
4
+
5
+ /// Back-deployment shims for APIs that moved or appeared after iOS 13, so
6
+ /// the kit compiles at the lowest deployment target `pn` accepts.
7
+ extension CLLocationManager {
8
+ /// `authorizationStatus` (instance, iOS 14+) with the class-method
9
+ /// fallback used on iOS 13.
10
+ var pnAuthorizationStatus: CLAuthorizationStatus {
11
+ if #available(iOS 14.0, *) {
12
+ return authorizationStatus
13
+ }
14
+ return CLLocationManager.authorizationStatus()
15
+ }
16
+ }
17
+
18
+ extension PHPhotoLibrary {
19
+ /// Read/write library access status on iOS 14+, the legacy status before.
20
+ static func pnReadWriteAuthorizationStatus() -> PHAuthorizationStatus {
21
+ if #available(iOS 14.0, *) {
22
+ return authorizationStatus(for: .readWrite)
23
+ }
24
+ return authorizationStatus()
25
+ }
26
+
27
+ /// Request read/write library access with the API available on this OS.
28
+ static func pnRequestReadWriteAuthorization(_ handler: @escaping (PHAuthorizationStatus) -> Void) {
29
+ if #available(iOS 14.0, *) {
30
+ requestAuthorization(for: .readWrite, handler: handler)
31
+ } else {
32
+ requestAuthorization(handler)
33
+ }
34
+ }
35
+ }
36
+
37
+ extension UIDatePicker {
38
+ /// Prefer the compact inline style where the OS offers it.
39
+ func pnApplyCompactStyle() {
40
+ if #available(iOS 14.0, *) {
41
+ preferredDatePickerStyle = .compact
42
+ } else if #available(iOS 13.4, *) {
43
+ preferredDatePickerStyle = .wheels
44
+ }
45
+ }
46
+ }
@@ -0,0 +1,173 @@
1
+ import Foundation
2
+
3
+ /// JSON helpers shared by the bridge, event emitters, and modules.
4
+ ///
5
+ /// Encoding tolerates values `JSONSerialization` rejects (non-finite
6
+ /// doubles, `Optional.none`, `CGFloat`, `URL`) by normalizing them
7
+ /// first; decoding turns the Python-side `"inf"` marker back into
8
+ /// `Double.infinity`.
9
+ public enum PNJSON {
10
+ /// Decode a JSON document into Foundation containers.
11
+ public static func decode(_ text: String) -> Any? {
12
+ guard let data = text.data(using: .utf8) else { return nil }
13
+ return try? JSONSerialization.jsonObject(with: data, options: [.fragmentsAllowed])
14
+ }
15
+
16
+ /// Decode a JSON object, returning an empty dictionary for anything else.
17
+ public static func decodeObject(_ text: String?) -> [String: Any] {
18
+ guard let text = text, let value = decode(text) as? [String: Any] else { return [:] }
19
+ return value
20
+ }
21
+
22
+ /// Encode any JSON-compatible value (a fragment is allowed).
23
+ public static func encode(_ value: Any?) -> String {
24
+ let normalized = normalize(value)
25
+ guard JSONSerialization.isValidJSONObject([normalized]) else { return "null" }
26
+ guard let data = try? JSONSerialization.data(withJSONObject: normalized, options: [.fragmentsAllowed]),
27
+ let text = String(data: data, encoding: .utf8)
28
+ else {
29
+ return "null"
30
+ }
31
+ return text
32
+ }
33
+
34
+ /// Replace every `"inf"` string in a decoded prop tree with `Double.infinity`.
35
+ public static func resolveInfinity(_ value: Any) -> Any {
36
+ if let string = value as? String {
37
+ return string == "inf" ? Double.infinity : string
38
+ }
39
+ if let dict = value as? [String: Any] {
40
+ var out: [String: Any] = [:]
41
+ out.reserveCapacity(dict.count)
42
+ for (key, item) in dict {
43
+ out[key] = resolveInfinity(item)
44
+ }
45
+ return out
46
+ }
47
+ if let array = value as? [Any] {
48
+ return array.map { resolveInfinity($0) }
49
+ }
50
+ return value
51
+ }
52
+
53
+ /// Convert arbitrary values to something `JSONSerialization` accepts.
54
+ static func normalize(_ value: Any?) -> Any {
55
+ guard let value = value else { return NSNull() }
56
+ switch value {
57
+ case let optional as Optional<Any>:
58
+ guard let unwrapped = optional else { return NSNull() }
59
+ return normalizeConcrete(unwrapped)
60
+ default:
61
+ return normalizeConcrete(value)
62
+ }
63
+ }
64
+
65
+ private static func normalizeConcrete(_ value: Any) -> Any {
66
+ switch value {
67
+ case is NSNull:
68
+ return value
69
+ case let bool as Bool:
70
+ return bool
71
+ case let double as Double:
72
+ return double.isFinite ? double : NSNull()
73
+ case let float as Float:
74
+ return float.isFinite ? Double(float) : NSNull()
75
+ case let cg as CGFloat:
76
+ return cg.isFinite ? Double(cg) : NSNull()
77
+ case let int as Int:
78
+ return int
79
+ case let int64 as Int64:
80
+ return int64
81
+ case let number as NSNumber:
82
+ return number
83
+ case let string as String:
84
+ return string
85
+ case let url as URL:
86
+ return url.absoluteString
87
+ case let dict as [String: Any]:
88
+ var out: [String: Any] = [:]
89
+ out.reserveCapacity(dict.count)
90
+ for (key, item) in dict {
91
+ out[key] = normalize(item)
92
+ }
93
+ return out
94
+ case let array as [Any]:
95
+ return array.map { normalize($0) }
96
+ default:
97
+ return String(describing: value)
98
+ }
99
+ }
100
+ }
101
+
102
+ /// Typed accessors for loosely typed prop dictionaries.
103
+ public enum PNProps {
104
+ /// Coerce numbers, numeric strings, and the `"inf"` marker to `Double`.
105
+ public static func double(_ value: Any?) -> Double? {
106
+ switch value {
107
+ case let d as Double: return d
108
+ case let f as Float: return Double(f)
109
+ case let cg as CGFloat: return Double(cg)
110
+ case let i as Int: return Double(i)
111
+ case let n as NSNumber: return n.doubleValue
112
+ case let s as String:
113
+ if s == "inf" { return Double.infinity }
114
+ if s == "-inf" { return -Double.infinity }
115
+ return Double(s)
116
+ default: return nil
117
+ }
118
+ }
119
+
120
+ /// Like `double(_:)` but non-finite values fall back to `fallback`.
121
+ public static func finite(_ value: Any?, _ fallback: Double = 0) -> Double {
122
+ guard let d = double(value), d.isFinite else { return fallback }
123
+ return d
124
+ }
125
+
126
+ public static func int(_ value: Any?) -> Int? {
127
+ switch value {
128
+ case let i as Int: return i
129
+ case let n as NSNumber: return n.intValue
130
+ case let d as Double: return d.isFinite ? Int(d) : nil
131
+ case let s as String: return Int(s) ?? Double(s).map { Int($0) }
132
+ default: return nil
133
+ }
134
+ }
135
+
136
+ public static func bool(_ value: Any?) -> Bool? {
137
+ switch value {
138
+ case let b as Bool: return b
139
+ case let n as NSNumber: return n.boolValue
140
+ case let s as String: return s.lowercased() == "true"
141
+ default: return nil
142
+ }
143
+ }
144
+
145
+ public static func string(_ value: Any?) -> String? {
146
+ switch value {
147
+ case nil: return nil
148
+ case is NSNull: return nil
149
+ case let s as String: return s
150
+ case let n as NSNumber: return n.stringValue
151
+ default: return String(describing: value as Any)
152
+ }
153
+ }
154
+
155
+ public static func dict(_ value: Any?) -> [String: Any]? {
156
+ value as? [String: Any]
157
+ }
158
+
159
+ public static func array(_ value: Any?) -> [Any]? {
160
+ value as? [Any]
161
+ }
162
+
163
+ /// True when the key is present, even if its value is `null`.
164
+ public static func has(_ props: [String: Any], _ key: String) -> Bool {
165
+ props.index(forKey: key) != nil
166
+ }
167
+
168
+ /// The value for `key`, treating JSON `null` as absent.
169
+ public static func value(_ props: [String: Any], _ key: String) -> Any? {
170
+ guard let v = props[key], !(v is NSNull) else { return nil }
171
+ return v
172
+ }
173
+ }
@@ -0,0 +1,47 @@
1
+ import Foundation
2
+ import os.log
3
+
4
+ /// A thin `os_log` wrapper with the subset of the `Logger` API the kit
5
+ /// uses. `Logger` itself is iOS 14+, and apps may set a deployment target
6
+ /// as low as iOS 13, so this keeps the kit buildable at the app's minimum.
7
+ struct PNLogger {
8
+ let log: OSLog
9
+
10
+ init(category: String) {
11
+ log = OSLog(subsystem: "com.pythonnative", category: category)
12
+ }
13
+
14
+ func error(_ message: String) { os_log("%{public}@", log: log, type: .error, message) }
15
+ func warning(_ message: String) { os_log("%{public}@", log: log, type: .default, message) }
16
+ func info(_ message: String) { os_log("%{public}@", log: log, type: .info, message) }
17
+ func debug(_ message: String) { os_log("%{public}@", log: log, type: .debug, message) }
18
+ }
19
+
20
+ /// Loggers for every PythonNativeKit subsystem, plus a rate limiter for
21
+ /// messages that could otherwise fire at frame rate.
22
+ enum PNLog {
23
+ static let bridge = PNLogger(category: "bridge")
24
+ static let components = PNLogger(category: "components")
25
+ static let gestures = PNLogger(category: "gestures")
26
+ static let animation = PNLogger(category: "animation")
27
+ static let modules = PNLogger(category: "modules")
28
+ static let screens = PNLogger(category: "screens")
29
+
30
+ private static var lastEmitted: [String: TimeInterval] = [:]
31
+ private static var onceKeys: Set<String> = []
32
+
33
+ /// Log `message` at most once per `interval` seconds for a given `key`.
34
+ static func rateLimited(_ logger: PNLogger, key: String, interval: TimeInterval = 1.0, _ message: @autoclosure () -> String) {
35
+ let now = Date().timeIntervalSinceReferenceDate
36
+ if let last = lastEmitted[key], now - last < interval { return }
37
+ lastEmitted[key] = now
38
+ logger.error(message())
39
+ }
40
+
41
+ /// Log `message` exactly once per process for a given `key`.
42
+ static func once(_ logger: PNLogger, key: String, _ message: @autoclosure () -> String) {
43
+ if onceKeys.contains(key) { return }
44
+ onceKeys.insert(key)
45
+ logger.warning(message())
46
+ }
47
+ }
@@ -0,0 +1,51 @@
1
+ import UIKit
2
+
3
+ /// Window and presenter lookups shared by modals, alerts, pickers, and portals.
4
+ public enum PNWindow {
5
+ /// The app's key window (scene-based lookup, no deprecated APIs).
6
+ public static func keyWindow() -> UIWindow? {
7
+ let scenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }
8
+ for scene in scenes where scene.activationState == .foregroundActive {
9
+ if let window = scene.windows.first(where: { $0.isKeyWindow }) { return window }
10
+ }
11
+ for scene in scenes {
12
+ if let window = scene.windows.first(where: { $0.isKeyWindow }) { return window }
13
+ }
14
+ return scenes.first?.windows.first
15
+ }
16
+
17
+ /// Bounds of the main screen (or `.zero` when no scene is connected).
18
+ public static func screenBounds() -> CGRect {
19
+ if let window = keyWindow() {
20
+ return window.windowScene?.screen.bounds ?? window.bounds
21
+ }
22
+ let scenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }
23
+ return scenes.first?.screen.bounds ?? .zero
24
+ }
25
+
26
+ /// The display scale of the main screen.
27
+ public static func screenScale() -> CGFloat {
28
+ let scenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }
29
+ return scenes.first?.screen.scale ?? 2.0
30
+ }
31
+
32
+ /// The topmost view controller suitable for presenting alerts and sheets.
33
+ public static func topViewController() -> UIViewController? {
34
+ guard var top = keyWindow()?.rootViewController else { return nil }
35
+ if let nav = top as? UINavigationController, let visible = nav.visibleViewController {
36
+ top = visible
37
+ } else if let tabs = top as? UITabBarController, let selected = tabs.selectedViewController {
38
+ top = selected
39
+ }
40
+ while let presented = top.presentedViewController {
41
+ top = presented
42
+ }
43
+ return top
44
+ }
45
+
46
+ /// Current system appearance as `"light"` or `"dark"`.
47
+ public static func colorScheme(for view: UIView? = nil) -> String {
48
+ let traits = view?.traitCollection ?? keyWindow()?.traitCollection ?? UITraitCollection.current
49
+ return traits.userInterfaceStyle == .dark ? "dark" : "light"
50
+ }
51
+ }
@@ -0,0 +1,26 @@
1
+ import XCTest
2
+ @testable import PythonNativeKit
3
+
4
+ final class PNAnimationGraphTests: XCTestCase {
5
+ override func tearDown() {
6
+ PNAnimationGraph.forget(9401)
7
+ super.tearDown()
8
+ }
9
+
10
+ func testDerivedGraphReplacementKeepsDriverAndReleasesItOnUnmount() {
11
+ let nodes: [[String: Any]] = [
12
+ ["id": 101, "kind": "value", "value": 0.0],
13
+ ["id": 102, "kind": "multiply", "inputs": [["node": 101], ["constant": 2.0]]],
14
+ ]
15
+ PNAnimationGraph.install(["id": 101, "nodes": nodes, "bindings": [[9401, "opacity", 102]]])
16
+ PNAnimationGraph.set(101, 0.25)
17
+ XCTAssertEqual(PNAnimationGraph.values[102], 0.5)
18
+ XCTAssertTrue(PNAnimationGraph.start(9501, node: 101, spec: ["kind": "timing", "to": 1.0]))
19
+ PNAnimationGraph.install(["id": 100, "nodes": nodes, "bindings": [[9401, "opacity", 102]]])
20
+ XCTAssertNotNil(PNAnimationGraph.drivers[9501])
21
+ XCTAssertEqual(PNAnimationGraph.values[101], 0.25)
22
+ PNAnimationGraph.forget(9401)
23
+ XCTAssertNil(PNAnimationGraph.drivers[9501])
24
+ XCTAssertNil(PNAnimationGraph.values[101])
25
+ }
26
+ }
@@ -0,0 +1,50 @@
1
+ import XCTest
2
+ @testable import PythonNativeKit
3
+
4
+ final class PNColorTests: XCTestCase {
5
+ private func rgba(_ color: UIColor?) -> [Int] {
6
+ guard let color = color else { return [] }
7
+ let c = PNColor.components(color)
8
+ return [c.r, c.g, c.b, c.a].map { Int(round($0 * 255)) }
9
+ }
10
+
11
+ func testHexForms() {
12
+ XCTAssertEqual(rgba(PNColor.parse("#f00")), [255, 0, 0, 255])
13
+ XCTAssertEqual(rgba(PNColor.parse("#00ff00")), [0, 255, 0, 255])
14
+ XCTAssertEqual(rgba(PNColor.parse("#800000ff")), [0, 0, 255, 128])
15
+ XCTAssertEqual(rgba(PNColor.parse("#0000FF")), [0, 0, 255, 255])
16
+ }
17
+
18
+ func testFunctionalForms() {
19
+ XCTAssertEqual(rgba(PNColor.parse("rgb(10, 20, 30)")), [10, 20, 30, 255])
20
+ XCTAssertEqual(rgba(PNColor.parse("rgba(10, 20, 30, 0.5)")), [10, 20, 30, 128])
21
+ }
22
+
23
+ func testNamedColorsAndTransparent() {
24
+ XCTAssertEqual(rgba(PNColor.parse("red")), [255, 0, 0, 255])
25
+ XCTAssertEqual(rgba(PNColor.parse("white")), [255, 255, 255, 255])
26
+ XCTAssertEqual(rgba(PNColor.parse("transparent")), [0, 0, 0, 0])
27
+ XCTAssertNil(PNColor.parse(nil))
28
+ XCTAssertNil(PNColor.parse(""))
29
+ XCTAssertNil(PNColor.parse("not-a-color"))
30
+ }
31
+
32
+ func testIntegerARGB() {
33
+ XCTAssertEqual(rgba(PNColor.parse(0xFF112233 as Int64)), [0x11, 0x22, 0x33, 255])
34
+ }
35
+
36
+ func testDynamicLightDark() {
37
+ let color = PNColor.parse(["light": "#ffffff", "dark": "#000000"])
38
+ XCTAssertNotNil(color)
39
+ let light = color?.resolvedColor(with: UITraitCollection(userInterfaceStyle: .light))
40
+ let dark = color?.resolvedColor(with: UITraitCollection(userInterfaceStyle: .dark))
41
+ XCTAssertEqual(rgba(light), [255, 255, 255, 255])
42
+ XCTAssertEqual(rgba(dark), [0, 0, 0, 255])
43
+ }
44
+
45
+ func testHexRoundTrip() {
46
+ let hex = PNColor.hexString(UIColor(red: 1, green: 0, blue: 0, alpha: 1))
47
+ XCTAssertEqual(hex.lowercased(), "#ffff0000", "ARGB, matching the Python animated color format")
48
+ XCTAssertEqual(rgba(PNColor.parse(hex)), [255, 0, 0, 255])
49
+ }
50
+ }