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
pythonnative/platform.py
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""Platform-aware constants and selectors.
|
|
2
|
+
|
|
3
|
+
A small, RN-style helper for branching app code on the host platform.
|
|
4
|
+
The public surface is the [`Platform`][pythonnative.Platform] class
|
|
5
|
+
exposing ``OS``, ``Version``, ``is_ios``, ``is_android``, ``is_web``,
|
|
6
|
+
and ``select`` so user code can write ``Platform.select({"ios": ..., ...})``
|
|
7
|
+
without importing the ``IS_*`` flags directly.
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
```python
|
|
11
|
+
import pythonnative as pn
|
|
12
|
+
|
|
13
|
+
title_size = pn.Platform.select({"ios": 17, "android": 16, "default": 16})
|
|
14
|
+
|
|
15
|
+
@pn.component
|
|
16
|
+
def App():
|
|
17
|
+
return pn.Text(
|
|
18
|
+
f"Running on {pn.Platform.OS} {pn.Platform.Version}",
|
|
19
|
+
style={"font_size": title_size},
|
|
20
|
+
)
|
|
21
|
+
```
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import os
|
|
27
|
+
import sys
|
|
28
|
+
from typing import Any, Dict, Optional
|
|
29
|
+
|
|
30
|
+
from .utils import IS_ANDROID, IS_IOS, IS_WEB
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _detect_os() -> str:
|
|
34
|
+
if IS_ANDROID:
|
|
35
|
+
return "android"
|
|
36
|
+
if IS_IOS:
|
|
37
|
+
return "ios"
|
|
38
|
+
if IS_WEB:
|
|
39
|
+
return "web"
|
|
40
|
+
return "test"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _detect_version() -> str:
|
|
44
|
+
"""Return a human-readable platform version string.
|
|
45
|
+
|
|
46
|
+
On device this asks the native ``Device`` module (``UIDevice
|
|
47
|
+
.systemVersion`` / ``Build.VERSION.RELEASE``); the browser preview
|
|
48
|
+
answers with the browser's user agent family. Off-device (the test
|
|
49
|
+
environment), returns the host Python's version so user code can
|
|
50
|
+
still introspect *something*.
|
|
51
|
+
"""
|
|
52
|
+
if IS_IOS or IS_ANDROID or IS_WEB:
|
|
53
|
+
try:
|
|
54
|
+
from .native_modules.registry import native_module
|
|
55
|
+
|
|
56
|
+
info = native_module("Device").call("info")
|
|
57
|
+
if isinstance(info, dict) and info.get("os_version"):
|
|
58
|
+
return str(info["os_version"])
|
|
59
|
+
except Exception:
|
|
60
|
+
pass
|
|
61
|
+
sim_version = os.environ.get("SIMULATOR_RUNTIME_VERSION")
|
|
62
|
+
if sim_version:
|
|
63
|
+
return sim_version
|
|
64
|
+
return f"python-{sys.version_info.major}.{sys.version_info.minor}"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class _LazyVersion:
|
|
68
|
+
"""Resolve ``Platform.Version`` on first access (it needs the bridge on device)."""
|
|
69
|
+
|
|
70
|
+
_value: Optional[str] = None
|
|
71
|
+
|
|
72
|
+
def __get__(self, obj: object, owner: Optional[type] = None) -> str:
|
|
73
|
+
if _LazyVersion._value is None:
|
|
74
|
+
_LazyVersion._value = _detect_version()
|
|
75
|
+
return _LazyVersion._value
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class Platform:
|
|
79
|
+
"""Platform-aware constants and the ``select`` dispatcher.
|
|
80
|
+
|
|
81
|
+
All attributes are read at import time. ``OS`` is one of
|
|
82
|
+
``"ios"``, ``"android"``, ``"web"`` (the browser preview), or
|
|
83
|
+
``"test"`` (when running off-device, e.g., in unit tests).
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
OS: str = _detect_os()
|
|
87
|
+
"""``"ios"``, ``"android"``, ``"web"``, or ``"test"``."""
|
|
88
|
+
|
|
89
|
+
Version: str = _LazyVersion() # type: ignore[assignment]
|
|
90
|
+
"""Best-effort OS version string (``"17.4"``, ``"14"``, ``"python-3.11"``)."""
|
|
91
|
+
|
|
92
|
+
is_ios: bool = IS_IOS
|
|
93
|
+
"""``True`` when running inside an iOS app bundle."""
|
|
94
|
+
|
|
95
|
+
is_android: bool = IS_ANDROID
|
|
96
|
+
"""``True`` when running inside an Android process."""
|
|
97
|
+
|
|
98
|
+
is_web: bool = IS_WEB
|
|
99
|
+
"""``True`` when running the browser preview (``pn preview``)."""
|
|
100
|
+
|
|
101
|
+
is_test: bool = OS == "test"
|
|
102
|
+
"""``True`` when running off-device (no native runtime)."""
|
|
103
|
+
|
|
104
|
+
@staticmethod
|
|
105
|
+
def select(spec: Dict[str, Any], default: Any = None) -> Any:
|
|
106
|
+
"""Pick the value matching the current platform.
|
|
107
|
+
|
|
108
|
+
Looks up ``spec[Platform.OS]``, then falls back to
|
|
109
|
+
``spec["native"]`` (matches iOS and Android, *not* the browser
|
|
110
|
+
preview, which is a development surface), then to
|
|
111
|
+
``spec["default"]``, then to the explicit ``default`` argument.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
spec: Mapping from platform name to value. Recognized keys:
|
|
115
|
+
``"ios"``, ``"android"``, ``"web"``, ``"test"``,
|
|
116
|
+
``"native"``, ``"default"``.
|
|
117
|
+
default: Value returned when ``spec`` has no matching key
|
|
118
|
+
and no ``"default"`` entry.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
The matching value, or ``default`` when nothing matches.
|
|
122
|
+
|
|
123
|
+
Example:
|
|
124
|
+
```python
|
|
125
|
+
font = pn.Platform.select(
|
|
126
|
+
{"ios": "Helvetica", "android": "Roboto", "default": None}
|
|
127
|
+
)
|
|
128
|
+
```
|
|
129
|
+
"""
|
|
130
|
+
if Platform.OS in spec:
|
|
131
|
+
return spec[Platform.OS]
|
|
132
|
+
if (Platform.is_ios or Platform.is_android) and "native" in spec:
|
|
133
|
+
return spec["native"]
|
|
134
|
+
if "default" in spec:
|
|
135
|
+
return spec["default"]
|
|
136
|
+
return default
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def get_platform() -> str:
|
|
140
|
+
"""Return the active platform name.
|
|
141
|
+
|
|
142
|
+
Equivalent to reading ``Platform.OS``, exposed as a function for
|
|
143
|
+
introspection from non-component code.
|
|
144
|
+
"""
|
|
145
|
+
return Platform.OS
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _set_platform_for_test(name: Optional[str]) -> None:
|
|
149
|
+
"""Override ``Platform.OS`` for unit tests.
|
|
150
|
+
|
|
151
|
+
Production code should not call this. Tests can pass ``"ios"``,
|
|
152
|
+
``"android"``, ``"web"``, ``"test"``, or ``None`` (to reset to
|
|
153
|
+
autodetect).
|
|
154
|
+
"""
|
|
155
|
+
if name is None:
|
|
156
|
+
Platform.OS = _detect_os()
|
|
157
|
+
Platform.is_ios = IS_IOS
|
|
158
|
+
Platform.is_android = IS_ANDROID
|
|
159
|
+
Platform.is_web = IS_WEB
|
|
160
|
+
Platform.is_test = Platform.OS == "test"
|
|
161
|
+
return
|
|
162
|
+
Platform.OS = name
|
|
163
|
+
Platform.is_ios = name == "ios"
|
|
164
|
+
Platform.is_android = name == "android"
|
|
165
|
+
Platform.is_web = name == "web"
|
|
166
|
+
Platform.is_test = name == "test"
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"""Platform-level metrics shared between screen hosts and view handlers.
|
|
2
|
+
|
|
3
|
+
The screen host (`pythonnative.hosts`) is the only place that knows
|
|
4
|
+
about native window/safe-area state because it is the only piece of
|
|
5
|
+
code that holds a reference to the native ``UIViewController``
|
|
6
|
+
(iOS) or ``Activity`` (Android). Native view handlers, however, need
|
|
7
|
+
that state to size themselves correctly:
|
|
8
|
+
|
|
9
|
+
- A bottom tab bar must claim both its visible 49 pt / 56 dp content
|
|
10
|
+
height **and** the bottom safe-area inset so its background reaches
|
|
11
|
+
the edge of the screen and the home indicator / gesture bar does
|
|
12
|
+
not draw on top of the labels.
|
|
13
|
+
- A future safe-area-aware container can read the same values instead
|
|
14
|
+
of asking each native view for window metrics.
|
|
15
|
+
|
|
16
|
+
Rather than threading those values through every
|
|
17
|
+
[`measure_intrinsic`][pythonnative.native_views.base.ViewHandler.measure_intrinsic]
|
|
18
|
+
call signature, the screen host writes them here and handlers read
|
|
19
|
+
them on demand. Values are in **dp on Android** and **pt on iOS**,
|
|
20
|
+
i.e., the same "layout units" the layout engine uses on each
|
|
21
|
+
platform, so handlers can add them to other layout-unit values
|
|
22
|
+
without further conversion. On iOS the screen host consumes the top
|
|
23
|
+
safe-area inset by positioning the root view below it, then publishes
|
|
24
|
+
``top=0`` here; Android publishes the raw system-bar insets because
|
|
25
|
+
the host view normally remains full-screen.
|
|
26
|
+
|
|
27
|
+
Example:
|
|
28
|
+
>>> from pythonnative.platform_metrics import (
|
|
29
|
+
... get_safe_area_insets, set_safe_area_insets,
|
|
30
|
+
... )
|
|
31
|
+
>>> set_safe_area_insets(top=44.0, left=0.0, bottom=34.0, right=0.0)
|
|
32
|
+
>>> get_safe_area_insets().bottom
|
|
33
|
+
34.0
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
from __future__ import annotations
|
|
37
|
+
|
|
38
|
+
import threading
|
|
39
|
+
from typing import Callable, List, NamedTuple
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class SafeAreaInsets(NamedTuple):
|
|
43
|
+
"""Safe-area insets in layout units (pt on iOS, dp on Android)."""
|
|
44
|
+
|
|
45
|
+
top: float
|
|
46
|
+
left: float
|
|
47
|
+
bottom: float
|
|
48
|
+
right: float
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class WindowDimensions(NamedTuple):
|
|
52
|
+
"""Viewport size in layout units (pt on iOS, dp on Android)."""
|
|
53
|
+
|
|
54
|
+
width: float
|
|
55
|
+
height: float
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
_safe_area_insets: SafeAreaInsets = SafeAreaInsets(0.0, 0.0, 0.0, 0.0)
|
|
59
|
+
_window_dimensions: WindowDimensions = WindowDimensions(0.0, 0.0)
|
|
60
|
+
_keyboard_height: float = 0.0
|
|
61
|
+
|
|
62
|
+
_subscribers: List[Callable[[], None]] = []
|
|
63
|
+
_subscribers_lock = threading.Lock()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _notify_subscribers() -> None:
|
|
67
|
+
"""Invoke every registered subscriber, swallowing exceptions.
|
|
68
|
+
|
|
69
|
+
Called from every metric setter so subscriber callbacks (the React
|
|
70
|
+
hooks ``use_window_dimensions``/``use_safe_area_insets``/
|
|
71
|
+
``use_keyboard_height`` for example) can re-render in response to
|
|
72
|
+
any metric change. Subscribers are called serially in registration
|
|
73
|
+
order; any callback that raises is logged-and-skipped so a single
|
|
74
|
+
misbehaving subscriber can't take down the whole notification
|
|
75
|
+
cycle.
|
|
76
|
+
"""
|
|
77
|
+
with _subscribers_lock:
|
|
78
|
+
callbacks = list(_subscribers)
|
|
79
|
+
for cb in callbacks:
|
|
80
|
+
try:
|
|
81
|
+
cb()
|
|
82
|
+
except Exception:
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def subscribe(callback: Callable[[], None]) -> Callable[[], None]:
|
|
87
|
+
"""Register *callback* to fire whenever any metric changes.
|
|
88
|
+
|
|
89
|
+
Returns an unsubscribe function. Hooks pass a state setter so a
|
|
90
|
+
component re-renders whenever the platform reports a new value.
|
|
91
|
+
Threadsafe: multiple subscribers may register/unregister
|
|
92
|
+
concurrently.
|
|
93
|
+
"""
|
|
94
|
+
with _subscribers_lock:
|
|
95
|
+
_subscribers.append(callback)
|
|
96
|
+
|
|
97
|
+
def _unsub() -> None:
|
|
98
|
+
with _subscribers_lock:
|
|
99
|
+
try:
|
|
100
|
+
_subscribers.remove(callback)
|
|
101
|
+
except ValueError:
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
return _unsub
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def set_safe_area_insets(top: float, left: float, bottom: float, right: float) -> None:
|
|
108
|
+
"""Publish the current safe-area insets.
|
|
109
|
+
|
|
110
|
+
Called by the platform-specific screen host whenever it learns a
|
|
111
|
+
new value (e.g., on first layout, on rotation, on multitasking
|
|
112
|
+
split-view changes). Negative inputs are clamped to ``0.0`` so
|
|
113
|
+
handlers don't have to defend against bad data from native
|
|
114
|
+
callers.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
top: Distance in layout units from the top of the host
|
|
118
|
+
container to the safe area (status bar / dynamic island /
|
|
119
|
+
navigation bar).
|
|
120
|
+
left: Inset from the left edge.
|
|
121
|
+
bottom: Inset from the bottom edge (home indicator / gesture
|
|
122
|
+
bar).
|
|
123
|
+
right: Inset from the right edge.
|
|
124
|
+
"""
|
|
125
|
+
global _safe_area_insets
|
|
126
|
+
new_insets = SafeAreaInsets(
|
|
127
|
+
max(0.0, float(top)),
|
|
128
|
+
max(0.0, float(left)),
|
|
129
|
+
max(0.0, float(bottom)),
|
|
130
|
+
max(0.0, float(right)),
|
|
131
|
+
)
|
|
132
|
+
if new_insets == _safe_area_insets:
|
|
133
|
+
return
|
|
134
|
+
_safe_area_insets = new_insets
|
|
135
|
+
_notify_subscribers()
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def get_safe_area_insets() -> SafeAreaInsets:
|
|
139
|
+
"""Return the current safe-area insets.
|
|
140
|
+
|
|
141
|
+
The default value is ``(0, 0, 0, 0)``; handlers should still
|
|
142
|
+
function correctly in a unit-test environment where no
|
|
143
|
+
screen host has published insets.
|
|
144
|
+
"""
|
|
145
|
+
return _safe_area_insets
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def reset_safe_area_insets() -> None:
|
|
149
|
+
"""Reset the insets back to ``(0, 0, 0, 0)``.
|
|
150
|
+
|
|
151
|
+
Intended for unit tests that need a clean slate between cases.
|
|
152
|
+
Production code should use
|
|
153
|
+
[`set_safe_area_insets`][pythonnative.platform_metrics.set_safe_area_insets]
|
|
154
|
+
instead.
|
|
155
|
+
"""
|
|
156
|
+
global _safe_area_insets
|
|
157
|
+
_safe_area_insets = SafeAreaInsets(0.0, 0.0, 0.0, 0.0)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def set_window_dimensions(width: float, height: float) -> None:
|
|
161
|
+
"""Publish the viewport size in layout units.
|
|
162
|
+
|
|
163
|
+
Called by the screen host on initial layout, rotation, and split-
|
|
164
|
+
view changes. Notifies subscribers (and therefore re-renders
|
|
165
|
+
components using ``use_window_dimensions``) only when the size
|
|
166
|
+
actually changes.
|
|
167
|
+
"""
|
|
168
|
+
global _window_dimensions
|
|
169
|
+
new_dims = WindowDimensions(max(0.0, float(width)), max(0.0, float(height)))
|
|
170
|
+
if new_dims == _window_dimensions:
|
|
171
|
+
return
|
|
172
|
+
_window_dimensions = new_dims
|
|
173
|
+
_notify_subscribers()
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def get_window_dimensions() -> WindowDimensions:
|
|
177
|
+
"""Return the current viewport size, or ``(0, 0)`` before first layout."""
|
|
178
|
+
return _window_dimensions
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def reset_window_dimensions() -> None:
|
|
182
|
+
"""Reset window dimensions back to ``(0, 0)``. Intended for tests."""
|
|
183
|
+
global _window_dimensions
|
|
184
|
+
_window_dimensions = WindowDimensions(0.0, 0.0)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def set_keyboard_height(height: float) -> None:
|
|
188
|
+
"""Publish the on-screen keyboard height in layout units.
|
|
189
|
+
|
|
190
|
+
Negative inputs are clamped to ``0.0``. Notifies subscribers only
|
|
191
|
+
when the value actually changes.
|
|
192
|
+
"""
|
|
193
|
+
global _keyboard_height
|
|
194
|
+
new_h = max(0.0, float(height))
|
|
195
|
+
if new_h == _keyboard_height:
|
|
196
|
+
return
|
|
197
|
+
_keyboard_height = new_h
|
|
198
|
+
_notify_subscribers()
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def get_keyboard_height() -> float:
|
|
202
|
+
"""Return the current on-screen keyboard height, or ``0.0`` if hidden."""
|
|
203
|
+
return _keyboard_height
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def reset_keyboard_height() -> None:
|
|
207
|
+
"""Reset the keyboard height back to ``0.0``. Intended for tests."""
|
|
208
|
+
global _keyboard_height
|
|
209
|
+
_keyboard_height = 0.0
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
# ======================================================================
|
|
213
|
+
# Per-platform tab-bar defaults
|
|
214
|
+
# ======================================================================
|
|
215
|
+
#
|
|
216
|
+
# Only iOS exposes an explicit constant here. The iOS handler can't
|
|
217
|
+
# trust ``UITabBar.sizeThatFits_`` (it has historically returned 0 in
|
|
218
|
+
# some configurations) and the screen host deliberately extends the
|
|
219
|
+
# root view past the bottom safe area so the bar reaches the home
|
|
220
|
+
# indicator; both pieces conspire to require a single source of
|
|
221
|
+
# truth for the height formula.
|
|
222
|
+
#
|
|
223
|
+
# Android intentionally has no equivalent: ``BottomNavigationView``
|
|
224
|
+
# reports a reliable natural height via ``measure(…)`` once attached
|
|
225
|
+
# to the window, and the active-indicator pill is positioned against
|
|
226
|
+
# that natural height. Forcing our own height threw off the pill
|
|
227
|
+
# geometry, so the Android handler defers entirely to the system.
|
|
228
|
+
|
|
229
|
+
IOS_TAB_BAR_BASE_HEIGHT_PT: float = 49.0
|
|
230
|
+
"""UIKit HIG tab-bar content height in points.
|
|
231
|
+
|
|
232
|
+
The total bar reaches ``IOS_TAB_BAR_BASE_HEIGHT_PT +
|
|
233
|
+
safe_area_insets.bottom`` so the pill background can extend over the
|
|
234
|
+
home indicator. Apple's HIG places the tab bar flush with the screen
|
|
235
|
+
edge and lets UIKit render its own internal padding for the home
|
|
236
|
+
indicator.
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def ios_tab_bar_height() -> float:
|
|
241
|
+
"""Return the iOS tab-bar intrinsic height in points.
|
|
242
|
+
|
|
243
|
+
Equal to ``IOS_TAB_BAR_BASE_HEIGHT_PT + safe_area_insets.bottom``
|
|
244
|
+
so the bar reaches the home indicator. The iOS screen host
|
|
245
|
+
deliberately extends the root view past the bottom safe area for
|
|
246
|
+
this very reason; the tab bar absorbs the inset and UIKit
|
|
247
|
+
renders the pill with internal padding for the home indicator.
|
|
248
|
+
The Swift ``TabBarManager`` applies the same formula natively;
|
|
249
|
+
this Python copy keeps the layout engine's expectations testable
|
|
250
|
+
off device.
|
|
251
|
+
"""
|
|
252
|
+
return IOS_TAB_BAR_BASE_HEIGHT_PT + get_safe_area_insets().bottom
|
pythonnative/preview.py
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"""Development server and browser preview.
|
|
2
|
+
|
|
3
|
+
The browser is a protocol-2 renderer with Yoga WebAssembly layout. Python runs
|
|
4
|
+
on the standard application asyncio loop; the transport's coordinator and
|
|
5
|
+
network threads forward messages to their owners. The dev server watches app
|
|
6
|
+
sources, synchronizes mobile clients, and applies compatible Fast Refresh.
|
|
7
|
+
|
|
8
|
+
PN_PLATFORM=web must be set before importing pythonnative; the CLI launches a
|
|
9
|
+
child process with that environment when needed.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
import sys
|
|
16
|
+
import threading
|
|
17
|
+
import time
|
|
18
|
+
import traceback
|
|
19
|
+
import webbrowser
|
|
20
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
21
|
+
|
|
22
|
+
__all__ = ["PreviewSession", "serve"]
|
|
23
|
+
|
|
24
|
+
Logger = Callable[[str], None]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class PreviewSession:
|
|
28
|
+
"""Everything one ``pn start`` invocation owns; see [`serve`][pythonnative.preview.serve]."""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
project_root: str,
|
|
33
|
+
entry_module: str,
|
|
34
|
+
*,
|
|
35
|
+
host: str = "0.0.0.0",
|
|
36
|
+
port: int = 8765,
|
|
37
|
+
project_name: str = "",
|
|
38
|
+
log: Optional[Logger] = None,
|
|
39
|
+
) -> None:
|
|
40
|
+
from .bridge.web import WebTransport
|
|
41
|
+
from .devserver import DevServer
|
|
42
|
+
|
|
43
|
+
self.project_root = os.path.abspath(project_root)
|
|
44
|
+
self.entry_module = entry_module
|
|
45
|
+
self.log: Logger = log or (lambda line: print(line, file=sys.stderr, flush=True))
|
|
46
|
+
self.server = DevServer(
|
|
47
|
+
self.project_root, entry_module, host=host, port=port, project_name=project_name, log=self.log
|
|
48
|
+
)
|
|
49
|
+
self.transport = WebTransport(log=self.log)
|
|
50
|
+
self.transport.on_peer_changed = self._on_peer_changed
|
|
51
|
+
self.transport.on_dev_message = self._on_dev_message
|
|
52
|
+
self._stop = threading.Event()
|
|
53
|
+
self._unsubscribe: Optional[Callable[[], None]] = None
|
|
54
|
+
|
|
55
|
+
# -- lifecycle -------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
def start(self) -> None:
|
|
58
|
+
"""Install the transport, start the server, and begin watching."""
|
|
59
|
+
from . import bridge, diagnostics
|
|
60
|
+
from .utils import IS_WEB
|
|
61
|
+
|
|
62
|
+
if not IS_WEB:
|
|
63
|
+
raise RuntimeError(
|
|
64
|
+
"The browser preview needs PN_PLATFORM=web before pythonnative is imported "
|
|
65
|
+
"(`pn start` and `pn preview` set it for you)."
|
|
66
|
+
)
|
|
67
|
+
if self.project_root not in sys.path:
|
|
68
|
+
sys.path.insert(0, self.project_root)
|
|
69
|
+
os.environ.setdefault("PN_ENTRY_MODULE", self.entry_module)
|
|
70
|
+
os.environ.setdefault("PN_STORAGE_DIR", os.path.join(self.project_root, "build", "preview", "storage"))
|
|
71
|
+
diagnostics.set_dev_mode(True)
|
|
72
|
+
# Bottom of the reporter stack: screens register their own RedBox
|
|
73
|
+
# above this, so it only sees errors raised before a screen exists
|
|
74
|
+
# (a failing import of the entry module, say).
|
|
75
|
+
diagnostics.set_error_reporter(self, self._report_error)
|
|
76
|
+
bridge.set_transport(self.transport)
|
|
77
|
+
from .runtime import start as start_runtime
|
|
78
|
+
|
|
79
|
+
start_runtime()
|
|
80
|
+
self.server.set_preview_channel(self.transport)
|
|
81
|
+
self._unsubscribe = self.server.add_change_listener(self._on_sources_changed)
|
|
82
|
+
self.server.start()
|
|
83
|
+
|
|
84
|
+
def run(self) -> None:
|
|
85
|
+
"""Run the main loop until ``stop`` (or Ctrl+C)."""
|
|
86
|
+
try:
|
|
87
|
+
self.transport.run_main_loop(until=self._stop.is_set)
|
|
88
|
+
except KeyboardInterrupt:
|
|
89
|
+
pass
|
|
90
|
+
|
|
91
|
+
def stop(self) -> None:
|
|
92
|
+
"""Tear everything down."""
|
|
93
|
+
self._stop.set()
|
|
94
|
+
self.transport.stop()
|
|
95
|
+
if self._unsubscribe is not None:
|
|
96
|
+
self._unsubscribe()
|
|
97
|
+
self._unsubscribe = None
|
|
98
|
+
try:
|
|
99
|
+
self._destroy_hosts()
|
|
100
|
+
except Exception:
|
|
101
|
+
pass
|
|
102
|
+
self.server.stop()
|
|
103
|
+
from . import bridge, diagnostics
|
|
104
|
+
|
|
105
|
+
diagnostics.set_error_reporter(self, None)
|
|
106
|
+
bridge.set_transport(None)
|
|
107
|
+
|
|
108
|
+
# -- source changes ----------------------------------------------------
|
|
109
|
+
|
|
110
|
+
def _on_sources_changed(self, change: Any, snapshot: Any) -> None:
|
|
111
|
+
"""Watcher thread: schedule a reload of the preview's own app."""
|
|
112
|
+
from .devserver.watcher import modules_for_paths
|
|
113
|
+
|
|
114
|
+
modules = modules_for_paths(list(change.changed) + list(change.removed))
|
|
115
|
+
if not modules:
|
|
116
|
+
return
|
|
117
|
+
|
|
118
|
+
def _apply() -> None:
|
|
119
|
+
from .hosts import live_hosts
|
|
120
|
+
from .hot_reload import apply_reload
|
|
121
|
+
|
|
122
|
+
hosts = list(live_hosts())
|
|
123
|
+
started = time.monotonic()
|
|
124
|
+
result = apply_reload(modules, hosts)
|
|
125
|
+
elapsed_ms = (time.monotonic() - started) * 1000.0
|
|
126
|
+
if result.mode == "error":
|
|
127
|
+
self.log("[pn] reload failed:")
|
|
128
|
+
for line in (result.error or "").rstrip().split("\n"):
|
|
129
|
+
self.log(f" {line}")
|
|
130
|
+
self.transport.send_dev({"type": "reload", "ok": False, "error": result.error, "modules": modules})
|
|
131
|
+
return
|
|
132
|
+
if result.mode == "none":
|
|
133
|
+
return
|
|
134
|
+
label = "Fast Refresh" if result.mode == "fast_refresh" else "Remounted"
|
|
135
|
+
self.log(f"[pn] {label}: {', '.join(result.reloaded)} ({elapsed_ms:.0f} ms)")
|
|
136
|
+
self.transport.send_dev(
|
|
137
|
+
{"type": "reload", "ok": True, "mode": result.mode, "modules": result.reloaded, "ms": elapsed_ms}
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
self.transport.post_to_application(_apply)
|
|
141
|
+
|
|
142
|
+
# -- peers -------------------------------------------------------------
|
|
143
|
+
|
|
144
|
+
def _on_peer_changed(self, connected: bool) -> None:
|
|
145
|
+
if connected:
|
|
146
|
+
self.log("[pn] browser preview connected")
|
|
147
|
+
self._send_hello()
|
|
148
|
+
return
|
|
149
|
+
self.log("[pn] browser preview disconnected")
|
|
150
|
+
self._destroy_hosts()
|
|
151
|
+
|
|
152
|
+
def _send_hello(self) -> None:
|
|
153
|
+
"""Tell the page what to mount; it answers by creating the entry screen."""
|
|
154
|
+
self.transport.send_dev(
|
|
155
|
+
{
|
|
156
|
+
"type": "hello",
|
|
157
|
+
"entry": self.entry_module,
|
|
158
|
+
"project": self.server.project_name or os.path.basename(self.project_root),
|
|
159
|
+
}
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
def _report_error(self, exc: BaseException, phase: str) -> None:
|
|
163
|
+
"""Print an error to the terminal and mirror it into the preview page."""
|
|
164
|
+
text = "".join(traceback.format_exception(type(exc), exc, exc.__traceback__)).rstrip()
|
|
165
|
+
self.log(f"[pn] error during {phase}:")
|
|
166
|
+
for line in text.split("\n"):
|
|
167
|
+
self.log(f" {line}")
|
|
168
|
+
self.transport.send_dev({"type": "error", "phase": phase, "text": text})
|
|
169
|
+
|
|
170
|
+
def _destroy_hosts(self) -> None:
|
|
171
|
+
"""Unmount every screen the page created (it is gone, so are its views)."""
|
|
172
|
+
from .hosts.native import live_hosts
|
|
173
|
+
from .native_views import get_registry
|
|
174
|
+
|
|
175
|
+
for host in list(live_hosts()):
|
|
176
|
+
try:
|
|
177
|
+
host.on_destroy()
|
|
178
|
+
except Exception:
|
|
179
|
+
traceback.print_exc()
|
|
180
|
+
backend = get_registry()
|
|
181
|
+
reset = getattr(backend, "reset", None)
|
|
182
|
+
if callable(reset):
|
|
183
|
+
reset()
|
|
184
|
+
|
|
185
|
+
def _on_dev_message(self, payload: Dict[str, Any]) -> None:
|
|
186
|
+
kind = payload.get("type")
|
|
187
|
+
if kind == "log":
|
|
188
|
+
self.log(f"[browser] {payload.get('text', '')}")
|
|
189
|
+
elif kind == "error":
|
|
190
|
+
self.log(f"[browser error] {payload.get('text', '')}")
|
|
191
|
+
elif kind == "hello":
|
|
192
|
+
agent = str(payload.get("user_agent", ""))
|
|
193
|
+
device = payload.get("device", "")
|
|
194
|
+
size = f"{payload.get('width', '?')}x{payload.get('height', '?')}"
|
|
195
|
+
self.log(f"[pn] preview page: {device} {size} ({_browser_family(agent)})")
|
|
196
|
+
elif kind == "remount":
|
|
197
|
+
self.log("[pn] remounting the app")
|
|
198
|
+
self._destroy_hosts()
|
|
199
|
+
self._send_hello()
|
|
200
|
+
|
|
201
|
+
# -- info ----------------------------------------------------------------
|
|
202
|
+
|
|
203
|
+
def urls(self) -> List[str]:
|
|
204
|
+
"""Every URL the server can be reached at (local first)."""
|
|
205
|
+
from .devserver import lan_addresses
|
|
206
|
+
|
|
207
|
+
info = self.server.info
|
|
208
|
+
urls = [info.url("localhost")]
|
|
209
|
+
for address in lan_addresses():
|
|
210
|
+
url = info.url(address)
|
|
211
|
+
if url not in urls:
|
|
212
|
+
urls.append(url)
|
|
213
|
+
return urls
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _browser_family(user_agent: str) -> str:
|
|
217
|
+
agent = user_agent.lower()
|
|
218
|
+
for needle, name in (("firefox", "Firefox"), ("edg/", "Edge"), ("chrome", "Chrome"), ("safari", "Safari")):
|
|
219
|
+
if needle in agent:
|
|
220
|
+
return name
|
|
221
|
+
return "browser"
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def serve(
|
|
225
|
+
entry_module: str,
|
|
226
|
+
*,
|
|
227
|
+
project_root: Optional[str] = None,
|
|
228
|
+
host: str = "0.0.0.0",
|
|
229
|
+
port: int = 8765,
|
|
230
|
+
project_name: str = "",
|
|
231
|
+
open_browser: bool = False,
|
|
232
|
+
log: Optional[Logger] = None,
|
|
233
|
+
banner: bool = True,
|
|
234
|
+
ready: Optional[Callable[[PreviewSession], None]] = None,
|
|
235
|
+
) -> None:
|
|
236
|
+
"""Run the dev server (and browser preview) until interrupted.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
entry_module: The app's entry module (``"app.main"``).
|
|
240
|
+
project_root: Directory containing ``app/``; defaults to the
|
|
241
|
+
current directory. Added to ``sys.path``.
|
|
242
|
+
host: Bind address (``0.0.0.0`` so devices can connect).
|
|
243
|
+
port: TCP port (``0`` picks a free one).
|
|
244
|
+
project_name: Shown in the preview page.
|
|
245
|
+
open_browser: Open the preview page in the default browser.
|
|
246
|
+
log: Where status lines go (stderr by default).
|
|
247
|
+
banner: Print the connection banner.
|
|
248
|
+
ready: Called once the server is listening (tests).
|
|
249
|
+
|
|
250
|
+
Raises:
|
|
251
|
+
RuntimeError: If ``PN_PLATFORM=web`` was not set before
|
|
252
|
+
PythonNative was imported (the CLI sets it for you).
|
|
253
|
+
OSError: If the port is taken.
|
|
254
|
+
"""
|
|
255
|
+
session = PreviewSession(
|
|
256
|
+
project_root or os.getcwd(),
|
|
257
|
+
entry_module,
|
|
258
|
+
host=host,
|
|
259
|
+
port=port,
|
|
260
|
+
project_name=project_name,
|
|
261
|
+
log=log,
|
|
262
|
+
)
|
|
263
|
+
session.start()
|
|
264
|
+
urls = session.urls()
|
|
265
|
+
if banner:
|
|
266
|
+
emit = session.log
|
|
267
|
+
emit("")
|
|
268
|
+
emit(f" PythonNative dev server for {project_name or entry_module}")
|
|
269
|
+
emit("")
|
|
270
|
+
emit(f" Browser preview: {urls[0]}")
|
|
271
|
+
for url in urls[1:]:
|
|
272
|
+
emit(f" Devices (LAN): {url}")
|
|
273
|
+
emit("")
|
|
274
|
+
emit(" Debug builds made with `pn run` connect here automatically.")
|
|
275
|
+
emit(" Press Ctrl+C to stop.")
|
|
276
|
+
emit("")
|
|
277
|
+
if open_browser:
|
|
278
|
+
try:
|
|
279
|
+
webbrowser.open(urls[0])
|
|
280
|
+
except Exception:
|
|
281
|
+
pass
|
|
282
|
+
if ready is not None:
|
|
283
|
+
ready(session)
|
|
284
|
+
try:
|
|
285
|
+
session.run()
|
|
286
|
+
finally:
|
|
287
|
+
session.stop()
|