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.
- pythonnative/__init__.py +411 -0
- pythonnative/_ios_log.py +92 -0
- pythonnative/_yoga.cp313-win_amd64.pyd +0 -0
- pythonnative/alerts.py +236 -0
- pythonnative/animated.py +1687 -0
- pythonnative/animation_graph.py +78 -0
- pythonnative/appearance.py +124 -0
- pythonnative/bootstrap.py +100 -0
- pythonnative/bridge/__init__.py +302 -0
- pythonnative/bridge/android.py +78 -0
- pythonnative/bridge/codec.py +170 -0
- pythonnative/bridge/commits.py +114 -0
- pythonnative/bridge/fake.py +243 -0
- pythonnative/bridge/ios.py +138 -0
- pythonnative/bridge/web.py +489 -0
- pythonnative/cli/__init__.py +7 -0
- pythonnative/cli/pn.py +1214 -0
- pythonnative/component.py +271 -0
- pythonnative/components/__init__.py +111 -0
- pythonnative/components/_base.py +166 -0
- pythonnative/components/controls.py +521 -0
- pythonnative/components/layout.py +518 -0
- pythonnative/components/lists.py +785 -0
- pythonnative/components/media.py +204 -0
- pythonnative/components/overlays.py +107 -0
- pythonnative/components/pressable.py +252 -0
- pythonnative/components/structural.py +167 -0
- pythonnative/components/text.py +291 -0
- pythonnative/devclient.py +824 -0
- pythonnative/devserver/__init__.py +34 -0
- pythonnative/devserver/server.py +718 -0
- pythonnative/devserver/static/animation_graph.js +82 -0
- pythonnative/devserver/static/bridge.js +147 -0
- pythonnative/devserver/static/colors.js +70 -0
- pythonnative/devserver/static/host.js +552 -0
- pythonnative/devserver/static/index.html +60 -0
- pythonnative/devserver/static/layout.js +85 -0
- pythonnative/devserver/static/preview.css +415 -0
- pythonnative/devserver/static/renderer.js +1994 -0
- pythonnative/devserver/static/shell.js +345 -0
- pythonnative/devserver/static/yoga/LICENSE +21 -0
- pythonnative/devserver/static/yoga/binaries/yoga-wasm-base64-esm.js +75 -0
- pythonnative/devserver/static/yoga/src/generated/YGEnums.d.ts +189 -0
- pythonnative/devserver/static/yoga/src/generated/YGEnums.js +211 -0
- pythonnative/devserver/static/yoga/src/generated/YGEnums.js.map +1 -0
- pythonnative/devserver/static/yoga/src/index.d.ts +12 -0
- pythonnative/devserver/static/yoga/src/index.js +16 -0
- pythonnative/devserver/static/yoga/src/index.js.map +1 -0
- pythonnative/devserver/static/yoga/src/load.d.ts +11 -0
- pythonnative/devserver/static/yoga/src/load.js +17 -0
- pythonnative/devserver/static/yoga/src/load.js.map +1 -0
- pythonnative/devserver/static/yoga/src/wrapAssembly.d.ts +155 -0
- pythonnative/devserver/static/yoga/src/wrapAssembly.js +125 -0
- pythonnative/devserver/static/yoga/src/wrapAssembly.js.map +1 -0
- pythonnative/devserver/watcher.py +267 -0
- pythonnative/devserver/ws.py +421 -0
- pythonnative/diagnostics.py +241 -0
- pythonnative/element.py +159 -0
- pythonnative/equality.py +19 -0
- pythonnative/events.py +231 -0
- pythonnative/gestures.py +1333 -0
- pythonnative/hooks.py +1621 -0
- pythonnative/hosts/__init__.py +63 -0
- pythonnative/hosts/base.py +596 -0
- pythonnative/hosts/native.py +302 -0
- pythonnative/hot_reload.py +585 -0
- pythonnative/layout.py +328 -0
- pythonnative/mutations.py +142 -0
- pythonnative/native/android/build.gradle +52 -0
- pythonnative/native/android/gradle.properties +3 -0
- pythonnative/native/android/settings.gradle +12 -0
- pythonnative/native/android/src/main/AndroidManifest.xml +11 -0
- pythonnative/native/android/src/main/cpp/CMakeLists.txt +9 -0
- pythonnative/native/android/src/main/cpp/yoga_jni.cpp +52 -0
- pythonnative/native/android/src/main/java/com/pythonnative/generated/NativeModules.kt +5 -0
- pythonnative/native/android/src/main/java/com/pythonnative/generated/NativeProps.kt +3705 -0
- pythonnative/native/android/src/main/java/com/pythonnative/generated/PNContracts.kt +129 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/PNBridge.kt +261 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/PythonHost.kt +20 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/AnimationGraph.kt +108 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/AnimationSpecs.kt +141 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/PNAnimator.kt +257 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/CommitState.kt +106 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/JsonUtil.kt +175 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/MainThread.kt +40 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNLog.kt +33 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNRegistry.kt +82 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNTransaction.kt +80 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/TransactionApplier.kt +117 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/ViewRegistry.kt +74 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/BuiltinComponents.kt +37 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ButtonManager.kt +32 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ComponentManager.kt +213 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ContainerManagers.kt +69 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ControlManagers.kt +186 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/DatePickerManager.kt +123 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ImageLoader.kt +141 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ImageManager.kt +162 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ModalManager.kt +134 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PNColor.kt +201 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PickerManager.kt +86 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PortalManager.kt +55 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PressableManager.kt +122 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ScreenStackManager.kt +110 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ScrollViewManager.kt +214 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/SegmentedControlManager.kt +113 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/StatusBarManager.kt +52 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TabBarManager.kt +131 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TextInputManager.kt +250 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TextManager.kt +233 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ViewStyler.kt +355 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/VirtualListManager.kt +159 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/WebViewManager.kt +149 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureArbiter.kt +233 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureCoordinator.kt +127 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureRecognizers.kt +527 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/layout/NativeLayout.kt +145 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/layout/YogaNode.kt +29 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/BuiltinModules.kt +73 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/DeviceModules.kt +316 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/HostModule.kt +61 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/MediaModules.kt +200 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/NativeModule.kt +158 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/NotificationsModule.kt +100 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/PermissionsModule.kt +99 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/StorageModules.kt +89 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/SystemModules.kt +178 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/plugins/GeneratedPlugins.kt +16 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/screens/PNScreenFragment.kt +188 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/screens/ScreenRegistry.kt +80 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNAccessibilityDelegate.kt +48 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNBorderDrawable.kt +76 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNEditText.kt +18 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNFrameLayout.kt +49 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/AnimationSpecsTest.kt +75 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/GestureArbiterTest.kt +197 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/PNColorTest.kt +47 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/PNTransactionTest.kt +78 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/PromiseTest.kt +89 -0
- pythonnative/native/ios/Package.swift +23 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Animation/PNAnimationGraph.swift +199 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Animation/PNAnimator.swift +312 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNBridge.swift +255 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNCommit.swift +104 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNRegistry.swift +142 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNTransaction.swift +166 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNViewRegistry.swift +53 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNButtonManagers.swift +207 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNColor.swift +173 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNComponentManager.swift +183 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNContainerView.swift +56 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNControlManagers.swift +281 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNImageManager.swift +222 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNOverlayManagers.swift +243 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNPressableManager.swift +114 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNScreenStackManager.swift +107 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNScrollViewManager.swift +197 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTabBarManager.swift +106 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTextInputManager.swift +297 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTextManager.swift +230 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTransform.swift +82 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewManager.swift +86 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewState.swift +87 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewStyler.swift +340 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNVirtualListManager.swift +155 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNWebViewManager.swift +124 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Generated/NativeModules.swift +1 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Generated/NativeProps.swift +3775 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Generated/PNContracts.swift +46 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Gestures/PNGestureCoordinator.swift +260 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Layout/PNLayout.swift +173 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/AlertModule.swift +92 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/HostModule.swift +80 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/LifecycleModules.swift +174 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/MediaModules.swift +177 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/PNNativeModule.swift +168 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/PermissionModules.swift +266 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/SystemModules.swift +251 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Plugins/PNPluginRegistration.swift +8 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Screens/PNScreenRegistry.swift +46 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Screens/PNViewController.swift +237 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Support/PNCompat.swift +46 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Support/PNJSON.swift +173 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Support/PNLog.swift +47 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Support/PNWindow.swift +51 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNAnimationGraphTests.swift +26 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNColorTests.swift +50 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNCommitTests.swift +86 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNGestureTests.swift +101 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNManagerTests.swift +126 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNModuleTests.swift +111 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNTransactionTests.swift +91 -0
- pythonnative/native/yoga/LICENSE +21 -0
- pythonnative/native/yoga/Package.swift +9 -0
- pythonnative/native/yoga/VENDORED.md +2 -0
- pythonnative/native/yoga/include/PNStyle.h +10 -0
- pythonnative/native/yoga/include/module.modulemap +5 -0
- pythonnative/native/yoga/include/yoga/YGConfig.h +158 -0
- pythonnative/native/yoga/include/yoga/YGEnums.h +142 -0
- pythonnative/native/yoga/include/yoga/YGMacros.h +99 -0
- pythonnative/native/yoga/include/yoga/YGNode.h +304 -0
- pythonnative/native/yoga/include/yoga/YGNodeLayout.h +35 -0
- pythonnative/native/yoga/include/yoga/YGNodeStyle.h +130 -0
- pythonnative/native/yoga/include/yoga/YGPixelGrid.h +29 -0
- pythonnative/native/yoga/include/yoga/YGValue.h +84 -0
- pythonnative/native/yoga/include/yoga/Yoga.h +21 -0
- pythonnative/native/yoga/python.cpp +16 -0
- pythonnative/native/yoga/style.cpp +272 -0
- pythonnative/native/yoga/yoga/CMakeLists.txt +41 -0
- pythonnative/native/yoga/yoga/YGConfig.cpp +92 -0
- pythonnative/native/yoga/yoga/YGConfig.h +158 -0
- pythonnative/native/yoga/yoga/YGEnums.cpp +262 -0
- pythonnative/native/yoga/yoga/YGEnums.h +142 -0
- pythonnative/native/yoga/yoga/YGMacros.h +99 -0
- pythonnative/native/yoga/yoga/YGNode.cpp +366 -0
- pythonnative/native/yoga/yoga/YGNode.h +304 -0
- pythonnative/native/yoga/yoga/YGNodeLayout.cpp +92 -0
- pythonnative/native/yoga/yoga/YGNodeLayout.h +35 -0
- pythonnative/native/yoga/yoga/YGNodeStyle.cpp +405 -0
- pythonnative/native/yoga/yoga/YGNodeStyle.h +130 -0
- pythonnative/native/yoga/yoga/YGPixelGrid.cpp +22 -0
- pythonnative/native/yoga/yoga/YGPixelGrid.h +29 -0
- pythonnative/native/yoga/yoga/YGValue.cpp +20 -0
- pythonnative/native/yoga/yoga/YGValue.h +84 -0
- pythonnative/native/yoga/yoga/Yoga.h +21 -0
- pythonnative/native/yoga/yoga/algorithm/AbsoluteLayout.cpp +563 -0
- pythonnative/native/yoga/yoga/algorithm/AbsoluteLayout.h +41 -0
- pythonnative/native/yoga/yoga/algorithm/Align.h +72 -0
- pythonnative/native/yoga/yoga/algorithm/Baseline.cpp +78 -0
- pythonnative/native/yoga/yoga/algorithm/Baseline.h +21 -0
- pythonnative/native/yoga/yoga/algorithm/BoundAxis.h +79 -0
- pythonnative/native/yoga/yoga/algorithm/Cache.cpp +121 -0
- pythonnative/native/yoga/yoga/algorithm/Cache.h +30 -0
- pythonnative/native/yoga/yoga/algorithm/CalculateLayout.cpp +2428 -0
- pythonnative/native/yoga/yoga/algorithm/CalculateLayout.h +38 -0
- pythonnative/native/yoga/yoga/algorithm/FlexDirection.h +120 -0
- pythonnative/native/yoga/yoga/algorithm/FlexLine.cpp +124 -0
- pythonnative/native/yoga/yoga/algorithm/FlexLine.h +75 -0
- pythonnative/native/yoga/yoga/algorithm/PixelGrid.cpp +132 -0
- pythonnative/native/yoga/yoga/algorithm/PixelGrid.h +29 -0
- pythonnative/native/yoga/yoga/algorithm/SizingMode.h +73 -0
- pythonnative/native/yoga/yoga/algorithm/TrailingPosition.h +44 -0
- pythonnative/native/yoga/yoga/config/Config.cpp +135 -0
- pythonnative/native/yoga/yoga/config/Config.h +92 -0
- pythonnative/native/yoga/yoga/debug/AssertFatal.cpp +53 -0
- pythonnative/native/yoga/yoga/debug/AssertFatal.h +29 -0
- pythonnative/native/yoga/yoga/debug/Log.cpp +107 -0
- pythonnative/native/yoga/yoga/debug/Log.h +34 -0
- pythonnative/native/yoga/yoga/enums/Align.h +47 -0
- pythonnative/native/yoga/yoga/enums/BoxSizing.h +40 -0
- pythonnative/native/yoga/yoga/enums/Dimension.h +40 -0
- pythonnative/native/yoga/yoga/enums/Direction.h +41 -0
- pythonnative/native/yoga/yoga/enums/Display.h +41 -0
- pythonnative/native/yoga/yoga/enums/Edge.h +47 -0
- pythonnative/native/yoga/yoga/enums/Errata.h +41 -0
- pythonnative/native/yoga/yoga/enums/ExperimentalFeature.h +39 -0
- pythonnative/native/yoga/yoga/enums/FlexDirection.h +42 -0
- pythonnative/native/yoga/yoga/enums/Gutter.h +41 -0
- pythonnative/native/yoga/yoga/enums/Justify.h +44 -0
- pythonnative/native/yoga/yoga/enums/LogLevel.h +44 -0
- pythonnative/native/yoga/yoga/enums/MeasureMode.h +41 -0
- pythonnative/native/yoga/yoga/enums/NodeType.h +40 -0
- pythonnative/native/yoga/yoga/enums/Overflow.h +41 -0
- pythonnative/native/yoga/yoga/enums/PhysicalEdge.h +21 -0
- pythonnative/native/yoga/yoga/enums/PositionType.h +41 -0
- pythonnative/native/yoga/yoga/enums/Unit.h +42 -0
- pythonnative/native/yoga/yoga/enums/Wrap.h +41 -0
- pythonnative/native/yoga/yoga/enums/YogaEnums.h +85 -0
- pythonnative/native/yoga/yoga/event/event.cpp +87 -0
- pythonnative/native/yoga/yoga/event/event.h +130 -0
- pythonnative/native/yoga/yoga/module.modulemap +21 -0
- pythonnative/native/yoga/yoga/node/CachedMeasurement.h +53 -0
- pythonnative/native/yoga/yoga/node/LayoutResults.cpp +48 -0
- pythonnative/native/yoga/yoga/node/LayoutResults.h +122 -0
- pythonnative/native/yoga/yoga/node/LayoutableChildren.h +148 -0
- pythonnative/native/yoga/yoga/node/Node.cpp +443 -0
- pythonnative/native/yoga/yoga/node/Node.h +337 -0
- pythonnative/native/yoga/yoga/numeric/Comparison.h +81 -0
- pythonnative/native/yoga/yoga/numeric/FloatOptional.h +93 -0
- pythonnative/native/yoga/yoga/style/SmallValueBuffer.h +133 -0
- pythonnative/native/yoga/yoga/style/Style.h +757 -0
- pythonnative/native/yoga/yoga/style/StyleLength.h +107 -0
- pythonnative/native/yoga/yoga/style/StyleValueHandle.h +98 -0
- pythonnative/native/yoga/yoga/style/StyleValuePool.h +127 -0
- pythonnative/native_modules/__init__.py +124 -0
- pythonnative/native_modules/app_state.py +99 -0
- pythonnative/native_modules/battery.py +75 -0
- pythonnative/native_modules/biometrics.py +42 -0
- pythonnative/native_modules/camera.py +65 -0
- pythonnative/native_modules/clipboard.py +46 -0
- pythonnative/native_modules/fallback.py +324 -0
- pythonnative/native_modules/file_system.py +145 -0
- pythonnative/native_modules/haptics.py +75 -0
- pythonnative/native_modules/linking.py +123 -0
- pythonnative/native_modules/location.py +75 -0
- pythonnative/native_modules/net_info.py +112 -0
- pythonnative/native_modules/notifications.py +129 -0
- pythonnative/native_modules/permissions.py +108 -0
- pythonnative/native_modules/registry.py +526 -0
- pythonnative/native_modules/secure_store.py +52 -0
- pythonnative/native_modules/share.py +51 -0
- pythonnative/native_views/__init__.py +375 -0
- pythonnative/native_views/base.py +316 -0
- pythonnative/native_views/bridge_backend.py +277 -0
- pythonnative/navigation/__init__.py +103 -0
- pythonnative/navigation/container.py +154 -0
- pythonnative/navigation/handle.py +540 -0
- pythonnative/navigation/hooks.py +133 -0
- pythonnative/navigation/host.py +58 -0
- pythonnative/navigation/linking.py +200 -0
- pythonnative/navigation/navigators.py +659 -0
- pythonnative/navigation/screen.py +149 -0
- pythonnative/navigation/state.py +269 -0
- pythonnative/net.py +248 -0
- pythonnative/platform.py +166 -0
- pythonnative/platform_metrics.py +252 -0
- pythonnative/preview.py +287 -0
- pythonnative/profiling.py +101 -0
- pythonnative/project/__init__.py +68 -0
- pythonnative/project/android.py +557 -0
- pythonnative/project/builder.py +817 -0
- pythonnative/project/config.py +773 -0
- pythonnative/project/deps.py +719 -0
- pythonnative/project/devices.py +304 -0
- pythonnative/project/doctor.py +283 -0
- pythonnative/project/fingerprint.py +171 -0
- pythonnative/project/icons.py +247 -0
- pythonnative/project/ios.py +325 -0
- pythonnative/project/lockfile.py +100 -0
- pythonnative/project/permissions.py +361 -0
- pythonnative/project/plugins.py +519 -0
- pythonnative/project/runtime_assets.py +183 -0
- pythonnative/py.typed +0 -0
- pythonnative/query.py +153 -0
- pythonnative/reconciler/__init__.py +29 -0
- pythonnative/reconciler/boundaries.py +375 -0
- pythonnative/reconciler/children.py +88 -0
- pythonnative/reconciler/core.py +1165 -0
- pythonnative/reconciler/layout_pass.py +393 -0
- pythonnative/reconciler/vnode.py +266 -0
- pythonnative/refresh.py +55 -0
- pythonnative/runtime.py +306 -0
- pythonnative/scheduler.py +159 -0
- pythonnative/sdk/__init__.py +153 -0
- pythonnative/sdk/_components.py +427 -0
- pythonnative/sdk/builtins.py +91 -0
- pythonnative/sdk/codegen.py +149 -0
- pythonnative/sdk/module_codegen.py +184 -0
- pythonnative/sdk/schema.py +224 -0
- pythonnative/sdk/templates/contracts.kt +46 -0
- pythonnative/sdk/templates/contracts.swift +46 -0
- pythonnative/storage.py +184 -0
- pythonnative/style.py +841 -0
- pythonnative/suspense.py +183 -0
- pythonnative/templates/android_template/app/build.gradle +75 -0
- pythonnative/templates/android_template/app/proguard-rules.pro +21 -0
- pythonnative/templates/android_template/app/src/androidTest/java/com/pythonnative/android_template/ExampleInstrumentedTest.kt +24 -0
- pythonnative/templates/android_template/app/src/debug/AndroidManifest.xml +7 -0
- pythonnative/templates/android_template/app/src/main/AndroidManifest.xml +32 -0
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +130 -0
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt +13 -0
- pythonnative/templates/android_template/app/src/main/res/drawable/ic_launcher_background.xml +170 -0
- pythonnative/templates/android_template/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +30 -0
- pythonnative/templates/android_template/app/src/main/res/layout/activity_main.xml +10 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +6 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +6 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/navigation/nav_graph.xml +22 -0
- pythonnative/templates/android_template/app/src/main/res/values/colors.xml +5 -0
- pythonnative/templates/android_template/app/src/main/res/values/strings.xml +5 -0
- pythonnative/templates/android_template/app/src/main/res/values/themes.xml +9 -0
- pythonnative/templates/android_template/app/src/main/res/values-night/themes.xml +7 -0
- pythonnative/templates/android_template/app/src/main/res/xml/backup_rules.xml +13 -0
- pythonnative/templates/android_template/app/src/main/res/xml/data_extraction_rules.xml +19 -0
- pythonnative/templates/android_template/app/src/test/java/com/pythonnative/android_template/ExampleUnitTest.kt +17 -0
- pythonnative/templates/android_template/build.gradle +10 -0
- pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.jar +0 -0
- pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties +6 -0
- pythonnative/templates/android_template/gradle.properties +23 -0
- pythonnative/templates/android_template/gradlew +185 -0
- pythonnative/templates/android_template/gradlew.bat +89 -0
- pythonnative/templates/android_template/settings.gradle +17 -0
- pythonnative/templates/ios_template/ios_template/AppDelegate.swift +51 -0
- pythonnative/templates/ios_template/ios_template/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
- pythonnative/templates/ios_template/ios_template/Assets.xcassets/AppIcon.appiconset/Contents.json +13 -0
- pythonnative/templates/ios_template/ios_template/Assets.xcassets/Contents.json +6 -0
- pythonnative/templates/ios_template/ios_template/Base.lproj/LaunchScreen.storyboard +25 -0
- pythonnative/templates/ios_template/ios_template/BridgingHeader.h +14 -0
- pythonnative/templates/ios_template/ios_template/Info.plist +28 -0
- pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +390 -0
- pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +61 -0
- pythonnative/templates/ios_template/ios_template/ViewController.swift +23 -0
- pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +745 -0
- pythonnative/templates/ios_template/ios_template.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- pythonnative/templates/ios_template/ios_templateTests/ios_templateTests.swift +36 -0
- pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITests.swift +41 -0
- pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITestsLaunchTests.swift +32 -0
- pythonnative/testing/__init__.py +53 -0
- pythonnative/testing/backend.py +276 -0
- pythonnative/testing/harness.py +369 -0
- pythonnative/utils.py +145 -0
- pythonnative-0.40.0.dist-info/DELVEWHEEL +2 -0
- pythonnative-0.40.0.dist-info/METADATA +150 -0
- pythonnative-0.40.0.dist-info/RECORD +418 -0
- pythonnative-0.40.0.dist-info/WHEEL +5 -0
- pythonnative-0.40.0.dist-info/entry_points.txt +2 -0
- pythonnative-0.40.0.dist-info/licenses/LICENSE +21 -0
- pythonnative-0.40.0.dist-info/top_level.txt +1 -0
- pythonnative.libs/msvcp140-a4c2229bdc2a2a630acdc095b4d86008.dll +0 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"""Developer diagnostics: dev mode, warnings, and error reporting.
|
|
2
|
+
|
|
3
|
+
PythonNative distinguishes **dev mode** (the `pn preview` window, `pn
|
|
4
|
+
run` with hot reload, or any process with ``PN_DEV=1``) from production.
|
|
5
|
+
Dev mode turns on:
|
|
6
|
+
|
|
7
|
+
- **Validation warnings** ([`warn`][pythonnative.diagnostics.warn] /
|
|
8
|
+
[`warn_once`][pythonnative.diagnostics.warn_once]): unknown style
|
|
9
|
+
keys, duplicate list keys, and similar mistakes are printed once with
|
|
10
|
+
a suggestion instead of failing silently.
|
|
11
|
+
- **Hook-order checking**: calling hooks conditionally corrupts slot
|
|
12
|
+
state; in dev mode the mismatch raises a
|
|
13
|
+
[`HookOrderError`][pythonnative.diagnostics.HookOrderError]
|
|
14
|
+
immediately instead of cross-wiring state.
|
|
15
|
+
- **The RedBox**: uncaught errors from render, effects, and event
|
|
16
|
+
handlers are routed to the screen host, which presents a full-screen
|
|
17
|
+
error overlay (see `pythonnative.hosts`) instead of crashing or
|
|
18
|
+
swallowing the traceback.
|
|
19
|
+
|
|
20
|
+
In production none of this runs: validation is skipped, hook-order
|
|
21
|
+
checks are skipped, and errors propagate exactly as raised.
|
|
22
|
+
|
|
23
|
+
This module has no dependencies on the rest of PythonNative, so any
|
|
24
|
+
module (hooks, reconciler, events) may import it freely.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import os
|
|
28
|
+
import sys
|
|
29
|
+
import threading
|
|
30
|
+
import traceback
|
|
31
|
+
from collections import deque
|
|
32
|
+
from typing import Any, Callable, Deque, List, Optional, Set, Tuple
|
|
33
|
+
|
|
34
|
+
__all__ = [
|
|
35
|
+
"HookOrderError",
|
|
36
|
+
"set_dev_mode",
|
|
37
|
+
"is_dev",
|
|
38
|
+
"warn",
|
|
39
|
+
"warn_once",
|
|
40
|
+
"swallowed",
|
|
41
|
+
"get_warnings",
|
|
42
|
+
"clear_warnings",
|
|
43
|
+
"set_error_reporter",
|
|
44
|
+
"report_error",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class HookOrderError(RuntimeError):
|
|
49
|
+
"""Raised in dev mode when hooks are called in a different order than the previous render.
|
|
50
|
+
|
|
51
|
+
Hooks map to state slots by call order, so calling them inside
|
|
52
|
+
conditionals or loops (or returning early between hook calls)
|
|
53
|
+
silently cross-wires state in production. Dev mode detects the
|
|
54
|
+
mismatch and raises this error with the offending component and
|
|
55
|
+
slot so the bug is caught at the source.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# ======================================================================
|
|
60
|
+
# Dev mode
|
|
61
|
+
# ======================================================================
|
|
62
|
+
|
|
63
|
+
# Tri-state: ``None`` means "not explicitly set, consult the environment".
|
|
64
|
+
_dev_mode: Optional[bool] = None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def set_dev_mode(enabled: bool) -> None:
|
|
68
|
+
"""Explicitly enable or disable dev mode for this process.
|
|
69
|
+
|
|
70
|
+
Called automatically by ``pn preview`` and by the screen host's
|
|
71
|
+
``enable_hot_reload`` (which the device templates invoke on debug
|
|
72
|
+
builds). An explicit call wins over the ``PN_DEV`` environment
|
|
73
|
+
variable.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
enabled: ``True`` to turn on dev diagnostics.
|
|
77
|
+
"""
|
|
78
|
+
global _dev_mode
|
|
79
|
+
_dev_mode = bool(enabled)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def is_dev() -> bool:
|
|
83
|
+
"""Return whether dev diagnostics are active.
|
|
84
|
+
|
|
85
|
+
Resolution order: an explicit
|
|
86
|
+
[`set_dev_mode`][pythonnative.diagnostics.set_dev_mode] call, then
|
|
87
|
+
the ``PN_DEV`` environment variable, then ``False``.
|
|
88
|
+
"""
|
|
89
|
+
if _dev_mode is not None:
|
|
90
|
+
return _dev_mode
|
|
91
|
+
return os.environ.get("PN_DEV", "").lower() in {"1", "true", "yes", "on"}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# ======================================================================
|
|
95
|
+
# Warnings (LogBox-lite)
|
|
96
|
+
# ======================================================================
|
|
97
|
+
|
|
98
|
+
_MAX_WARNINGS = 200
|
|
99
|
+
|
|
100
|
+
_warn_lock = threading.Lock()
|
|
101
|
+
_warned_keys: Set[str] = set()
|
|
102
|
+
_warnings: Deque[str] = deque(maxlen=_MAX_WARNINGS)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def warn(message: str) -> None:
|
|
106
|
+
"""Print a dev warning and record it in the warning log.
|
|
107
|
+
|
|
108
|
+
No-op in production. Warnings are prefixed with ``[PN] WARN`` on
|
|
109
|
+
stderr and retained (most recent 200) for inspection via
|
|
110
|
+
[`get_warnings`][pythonnative.diagnostics.get_warnings].
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
message: Human-readable description of the problem, ideally
|
|
114
|
+
with a suggestion for the fix.
|
|
115
|
+
"""
|
|
116
|
+
if not is_dev():
|
|
117
|
+
return
|
|
118
|
+
with _warn_lock:
|
|
119
|
+
_warnings.append(message)
|
|
120
|
+
try:
|
|
121
|
+
print(f"[PN] WARN: {message}", file=sys.stderr, flush=True)
|
|
122
|
+
except Exception:
|
|
123
|
+
pass
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def warn_once(message: str, key: Optional[str] = None) -> None:
|
|
127
|
+
"""Like [`warn`][pythonnative.diagnostics.warn], but at most once per ``key``.
|
|
128
|
+
|
|
129
|
+
Use for per-render validation (style keys, list keys) so a warning
|
|
130
|
+
fires once instead of sixty times a second.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
message: The warning message.
|
|
134
|
+
key: Dedupe key, e.g. ``"style:Text:font_siez"``. Defaults to
|
|
135
|
+
the message itself.
|
|
136
|
+
"""
|
|
137
|
+
if not is_dev():
|
|
138
|
+
return
|
|
139
|
+
dedupe = key if key is not None else message
|
|
140
|
+
with _warn_lock:
|
|
141
|
+
if dedupe in _warned_keys:
|
|
142
|
+
return
|
|
143
|
+
_warned_keys.add(dedupe)
|
|
144
|
+
warn(message)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def swallowed(context: str) -> None:
|
|
148
|
+
"""Surface a suppressed native-backend exception (dev mode only).
|
|
149
|
+
|
|
150
|
+
The native view/module backends intentionally degrade gracefully in
|
|
151
|
+
production: a failed style application or a missing OS API must not
|
|
152
|
+
crash the app. Call this from the ``except`` block instead of
|
|
153
|
+
``pass`` so that, in dev mode, each suppression is reported once per
|
|
154
|
+
call site instead of disappearing.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
context: Where the suppression happened, e.g.
|
|
158
|
+
``"ios.TextHandler._apply"``.
|
|
159
|
+
"""
|
|
160
|
+
if not is_dev():
|
|
161
|
+
return
|
|
162
|
+
exc = sys.exc_info()[1]
|
|
163
|
+
if exc is None:
|
|
164
|
+
return
|
|
165
|
+
frame = sys._getframe(1)
|
|
166
|
+
location = f"{os.path.basename(frame.f_code.co_filename)}:{frame.f_lineno}"
|
|
167
|
+
warn_once(
|
|
168
|
+
f"{context} suppressed {type(exc).__name__}: {exc} ({location})",
|
|
169
|
+
key=f"swallowed:{context}:{location}",
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def get_warnings() -> List[str]:
|
|
174
|
+
"""Return a snapshot of the recorded warnings (oldest first)."""
|
|
175
|
+
with _warn_lock:
|
|
176
|
+
return list(_warnings)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def clear_warnings() -> None:
|
|
180
|
+
"""Drop all recorded warnings and dedupe keys (test helper)."""
|
|
181
|
+
with _warn_lock:
|
|
182
|
+
_warnings.clear()
|
|
183
|
+
_warned_keys.clear()
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
# ======================================================================
|
|
187
|
+
# Error reporting (RedBox routing)
|
|
188
|
+
# ======================================================================
|
|
189
|
+
#
|
|
190
|
+
# Screen hosts register themselves as error reporters. When an error
|
|
191
|
+
# escapes user code in a context that would otherwise be swallowed
|
|
192
|
+
# (event handlers) or crash the process (async tasks), dev mode routes
|
|
193
|
+
# it to the most recently registered reporter, which shows the RedBox.
|
|
194
|
+
# Reporters form a stack: the top entry is the most recently created
|
|
195
|
+
# (and therefore frontmost) screen.
|
|
196
|
+
|
|
197
|
+
_reporter_lock = threading.Lock()
|
|
198
|
+
_reporters: List[Tuple[int, Callable[[BaseException, str], None]]] = []
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def set_error_reporter(owner: Any, reporter: Optional[Callable[[BaseException, str], None]]) -> None:
|
|
202
|
+
"""Register or unregister ``owner``'s RedBox reporter.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
owner: Any object identifying the registration (a screen
|
|
206
|
+
host); keyed by ``id(owner)``.
|
|
207
|
+
reporter: ``reporter(exc, phase)`` callable, or ``None`` to
|
|
208
|
+
unregister the owner's reporter.
|
|
209
|
+
"""
|
|
210
|
+
key = id(owner)
|
|
211
|
+
with _reporter_lock:
|
|
212
|
+
_reporters[:] = [(k, r) for (k, r) in _reporters if k != key]
|
|
213
|
+
if reporter is not None:
|
|
214
|
+
_reporters.append((key, reporter))
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def report_error(exc: BaseException, phase: str = "runtime") -> bool:
|
|
218
|
+
"""Route ``exc`` to the active RedBox reporter (dev mode only).
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
exc: The exception to display.
|
|
222
|
+
phase: Where it came from: ``"render"``, ``"effect"``,
|
|
223
|
+
``"event"``, or ``"async"``.
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
``True`` when a reporter accepted the error, ``False`` when no
|
|
227
|
+
reporter is registered (or dev mode is off), in which case the
|
|
228
|
+
caller should fall back to its default behavior.
|
|
229
|
+
"""
|
|
230
|
+
if not is_dev():
|
|
231
|
+
return False
|
|
232
|
+
with _reporter_lock:
|
|
233
|
+
reporter = _reporters[-1][1] if _reporters else None
|
|
234
|
+
if reporter is None:
|
|
235
|
+
return False
|
|
236
|
+
try:
|
|
237
|
+
reporter(exc, phase)
|
|
238
|
+
return True
|
|
239
|
+
except Exception:
|
|
240
|
+
traceback.print_exc()
|
|
241
|
+
return False
|
pythonnative/element.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""Lightweight element descriptors for the virtual view tree.
|
|
2
|
+
|
|
3
|
+
An [`Element`][pythonnative.Element] is an immutable description of a UI
|
|
4
|
+
node, analogous to a React element. It captures a type, a props dict,
|
|
5
|
+
and an ordered list of children without creating any native platform
|
|
6
|
+
objects. The reconciler consumes these trees to determine what native
|
|
7
|
+
views must be created, updated, or removed.
|
|
8
|
+
|
|
9
|
+
An element's ``type`` is one of three things:
|
|
10
|
+
|
|
11
|
+
- a ``str`` naming a **native view** (``"Text"``, ``"View"``, ...),
|
|
12
|
+
- a [`Component`][pythonnative.Component] produced by
|
|
13
|
+
[`@component`][pythonnative.component.component], or
|
|
14
|
+
- a **structural type**: one of the singletons defined here
|
|
15
|
+
([`FRAGMENT`][pythonnative.element.FRAGMENT],
|
|
16
|
+
[`ERROR_BOUNDARY`][pythonnative.element.ERROR_BOUNDARY],
|
|
17
|
+
[`SUSPENSE`][pythonnative.element.SUSPENSE]) or a
|
|
18
|
+
[`Context`][pythonnative.Context] (whose elements are providers).
|
|
19
|
+
|
|
20
|
+
Structural types are real objects rather than magic strings so the
|
|
21
|
+
reconciler can dispatch on them with identity checks and no user
|
|
22
|
+
element can collide with them.
|
|
23
|
+
|
|
24
|
+
Elements are produced by built-in factories such as
|
|
25
|
+
[`Text`][pythonnative.Text], [`Button`][pythonnative.Button], and
|
|
26
|
+
[`Column`][pythonnative.Column], or by calling components.
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
```python
|
|
30
|
+
from pythonnative import Element
|
|
31
|
+
|
|
32
|
+
node = Element("Text", {"text": "Hello"}, [])
|
|
33
|
+
```
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
from __future__ import annotations
|
|
37
|
+
|
|
38
|
+
from typing import Any, Dict, Iterable, List, Optional, Union
|
|
39
|
+
|
|
40
|
+
__all__ = [
|
|
41
|
+
"ERROR_BOUNDARY",
|
|
42
|
+
"FRAGMENT",
|
|
43
|
+
"SUSPENSE",
|
|
44
|
+
"Element",
|
|
45
|
+
"Node",
|
|
46
|
+
"StructuralType",
|
|
47
|
+
"type_label",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class StructuralType:
|
|
52
|
+
"""Identity object naming a reconciler-owned element kind.
|
|
53
|
+
|
|
54
|
+
Instances are singletons compared by identity; ``repr`` shows the
|
|
55
|
+
kind for debugging (``<Fragment>``).
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
__slots__ = ("name",)
|
|
59
|
+
|
|
60
|
+
def __init__(self, name: str) -> None:
|
|
61
|
+
self.name = name
|
|
62
|
+
|
|
63
|
+
def __repr__(self) -> str:
|
|
64
|
+
return f"<{self.name}>"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
FRAGMENT = StructuralType("Fragment")
|
|
68
|
+
"""Type of [`Fragment`][pythonnative.Fragment] elements: a transparent group."""
|
|
69
|
+
|
|
70
|
+
ERROR_BOUNDARY = StructuralType("ErrorBoundary")
|
|
71
|
+
"""Type of [`ErrorBoundary`][pythonnative.ErrorBoundary] elements."""
|
|
72
|
+
|
|
73
|
+
SUSPENSE = StructuralType("Suspense")
|
|
74
|
+
"""Type of [`Suspense`][pythonnative.Suspense] elements."""
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class Element:
|
|
78
|
+
"""Immutable description of a single UI node.
|
|
79
|
+
|
|
80
|
+
Built-in elements use a string ``type`` (``"Text"``, ``"Button"``,
|
|
81
|
+
``"Column"``, etc.); components use the
|
|
82
|
+
[`Component`][pythonnative.Component] object itself as ``type``;
|
|
83
|
+
structural elements use a [`StructuralType`][pythonnative.element.StructuralType]
|
|
84
|
+
or a [`Context`][pythonnative.Context]. The reconciler dispatches on
|
|
85
|
+
this distinction when mounting the tree.
|
|
86
|
+
|
|
87
|
+
Attributes:
|
|
88
|
+
type: The element kind (see the module docstring).
|
|
89
|
+
props: Dict of properties passed to the native handler or the
|
|
90
|
+
component function.
|
|
91
|
+
children: Ordered list of child nodes. ``None`` and ``False``
|
|
92
|
+
entries are permitted and dropped during reconciliation, so
|
|
93
|
+
conditional children (``cond and Text(...)``) need no special
|
|
94
|
+
casing. Components receive these as their ``*children``.
|
|
95
|
+
key: Optional stable identity used by the reconciler when
|
|
96
|
+
diffing keyed lists. Two elements with the same ``type`` and
|
|
97
|
+
``key`` are treated as the same logical node across renders.
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
__slots__ = ("type", "props", "children", "key")
|
|
101
|
+
|
|
102
|
+
def __init__(
|
|
103
|
+
self,
|
|
104
|
+
type_: Any,
|
|
105
|
+
props: Optional[Dict[str, Any]] = None,
|
|
106
|
+
children: Optional[Iterable[Any]] = None,
|
|
107
|
+
key: Optional[str] = None,
|
|
108
|
+
) -> None:
|
|
109
|
+
self.type = type_
|
|
110
|
+
self.props: Dict[str, Any] = props if props is not None else {}
|
|
111
|
+
self.children: List[Any] = list(children) if children is not None else []
|
|
112
|
+
self.key = key
|
|
113
|
+
|
|
114
|
+
def __repr__(self) -> str:
|
|
115
|
+
return f"Element({type_label(self.type)!r}, props={sorted(self.props)}, children={len(self.children)})"
|
|
116
|
+
|
|
117
|
+
def __eq__(self, other: object) -> bool:
|
|
118
|
+
if not isinstance(other, Element):
|
|
119
|
+
return NotImplemented
|
|
120
|
+
return (
|
|
121
|
+
self.type == other.type
|
|
122
|
+
and self.props == other.props
|
|
123
|
+
and self.children == other.children
|
|
124
|
+
and self.key == other.key
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
def __ne__(self, other: object) -> bool:
|
|
128
|
+
result = self.__eq__(other)
|
|
129
|
+
if result is NotImplemented:
|
|
130
|
+
return result
|
|
131
|
+
return not result
|
|
132
|
+
|
|
133
|
+
__hash__ = None # type: ignore[assignment,unused-ignore]
|
|
134
|
+
|
|
135
|
+
def with_key(self, key: Optional[str]) -> "Element":
|
|
136
|
+
"""Return a copy of this element carrying ``key``.
|
|
137
|
+
|
|
138
|
+
Handy when a factory result needs a key after the fact, for
|
|
139
|
+
example while building a list comprehension over elements
|
|
140
|
+
produced by a helper that doesn't take ``key``.
|
|
141
|
+
"""
|
|
142
|
+
return Element(self.type, self.props, self.children, key=key)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
Node = Union[Element, None, bool, Iterable[Any]]
|
|
146
|
+
"""Anything a component may render: an element, ``None`` / ``False``
|
|
147
|
+
for "nothing", or a (possibly nested) iterable of nodes."""
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def type_label(type_obj: Any) -> str:
|
|
151
|
+
"""Return a human-readable name for an element type (for messages)."""
|
|
152
|
+
if isinstance(type_obj, str):
|
|
153
|
+
return type_obj
|
|
154
|
+
if isinstance(type_obj, StructuralType):
|
|
155
|
+
return type_obj.name
|
|
156
|
+
name = getattr(type_obj, "display_name", None) or getattr(type_obj, "__name__", None)
|
|
157
|
+
if name:
|
|
158
|
+
return str(name)
|
|
159
|
+
return repr(type_obj)
|
pythonnative/equality.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Equality rules for immutable application snapshots.
|
|
2
|
+
|
|
3
|
+
Identity is equal. Scalar boolean equality is respected. Objects whose
|
|
4
|
+
comparison returns a nonboolean result (such as arrays) require a new
|
|
5
|
+
identity to signal a change. In-place mutations don't signal updates.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def equal(left: Any, right: Any) -> bool:
|
|
12
|
+
"""Compare values without coercing array expressions to booleans."""
|
|
13
|
+
if left is right:
|
|
14
|
+
return True
|
|
15
|
+
try:
|
|
16
|
+
result = left == right
|
|
17
|
+
return result if isinstance(result, bool) else False
|
|
18
|
+
except Exception:
|
|
19
|
+
return False
|
pythonnative/events.py
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""Tag-based event routing between native views and Python callbacks.
|
|
2
|
+
|
|
3
|
+
Before the batched-commit overhaul, every event prop (``on_press``,
|
|
4
|
+
``on_change``, …) was wired by storing the Python callable on (or next
|
|
5
|
+
to) the native view, and every re-render re-pushed fresh closures across
|
|
6
|
+
the bridge. This module replaces that with a single dispatch channel:
|
|
7
|
+
|
|
8
|
+
- The reconciler strips callable props out of the payload sent to
|
|
9
|
+
native handlers and registers them here, keyed by ``(tag, name)``.
|
|
10
|
+
- Handlers wire their platform listener **once** at view creation; the
|
|
11
|
+
listener calls [`dispatch_event`][pythonnative.events.dispatch_event]
|
|
12
|
+
with the view's tag and the event name.
|
|
13
|
+
- Re-renders only mutate this Python-side registry; no native call is
|
|
14
|
+
made when just a callback identity changes.
|
|
15
|
+
|
|
16
|
+
The set of event names present on an element is forwarded to handlers
|
|
17
|
+
under the [`EVENTS_PROP`][pythonnative.events.EVENTS_PROP] key (a
|
|
18
|
+
``frozenset``), so handlers that wire expensive listeners (scroll
|
|
19
|
+
delegates, gesture recognizers) can do so conditionally. Dispatching an
|
|
20
|
+
event nobody listens to is a cheap dict miss.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import threading
|
|
24
|
+
from typing import Any, Callable, Dict, FrozenSet, Optional, Tuple
|
|
25
|
+
|
|
26
|
+
EVENTS_PROP = "_pn_events"
|
|
27
|
+
"""Prop key carrying the ``frozenset`` of event names wired on an element."""
|
|
28
|
+
|
|
29
|
+
GESTURES_PROP = "gestures"
|
|
30
|
+
"""Prop key carrying gesture descriptors (see ``pythonnative.gestures``)."""
|
|
31
|
+
|
|
32
|
+
# Prop dicts that may carry nested callables, mapped to the event name
|
|
33
|
+
# each nested key is hoisted to.
|
|
34
|
+
_NESTED_EVENT_PROPS: Dict[str, Dict[str, str]] = {
|
|
35
|
+
"refresh_control": {"on_refresh": "on_refresh"},
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class EventRegistry:
|
|
40
|
+
"""Process-wide map of ``(tag, event name) -> Python callback``.
|
|
41
|
+
|
|
42
|
+
Thread-safe: native backends may dispatch from the platform UI
|
|
43
|
+
thread while the reconciler updates registrations from the render
|
|
44
|
+
thread.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(self) -> None:
|
|
48
|
+
self._lock = threading.Lock()
|
|
49
|
+
self._scopes: Dict[int, Any] = {}
|
|
50
|
+
self._callbacks: Dict[int, Dict[str, Callable[..., Any]]] = {}
|
|
51
|
+
|
|
52
|
+
def set_events(self, tag: int, events: Dict[str, Callable[..., Any]]) -> None:
|
|
53
|
+
"""Replace every registration for ``tag`` with ``events``."""
|
|
54
|
+
with self._lock:
|
|
55
|
+
if events:
|
|
56
|
+
self._callbacks[tag] = dict(events)
|
|
57
|
+
else:
|
|
58
|
+
self._callbacks.pop(tag, None)
|
|
59
|
+
|
|
60
|
+
def set_scope(self, tag: int, scope: Any) -> None:
|
|
61
|
+
"""Associate callbacks with the component that owns this view."""
|
|
62
|
+
self._scopes[tag] = scope
|
|
63
|
+
|
|
64
|
+
def invoke(self, tag: int, name: str, *args: Any) -> Any:
|
|
65
|
+
"""Invoke a sync or async handler with component cancellation."""
|
|
66
|
+
from .runtime import invoke
|
|
67
|
+
|
|
68
|
+
callback = self.get(tag, name)
|
|
69
|
+
if callback is not None:
|
|
70
|
+
return invoke(callback, *args, scope=self._scopes.get(tag))
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
def clear(self, tag: int) -> None:
|
|
74
|
+
"""Drop every registration for ``tag`` (called on view destroy)."""
|
|
75
|
+
with self._lock:
|
|
76
|
+
self._callbacks.pop(tag, None)
|
|
77
|
+
self._scopes.pop(tag, None)
|
|
78
|
+
|
|
79
|
+
def get(self, tag: int, name: str) -> Optional[Callable[..., Any]]:
|
|
80
|
+
"""Return the callback for ``(tag, name)``, or ``None``."""
|
|
81
|
+
with self._lock:
|
|
82
|
+
bucket = self._callbacks.get(tag)
|
|
83
|
+
if bucket is None:
|
|
84
|
+
return None
|
|
85
|
+
return bucket.get(name)
|
|
86
|
+
|
|
87
|
+
def has(self, tag: int, name: str) -> bool:
|
|
88
|
+
"""Return whether a callback is registered for ``(tag, name)``."""
|
|
89
|
+
return self.get(tag, name) is not None
|
|
90
|
+
|
|
91
|
+
def dispatch(self, tag: int, name: str, *args: Any) -> bool:
|
|
92
|
+
"""Invoke the callback for ``(tag, name)`` with ``args``.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
``True`` when a callback existed and was invoked (even if
|
|
96
|
+
it raised: exceptions never propagate into the platform's
|
|
97
|
+
UI thread; in dev mode they are routed to the RedBox via
|
|
98
|
+
[`report_error`][pythonnative.diagnostics.report_error],
|
|
99
|
+
otherwise the traceback is printed), ``False`` when nothing
|
|
100
|
+
is registered.
|
|
101
|
+
"""
|
|
102
|
+
callback = self.get(tag, name)
|
|
103
|
+
if callback is None:
|
|
104
|
+
return False
|
|
105
|
+
try:
|
|
106
|
+
self.invoke(tag, name, *args)
|
|
107
|
+
except Exception as exc:
|
|
108
|
+
from . import diagnostics
|
|
109
|
+
|
|
110
|
+
if not diagnostics.report_error(exc, phase=f"event {name!r}"):
|
|
111
|
+
import traceback
|
|
112
|
+
|
|
113
|
+
traceback.print_exc()
|
|
114
|
+
return True
|
|
115
|
+
|
|
116
|
+
def reset(self) -> None:
|
|
117
|
+
"""Drop every registration (test helper)."""
|
|
118
|
+
with self._lock:
|
|
119
|
+
self._callbacks.clear()
|
|
120
|
+
self._scopes.clear()
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
_registry = EventRegistry()
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def get_event_registry() -> EventRegistry:
|
|
127
|
+
"""Return the process-wide [`EventRegistry`][pythonnative.events.EventRegistry]."""
|
|
128
|
+
return _registry
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def dispatch_event(tag: int, name: str, *args: Any) -> bool:
|
|
132
|
+
"""Dispatch an event from a native view into Python.
|
|
133
|
+
|
|
134
|
+
This is the single entry point platform handlers call when a
|
|
135
|
+
native listener fires.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
tag: The view's reconciler-assigned tag.
|
|
139
|
+
name: Event name, the original prop name (``"on_press"``,
|
|
140
|
+
``"on_change"``, …) or a gesture channel (``"gesture:0"``).
|
|
141
|
+
*args: Positional arguments forwarded to the user callback,
|
|
142
|
+
preserving each prop's documented signature.
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Whether a callback was registered for ``(tag, name)``.
|
|
146
|
+
"""
|
|
147
|
+
return _registry.dispatch(tag, name, *args)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
# ======================================================================
|
|
151
|
+
# Prop splitting
|
|
152
|
+
# ======================================================================
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def extract_events(props: Dict[str, Any]) -> Tuple[Dict[str, Any], Dict[str, Callable[..., Any]]]:
|
|
156
|
+
"""Split ``props`` into native-safe props and Python event callbacks.
|
|
157
|
+
|
|
158
|
+
Rules:
|
|
159
|
+
|
|
160
|
+
- Top-level callables named ``on_*`` become events under their prop
|
|
161
|
+
name and are removed from the native payload.
|
|
162
|
+
- ``refresh_control`` dicts have their nested ``on_refresh``
|
|
163
|
+
hoisted to the ``"on_refresh"`` event; the remaining keys
|
|
164
|
+
(``refreshing``, ``tint_color``) stay in the payload.
|
|
165
|
+
- ``gestures`` lists of gesture descriptors are serialized to plain
|
|
166
|
+
dicts (handlers wire recognizers from them) while their callbacks
|
|
167
|
+
are folded into per-gesture ``"gesture:<i>"`` routers.
|
|
168
|
+
- The resulting payload carries ``_pn_events`` (a frozenset of the
|
|
169
|
+
event names present), so handlers can wire listeners
|
|
170
|
+
conditionally and the prop differ can detect listener
|
|
171
|
+
addition/removal without comparing closures.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
props: Raw element props (already stripped of reconciler-owned
|
|
175
|
+
keys).
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
``(clean_props, events)`` where ``clean_props`` contains no
|
|
179
|
+
callables and ``events`` maps event names to callbacks.
|
|
180
|
+
"""
|
|
181
|
+
clean: Dict[str, Any] = {}
|
|
182
|
+
events: Dict[str, Callable[..., Any]] = {}
|
|
183
|
+
|
|
184
|
+
for key, value in props.items():
|
|
185
|
+
if key.startswith("on_") and callable(value):
|
|
186
|
+
events[key] = value
|
|
187
|
+
continue
|
|
188
|
+
nested_spec = _NESTED_EVENT_PROPS.get(key)
|
|
189
|
+
if nested_spec is not None and isinstance(value, dict):
|
|
190
|
+
remainder: Dict[str, Any] = {}
|
|
191
|
+
for nested_key, nested_value in value.items():
|
|
192
|
+
event_name = nested_spec.get(nested_key)
|
|
193
|
+
if event_name is not None and callable(nested_value):
|
|
194
|
+
events[event_name] = nested_value
|
|
195
|
+
else:
|
|
196
|
+
remainder[nested_key] = nested_value
|
|
197
|
+
clean[key] = remainder
|
|
198
|
+
continue
|
|
199
|
+
if key == GESTURES_PROP and value:
|
|
200
|
+
from .gestures import serialize_gestures
|
|
201
|
+
|
|
202
|
+
specs, gesture_events = serialize_gestures(value)
|
|
203
|
+
clean[key] = specs
|
|
204
|
+
events.update(gesture_events)
|
|
205
|
+
continue
|
|
206
|
+
clean[key] = value
|
|
207
|
+
|
|
208
|
+
if events:
|
|
209
|
+
clean[EVENTS_PROP] = frozenset(events)
|
|
210
|
+
return clean, events
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def event_names(props: Dict[str, Any]) -> FrozenSet[str]:
|
|
214
|
+
"""Return the event-name set a handler should consult for ``props``."""
|
|
215
|
+
names = props.get(EVENTS_PROP)
|
|
216
|
+
if isinstance(names, frozenset):
|
|
217
|
+
return names
|
|
218
|
+
if isinstance(names, (set, list, tuple)):
|
|
219
|
+
return frozenset(names)
|
|
220
|
+
return frozenset()
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
__all__ = [
|
|
224
|
+
"EVENTS_PROP",
|
|
225
|
+
"GESTURES_PROP",
|
|
226
|
+
"EventRegistry",
|
|
227
|
+
"get_event_registry",
|
|
228
|
+
"dispatch_event",
|
|
229
|
+
"extract_events",
|
|
230
|
+
"event_names",
|
|
231
|
+
]
|