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/refresh.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Conservative component compatibility for Fast Refresh."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import ast
|
|
6
|
+
import inspect
|
|
7
|
+
import textwrap
|
|
8
|
+
from typing import Any, Callable
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def hook_signature(function: Callable[..., Any], _seen: frozenset[int] = frozenset()) -> tuple[str, ...] | None:
|
|
12
|
+
"""Record hook order and binding names before the source file changes.
|
|
13
|
+
|
|
14
|
+
Binding names distinguish inserting or moving hooks of the same kind.
|
|
15
|
+
Uninspectable functions remount instead of risking slot corruption.
|
|
16
|
+
"""
|
|
17
|
+
if id(function) in _seen:
|
|
18
|
+
return None
|
|
19
|
+
_seen = _seen | {id(function)}
|
|
20
|
+
try:
|
|
21
|
+
tree = ast.parse(textwrap.dedent(inspect.getsource(function)))
|
|
22
|
+
except (OSError, TypeError, SyntaxError):
|
|
23
|
+
return None
|
|
24
|
+
result = []
|
|
25
|
+
parents = {id(child): node for node in ast.walk(tree) for child in ast.iter_child_nodes(node)}
|
|
26
|
+
for node in ast.walk(tree):
|
|
27
|
+
if not isinstance(node, ast.Call):
|
|
28
|
+
continue
|
|
29
|
+
name = (
|
|
30
|
+
node.func.id
|
|
31
|
+
if isinstance(node.func, ast.Name)
|
|
32
|
+
else node.func.attr if isinstance(node.func, ast.Attribute) else ""
|
|
33
|
+
)
|
|
34
|
+
if not name.startswith("use_"):
|
|
35
|
+
continue
|
|
36
|
+
owner = parents.get(id(node))
|
|
37
|
+
binding = (
|
|
38
|
+
ast.dump(owner.targets[0])
|
|
39
|
+
if isinstance(owner, ast.Assign)
|
|
40
|
+
else ast.dump(owner.target) if isinstance(owner, ast.AnnAssign) else ""
|
|
41
|
+
)
|
|
42
|
+
signature = name + ":" + binding
|
|
43
|
+
target = None
|
|
44
|
+
if isinstance(node.func, ast.Name):
|
|
45
|
+
target = function.__globals__.get(node.func.id)
|
|
46
|
+
elif isinstance(node.func, ast.Attribute) and isinstance(node.func.value, ast.Name):
|
|
47
|
+
owner = function.__globals__.get(node.func.value.id)
|
|
48
|
+
target = getattr(owner, node.func.attr, None)
|
|
49
|
+
if inspect.isfunction(target) and not target.__module__.startswith("pythonnative."):
|
|
50
|
+
nested = hook_signature(target, _seen)
|
|
51
|
+
if nested is None:
|
|
52
|
+
return None
|
|
53
|
+
signature += repr(nested)
|
|
54
|
+
result.append((node.lineno, node.col_offset, signature))
|
|
55
|
+
return tuple(value for _, _, value in sorted(result))
|
pythonnative/runtime.py
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"""Application execution on a standard asyncio event loop.
|
|
2
|
+
|
|
3
|
+
Mobile hosts start one application thread with :func:`start`. All Python
|
|
4
|
+
rendering, callbacks, and tasks run there. Native hosts own their UI threads
|
|
5
|
+
and marshal view operations themselves. Headless programs can instead drive
|
|
6
|
+
an ordinary local loop with :func:`run_blocking` and :func:`drain`.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import asyncio
|
|
12
|
+
import contextvars
|
|
13
|
+
import inspect
|
|
14
|
+
import threading
|
|
15
|
+
from typing import Any, Awaitable, Callable, Coroutine, TypeVar
|
|
16
|
+
|
|
17
|
+
T = TypeVar("T")
|
|
18
|
+
Awaitlike = Coroutine[Any, Any, T] | Awaitable[T]
|
|
19
|
+
_loop: asyncio.AbstractEventLoop | None = None
|
|
20
|
+
_thread: threading.Thread | None = None
|
|
21
|
+
_owner: threading.Thread | None = None
|
|
22
|
+
_lock = threading.RLock()
|
|
23
|
+
_scope: contextvars.ContextVar[TaskScope | None] = contextvars.ContextVar("pn_task_scope", default=None)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class TaskScope:
|
|
27
|
+
"""Own tasks until an application or component is disposed.
|
|
28
|
+
|
|
29
|
+
Closing a scope cancels its tasks and rejects new work. A task removes
|
|
30
|
+
itself when it finishes, including when it fails or is cancelled.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, name: str = "application") -> None:
|
|
34
|
+
self.name = name
|
|
35
|
+
self.closed = False
|
|
36
|
+
self._tasks: set[Any] = set()
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def pending(self) -> int:
|
|
40
|
+
"""Number of unfinished tasks owned by this scope."""
|
|
41
|
+
return sum(not task.done() for task in self._tasks)
|
|
42
|
+
|
|
43
|
+
def create_task(self, awaitable: Awaitlike[T], *, report_errors: bool = False) -> Any:
|
|
44
|
+
"""Schedule work with this scope's lifetime and context."""
|
|
45
|
+
if self.closed:
|
|
46
|
+
if inspect.iscoroutine(awaitable):
|
|
47
|
+
awaitable.close()
|
|
48
|
+
raise RuntimeError(f"Task scope {self.name!r} is closed")
|
|
49
|
+
token = _scope.set(self)
|
|
50
|
+
try:
|
|
51
|
+
task = _schedule(awaitable)
|
|
52
|
+
finally:
|
|
53
|
+
_scope.reset(token)
|
|
54
|
+
self._tasks.add(task)
|
|
55
|
+
|
|
56
|
+
def finished(done: Any) -> None:
|
|
57
|
+
self._tasks.discard(done)
|
|
58
|
+
if report_errors and not done.cancelled():
|
|
59
|
+
error = done.exception()
|
|
60
|
+
if error is not None:
|
|
61
|
+
from .diagnostics import report_error
|
|
62
|
+
|
|
63
|
+
if not report_error(error, phase=f"task in {self.name}"):
|
|
64
|
+
get_loop().call_exception_handler(
|
|
65
|
+
{"message": f"Task in {self.name} failed", "exception": error}
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
task.add_done_callback(finished)
|
|
69
|
+
return task
|
|
70
|
+
|
|
71
|
+
def close(self) -> None:
|
|
72
|
+
"""Cancel owned work; calling this more than once is harmless."""
|
|
73
|
+
self.closed = True
|
|
74
|
+
for task in tuple(self._tasks):
|
|
75
|
+
if (
|
|
76
|
+
isinstance(task, asyncio.Future)
|
|
77
|
+
and task.get_loop().is_running()
|
|
78
|
+
and not _on_loop_thread(task.get_loop())
|
|
79
|
+
):
|
|
80
|
+
task.get_loop().call_soon_threadsafe(task.cancel)
|
|
81
|
+
else:
|
|
82
|
+
task.cancel()
|
|
83
|
+
self._tasks.clear()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
_application_scope = TaskScope()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def start() -> asyncio.AbstractEventLoop:
|
|
90
|
+
"""Start the application thread once and return its event loop."""
|
|
91
|
+
global _loop, _thread, _owner
|
|
92
|
+
with _lock:
|
|
93
|
+
if _thread is not None and _thread.is_alive():
|
|
94
|
+
assert _loop is not None
|
|
95
|
+
return _loop
|
|
96
|
+
if _loop is not None and not _loop.is_closed():
|
|
97
|
+
raise RuntimeError("Start the application runtime before creating headless tasks")
|
|
98
|
+
ready = threading.Event()
|
|
99
|
+
loop = asyncio.new_event_loop()
|
|
100
|
+
_loop = loop
|
|
101
|
+
|
|
102
|
+
def serve() -> None:
|
|
103
|
+
asyncio.set_event_loop(loop)
|
|
104
|
+
ready.set()
|
|
105
|
+
try:
|
|
106
|
+
loop.run_forever()
|
|
107
|
+
finally:
|
|
108
|
+
loop.run_until_complete(_cancel_tasks(loop))
|
|
109
|
+
loop.run_until_complete(loop.shutdown_asyncgens())
|
|
110
|
+
loop.run_until_complete(loop.shutdown_default_executor())
|
|
111
|
+
loop.close()
|
|
112
|
+
|
|
113
|
+
_thread = threading.Thread(target=serve, name="PythonNative", daemon=True)
|
|
114
|
+
_owner = _thread
|
|
115
|
+
_thread.start()
|
|
116
|
+
ready.wait()
|
|
117
|
+
return loop
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def get_loop() -> asyncio.AbstractEventLoop:
|
|
121
|
+
"""Return the application loop, or the caller's loop in headless mode."""
|
|
122
|
+
global _loop, _owner
|
|
123
|
+
if _thread is not None and _thread.is_alive():
|
|
124
|
+
assert _loop is not None
|
|
125
|
+
return _loop
|
|
126
|
+
try:
|
|
127
|
+
return asyncio.get_running_loop()
|
|
128
|
+
except RuntimeError:
|
|
129
|
+
with _lock:
|
|
130
|
+
if _loop is None or _loop.is_closed():
|
|
131
|
+
_loop = asyncio.new_event_loop()
|
|
132
|
+
_owner = threading.current_thread()
|
|
133
|
+
return _loop
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _on_loop_thread(loop: asyncio.AbstractEventLoop) -> bool:
|
|
137
|
+
try:
|
|
138
|
+
return asyncio.get_running_loop() is loop
|
|
139
|
+
except RuntimeError:
|
|
140
|
+
return loop is _loop and threading.current_thread() is _owner
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
async def _ensure_coro(awaitable: Awaitlike[T]) -> T:
|
|
144
|
+
return await awaitable
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _schedule(awaitable: Awaitlike[T]) -> Any:
|
|
148
|
+
loop = get_loop()
|
|
149
|
+
if _on_loop_thread(loop):
|
|
150
|
+
return asyncio.ensure_future(awaitable, loop=loop)
|
|
151
|
+
return asyncio.run_coroutine_threadsafe(_ensure_coro(awaitable), loop)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def run_async(awaitable: Awaitlike[T]) -> Any:
|
|
155
|
+
"""Schedule work in the current component scope or the application scope.
|
|
156
|
+
|
|
157
|
+
Returns an asyncio task on the application thread and a concurrent
|
|
158
|
+
future on other threads. Both handles support cancellation.
|
|
159
|
+
"""
|
|
160
|
+
return (_scope.get() or _application_scope).create_task(awaitable)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def run_application_task(awaitable: Awaitlike[T]) -> Any:
|
|
164
|
+
"""Start work that should survive the component that requested it."""
|
|
165
|
+
return _application_scope.create_task(awaitable)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def invoke(callback: Callable[..., Any], *args: Any, scope: TaskScope | None = None) -> Any:
|
|
169
|
+
"""Invoke a callback and schedule an awaitable result in its owner's scope."""
|
|
170
|
+
owner = scope or _scope.get() or _application_scope
|
|
171
|
+
if owner.closed:
|
|
172
|
+
return None
|
|
173
|
+
token = _scope.set(owner)
|
|
174
|
+
try:
|
|
175
|
+
result = callback(*args)
|
|
176
|
+
if inspect.isawaitable(result):
|
|
177
|
+
return owner.create_task(result, report_errors=True)
|
|
178
|
+
return result
|
|
179
|
+
finally:
|
|
180
|
+
_scope.reset(token)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def run_blocking(awaitable: Awaitlike[T], timeout: float | None = None) -> T:
|
|
184
|
+
"""Wait for work from synchronous code; never block the application loop."""
|
|
185
|
+
loop = get_loop()
|
|
186
|
+
if loop.is_running() and _on_loop_thread(loop):
|
|
187
|
+
if inspect.iscoroutine(awaitable):
|
|
188
|
+
awaitable.close()
|
|
189
|
+
raise RuntimeError("run_blocking() cannot run inside the event loop; await the result")
|
|
190
|
+
|
|
191
|
+
async def bounded() -> T:
|
|
192
|
+
async with asyncio.timeout(timeout):
|
|
193
|
+
return await awaitable
|
|
194
|
+
|
|
195
|
+
if loop.is_running():
|
|
196
|
+
return asyncio.run_coroutine_threadsafe(bounded(), loop).result()
|
|
197
|
+
return loop.run_until_complete(bounded())
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def drain(timeout: float = 1.0, *, until: Callable[[], bool] | None = None) -> bool:
|
|
201
|
+
"""Settle headless work or wait for application tasks from a test thread.
|
|
202
|
+
|
|
203
|
+
Long-lived application tasks require an explicit ``until`` predicate.
|
|
204
|
+
This uses public asyncio APIs and never inspects selector internals.
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
async def settle() -> bool:
|
|
208
|
+
loop = asyncio.get_running_loop()
|
|
209
|
+
deadline = loop.time() + timeout
|
|
210
|
+
idle_turns = 0
|
|
211
|
+
while loop.time() < deadline:
|
|
212
|
+
await asyncio.sleep(0)
|
|
213
|
+
if until is not None and until():
|
|
214
|
+
return True
|
|
215
|
+
pending = [task for task in asyncio.all_tasks() if task is not asyncio.current_task() and not task.done()]
|
|
216
|
+
if until is None and not pending:
|
|
217
|
+
idle_turns += 1
|
|
218
|
+
if idle_turns >= 3:
|
|
219
|
+
return True
|
|
220
|
+
else:
|
|
221
|
+
idle_turns = 0
|
|
222
|
+
await asyncio.sleep(0.001)
|
|
223
|
+
return False
|
|
224
|
+
|
|
225
|
+
return run_blocking(settle())
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def call_threadsafe(callback: Callable[..., Any], *args: Any) -> None:
|
|
229
|
+
"""Enqueue a callback on the application event loop from any thread."""
|
|
230
|
+
get_loop().call_soon_threadsafe(callback, *args)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def call_on_application_thread(fn: Callable[[], None]) -> None:
|
|
234
|
+
"""Execute Python work on its owner thread, inline when already there."""
|
|
235
|
+
loop = get_loop()
|
|
236
|
+
if _on_loop_thread(loop):
|
|
237
|
+
fn()
|
|
238
|
+
else:
|
|
239
|
+
loop.call_soon_threadsafe(fn)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def resolve_future(future: asyncio.Future[T], value: T) -> None:
|
|
243
|
+
"""Complete a future from any thread unless it was cancelled."""
|
|
244
|
+
|
|
245
|
+
def complete() -> None:
|
|
246
|
+
if not future.done():
|
|
247
|
+
future.set_result(value)
|
|
248
|
+
|
|
249
|
+
future.get_loop().call_soon_threadsafe(complete)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def reject_future(future: asyncio.Future[Any], error: BaseException) -> None:
|
|
253
|
+
"""Fail a future from any thread unless it was cancelled."""
|
|
254
|
+
|
|
255
|
+
def complete() -> None:
|
|
256
|
+
if not future.done():
|
|
257
|
+
future.set_exception(error)
|
|
258
|
+
|
|
259
|
+
future.get_loop().call_soon_threadsafe(complete)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def create_future() -> asyncio.Future[Any]:
|
|
263
|
+
"""Create a future for completion by a native request."""
|
|
264
|
+
return get_loop().create_future()
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
async def _cancel_tasks(loop: asyncio.AbstractEventLoop) -> None:
|
|
268
|
+
tasks = [task for task in asyncio.all_tasks(loop) if task is not asyncio.current_task()]
|
|
269
|
+
for task in tasks:
|
|
270
|
+
task.cancel()
|
|
271
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _shutdown_for_tests() -> None:
|
|
275
|
+
global _loop, _thread, _owner, _application_scope
|
|
276
|
+
with _lock:
|
|
277
|
+
loop, thread = _loop, _thread
|
|
278
|
+
_application_scope.close()
|
|
279
|
+
if loop is not None and not loop.is_closed():
|
|
280
|
+
if thread is not None:
|
|
281
|
+
loop.call_soon_threadsafe(loop.stop)
|
|
282
|
+
thread.join(timeout=5)
|
|
283
|
+
if thread.is_alive():
|
|
284
|
+
raise RuntimeError("Application thread did not stop")
|
|
285
|
+
elif not loop.is_running():
|
|
286
|
+
loop.run_until_complete(_cancel_tasks(loop))
|
|
287
|
+
loop.run_until_complete(loop.shutdown_asyncgens())
|
|
288
|
+
loop.close()
|
|
289
|
+
_loop = _thread = _owner = None
|
|
290
|
+
_application_scope = TaskScope()
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
__all__ = [
|
|
294
|
+
"TaskScope",
|
|
295
|
+
"call_on_application_thread",
|
|
296
|
+
"call_threadsafe",
|
|
297
|
+
"create_future",
|
|
298
|
+
"drain",
|
|
299
|
+
"get_loop",
|
|
300
|
+
"reject_future",
|
|
301
|
+
"resolve_future",
|
|
302
|
+
"run_application_task",
|
|
303
|
+
"run_async",
|
|
304
|
+
"run_blocking",
|
|
305
|
+
"start",
|
|
306
|
+
]
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""Update scheduling primitives shared by hooks and the reconciler.
|
|
2
|
+
|
|
3
|
+
Two pieces live here:
|
|
4
|
+
|
|
5
|
+
- [`batch_updates`][pythonnative.scheduler.batch_updates], a context manager that
|
|
6
|
+
coalesces the render triggers fired by several state setters into
|
|
7
|
+
one, tracked per execution context (so it composes with ``async``
|
|
8
|
+
code).
|
|
9
|
+
- [`TransitionQueue`][pythonnative.scheduler.TransitionQueue], the
|
|
10
|
+
per-reconciler queue behind
|
|
11
|
+
[`use_transition`][pythonnative.use_transition]: renders marked as
|
|
12
|
+
transitions are deferred to a later turn of the framework loop so
|
|
13
|
+
urgent updates (typing, presses) stay responsive.
|
|
14
|
+
|
|
15
|
+
Nothing here is global mutable state beyond context variables, so
|
|
16
|
+
several reconcilers (screens, list rows, tests) never interfere.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from contextlib import contextmanager
|
|
22
|
+
from contextvars import ContextVar
|
|
23
|
+
from typing import Callable, Generator, List, Optional
|
|
24
|
+
|
|
25
|
+
from . import diagnostics
|
|
26
|
+
|
|
27
|
+
__all__ = ["TransitionQueue", "batch_updates", "in_transition", "run_in_transition", "schedule_trigger"]
|
|
28
|
+
|
|
29
|
+
# Depth of nested ``batch_updates`` blocks plus the triggers deferred by
|
|
30
|
+
# the outermost block, per execution context.
|
|
31
|
+
_batch_depth: ContextVar[int] = ContextVar("pn_batch_depth", default=0)
|
|
32
|
+
_batch_pending: ContextVar[Optional[List[Callable[[], None]]]] = ContextVar("pn_batch_pending", default=None)
|
|
33
|
+
|
|
34
|
+
# Whether state updates in the current execution context are marked as
|
|
35
|
+
# transitions (see ``use_transition``).
|
|
36
|
+
_transition_var: ContextVar[bool] = ContextVar("pn_transition", default=False)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def schedule_trigger(trigger: Callable[[], None]) -> None:
|
|
40
|
+
"""Run ``trigger`` now, or defer it to the end of the enclosing ``batch_updates`` block."""
|
|
41
|
+
pending = _batch_pending.get()
|
|
42
|
+
if _batch_depth.get() > 0 and pending is not None:
|
|
43
|
+
if trigger not in pending:
|
|
44
|
+
pending.append(trigger)
|
|
45
|
+
else:
|
|
46
|
+
trigger()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@contextmanager
|
|
50
|
+
def batch_updates() -> Generator[None, None, None]:
|
|
51
|
+
"""Coalesce multiple state updates into a single re-render.
|
|
52
|
+
|
|
53
|
+
State setters called inside the ``with`` block defer their
|
|
54
|
+
re-render trigger until the block exits, so any number of
|
|
55
|
+
``set_*`` calls produce at most one render pass.
|
|
56
|
+
|
|
57
|
+
Example:
|
|
58
|
+
```python
|
|
59
|
+
import pythonnative as pn
|
|
60
|
+
|
|
61
|
+
with pn.batch_updates():
|
|
62
|
+
set_count(1)
|
|
63
|
+
set_name("hello")
|
|
64
|
+
```
|
|
65
|
+
"""
|
|
66
|
+
depth = _batch_depth.get()
|
|
67
|
+
depth_token = _batch_depth.set(depth + 1)
|
|
68
|
+
pending_token = None
|
|
69
|
+
if depth == 0:
|
|
70
|
+
pending_token = _batch_pending.set([])
|
|
71
|
+
try:
|
|
72
|
+
yield
|
|
73
|
+
finally:
|
|
74
|
+
_batch_depth.reset(depth_token)
|
|
75
|
+
if pending_token is not None:
|
|
76
|
+
triggers = _batch_pending.get() or []
|
|
77
|
+
_batch_pending.reset(pending_token)
|
|
78
|
+
for trigger in triggers:
|
|
79
|
+
trigger()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def in_transition() -> bool:
|
|
83
|
+
"""Whether state updates in the current context are transitions."""
|
|
84
|
+
return _transition_var.get()
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def run_in_transition(fn: Callable[[], None]) -> None:
|
|
88
|
+
"""Run ``fn`` with its state updates marked as transitions."""
|
|
89
|
+
token = _transition_var.set(True)
|
|
90
|
+
try:
|
|
91
|
+
fn()
|
|
92
|
+
finally:
|
|
93
|
+
_transition_var.reset(token)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class TransitionQueue:
|
|
97
|
+
"""Deferred render triggers and completion callbacks for one reconciler.
|
|
98
|
+
|
|
99
|
+
Triggers added via [`defer`][pythonnative.scheduler.TransitionQueue.defer]
|
|
100
|
+
run together on a later loop turn; callbacks added via
|
|
101
|
+
[`on_complete`][pythonnative.scheduler.TransitionQueue.on_complete]
|
|
102
|
+
run right after them (this is how ``use_transition`` flips its
|
|
103
|
+
``is_pending`` flag back off once the deferred render committed).
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
__slots__ = ("_triggers", "_callbacks", "_scheduled")
|
|
107
|
+
|
|
108
|
+
def __init__(self) -> None:
|
|
109
|
+
self._triggers: List[Callable[[], None]] = []
|
|
110
|
+
self._callbacks: List[Callable[[], None]] = []
|
|
111
|
+
self._scheduled = False
|
|
112
|
+
|
|
113
|
+
def defer(self, trigger: Callable[[], None]) -> None:
|
|
114
|
+
"""Queue ``trigger`` for the next flush."""
|
|
115
|
+
if trigger not in self._triggers:
|
|
116
|
+
self._triggers.append(trigger)
|
|
117
|
+
self._ensure_scheduled()
|
|
118
|
+
|
|
119
|
+
def on_complete(self, callback: Callable[[], None]) -> None:
|
|
120
|
+
"""Queue ``callback`` to run after the next flush's triggers."""
|
|
121
|
+
self._callbacks.append(callback)
|
|
122
|
+
self._ensure_scheduled()
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def pending(self) -> bool:
|
|
126
|
+
"""Whether a flush is scheduled or work is queued."""
|
|
127
|
+
return self._scheduled or bool(self._triggers) or bool(self._callbacks)
|
|
128
|
+
|
|
129
|
+
def flush(self) -> None:
|
|
130
|
+
"""Run every deferred trigger, then the completion callbacks."""
|
|
131
|
+
self._scheduled = False
|
|
132
|
+
triggers = self._triggers
|
|
133
|
+
callbacks = self._callbacks
|
|
134
|
+
self._triggers = []
|
|
135
|
+
self._callbacks = []
|
|
136
|
+
for fn in triggers + callbacks:
|
|
137
|
+
try:
|
|
138
|
+
fn()
|
|
139
|
+
except Exception as exc:
|
|
140
|
+
if not diagnostics.report_error(exc, phase="transition"):
|
|
141
|
+
raise
|
|
142
|
+
|
|
143
|
+
def clear(self) -> None:
|
|
144
|
+
"""Drop queued work (used on unmount)."""
|
|
145
|
+
self._triggers = []
|
|
146
|
+
self._callbacks = []
|
|
147
|
+
self._scheduled = False
|
|
148
|
+
|
|
149
|
+
def _ensure_scheduled(self) -> None:
|
|
150
|
+
if self._scheduled:
|
|
151
|
+
return
|
|
152
|
+
self._scheduled = True
|
|
153
|
+
from .runtime import get_loop
|
|
154
|
+
|
|
155
|
+
get_loop().call_soon(self._flush_if_scheduled)
|
|
156
|
+
|
|
157
|
+
def _flush_if_scheduled(self) -> None:
|
|
158
|
+
if self._scheduled:
|
|
159
|
+
self.flush()
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""Public extension surface for PythonNative.
|
|
2
|
+
|
|
3
|
+
The ``pythonnative.sdk`` package collects the *stable* extension
|
|
4
|
+
contract that third-party packages rely on: the
|
|
5
|
+
[`Style`][pythonnative.sdk.Style] type, the
|
|
6
|
+
[`@native_component`][pythonnative.sdk.native_component] /
|
|
7
|
+
[`register_component`][pythonnative.sdk.register_component]
|
|
8
|
+
registration helpers, the
|
|
9
|
+
[`element_factory`][pythonnative.sdk.element_factory] helper for
|
|
10
|
+
producing strongly-typed element constructors, the
|
|
11
|
+
[`ViewHandler`][pythonnative.sdk.ViewHandler] protocol for off-device
|
|
12
|
+
stand-ins, and the native module registry
|
|
13
|
+
([`native_module`][pythonnative.sdk.native_module],
|
|
14
|
+
[`register_python_module`][pythonnative.sdk.register_python_module]).
|
|
15
|
+
|
|
16
|
+
A custom native component is three things:
|
|
17
|
+
|
|
18
|
+
1. A typed, frozen [`Props`][pythonnative.sdk.Props] dataclass listing
|
|
19
|
+
the public properties the component accepts.
|
|
20
|
+
2. A Swift ``PNComponentManager`` and a Kotlin ``ComponentManager``
|
|
21
|
+
registered under the component's name by the package's native
|
|
22
|
+
plugin (``pn_plugin.json`` next to ``ios/`` and ``android/`` source
|
|
23
|
+
folders; see ``docs/guides/custom-components.md``).
|
|
24
|
+
3. A registration call in Python
|
|
25
|
+
([`register_component`][pythonnative.sdk.register_component], or the
|
|
26
|
+
[`@native_component`][pythonnative.sdk.native_component] decorator
|
|
27
|
+
when you also supply a test
|
|
28
|
+
[`ViewHandler`][pythonnative.sdk.ViewHandler] for the Python backend)
|
|
29
|
+
that declares the element name and binds its props type.
|
|
30
|
+
|
|
31
|
+
Once registered, the component appears alongside the built-ins: the
|
|
32
|
+
reconciler, layout engine, and Fast Refresh treat it identically.
|
|
33
|
+
|
|
34
|
+
A native module (device API without a view) follows the same split: a
|
|
35
|
+
Swift / Kotlin class registered by name in the plugin, a Python facade
|
|
36
|
+
that calls ``native_module(name).call(...)``, and optionally a Python
|
|
37
|
+
implementation registered with ``register_python_module`` for the
|
|
38
|
+
browser preview and tests.
|
|
39
|
+
|
|
40
|
+
PyPI packages can ship both without users importing them explicitly
|
|
41
|
+
by declaring entry points in the ``pythonnative.handlers`` (Python
|
|
42
|
+
side) and ``pythonnative.plugins`` (native source) groups; ``pn build``
|
|
43
|
+
compiles the native sources into the app.
|
|
44
|
+
|
|
45
|
+
Example:
|
|
46
|
+
```python
|
|
47
|
+
from dataclasses import dataclass
|
|
48
|
+
import pythonnative as pn
|
|
49
|
+
from pythonnative.sdk import Props, element_factory, register_component
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass(frozen=True)
|
|
53
|
+
class BadgeProps(Props):
|
|
54
|
+
text: str = ""
|
|
55
|
+
color: str = "#FF3B30"
|
|
56
|
+
style: pn.StyleProp = None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
register_component(name="Badge", props=BadgeProps)
|
|
60
|
+
Badge = element_factory("Badge")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@pn.component
|
|
64
|
+
def App():
|
|
65
|
+
return pn.Column(
|
|
66
|
+
Badge(text="3", color="#0A84FF"),
|
|
67
|
+
pn.Text("Inbox"),
|
|
68
|
+
)
|
|
69
|
+
```
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
from ..element import Element
|
|
73
|
+
from ..native_modules.registry import (
|
|
74
|
+
NativeModule,
|
|
75
|
+
NativeModuleError,
|
|
76
|
+
PythonModule,
|
|
77
|
+
emit,
|
|
78
|
+
native_module,
|
|
79
|
+
register_python_module,
|
|
80
|
+
)
|
|
81
|
+
from ..native_views.base import ViewHandler, parse_color_int
|
|
82
|
+
from ..style import (
|
|
83
|
+
Color,
|
|
84
|
+
Dimension,
|
|
85
|
+
EdgeInsets,
|
|
86
|
+
EdgeValue,
|
|
87
|
+
FlexDirection,
|
|
88
|
+
JustifyContent,
|
|
89
|
+
Overflow,
|
|
90
|
+
Position,
|
|
91
|
+
Style,
|
|
92
|
+
StyleProp,
|
|
93
|
+
TransformSpec,
|
|
94
|
+
style,
|
|
95
|
+
)
|
|
96
|
+
from ._components import (
|
|
97
|
+
ENTRY_POINT_GROUP,
|
|
98
|
+
Props,
|
|
99
|
+
element_factory,
|
|
100
|
+
get_props_type,
|
|
101
|
+
get_test_handler,
|
|
102
|
+
install_into_registry,
|
|
103
|
+
list_components,
|
|
104
|
+
native_component,
|
|
105
|
+
register_component,
|
|
106
|
+
unregister_component,
|
|
107
|
+
)
|
|
108
|
+
from .codegen import generate
|
|
109
|
+
from .schema import ComponentSchema, ModuleSchema, NativeField, register_schema
|
|
110
|
+
|
|
111
|
+
__all__ = [
|
|
112
|
+
"ComponentSchema",
|
|
113
|
+
"ModuleSchema",
|
|
114
|
+
"NativeField",
|
|
115
|
+
"register_schema",
|
|
116
|
+
"generate",
|
|
117
|
+
# Core types
|
|
118
|
+
"Element",
|
|
119
|
+
"ViewHandler",
|
|
120
|
+
# Style types
|
|
121
|
+
"Color",
|
|
122
|
+
"Dimension",
|
|
123
|
+
"EdgeInsets",
|
|
124
|
+
"EdgeValue",
|
|
125
|
+
"FlexDirection",
|
|
126
|
+
"JustifyContent",
|
|
127
|
+
"Overflow",
|
|
128
|
+
"Position",
|
|
129
|
+
"Style",
|
|
130
|
+
"StyleProp",
|
|
131
|
+
"TransformSpec",
|
|
132
|
+
"style",
|
|
133
|
+
# SDK helpers (re-exported so users only import from one place)
|
|
134
|
+
"parse_color_int",
|
|
135
|
+
# Native-component SDK
|
|
136
|
+
"ENTRY_POINT_GROUP",
|
|
137
|
+
"Props",
|
|
138
|
+
"element_factory",
|
|
139
|
+
"get_test_handler",
|
|
140
|
+
"get_props_type",
|
|
141
|
+
"install_into_registry",
|
|
142
|
+
"list_components",
|
|
143
|
+
"native_component",
|
|
144
|
+
"register_component",
|
|
145
|
+
"unregister_component",
|
|
146
|
+
# Native-module SDK
|
|
147
|
+
"NativeModule",
|
|
148
|
+
"NativeModuleError",
|
|
149
|
+
"PythonModule",
|
|
150
|
+
"emit",
|
|
151
|
+
"native_module",
|
|
152
|
+
"register_python_module",
|
|
153
|
+
]
|