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,489 @@
|
|
|
1
|
+
"""WebSocket transport for the browser renderer.
|
|
2
|
+
|
|
3
|
+
Network I/O and request settlement execute independently of the application
|
|
4
|
+
asyncio loop. Python owns the component tree; the page owns DOM widgets and Yoga
|
|
5
|
+
layout. Revisioned commits receive explicit acknowledgements, and callbacks
|
|
6
|
+
are queued onto the Python application thread. No asyncio loop is pumped by
|
|
7
|
+
the browser transport.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import itertools
|
|
13
|
+
import json
|
|
14
|
+
import queue
|
|
15
|
+
import sys
|
|
16
|
+
import threading
|
|
17
|
+
import time
|
|
18
|
+
import traceback
|
|
19
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
20
|
+
|
|
21
|
+
from . import PROTOCOL_VERSION, codec
|
|
22
|
+
|
|
23
|
+
__all__ = ["BROWSER_MODULES", "WebTransport"]
|
|
24
|
+
|
|
25
|
+
BROWSER_MODULES = frozenset(
|
|
26
|
+
{"Host", "Layout", "Alert", "Clipboard", "Linking", "Share", "Haptics", "NetInfo", "AppState", "Device"}
|
|
27
|
+
)
|
|
28
|
+
"""Native modules the preview page implements; the rest use Python fallbacks."""
|
|
29
|
+
|
|
30
|
+
REQUEST_TIMEOUT_S = 15.0
|
|
31
|
+
"""How long a synchronous request waits for the page before giving up."""
|
|
32
|
+
|
|
33
|
+
Callback = Callable[[str, int, str, str], Optional[str]]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class _Waiter:
|
|
37
|
+
__slots__ = ("event", "result", "failed")
|
|
38
|
+
|
|
39
|
+
def __init__(self) -> None:
|
|
40
|
+
self.event = threading.Event()
|
|
41
|
+
self.result: Any = None
|
|
42
|
+
self.failed = False
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class WebTransport:
|
|
46
|
+
"""Bridge transport whose native side is a browser page.
|
|
47
|
+
|
|
48
|
+
Install it with ``pythonnative.bridge.set_transport`` and hand it to
|
|
49
|
+
``DevServer.set_preview_channel``; the page does the rest.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
log: Where diagnostics go (defaults to stderr).
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
name = "web"
|
|
56
|
+
|
|
57
|
+
def __init__(self, *, log: Optional[Callable[[str], None]] = None) -> None:
|
|
58
|
+
self._log = log or (lambda line: print(line, file=sys.stderr, flush=True))
|
|
59
|
+
self._callback: Optional[Callback] = None
|
|
60
|
+
self._peer: Any = None
|
|
61
|
+
self._peer_lock = threading.Lock()
|
|
62
|
+
self._ids = itertools.count(1)
|
|
63
|
+
self._waiters: Dict[int, _Waiter] = {}
|
|
64
|
+
self._waiters_lock = threading.Lock()
|
|
65
|
+
self._main: "queue.Queue[Callable[[], None]]" = queue.Queue()
|
|
66
|
+
self._main_thread: Optional[threading.Thread] = None
|
|
67
|
+
self._stop = threading.Event()
|
|
68
|
+
self._gestures: Dict[int, Tuple[str, Any]] = {}
|
|
69
|
+
self._gesture_timer: Optional[Any] = None
|
|
70
|
+
self._warned_no_peer = False
|
|
71
|
+
self._python_modules: Dict[str, Any] = {}
|
|
72
|
+
self.on_dev_message: Optional[Callable[[Dict[str, Any]], None]] = None
|
|
73
|
+
"""Hook for ``["dev", {...}]`` messages from the page (runs on the main thread)."""
|
|
74
|
+
self.on_peer_changed: Optional[Callable[[bool], None]] = None
|
|
75
|
+
"""Called on the main thread with ``True`` on connect and ``False`` on disconnect."""
|
|
76
|
+
|
|
77
|
+
# ------------------------------------------------------------------
|
|
78
|
+
# Transport protocol
|
|
79
|
+
# ------------------------------------------------------------------
|
|
80
|
+
|
|
81
|
+
def protocol_version(self) -> int:
|
|
82
|
+
"""The page speaks whatever this package speaks; they ship together."""
|
|
83
|
+
return PROTOCOL_VERSION
|
|
84
|
+
|
|
85
|
+
def set_callback(self, callback: Callback) -> None:
|
|
86
|
+
"""Install the native -> Python entry point (``bridge.native_callback``)."""
|
|
87
|
+
self._callback = callback
|
|
88
|
+
|
|
89
|
+
def apply(self, transaction_json: str) -> str:
|
|
90
|
+
"""Wait for the page to acknowledge this exact commit revision."""
|
|
91
|
+
return codec.dumps(self._request(["apply", None, codec.loads(transaction_json)]))
|
|
92
|
+
|
|
93
|
+
def measure(self, tag: int, max_width: float, max_height: float) -> Tuple[float, float]:
|
|
94
|
+
"""Ask the page for the intrinsic size of ``tag``."""
|
|
95
|
+
result = self._request(["measure", None, int(tag), _finite(max_width), _finite(max_height)])
|
|
96
|
+
if isinstance(result, (list, tuple)) and len(result) >= 2:
|
|
97
|
+
try:
|
|
98
|
+
return (float(result[0]), float(result[1]))
|
|
99
|
+
except (TypeError, ValueError):
|
|
100
|
+
return (0.0, 0.0)
|
|
101
|
+
return (0.0, 0.0)
|
|
102
|
+
|
|
103
|
+
def command(self, tag: int, name: str, args_json: str) -> Optional[str]:
|
|
104
|
+
"""Run an imperative command on one view; returns its JSON result or ``None``."""
|
|
105
|
+
result = self._request(["command", None, int(tag), name, args_json])
|
|
106
|
+
return _as_json_text(result)
|
|
107
|
+
|
|
108
|
+
def animate(self, tag: int, request_json: str) -> Optional[str]:
|
|
109
|
+
"""Handle an animation request (``set`` / ``start`` / ``cancel``) for one view."""
|
|
110
|
+
result = self._request(["animate", None, int(tag), request_json])
|
|
111
|
+
return _as_json_text(result)
|
|
112
|
+
|
|
113
|
+
def call(self, module: str, method: str, args_json: str) -> Optional[str]:
|
|
114
|
+
"""Call a native module: ``Host.post`` locally, page modules over the wire, others in Python."""
|
|
115
|
+
if module in BROWSER_MODULES:
|
|
116
|
+
result = self._request(["call", None, module, method, args_json])
|
|
117
|
+
return _as_json_text(result)
|
|
118
|
+
return self._call_python_module(module, method, args_json)
|
|
119
|
+
|
|
120
|
+
# ------------------------------------------------------------------
|
|
121
|
+
# Main thread
|
|
122
|
+
# ------------------------------------------------------------------
|
|
123
|
+
|
|
124
|
+
def post_to_application(self, fn: Callable[[], None]) -> None:
|
|
125
|
+
"""Queue ``fn`` for the main loop (never runs inline)."""
|
|
126
|
+
from ..runtime import get_loop
|
|
127
|
+
|
|
128
|
+
loop = get_loop()
|
|
129
|
+
if loop.is_running():
|
|
130
|
+
loop.call_soon_threadsafe(self._run_job, fn)
|
|
131
|
+
else:
|
|
132
|
+
self._main.put(fn)
|
|
133
|
+
|
|
134
|
+
def run_main_loop(self, *, until: Optional[Callable[[], bool]] = None) -> None:
|
|
135
|
+
"""Drain main-thread work until ``stop`` is called (or ``until`` holds).
|
|
136
|
+
|
|
137
|
+
This is the preview's event loop: the browser's stand-in for the
|
|
138
|
+
platform main queue. Call it from the thread that should own the
|
|
139
|
+
framework (the process main thread under ``pn preview``).
|
|
140
|
+
"""
|
|
141
|
+
self._main_thread = threading.current_thread()
|
|
142
|
+
self._stop.clear()
|
|
143
|
+
while not self._stop.is_set():
|
|
144
|
+
if until is not None and until():
|
|
145
|
+
return
|
|
146
|
+
try:
|
|
147
|
+
job = self._main.get(timeout=0.1)
|
|
148
|
+
except queue.Empty:
|
|
149
|
+
continue
|
|
150
|
+
self._run_job(job)
|
|
151
|
+
|
|
152
|
+
def drain_main(self, timeout: float = 0.0) -> int:
|
|
153
|
+
"""Run queued main-thread work inline (tests); returns how many jobs ran."""
|
|
154
|
+
self._main_thread = threading.current_thread()
|
|
155
|
+
deadline = time.monotonic() + timeout
|
|
156
|
+
ran = 0
|
|
157
|
+
while True:
|
|
158
|
+
try:
|
|
159
|
+
job = self._main.get_nowait()
|
|
160
|
+
except queue.Empty:
|
|
161
|
+
if timeout <= 0 or time.monotonic() >= deadline:
|
|
162
|
+
return ran
|
|
163
|
+
time.sleep(0.002)
|
|
164
|
+
continue
|
|
165
|
+
self._run_job(job)
|
|
166
|
+
ran += 1
|
|
167
|
+
|
|
168
|
+
def stop(self) -> None:
|
|
169
|
+
"""Make ``run_main_loop`` return."""
|
|
170
|
+
self._stop.set()
|
|
171
|
+
|
|
172
|
+
def _run_job(self, job: Callable[[], None]) -> None:
|
|
173
|
+
try:
|
|
174
|
+
job()
|
|
175
|
+
except Exception:
|
|
176
|
+
self._log("[pn preview] main-thread job raised:")
|
|
177
|
+
self._log(traceback.format_exc())
|
|
178
|
+
|
|
179
|
+
# ------------------------------------------------------------------
|
|
180
|
+
# PreviewChannel (called on the dev server thread)
|
|
181
|
+
# ------------------------------------------------------------------
|
|
182
|
+
|
|
183
|
+
def on_preview_connected(self, peer: Any, info: Dict[str, Any]) -> None:
|
|
184
|
+
"""A page connected; it becomes the native side."""
|
|
185
|
+
del info
|
|
186
|
+
with self._peer_lock:
|
|
187
|
+
self._peer = peer
|
|
188
|
+
self._warned_no_peer = False
|
|
189
|
+
self.post_to_application(lambda: self._peer_changed(True))
|
|
190
|
+
|
|
191
|
+
def on_preview_disconnected(self, peer: Any) -> None:
|
|
192
|
+
"""The page went away: fail waiting requests and tear down its screens."""
|
|
193
|
+
with self._peer_lock:
|
|
194
|
+
if self._peer is not peer:
|
|
195
|
+
return
|
|
196
|
+
self._peer = None
|
|
197
|
+
self._fail_all_waiters()
|
|
198
|
+
self.post_to_application(lambda: self._peer_changed(False))
|
|
199
|
+
|
|
200
|
+
def on_preview_message(self, peer: Any, text: str) -> None:
|
|
201
|
+
"""Route one frame from the page."""
|
|
202
|
+
with self._peer_lock:
|
|
203
|
+
if peer is not self._peer:
|
|
204
|
+
return
|
|
205
|
+
try:
|
|
206
|
+
message = json.loads(text)
|
|
207
|
+
except ValueError:
|
|
208
|
+
self._log(f"[pn preview] dropped malformed message: {text[:120]!r}")
|
|
209
|
+
return
|
|
210
|
+
if not isinstance(message, list) or not message:
|
|
211
|
+
return
|
|
212
|
+
kind = message[0]
|
|
213
|
+
if kind == "res":
|
|
214
|
+
self._settle(message)
|
|
215
|
+
return
|
|
216
|
+
if kind == "cb":
|
|
217
|
+
self.post_to_application(lambda: self._deliver_fire_and_forget(message))
|
|
218
|
+
return
|
|
219
|
+
if kind == "req":
|
|
220
|
+
self.post_to_application(lambda: self._deliver_request(peer, message))
|
|
221
|
+
return
|
|
222
|
+
if kind == "gesture":
|
|
223
|
+
self.post_to_application(lambda: self._deliver_gesture(message))
|
|
224
|
+
return
|
|
225
|
+
if kind == "dev":
|
|
226
|
+
payload = message[1] if len(message) > 1 and isinstance(message[1], dict) else {}
|
|
227
|
+
self.post_to_application(lambda: self._deliver_dev(payload))
|
|
228
|
+
return
|
|
229
|
+
self._log(f"[pn preview] unknown message kind {kind!r}")
|
|
230
|
+
|
|
231
|
+
# ------------------------------------------------------------------
|
|
232
|
+
# Sending
|
|
233
|
+
# ------------------------------------------------------------------
|
|
234
|
+
|
|
235
|
+
@property
|
|
236
|
+
def connected(self) -> bool:
|
|
237
|
+
"""Whether a page is attached."""
|
|
238
|
+
with self._peer_lock:
|
|
239
|
+
return self._peer is not None
|
|
240
|
+
|
|
241
|
+
def send_dev(self, payload: Dict[str, Any]) -> None:
|
|
242
|
+
"""Send a ``["dev", {...}]`` message to the page (logs, reload status, ...)."""
|
|
243
|
+
self._send(codec.dumps(["dev", codec.to_jsonable(payload)]))
|
|
244
|
+
|
|
245
|
+
def _send(self, text: str) -> bool:
|
|
246
|
+
with self._peer_lock:
|
|
247
|
+
peer = self._peer
|
|
248
|
+
if peer is None:
|
|
249
|
+
return False
|
|
250
|
+
try:
|
|
251
|
+
peer.send(text)
|
|
252
|
+
except Exception as exc:
|
|
253
|
+
self._log(f"[pn preview] send failed: {exc!r}")
|
|
254
|
+
return False
|
|
255
|
+
return True
|
|
256
|
+
|
|
257
|
+
def _request(self, message: List[Any]) -> Any:
|
|
258
|
+
"""Send ``message`` (slot 1 receives the id) and block for the ``res``."""
|
|
259
|
+
with self._peer_lock:
|
|
260
|
+
peer = self._peer
|
|
261
|
+
if peer is None:
|
|
262
|
+
if not self._warned_no_peer:
|
|
263
|
+
self._warned_no_peer = True
|
|
264
|
+
self._log("[pn preview] request made with no browser attached; answering with defaults")
|
|
265
|
+
return None
|
|
266
|
+
request_id = next(self._ids)
|
|
267
|
+
message[1] = request_id
|
|
268
|
+
waiter = _Waiter()
|
|
269
|
+
with self._waiters_lock:
|
|
270
|
+
self._waiters[request_id] = waiter
|
|
271
|
+
try:
|
|
272
|
+
peer.send(codec.dumps(message))
|
|
273
|
+
except Exception as exc:
|
|
274
|
+
with self._waiters_lock:
|
|
275
|
+
self._waiters.pop(request_id, None)
|
|
276
|
+
self._log(f"[pn preview] request send failed: {exc!r}")
|
|
277
|
+
return None
|
|
278
|
+
if not waiter.event.wait(REQUEST_TIMEOUT_S):
|
|
279
|
+
with self._waiters_lock:
|
|
280
|
+
self._waiters.pop(request_id, None)
|
|
281
|
+
self._log(f"[pn preview] {message[0]} request timed out after {REQUEST_TIMEOUT_S:g}s")
|
|
282
|
+
return None
|
|
283
|
+
if waiter.failed:
|
|
284
|
+
return None
|
|
285
|
+
return waiter.result
|
|
286
|
+
|
|
287
|
+
def _settle(self, message: List[Any]) -> None:
|
|
288
|
+
if len(message) < 2:
|
|
289
|
+
return
|
|
290
|
+
try:
|
|
291
|
+
request_id = int(message[1])
|
|
292
|
+
except (TypeError, ValueError):
|
|
293
|
+
return
|
|
294
|
+
with self._waiters_lock:
|
|
295
|
+
waiter = self._waiters.pop(request_id, None)
|
|
296
|
+
if waiter is None:
|
|
297
|
+
return
|
|
298
|
+
waiter.result = message[2] if len(message) > 2 else None
|
|
299
|
+
waiter.event.set()
|
|
300
|
+
|
|
301
|
+
def _fail_all_waiters(self) -> None:
|
|
302
|
+
with self._waiters_lock:
|
|
303
|
+
waiters = list(self._waiters.values())
|
|
304
|
+
self._waiters.clear()
|
|
305
|
+
for waiter in waiters:
|
|
306
|
+
waiter.failed = True
|
|
307
|
+
waiter.event.set()
|
|
308
|
+
|
|
309
|
+
# ------------------------------------------------------------------
|
|
310
|
+
# Inbound (main thread)
|
|
311
|
+
# ------------------------------------------------------------------
|
|
312
|
+
|
|
313
|
+
def _deliver_callback(self, message: List[Any]) -> Optional[str]:
|
|
314
|
+
callback = self._callback
|
|
315
|
+
if callback is None or len(message) < 5:
|
|
316
|
+
return None
|
|
317
|
+
_, kind, tag, name, payload = message[:5]
|
|
318
|
+
return callback(str(kind), int(tag or 0), str(name), _payload_text(payload))
|
|
319
|
+
|
|
320
|
+
def _deliver_fire_and_forget(self, message: List[Any]) -> None:
|
|
321
|
+
self._deliver_callback(message)
|
|
322
|
+
|
|
323
|
+
def _deliver_request(self, peer: Any, message: List[Any]) -> None:
|
|
324
|
+
if len(message) < 6:
|
|
325
|
+
return
|
|
326
|
+
request_id = message[1]
|
|
327
|
+
result = self._deliver_callback(["cb", *message[2:6]])
|
|
328
|
+
try:
|
|
329
|
+
peer.send(codec.dumps(["res", request_id, result]))
|
|
330
|
+
except Exception as exc:
|
|
331
|
+
self._log(f"[pn preview] reply failed: {exc!r}")
|
|
332
|
+
|
|
333
|
+
def _deliver_dev(self, payload: Dict[str, Any]) -> None:
|
|
334
|
+
hook = self.on_dev_message
|
|
335
|
+
if hook is not None:
|
|
336
|
+
hook(payload)
|
|
337
|
+
|
|
338
|
+
def _peer_changed(self, connected: bool) -> None:
|
|
339
|
+
if not connected:
|
|
340
|
+
self._gestures.clear()
|
|
341
|
+
hook = self.on_peer_changed
|
|
342
|
+
if hook is not None:
|
|
343
|
+
hook(connected)
|
|
344
|
+
|
|
345
|
+
# -- gestures --------------------------------------------------------
|
|
346
|
+
|
|
347
|
+
def _deliver_gesture(self, message: List[Any]) -> None:
|
|
348
|
+
"""Feed a pointer event to the tag's arbiter (built from the specs the page sends)."""
|
|
349
|
+
if len(message) < 4:
|
|
350
|
+
return
|
|
351
|
+
_, tag, phase, info = message[:4]
|
|
352
|
+
if not isinstance(info, dict):
|
|
353
|
+
return
|
|
354
|
+
tag = int(tag)
|
|
355
|
+
arbiter = self._arbiter_for(tag, info.get("specs"))
|
|
356
|
+
if arbiter is None:
|
|
357
|
+
return
|
|
358
|
+
t = float(info.get("t", time.monotonic()))
|
|
359
|
+
pointer = int(info.get("id", 0))
|
|
360
|
+
x = float(info.get("x", 0.0))
|
|
361
|
+
y = float(info.get("y", 0.0))
|
|
362
|
+
if phase == "down":
|
|
363
|
+
arbiter.pointer_down(pointer, x, y, t)
|
|
364
|
+
elif phase == "move":
|
|
365
|
+
arbiter.pointer_move(pointer, x, y, t)
|
|
366
|
+
elif phase == "up":
|
|
367
|
+
arbiter.pointer_up(pointer, x, y, t)
|
|
368
|
+
elif phase == "cancel":
|
|
369
|
+
arbiter.cancel(t)
|
|
370
|
+
elif phase == "clear":
|
|
371
|
+
self._gestures.pop(tag, None)
|
|
372
|
+
return
|
|
373
|
+
self._schedule_gesture_poll()
|
|
374
|
+
|
|
375
|
+
def _arbiter_for(self, tag: int, specs: Any) -> Any:
|
|
376
|
+
if not isinstance(specs, list) or not specs:
|
|
377
|
+
self._gestures.pop(tag, None)
|
|
378
|
+
return None
|
|
379
|
+
key = json.dumps(specs, sort_keys=True)
|
|
380
|
+
existing = self._gestures.get(tag)
|
|
381
|
+
if existing is not None and existing[0] == key:
|
|
382
|
+
return existing[1]
|
|
383
|
+
from ..events import dispatch_event
|
|
384
|
+
from ..gestures import make_arbiter
|
|
385
|
+
|
|
386
|
+
def _emit(index: int, payload: Dict[str, Any]) -> None:
|
|
387
|
+
dispatch_event(tag, f"gesture:{index}", payload)
|
|
388
|
+
|
|
389
|
+
arbiter = make_arbiter([s for s in specs if isinstance(s, dict)], _emit)
|
|
390
|
+
self._gestures[tag] = (key, arbiter)
|
|
391
|
+
return arbiter
|
|
392
|
+
|
|
393
|
+
def _schedule_gesture_poll(self) -> None:
|
|
394
|
+
"""Arm one timer for the earliest recognizer deadline (long press, multi-tap windows)."""
|
|
395
|
+
deadlines = [d for _, arb in self._gestures.values() if (d := arb.next_deadline()) is not None]
|
|
396
|
+
if not deadlines:
|
|
397
|
+
return
|
|
398
|
+
delay = max(0.0, min(deadlines) - time.monotonic())
|
|
399
|
+
from ..runtime import get_loop
|
|
400
|
+
|
|
401
|
+
loop = get_loop()
|
|
402
|
+
if self._gesture_timer is not None:
|
|
403
|
+
try:
|
|
404
|
+
self._gesture_timer.cancel()
|
|
405
|
+
except Exception:
|
|
406
|
+
pass
|
|
407
|
+
self._gesture_timer = loop.call_later(delay + 0.001, self._poll_gestures)
|
|
408
|
+
|
|
409
|
+
def _poll_gestures(self) -> None:
|
|
410
|
+
self._gesture_timer = None
|
|
411
|
+
now = time.monotonic()
|
|
412
|
+
for _, arbiter in list(self._gestures.values()):
|
|
413
|
+
arbiter.poll(now)
|
|
414
|
+
self._schedule_gesture_poll()
|
|
415
|
+
|
|
416
|
+
# -- Python module fallbacks ------------------------------------------
|
|
417
|
+
|
|
418
|
+
def _call_python_module(self, module: str, method: str, args_json: str) -> Optional[str]:
|
|
419
|
+
from ..native_modules.registry import NativeModuleError, PythonModule, dispatch_module_message
|
|
420
|
+
|
|
421
|
+
envelope = codec.loads(args_json) or {}
|
|
422
|
+
args = envelope.get("args") if isinstance(envelope, dict) else None
|
|
423
|
+
call_id = int(envelope.get("call_id", 0) or 0) if isinstance(envelope, dict) else 0
|
|
424
|
+
# One instance per module for the life of the transport, so stateful
|
|
425
|
+
# fallbacks (Storage, SecureStore, ...) remember between calls.
|
|
426
|
+
impl = self._python_modules.get(module)
|
|
427
|
+
if impl is None:
|
|
428
|
+
impl = self._python_modules[module] = PythonModule(module)
|
|
429
|
+
try:
|
|
430
|
+
fn = impl._method(method)
|
|
431
|
+
value = fn(**(args or {}))
|
|
432
|
+
except KeyError:
|
|
433
|
+
return codec.dumps({"ok": False, "error": f"no module named {module!r}", "code": "unknown_module"})
|
|
434
|
+
except NativeModuleError as exc:
|
|
435
|
+
return codec.dumps({"ok": False, "error": exc.message, "code": exc.code})
|
|
436
|
+
except Exception as exc:
|
|
437
|
+
return codec.dumps({"ok": False, "error": str(exc)})
|
|
438
|
+
if hasattr(value, "__await__"):
|
|
439
|
+
if call_id == 0:
|
|
440
|
+
return codec.dumps({"ok": False, "error": f"{module}.{method} is asynchronous; use call_async()"})
|
|
441
|
+
from ..runtime import run_async
|
|
442
|
+
|
|
443
|
+
async def _finish() -> None:
|
|
444
|
+
try:
|
|
445
|
+
result = await value
|
|
446
|
+
dispatch_module_message(
|
|
447
|
+
module, {"call_id": call_id, "ok": True, "value": codec.to_jsonable(result)}
|
|
448
|
+
)
|
|
449
|
+
except Exception as exc:
|
|
450
|
+
dispatch_module_message(module, {"call_id": call_id, "ok": False, "error": str(exc)})
|
|
451
|
+
|
|
452
|
+
run_async(_finish())
|
|
453
|
+
return codec.dumps({"pending": True})
|
|
454
|
+
try:
|
|
455
|
+
return codec.dumps({"ok": True, "value": codec.to_jsonable(value)})
|
|
456
|
+
except TypeError:
|
|
457
|
+
return codec.dumps({"ok": True, "value": None})
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
_UNBOUNDED = 1e6
|
|
461
|
+
"""Wire sentinel for an unconstrained measure axis (matches the native runtimes)."""
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def _finite(value: Any) -> float:
|
|
465
|
+
"""JSON has no infinity: clamp unconstrained measure axes to the wire sentinel."""
|
|
466
|
+
try:
|
|
467
|
+
f = float(value)
|
|
468
|
+
except (TypeError, ValueError):
|
|
469
|
+
return _UNBOUNDED
|
|
470
|
+
if f != f or f > _UNBOUNDED or f == float("inf"):
|
|
471
|
+
return _UNBOUNDED
|
|
472
|
+
return max(0.0, f)
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
def _as_json_text(result: Any) -> Optional[str]:
|
|
476
|
+
"""Normalize a page result to the JSON text the native protocol returns."""
|
|
477
|
+
if result is None:
|
|
478
|
+
return None
|
|
479
|
+
if isinstance(result, str):
|
|
480
|
+
return result
|
|
481
|
+
return codec.dumps(result)
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
def _payload_text(payload: Any) -> str:
|
|
485
|
+
if payload is None:
|
|
486
|
+
return ""
|
|
487
|
+
if isinstance(payload, str):
|
|
488
|
+
return payload
|
|
489
|
+
return codec.dumps(payload)
|