pythonnative 0.40.0__cp313-cp313-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pythonnative/__init__.py +411 -0
- pythonnative/_ios_log.py +92 -0
- pythonnative/_yoga.cp313-win_amd64.pyd +0 -0
- pythonnative/alerts.py +236 -0
- pythonnative/animated.py +1687 -0
- pythonnative/animation_graph.py +78 -0
- pythonnative/appearance.py +124 -0
- pythonnative/bootstrap.py +100 -0
- pythonnative/bridge/__init__.py +302 -0
- pythonnative/bridge/android.py +78 -0
- pythonnative/bridge/codec.py +170 -0
- pythonnative/bridge/commits.py +114 -0
- pythonnative/bridge/fake.py +243 -0
- pythonnative/bridge/ios.py +138 -0
- pythonnative/bridge/web.py +489 -0
- pythonnative/cli/__init__.py +7 -0
- pythonnative/cli/pn.py +1214 -0
- pythonnative/component.py +271 -0
- pythonnative/components/__init__.py +111 -0
- pythonnative/components/_base.py +166 -0
- pythonnative/components/controls.py +521 -0
- pythonnative/components/layout.py +518 -0
- pythonnative/components/lists.py +785 -0
- pythonnative/components/media.py +204 -0
- pythonnative/components/overlays.py +107 -0
- pythonnative/components/pressable.py +252 -0
- pythonnative/components/structural.py +167 -0
- pythonnative/components/text.py +291 -0
- pythonnative/devclient.py +824 -0
- pythonnative/devserver/__init__.py +34 -0
- pythonnative/devserver/server.py +718 -0
- pythonnative/devserver/static/animation_graph.js +82 -0
- pythonnative/devserver/static/bridge.js +147 -0
- pythonnative/devserver/static/colors.js +70 -0
- pythonnative/devserver/static/host.js +552 -0
- pythonnative/devserver/static/index.html +60 -0
- pythonnative/devserver/static/layout.js +85 -0
- pythonnative/devserver/static/preview.css +415 -0
- pythonnative/devserver/static/renderer.js +1994 -0
- pythonnative/devserver/static/shell.js +345 -0
- pythonnative/devserver/static/yoga/LICENSE +21 -0
- pythonnative/devserver/static/yoga/binaries/yoga-wasm-base64-esm.js +75 -0
- pythonnative/devserver/static/yoga/src/generated/YGEnums.d.ts +189 -0
- pythonnative/devserver/static/yoga/src/generated/YGEnums.js +211 -0
- pythonnative/devserver/static/yoga/src/generated/YGEnums.js.map +1 -0
- pythonnative/devserver/static/yoga/src/index.d.ts +12 -0
- pythonnative/devserver/static/yoga/src/index.js +16 -0
- pythonnative/devserver/static/yoga/src/index.js.map +1 -0
- pythonnative/devserver/static/yoga/src/load.d.ts +11 -0
- pythonnative/devserver/static/yoga/src/load.js +17 -0
- pythonnative/devserver/static/yoga/src/load.js.map +1 -0
- pythonnative/devserver/static/yoga/src/wrapAssembly.d.ts +155 -0
- pythonnative/devserver/static/yoga/src/wrapAssembly.js +125 -0
- pythonnative/devserver/static/yoga/src/wrapAssembly.js.map +1 -0
- pythonnative/devserver/watcher.py +267 -0
- pythonnative/devserver/ws.py +421 -0
- pythonnative/diagnostics.py +241 -0
- pythonnative/element.py +159 -0
- pythonnative/equality.py +19 -0
- pythonnative/events.py +231 -0
- pythonnative/gestures.py +1333 -0
- pythonnative/hooks.py +1621 -0
- pythonnative/hosts/__init__.py +63 -0
- pythonnative/hosts/base.py +596 -0
- pythonnative/hosts/native.py +302 -0
- pythonnative/hot_reload.py +585 -0
- pythonnative/layout.py +328 -0
- pythonnative/mutations.py +142 -0
- pythonnative/native/android/build.gradle +52 -0
- pythonnative/native/android/gradle.properties +3 -0
- pythonnative/native/android/settings.gradle +12 -0
- pythonnative/native/android/src/main/AndroidManifest.xml +11 -0
- pythonnative/native/android/src/main/cpp/CMakeLists.txt +9 -0
- pythonnative/native/android/src/main/cpp/yoga_jni.cpp +52 -0
- pythonnative/native/android/src/main/java/com/pythonnative/generated/NativeModules.kt +5 -0
- pythonnative/native/android/src/main/java/com/pythonnative/generated/NativeProps.kt +3705 -0
- pythonnative/native/android/src/main/java/com/pythonnative/generated/PNContracts.kt +129 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/PNBridge.kt +261 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/PythonHost.kt +20 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/AnimationGraph.kt +108 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/AnimationSpecs.kt +141 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/PNAnimator.kt +257 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/CommitState.kt +106 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/JsonUtil.kt +175 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/MainThread.kt +40 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNLog.kt +33 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNRegistry.kt +82 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNTransaction.kt +80 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/TransactionApplier.kt +117 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/ViewRegistry.kt +74 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/BuiltinComponents.kt +37 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ButtonManager.kt +32 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ComponentManager.kt +213 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ContainerManagers.kt +69 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ControlManagers.kt +186 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/DatePickerManager.kt +123 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ImageLoader.kt +141 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ImageManager.kt +162 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ModalManager.kt +134 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PNColor.kt +201 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PickerManager.kt +86 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PortalManager.kt +55 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PressableManager.kt +122 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ScreenStackManager.kt +110 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ScrollViewManager.kt +214 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/SegmentedControlManager.kt +113 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/StatusBarManager.kt +52 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TabBarManager.kt +131 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TextInputManager.kt +250 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TextManager.kt +233 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ViewStyler.kt +355 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/VirtualListManager.kt +159 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/WebViewManager.kt +149 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureArbiter.kt +233 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureCoordinator.kt +127 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureRecognizers.kt +527 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/layout/NativeLayout.kt +145 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/layout/YogaNode.kt +29 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/BuiltinModules.kt +73 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/DeviceModules.kt +316 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/HostModule.kt +61 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/MediaModules.kt +200 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/NativeModule.kt +158 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/NotificationsModule.kt +100 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/PermissionsModule.kt +99 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/StorageModules.kt +89 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/SystemModules.kt +178 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/plugins/GeneratedPlugins.kt +16 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/screens/PNScreenFragment.kt +188 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/screens/ScreenRegistry.kt +80 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNAccessibilityDelegate.kt +48 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNBorderDrawable.kt +76 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNEditText.kt +18 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNFrameLayout.kt +49 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/AnimationSpecsTest.kt +75 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/GestureArbiterTest.kt +197 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/PNColorTest.kt +47 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/PNTransactionTest.kt +78 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/PromiseTest.kt +89 -0
- pythonnative/native/ios/Package.swift +23 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Animation/PNAnimationGraph.swift +199 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Animation/PNAnimator.swift +312 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNBridge.swift +255 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNCommit.swift +104 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNRegistry.swift +142 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNTransaction.swift +166 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNViewRegistry.swift +53 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNButtonManagers.swift +207 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNColor.swift +173 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNComponentManager.swift +183 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNContainerView.swift +56 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNControlManagers.swift +281 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNImageManager.swift +222 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNOverlayManagers.swift +243 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNPressableManager.swift +114 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNScreenStackManager.swift +107 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNScrollViewManager.swift +197 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTabBarManager.swift +106 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTextInputManager.swift +297 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTextManager.swift +230 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTransform.swift +82 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewManager.swift +86 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewState.swift +87 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewStyler.swift +340 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNVirtualListManager.swift +155 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNWebViewManager.swift +124 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Generated/NativeModules.swift +1 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Generated/NativeProps.swift +3775 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Generated/PNContracts.swift +46 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Gestures/PNGestureCoordinator.swift +260 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Layout/PNLayout.swift +173 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/AlertModule.swift +92 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/HostModule.swift +80 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/LifecycleModules.swift +174 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/MediaModules.swift +177 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/PNNativeModule.swift +168 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/PermissionModules.swift +266 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/SystemModules.swift +251 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Plugins/PNPluginRegistration.swift +8 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Screens/PNScreenRegistry.swift +46 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Screens/PNViewController.swift +237 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Support/PNCompat.swift +46 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Support/PNJSON.swift +173 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Support/PNLog.swift +47 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Support/PNWindow.swift +51 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNAnimationGraphTests.swift +26 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNColorTests.swift +50 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNCommitTests.swift +86 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNGestureTests.swift +101 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNManagerTests.swift +126 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNModuleTests.swift +111 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNTransactionTests.swift +91 -0
- pythonnative/native/yoga/LICENSE +21 -0
- pythonnative/native/yoga/Package.swift +9 -0
- pythonnative/native/yoga/VENDORED.md +2 -0
- pythonnative/native/yoga/include/PNStyle.h +10 -0
- pythonnative/native/yoga/include/module.modulemap +5 -0
- pythonnative/native/yoga/include/yoga/YGConfig.h +158 -0
- pythonnative/native/yoga/include/yoga/YGEnums.h +142 -0
- pythonnative/native/yoga/include/yoga/YGMacros.h +99 -0
- pythonnative/native/yoga/include/yoga/YGNode.h +304 -0
- pythonnative/native/yoga/include/yoga/YGNodeLayout.h +35 -0
- pythonnative/native/yoga/include/yoga/YGNodeStyle.h +130 -0
- pythonnative/native/yoga/include/yoga/YGPixelGrid.h +29 -0
- pythonnative/native/yoga/include/yoga/YGValue.h +84 -0
- pythonnative/native/yoga/include/yoga/Yoga.h +21 -0
- pythonnative/native/yoga/python.cpp +16 -0
- pythonnative/native/yoga/style.cpp +272 -0
- pythonnative/native/yoga/yoga/CMakeLists.txt +41 -0
- pythonnative/native/yoga/yoga/YGConfig.cpp +92 -0
- pythonnative/native/yoga/yoga/YGConfig.h +158 -0
- pythonnative/native/yoga/yoga/YGEnums.cpp +262 -0
- pythonnative/native/yoga/yoga/YGEnums.h +142 -0
- pythonnative/native/yoga/yoga/YGMacros.h +99 -0
- pythonnative/native/yoga/yoga/YGNode.cpp +366 -0
- pythonnative/native/yoga/yoga/YGNode.h +304 -0
- pythonnative/native/yoga/yoga/YGNodeLayout.cpp +92 -0
- pythonnative/native/yoga/yoga/YGNodeLayout.h +35 -0
- pythonnative/native/yoga/yoga/YGNodeStyle.cpp +405 -0
- pythonnative/native/yoga/yoga/YGNodeStyle.h +130 -0
- pythonnative/native/yoga/yoga/YGPixelGrid.cpp +22 -0
- pythonnative/native/yoga/yoga/YGPixelGrid.h +29 -0
- pythonnative/native/yoga/yoga/YGValue.cpp +20 -0
- pythonnative/native/yoga/yoga/YGValue.h +84 -0
- pythonnative/native/yoga/yoga/Yoga.h +21 -0
- pythonnative/native/yoga/yoga/algorithm/AbsoluteLayout.cpp +563 -0
- pythonnative/native/yoga/yoga/algorithm/AbsoluteLayout.h +41 -0
- pythonnative/native/yoga/yoga/algorithm/Align.h +72 -0
- pythonnative/native/yoga/yoga/algorithm/Baseline.cpp +78 -0
- pythonnative/native/yoga/yoga/algorithm/Baseline.h +21 -0
- pythonnative/native/yoga/yoga/algorithm/BoundAxis.h +79 -0
- pythonnative/native/yoga/yoga/algorithm/Cache.cpp +121 -0
- pythonnative/native/yoga/yoga/algorithm/Cache.h +30 -0
- pythonnative/native/yoga/yoga/algorithm/CalculateLayout.cpp +2428 -0
- pythonnative/native/yoga/yoga/algorithm/CalculateLayout.h +38 -0
- pythonnative/native/yoga/yoga/algorithm/FlexDirection.h +120 -0
- pythonnative/native/yoga/yoga/algorithm/FlexLine.cpp +124 -0
- pythonnative/native/yoga/yoga/algorithm/FlexLine.h +75 -0
- pythonnative/native/yoga/yoga/algorithm/PixelGrid.cpp +132 -0
- pythonnative/native/yoga/yoga/algorithm/PixelGrid.h +29 -0
- pythonnative/native/yoga/yoga/algorithm/SizingMode.h +73 -0
- pythonnative/native/yoga/yoga/algorithm/TrailingPosition.h +44 -0
- pythonnative/native/yoga/yoga/config/Config.cpp +135 -0
- pythonnative/native/yoga/yoga/config/Config.h +92 -0
- pythonnative/native/yoga/yoga/debug/AssertFatal.cpp +53 -0
- pythonnative/native/yoga/yoga/debug/AssertFatal.h +29 -0
- pythonnative/native/yoga/yoga/debug/Log.cpp +107 -0
- pythonnative/native/yoga/yoga/debug/Log.h +34 -0
- pythonnative/native/yoga/yoga/enums/Align.h +47 -0
- pythonnative/native/yoga/yoga/enums/BoxSizing.h +40 -0
- pythonnative/native/yoga/yoga/enums/Dimension.h +40 -0
- pythonnative/native/yoga/yoga/enums/Direction.h +41 -0
- pythonnative/native/yoga/yoga/enums/Display.h +41 -0
- pythonnative/native/yoga/yoga/enums/Edge.h +47 -0
- pythonnative/native/yoga/yoga/enums/Errata.h +41 -0
- pythonnative/native/yoga/yoga/enums/ExperimentalFeature.h +39 -0
- pythonnative/native/yoga/yoga/enums/FlexDirection.h +42 -0
- pythonnative/native/yoga/yoga/enums/Gutter.h +41 -0
- pythonnative/native/yoga/yoga/enums/Justify.h +44 -0
- pythonnative/native/yoga/yoga/enums/LogLevel.h +44 -0
- pythonnative/native/yoga/yoga/enums/MeasureMode.h +41 -0
- pythonnative/native/yoga/yoga/enums/NodeType.h +40 -0
- pythonnative/native/yoga/yoga/enums/Overflow.h +41 -0
- pythonnative/native/yoga/yoga/enums/PhysicalEdge.h +21 -0
- pythonnative/native/yoga/yoga/enums/PositionType.h +41 -0
- pythonnative/native/yoga/yoga/enums/Unit.h +42 -0
- pythonnative/native/yoga/yoga/enums/Wrap.h +41 -0
- pythonnative/native/yoga/yoga/enums/YogaEnums.h +85 -0
- pythonnative/native/yoga/yoga/event/event.cpp +87 -0
- pythonnative/native/yoga/yoga/event/event.h +130 -0
- pythonnative/native/yoga/yoga/module.modulemap +21 -0
- pythonnative/native/yoga/yoga/node/CachedMeasurement.h +53 -0
- pythonnative/native/yoga/yoga/node/LayoutResults.cpp +48 -0
- pythonnative/native/yoga/yoga/node/LayoutResults.h +122 -0
- pythonnative/native/yoga/yoga/node/LayoutableChildren.h +148 -0
- pythonnative/native/yoga/yoga/node/Node.cpp +443 -0
- pythonnative/native/yoga/yoga/node/Node.h +337 -0
- pythonnative/native/yoga/yoga/numeric/Comparison.h +81 -0
- pythonnative/native/yoga/yoga/numeric/FloatOptional.h +93 -0
- pythonnative/native/yoga/yoga/style/SmallValueBuffer.h +133 -0
- pythonnative/native/yoga/yoga/style/Style.h +757 -0
- pythonnative/native/yoga/yoga/style/StyleLength.h +107 -0
- pythonnative/native/yoga/yoga/style/StyleValueHandle.h +98 -0
- pythonnative/native/yoga/yoga/style/StyleValuePool.h +127 -0
- pythonnative/native_modules/__init__.py +124 -0
- pythonnative/native_modules/app_state.py +99 -0
- pythonnative/native_modules/battery.py +75 -0
- pythonnative/native_modules/biometrics.py +42 -0
- pythonnative/native_modules/camera.py +65 -0
- pythonnative/native_modules/clipboard.py +46 -0
- pythonnative/native_modules/fallback.py +324 -0
- pythonnative/native_modules/file_system.py +145 -0
- pythonnative/native_modules/haptics.py +75 -0
- pythonnative/native_modules/linking.py +123 -0
- pythonnative/native_modules/location.py +75 -0
- pythonnative/native_modules/net_info.py +112 -0
- pythonnative/native_modules/notifications.py +129 -0
- pythonnative/native_modules/permissions.py +108 -0
- pythonnative/native_modules/registry.py +526 -0
- pythonnative/native_modules/secure_store.py +52 -0
- pythonnative/native_modules/share.py +51 -0
- pythonnative/native_views/__init__.py +375 -0
- pythonnative/native_views/base.py +316 -0
- pythonnative/native_views/bridge_backend.py +277 -0
- pythonnative/navigation/__init__.py +103 -0
- pythonnative/navigation/container.py +154 -0
- pythonnative/navigation/handle.py +540 -0
- pythonnative/navigation/hooks.py +133 -0
- pythonnative/navigation/host.py +58 -0
- pythonnative/navigation/linking.py +200 -0
- pythonnative/navigation/navigators.py +659 -0
- pythonnative/navigation/screen.py +149 -0
- pythonnative/navigation/state.py +269 -0
- pythonnative/net.py +248 -0
- pythonnative/platform.py +166 -0
- pythonnative/platform_metrics.py +252 -0
- pythonnative/preview.py +287 -0
- pythonnative/profiling.py +101 -0
- pythonnative/project/__init__.py +68 -0
- pythonnative/project/android.py +557 -0
- pythonnative/project/builder.py +817 -0
- pythonnative/project/config.py +773 -0
- pythonnative/project/deps.py +719 -0
- pythonnative/project/devices.py +304 -0
- pythonnative/project/doctor.py +283 -0
- pythonnative/project/fingerprint.py +171 -0
- pythonnative/project/icons.py +247 -0
- pythonnative/project/ios.py +325 -0
- pythonnative/project/lockfile.py +100 -0
- pythonnative/project/permissions.py +361 -0
- pythonnative/project/plugins.py +519 -0
- pythonnative/project/runtime_assets.py +183 -0
- pythonnative/py.typed +0 -0
- pythonnative/query.py +153 -0
- pythonnative/reconciler/__init__.py +29 -0
- pythonnative/reconciler/boundaries.py +375 -0
- pythonnative/reconciler/children.py +88 -0
- pythonnative/reconciler/core.py +1165 -0
- pythonnative/reconciler/layout_pass.py +393 -0
- pythonnative/reconciler/vnode.py +266 -0
- pythonnative/refresh.py +55 -0
- pythonnative/runtime.py +306 -0
- pythonnative/scheduler.py +159 -0
- pythonnative/sdk/__init__.py +153 -0
- pythonnative/sdk/_components.py +427 -0
- pythonnative/sdk/builtins.py +91 -0
- pythonnative/sdk/codegen.py +149 -0
- pythonnative/sdk/module_codegen.py +184 -0
- pythonnative/sdk/schema.py +224 -0
- pythonnative/sdk/templates/contracts.kt +46 -0
- pythonnative/sdk/templates/contracts.swift +46 -0
- pythonnative/storage.py +184 -0
- pythonnative/style.py +841 -0
- pythonnative/suspense.py +183 -0
- pythonnative/templates/android_template/app/build.gradle +75 -0
- pythonnative/templates/android_template/app/proguard-rules.pro +21 -0
- pythonnative/templates/android_template/app/src/androidTest/java/com/pythonnative/android_template/ExampleInstrumentedTest.kt +24 -0
- pythonnative/templates/android_template/app/src/debug/AndroidManifest.xml +7 -0
- pythonnative/templates/android_template/app/src/main/AndroidManifest.xml +32 -0
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +130 -0
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt +13 -0
- pythonnative/templates/android_template/app/src/main/res/drawable/ic_launcher_background.xml +170 -0
- pythonnative/templates/android_template/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +30 -0
- pythonnative/templates/android_template/app/src/main/res/layout/activity_main.xml +10 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +6 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +6 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/navigation/nav_graph.xml +22 -0
- pythonnative/templates/android_template/app/src/main/res/values/colors.xml +5 -0
- pythonnative/templates/android_template/app/src/main/res/values/strings.xml +5 -0
- pythonnative/templates/android_template/app/src/main/res/values/themes.xml +9 -0
- pythonnative/templates/android_template/app/src/main/res/values-night/themes.xml +7 -0
- pythonnative/templates/android_template/app/src/main/res/xml/backup_rules.xml +13 -0
- pythonnative/templates/android_template/app/src/main/res/xml/data_extraction_rules.xml +19 -0
- pythonnative/templates/android_template/app/src/test/java/com/pythonnative/android_template/ExampleUnitTest.kt +17 -0
- pythonnative/templates/android_template/build.gradle +10 -0
- pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.jar +0 -0
- pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties +6 -0
- pythonnative/templates/android_template/gradle.properties +23 -0
- pythonnative/templates/android_template/gradlew +185 -0
- pythonnative/templates/android_template/gradlew.bat +89 -0
- pythonnative/templates/android_template/settings.gradle +17 -0
- pythonnative/templates/ios_template/ios_template/AppDelegate.swift +51 -0
- pythonnative/templates/ios_template/ios_template/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
- pythonnative/templates/ios_template/ios_template/Assets.xcassets/AppIcon.appiconset/Contents.json +13 -0
- pythonnative/templates/ios_template/ios_template/Assets.xcassets/Contents.json +6 -0
- pythonnative/templates/ios_template/ios_template/Base.lproj/LaunchScreen.storyboard +25 -0
- pythonnative/templates/ios_template/ios_template/BridgingHeader.h +14 -0
- pythonnative/templates/ios_template/ios_template/Info.plist +28 -0
- pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +390 -0
- pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +61 -0
- pythonnative/templates/ios_template/ios_template/ViewController.swift +23 -0
- pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +745 -0
- pythonnative/templates/ios_template/ios_template.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- pythonnative/templates/ios_template/ios_templateTests/ios_templateTests.swift +36 -0
- pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITests.swift +41 -0
- pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITestsLaunchTests.swift +32 -0
- pythonnative/testing/__init__.py +53 -0
- pythonnative/testing/backend.py +276 -0
- pythonnative/testing/harness.py +369 -0
- pythonnative/utils.py +145 -0
- pythonnative-0.40.0.dist-info/DELVEWHEEL +2 -0
- pythonnative-0.40.0.dist-info/METADATA +150 -0
- pythonnative-0.40.0.dist-info/RECORD +418 -0
- pythonnative-0.40.0.dist-info/WHEEL +5 -0
- pythonnative-0.40.0.dist-info/entry_points.txt +2 -0
- pythonnative-0.40.0.dist-info/licenses/LICENSE +21 -0
- pythonnative-0.40.0.dist-info/top_level.txt +1 -0
- pythonnative.libs/msvcp140-a4c2229bdc2a2a630acdc095b4d86008.dll +0 -0
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
"""Native module registry: name -> callable module, on device or off.
|
|
2
|
+
|
|
3
|
+
A *native module* is a named bag of methods implemented in Swift and
|
|
4
|
+
Kotlin (``Camera``, ``Storage``, ``Haptics``, ...). Python facades in
|
|
5
|
+
this package never touch platform APIs; they obtain a
|
|
6
|
+
[`NativeModule`][pythonnative.native_modules.registry.NativeModule]
|
|
7
|
+
through [`native_module`][pythonnative.native_modules.registry.native_module]
|
|
8
|
+
and call methods on it:
|
|
9
|
+
|
|
10
|
+
```python
|
|
11
|
+
_clipboard = native_module("Clipboard")
|
|
12
|
+
_clipboard.call("set_string", text="hello")
|
|
13
|
+
text = _clipboard.call("get_string")
|
|
14
|
+
result = await _camera.call_async("take_photo")
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
On device the module is a
|
|
18
|
+
[`BridgeModule`][pythonnative.native_modules.registry.BridgeModule]
|
|
19
|
+
that speaks the ``call(module, method, args_json)`` protocol described
|
|
20
|
+
in ``docs/concepts/bridge.md``. Off device (tests, ``pn preview``) the
|
|
21
|
+
same name resolves to a
|
|
22
|
+
[`PythonModule`][pythonnative.native_modules.registry.PythonModule]
|
|
23
|
+
wrapping a plain Python object with the same method names; the
|
|
24
|
+
built-in fallbacks live in ``pythonnative.native_modules.fallback`` and
|
|
25
|
+
third-party packages register theirs through the
|
|
26
|
+
``pythonnative.modules`` entry point group or
|
|
27
|
+
[`register_python_module`][pythonnative.native_modules.registry.register_python_module].
|
|
28
|
+
|
|
29
|
+
Modules can also push events (``AppState`` changes, deep links,
|
|
30
|
+
battery updates). Facades subscribe with
|
|
31
|
+
[`NativeModule.add_listener`][pythonnative.native_modules.registry.NativeModule.add_listener];
|
|
32
|
+
native delivers through
|
|
33
|
+
[`dispatch_module_message`][pythonnative.native_modules.registry.dispatch_module_message]
|
|
34
|
+
and Python implementations through
|
|
35
|
+
[`emit`][pythonnative.native_modules.registry.emit].
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
from __future__ import annotations
|
|
39
|
+
|
|
40
|
+
import asyncio
|
|
41
|
+
import inspect
|
|
42
|
+
import itertools
|
|
43
|
+
import threading
|
|
44
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
45
|
+
|
|
46
|
+
from ..bridge import codec
|
|
47
|
+
|
|
48
|
+
__all__ = [
|
|
49
|
+
"ENTRY_POINT_GROUP",
|
|
50
|
+
"BridgeModule",
|
|
51
|
+
"NativeModule",
|
|
52
|
+
"NativeModuleError",
|
|
53
|
+
"PythonModule",
|
|
54
|
+
"dispatch_module_message",
|
|
55
|
+
"emit",
|
|
56
|
+
"native_module",
|
|
57
|
+
"on_event",
|
|
58
|
+
"register_python_module",
|
|
59
|
+
"unregister_python_module",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
ENTRY_POINT_GROUP = "pythonnative.modules"
|
|
63
|
+
"""Entry-point group for Python (fallback / test) implementations of native modules."""
|
|
64
|
+
|
|
65
|
+
Listener = Callable[[Any], None]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class NativeModuleError(RuntimeError):
|
|
69
|
+
"""A native module method failed.
|
|
70
|
+
|
|
71
|
+
Attributes:
|
|
72
|
+
module: Module name.
|
|
73
|
+
method: Method name.
|
|
74
|
+
code: Optional machine-readable code supplied by native.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
def __init__(self, module: str, method: str, message: str, code: Optional[str] = None) -> None:
|
|
78
|
+
super().__init__(f"{module}.{method}: {message}")
|
|
79
|
+
self.module = module
|
|
80
|
+
self.method = method
|
|
81
|
+
self.message = message
|
|
82
|
+
self.code = code
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class NativeModule:
|
|
86
|
+
"""Common surface of bridge-backed and Python-backed modules."""
|
|
87
|
+
|
|
88
|
+
name: str
|
|
89
|
+
|
|
90
|
+
def __init__(self, name: str) -> None:
|
|
91
|
+
self.name = name
|
|
92
|
+
self._listeners: Dict[str, List[Listener]] = {}
|
|
93
|
+
self._lock = threading.Lock()
|
|
94
|
+
|
|
95
|
+
def call(self, method: str, **args: Any) -> Any:
|
|
96
|
+
"""Invoke ``method`` synchronously and return its value.
|
|
97
|
+
|
|
98
|
+
Raises:
|
|
99
|
+
NativeModuleError: When the native side reports a failure.
|
|
100
|
+
RuntimeError: When the method only completes asynchronously
|
|
101
|
+
(use [`call_async`][pythonnative.native_modules.registry.NativeModule.call_async]).
|
|
102
|
+
"""
|
|
103
|
+
raise NotImplementedError
|
|
104
|
+
|
|
105
|
+
async def call_async(self, method: str, **args: Any) -> Any:
|
|
106
|
+
"""Invoke ``method`` and await its result."""
|
|
107
|
+
raise NotImplementedError
|
|
108
|
+
|
|
109
|
+
def add_listener(self, event: str, callback: Listener) -> Callable[[], None]:
|
|
110
|
+
"""Subscribe to ``event``; returns an unsubscribe callable."""
|
|
111
|
+
with self._lock:
|
|
112
|
+
self._listeners.setdefault(event, []).append(callback)
|
|
113
|
+
|
|
114
|
+
def _remove() -> None:
|
|
115
|
+
with self._lock:
|
|
116
|
+
bucket = self._listeners.get(event)
|
|
117
|
+
if bucket is None:
|
|
118
|
+
return
|
|
119
|
+
try:
|
|
120
|
+
bucket.remove(callback)
|
|
121
|
+
except ValueError:
|
|
122
|
+
pass
|
|
123
|
+
if not bucket:
|
|
124
|
+
self._listeners.pop(event, None)
|
|
125
|
+
|
|
126
|
+
return _remove
|
|
127
|
+
|
|
128
|
+
def listener_count(self, event: Optional[str] = None) -> int:
|
|
129
|
+
"""Number of listeners for ``event`` (or for every event when ``None``)."""
|
|
130
|
+
with self._lock:
|
|
131
|
+
if event is None:
|
|
132
|
+
return sum(len(b) for b in self._listeners.values())
|
|
133
|
+
return len(self._listeners.get(event, ()))
|
|
134
|
+
|
|
135
|
+
def _deliver(self, event: str, payload: Any) -> None:
|
|
136
|
+
with self._lock:
|
|
137
|
+
callbacks = list(self._listeners.get(event, ()))
|
|
138
|
+
for callback in callbacks:
|
|
139
|
+
try:
|
|
140
|
+
callback(payload)
|
|
141
|
+
except Exception:
|
|
142
|
+
from .. import diagnostics
|
|
143
|
+
|
|
144
|
+
diagnostics.swallowed(f"native_modules.{self.name}.{event}")
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
# ======================================================================
|
|
148
|
+
# Bridge-backed modules (device)
|
|
149
|
+
# ======================================================================
|
|
150
|
+
|
|
151
|
+
_call_ids = itertools.count(1)
|
|
152
|
+
_pending: Dict[int, "asyncio.Future[Any]"] = {}
|
|
153
|
+
_pending_meta: Dict[int, tuple] = {}
|
|
154
|
+
_pending_lock = threading.Lock()
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class BridgeModule(NativeModule):
|
|
158
|
+
"""A module implemented natively; every call crosses the bridge once."""
|
|
159
|
+
|
|
160
|
+
def __init__(self, name: str, transport: Any = None) -> None:
|
|
161
|
+
super().__init__(name)
|
|
162
|
+
self._transport = transport
|
|
163
|
+
|
|
164
|
+
@property
|
|
165
|
+
def transport(self) -> Any:
|
|
166
|
+
"""The transport in use (resolved lazily on first access)."""
|
|
167
|
+
if self._transport is None:
|
|
168
|
+
from ..bridge import get_transport
|
|
169
|
+
|
|
170
|
+
self._transport = get_transport()
|
|
171
|
+
return self._transport
|
|
172
|
+
|
|
173
|
+
def _invoke(self, method: str, args: Dict[str, Any], call_id: int) -> Any:
|
|
174
|
+
envelope = codec.dumps({"call_id": call_id, "args": codec.to_jsonable(args)})
|
|
175
|
+
raw = self.transport.call(self.name, method, envelope)
|
|
176
|
+
result = codec.loads(raw)
|
|
177
|
+
if result is None:
|
|
178
|
+
return {"ok": True, "value": None}
|
|
179
|
+
if not isinstance(result, dict):
|
|
180
|
+
return {"ok": True, "value": result}
|
|
181
|
+
return result
|
|
182
|
+
|
|
183
|
+
def call(self, method: str, **args: Any) -> Any:
|
|
184
|
+
"""Call a native module method with a ``{"call_id", "args"}`` envelope."""
|
|
185
|
+
result = self._invoke(method, args, 0)
|
|
186
|
+
if result.get("pending"):
|
|
187
|
+
raise RuntimeError(f"{self.name}.{method} completes asynchronously; use call_async()")
|
|
188
|
+
if result.get("ok", True):
|
|
189
|
+
return result.get("value")
|
|
190
|
+
raise NativeModuleError(self.name, method, str(result.get("error", "unknown error")), result.get("code"))
|
|
191
|
+
|
|
192
|
+
async def call_async(self, method: str, **args: Any) -> Any:
|
|
193
|
+
"""Invoke ``method`` and await its result."""
|
|
194
|
+
loop = asyncio.get_running_loop()
|
|
195
|
+
call_id = next(_call_ids)
|
|
196
|
+
future: asyncio.Future[Any] = loop.create_future()
|
|
197
|
+
with _pending_lock:
|
|
198
|
+
_pending[call_id] = future
|
|
199
|
+
_pending_meta[call_id] = (self.name, method)
|
|
200
|
+
try:
|
|
201
|
+
result = self._invoke(method, args, call_id)
|
|
202
|
+
except Exception:
|
|
203
|
+
with _pending_lock:
|
|
204
|
+
_pending.pop(call_id, None)
|
|
205
|
+
_pending_meta.pop(call_id, None)
|
|
206
|
+
raise
|
|
207
|
+
if not result.get("pending"):
|
|
208
|
+
with _pending_lock:
|
|
209
|
+
_pending.pop(call_id, None)
|
|
210
|
+
_pending_meta.pop(call_id, None)
|
|
211
|
+
if result.get("ok", True):
|
|
212
|
+
return result.get("value")
|
|
213
|
+
raise NativeModuleError(self.name, method, str(result.get("error", "unknown error")), result.get("code"))
|
|
214
|
+
try:
|
|
215
|
+
return await future
|
|
216
|
+
except asyncio.CancelledError:
|
|
217
|
+
# Native implementations can release their work through the
|
|
218
|
+
# promise's cancellation hook; late settlements are discarded.
|
|
219
|
+
try:
|
|
220
|
+
self._invoke("_pn_cancel", {"call_id": call_id}, 0)
|
|
221
|
+
except Exception:
|
|
222
|
+
pass
|
|
223
|
+
raise
|
|
224
|
+
finally:
|
|
225
|
+
with _pending_lock:
|
|
226
|
+
_pending.pop(call_id, None)
|
|
227
|
+
_pending_meta.pop(call_id, None)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _settle(call_id: int, message: Dict[str, Any]) -> None:
|
|
231
|
+
with _pending_lock:
|
|
232
|
+
future = _pending.pop(call_id, None)
|
|
233
|
+
meta = _pending_meta.pop(call_id, ("?", "?"))
|
|
234
|
+
if future is None:
|
|
235
|
+
return
|
|
236
|
+
from ..runtime import reject_future, resolve_future
|
|
237
|
+
|
|
238
|
+
if message.get("ok", True):
|
|
239
|
+
resolve_future(future, message.get("value"))
|
|
240
|
+
else:
|
|
241
|
+
module, method = meta
|
|
242
|
+
reject_future(
|
|
243
|
+
future, NativeModuleError(module, method, str(message.get("error", "unknown error")), message.get("code"))
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
# ======================================================================
|
|
248
|
+
# Python-backed modules (fallbacks / tests)
|
|
249
|
+
# ======================================================================
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class PythonModule(NativeModule):
|
|
253
|
+
"""A module implemented by a plain Python object.
|
|
254
|
+
|
|
255
|
+
Methods are looked up by name on ``impl``; keyword arguments are
|
|
256
|
+
forwarded. A method may be a coroutine function (or return an
|
|
257
|
+
awaitable), in which case ``call`` raises and ``call_async`` awaits
|
|
258
|
+
it. The implementation can push events with
|
|
259
|
+
[`emit`][pythonnative.native_modules.registry.emit].
|
|
260
|
+
"""
|
|
261
|
+
|
|
262
|
+
def __init__(self, name: str, impl: Any = None) -> None:
|
|
263
|
+
super().__init__(name)
|
|
264
|
+
self._impl = impl
|
|
265
|
+
|
|
266
|
+
@property
|
|
267
|
+
def impl(self) -> Any:
|
|
268
|
+
"""The implementation object, resolved on first use.
|
|
269
|
+
|
|
270
|
+
Facades call ``native_module()`` at import time, so resolution is
|
|
271
|
+
deferred until the first method call; by then the package's
|
|
272
|
+
fallback implementation (or entry point) has had a chance to
|
|
273
|
+
register.
|
|
274
|
+
|
|
275
|
+
Raises:
|
|
276
|
+
KeyError: If no implementation is registered for the module.
|
|
277
|
+
"""
|
|
278
|
+
if self._impl is None:
|
|
279
|
+
self._impl = _resolve_python_impl(self.name)
|
|
280
|
+
return self._impl
|
|
281
|
+
|
|
282
|
+
def _method(self, method: str) -> Callable[..., Any]:
|
|
283
|
+
fn = getattr(self.impl, method, None)
|
|
284
|
+
if fn is None or not callable(fn):
|
|
285
|
+
raise NativeModuleError(self.name, method, "unknown method", code="unknown_method")
|
|
286
|
+
return fn
|
|
287
|
+
|
|
288
|
+
def call(self, method: str, **args: Any) -> Any:
|
|
289
|
+
"""Call a native module method with a ``{"call_id", "args"}`` envelope."""
|
|
290
|
+
fn = self._method(method)
|
|
291
|
+
if inspect.iscoroutinefunction(fn):
|
|
292
|
+
raise RuntimeError(f"{self.name}.{method} is asynchronous; use call_async()")
|
|
293
|
+
try:
|
|
294
|
+
value = fn(**args)
|
|
295
|
+
except NativeModuleError:
|
|
296
|
+
raise
|
|
297
|
+
except Exception as exc:
|
|
298
|
+
raise NativeModuleError(self.name, method, str(exc)) from exc
|
|
299
|
+
if inspect.isawaitable(value):
|
|
300
|
+
raise RuntimeError(f"{self.name}.{method} returned an awaitable; use call_async()")
|
|
301
|
+
return value
|
|
302
|
+
|
|
303
|
+
async def call_async(self, method: str, **args: Any) -> Any:
|
|
304
|
+
"""Invoke ``method`` and await its result."""
|
|
305
|
+
fn = self._method(method)
|
|
306
|
+
try:
|
|
307
|
+
value = fn(**args)
|
|
308
|
+
if inspect.isawaitable(value):
|
|
309
|
+
value = await value
|
|
310
|
+
except NativeModuleError:
|
|
311
|
+
raise
|
|
312
|
+
except Exception as exc:
|
|
313
|
+
raise NativeModuleError(self.name, method, str(exc)) from exc
|
|
314
|
+
return value
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
# ======================================================================
|
|
318
|
+
# Registry
|
|
319
|
+
# ======================================================================
|
|
320
|
+
|
|
321
|
+
_modules: Dict[str, NativeModule] = {}
|
|
322
|
+
_python_impls: Dict[str, Any] = {}
|
|
323
|
+
_registry_lock = threading.Lock()
|
|
324
|
+
_discovered = False
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def register_python_module(name: str, impl: Any) -> None:
|
|
328
|
+
"""Register ``impl`` as the off-device implementation of module ``name``.
|
|
329
|
+
|
|
330
|
+
``impl`` is an object (or a zero-arg factory returning one) whose
|
|
331
|
+
methods match the module's method names. Registering replaces any
|
|
332
|
+
earlier implementation and invalidates the cached module so the
|
|
333
|
+
next [`native_module`][pythonnative.native_modules.registry.native_module]
|
|
334
|
+
call picks it up.
|
|
335
|
+
"""
|
|
336
|
+
with _registry_lock:
|
|
337
|
+
_python_impls[name] = impl
|
|
338
|
+
_modules.pop(name, None)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def unregister_python_module(name: str) -> None:
|
|
342
|
+
"""Remove a Python implementation registered for ``name`` (tests)."""
|
|
343
|
+
with _registry_lock:
|
|
344
|
+
_python_impls.pop(name, None)
|
|
345
|
+
_modules.pop(name, None)
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def native_module(name: str) -> NativeModule:
|
|
349
|
+
"""Return the module registered under ``name`` for the current platform.
|
|
350
|
+
|
|
351
|
+
On iOS and Android this is always a
|
|
352
|
+
[`BridgeModule`][pythonnative.native_modules.registry.BridgeModule];
|
|
353
|
+
the native runtime decides whether the module exists when a method
|
|
354
|
+
is first called. Elsewhere it is the registered
|
|
355
|
+
[`PythonModule`][pythonnative.native_modules.registry.PythonModule].
|
|
356
|
+
|
|
357
|
+
Off device the implementation is resolved lazily, on the first
|
|
358
|
+
method call, so facades can call this at import time; a missing
|
|
359
|
+
implementation surfaces then as ``KeyError``.
|
|
360
|
+
"""
|
|
361
|
+
module = _modules.get(name)
|
|
362
|
+
if module is not None:
|
|
363
|
+
return module
|
|
364
|
+
with _registry_lock:
|
|
365
|
+
module = _modules.get(name)
|
|
366
|
+
if module is not None:
|
|
367
|
+
return module
|
|
368
|
+
module = _create(name)
|
|
369
|
+
_modules[name] = module
|
|
370
|
+
return module
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def _create(name: str) -> NativeModule:
|
|
374
|
+
from ..bridge import has_transport
|
|
375
|
+
|
|
376
|
+
if has_transport():
|
|
377
|
+
return BridgeModule(name)
|
|
378
|
+
return PythonModule(name)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def _resolve_python_impl(name: str) -> Any:
|
|
382
|
+
_discover_entry_points()
|
|
383
|
+
with _registry_lock:
|
|
384
|
+
impl = _python_impls.get(name)
|
|
385
|
+
if impl is None:
|
|
386
|
+
from . import fallback
|
|
387
|
+
|
|
388
|
+
impl = fallback.default_implementation(name)
|
|
389
|
+
if impl is None:
|
|
390
|
+
raise KeyError(
|
|
391
|
+
f"No native module named {name!r} is available off device; register one with register_python_module()"
|
|
392
|
+
)
|
|
393
|
+
if inspect.isclass(impl) or inspect.isfunction(impl):
|
|
394
|
+
impl = impl()
|
|
395
|
+
return impl
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def _discover_entry_points() -> None:
|
|
399
|
+
global _discovered
|
|
400
|
+
if _discovered:
|
|
401
|
+
return
|
|
402
|
+
_discovered = True
|
|
403
|
+
try:
|
|
404
|
+
from importlib.metadata import entry_points
|
|
405
|
+
except ImportError: # pragma: no cover
|
|
406
|
+
return
|
|
407
|
+
try:
|
|
408
|
+
eps = entry_points()
|
|
409
|
+
except Exception: # pragma: no cover
|
|
410
|
+
return
|
|
411
|
+
selected: List[Any] = []
|
|
412
|
+
if hasattr(eps, "select"):
|
|
413
|
+
try:
|
|
414
|
+
selected = list(eps.select(group=ENTRY_POINT_GROUP))
|
|
415
|
+
except Exception:
|
|
416
|
+
selected = []
|
|
417
|
+
if not selected:
|
|
418
|
+
getter = getattr(eps, "get", None)
|
|
419
|
+
if getter is not None:
|
|
420
|
+
try:
|
|
421
|
+
selected = list(getter(ENTRY_POINT_GROUP, []))
|
|
422
|
+
except Exception:
|
|
423
|
+
selected = []
|
|
424
|
+
for ep in selected:
|
|
425
|
+
try:
|
|
426
|
+
loaded = ep.load()
|
|
427
|
+
except Exception as exc: # pragma: no cover - defensive
|
|
428
|
+
import sys
|
|
429
|
+
|
|
430
|
+
print(
|
|
431
|
+
f"[pythonnative.native_modules] Failed to load module entry point {ep.name!r}: {exc!r}", file=sys.stderr
|
|
432
|
+
)
|
|
433
|
+
continue
|
|
434
|
+
module_name = getattr(loaded, "name", None) or ep.name
|
|
435
|
+
if inspect.isfunction(loaded):
|
|
436
|
+
try:
|
|
437
|
+
loaded()
|
|
438
|
+
except Exception as exc: # pragma: no cover - defensive
|
|
439
|
+
import sys
|
|
440
|
+
|
|
441
|
+
print(f"[pythonnative.native_modules] entry point {ep.name!r} raised: {exc!r}", file=sys.stderr)
|
|
442
|
+
continue
|
|
443
|
+
_python_impls.setdefault(str(module_name), loaded)
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
# ======================================================================
|
|
447
|
+
# Inbound messages and events
|
|
448
|
+
# ======================================================================
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def dispatch_module_message(module: str, message: Dict[str, Any]) -> None:
|
|
452
|
+
"""Route a native ``callback("module", ...)`` payload.
|
|
453
|
+
|
|
454
|
+
``{"call_id": n, "ok": ..., "value"|"error": ...}`` settles a
|
|
455
|
+
pending ``call_async``; ``{"event": name, "payload": ...}`` fans
|
|
456
|
+
out to listeners.
|
|
457
|
+
"""
|
|
458
|
+
if not isinstance(message, dict):
|
|
459
|
+
return
|
|
460
|
+
if "call_id" in message:
|
|
461
|
+
try:
|
|
462
|
+
_settle(int(message["call_id"]), message)
|
|
463
|
+
except (TypeError, ValueError):
|
|
464
|
+
pass
|
|
465
|
+
return
|
|
466
|
+
event = message.get("event")
|
|
467
|
+
if event:
|
|
468
|
+
emit(module, str(event), message.get("payload"))
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
_hooks: Dict[tuple, List[Listener]] = {}
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
def on_event(module: str, event: str, callback: Listener) -> Callable[[], None]:
|
|
475
|
+
"""Subscribe to ``module``/``event`` without resolving the module.
|
|
476
|
+
|
|
477
|
+
Facades use this at import time to route native pushes (``AppState``
|
|
478
|
+
``change``, ``Linking`` ``url``, ...) into their own listener lists.
|
|
479
|
+
Unlike [`NativeModule.add_listener`][pythonnative.native_modules.registry.NativeModule.add_listener]
|
|
480
|
+
the subscription survives module re-creation and never touches the
|
|
481
|
+
platform. Returns an unsubscribe callable.
|
|
482
|
+
"""
|
|
483
|
+
key = (module, event)
|
|
484
|
+
with _registry_lock:
|
|
485
|
+
_hooks.setdefault(key, []).append(callback)
|
|
486
|
+
|
|
487
|
+
def _remove() -> None:
|
|
488
|
+
with _registry_lock:
|
|
489
|
+
bucket = _hooks.get(key)
|
|
490
|
+
if bucket and callback in bucket:
|
|
491
|
+
bucket.remove(callback)
|
|
492
|
+
|
|
493
|
+
return _remove
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def emit(module: str, event: str, payload: Any = None) -> None:
|
|
497
|
+
"""Deliver ``event`` to every listener of ``module`` (any platform).
|
|
498
|
+
|
|
499
|
+
Python implementations use this to behave like their native
|
|
500
|
+
counterparts (a test can emit ``AppState`` ``change`` events, for
|
|
501
|
+
example).
|
|
502
|
+
"""
|
|
503
|
+
with _registry_lock:
|
|
504
|
+
hooks = list(_hooks.get((module, event), ()))
|
|
505
|
+
for hook in hooks:
|
|
506
|
+
try:
|
|
507
|
+
hook(payload)
|
|
508
|
+
except Exception:
|
|
509
|
+
from .. import diagnostics
|
|
510
|
+
|
|
511
|
+
diagnostics.swallowed(f"native_modules.{module}.{event}")
|
|
512
|
+
target = _modules.get(module)
|
|
513
|
+
if target is not None:
|
|
514
|
+
target._deliver(event, payload)
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
def _reset_for_tests() -> None:
|
|
518
|
+
"""Forget cached modules and pending calls (test isolation)."""
|
|
519
|
+
global _discovered
|
|
520
|
+
with _registry_lock:
|
|
521
|
+
_modules.clear()
|
|
522
|
+
_python_impls.clear()
|
|
523
|
+
_discovered = False
|
|
524
|
+
with _pending_lock:
|
|
525
|
+
_pending.clear()
|
|
526
|
+
_pending_meta.clear()
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Encrypted key/value storage for secrets (tokens, credentials).
|
|
2
|
+
|
|
3
|
+
[`SecureStore`][pythonnative.SecureStore] persists small string values
|
|
4
|
+
in the iOS Keychain and Android ``EncryptedSharedPreferences`` (the
|
|
5
|
+
native ``SecureStore`` module), the right place for auth tokens and
|
|
6
|
+
other secrets that [`AsyncStorage`][pythonnative.AsyncStorage] (plain,
|
|
7
|
+
unencrypted) should never hold.
|
|
8
|
+
|
|
9
|
+
Both backing stores complete on the calling thread, so every method is
|
|
10
|
+
synchronous. Reads return ``Optional[str]``; writes return nothing and
|
|
11
|
+
raise on failure. Off device the module falls back to an in-process dict
|
|
12
|
+
so code paths stay exercisable without a device Keychain.
|
|
13
|
+
|
|
14
|
+
Example:
|
|
15
|
+
```python
|
|
16
|
+
import pythonnative as pn
|
|
17
|
+
|
|
18
|
+
pn.SecureStore.set_item("token", "abc123")
|
|
19
|
+
token = pn.SecureStore.get_item("token")
|
|
20
|
+
```
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
from typing import Optional
|
|
26
|
+
|
|
27
|
+
from .registry import native_module
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class SecureStore:
|
|
31
|
+
"""Encrypted secret storage (synchronous).
|
|
32
|
+
|
|
33
|
+
Raises:
|
|
34
|
+
NativeModuleError: If the Keychain / EncryptedSharedPreferences
|
|
35
|
+
operation fails (for example a Keychain entitlement problem).
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def set_item(key: str, value: str) -> None:
|
|
40
|
+
"""Store ``value`` under ``key``, replacing any previous value."""
|
|
41
|
+
native_module("SecureStore").call("set_item", key=key, value=value)
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def get_item(key: str) -> Optional[str]:
|
|
45
|
+
"""Return the value for ``key``, or ``None`` if absent."""
|
|
46
|
+
value = native_module("SecureStore").call("get_item", key=key)
|
|
47
|
+
return None if value is None else str(value)
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def delete_item(key: str) -> bool:
|
|
51
|
+
"""Delete ``key``. Returns ``True`` if it existed, ``False`` if there was nothing to delete."""
|
|
52
|
+
return bool(native_module("SecureStore").call("delete_item", key=key))
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Present the system share sheet.
|
|
2
|
+
|
|
3
|
+
[`Share.share`][pythonnative.Share] is a coroutine that opens
|
|
4
|
+
``UIActivityViewController`` (iOS) or an ``ACTION_SEND`` chooser
|
|
5
|
+
(Android) through the native ``Share`` module and resolves to ``True``
|
|
6
|
+
once the user completes a share or ``False`` if they dismiss it.
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
```python
|
|
10
|
+
import pythonnative as pn
|
|
11
|
+
|
|
12
|
+
async def share_link():
|
|
13
|
+
await pn.Share.share(
|
|
14
|
+
message="Check out PythonNative!",
|
|
15
|
+
url="https://example.com",
|
|
16
|
+
)
|
|
17
|
+
```
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from typing import Optional
|
|
23
|
+
|
|
24
|
+
from .registry import native_module
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Share:
|
|
28
|
+
"""System share-sheet interface."""
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
async def share(
|
|
32
|
+
*,
|
|
33
|
+
message: Optional[str] = None,
|
|
34
|
+
url: Optional[str] = None,
|
|
35
|
+
title: Optional[str] = None,
|
|
36
|
+
) -> bool:
|
|
37
|
+
"""Open the share sheet with ``message`` / ``url``.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
message: Text body to share.
|
|
41
|
+
url: A URL to share (combined with ``message`` on Android).
|
|
42
|
+
title: Chooser title (Android) / subject (iOS mail).
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
``True`` if the user completed a share, ``False`` if they
|
|
46
|
+
dismissed the sheet or the platform has no share UI (tests).
|
|
47
|
+
|
|
48
|
+
Raises:
|
|
49
|
+
NativeModuleError: If the sheet could not be presented.
|
|
50
|
+
"""
|
|
51
|
+
return bool(await native_module("Share").call_async("share", message=message, url=url, title=title))
|