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,65 @@
|
|
|
1
|
+
"""Cross-platform camera and gallery access.
|
|
2
|
+
|
|
3
|
+
Both entry points are coroutines: ``await Camera.take_photo()`` returns
|
|
4
|
+
the saved image path (a ``str``) or ``None`` if the user cancels. The
|
|
5
|
+
native ``Camera`` module presents ``UIImagePickerController`` (iOS) or
|
|
6
|
+
launches ``MediaStore.ACTION_IMAGE_CAPTURE`` / ``ACTION_PICK``
|
|
7
|
+
(Android) and resolves the call when the picker finishes; Python only
|
|
8
|
+
awaits the promise.
|
|
9
|
+
|
|
10
|
+
Example:
|
|
11
|
+
```python
|
|
12
|
+
import pythonnative as pn
|
|
13
|
+
|
|
14
|
+
async def add_photo():
|
|
15
|
+
path = await pn.Camera.take_photo()
|
|
16
|
+
if path is None:
|
|
17
|
+
return # user cancelled
|
|
18
|
+
await save_to_album(path)
|
|
19
|
+
```
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from typing import Any, Optional
|
|
25
|
+
|
|
26
|
+
from .registry import native_module
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Camera:
|
|
30
|
+
"""Camera and image-picker interface (static coroutines)."""
|
|
31
|
+
|
|
32
|
+
@staticmethod
|
|
33
|
+
async def take_photo(**options: Any) -> Optional[str]:
|
|
34
|
+
"""Launch the device camera to capture a photo.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
**options: Forwarded to the native module (``quality``,
|
|
38
|
+
``allow_editing``, ...). Unknown keys are ignored.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
The saved image path, or ``None`` if the user cancelled (or
|
|
42
|
+
there is no camera to present, as in the browser preview).
|
|
43
|
+
|
|
44
|
+
Raises:
|
|
45
|
+
NativeModuleError: If the picker can't be presented, for
|
|
46
|
+
example because another picker is already open.
|
|
47
|
+
"""
|
|
48
|
+
return await _launch("take_photo", options)
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
async def pick_from_gallery(**options: Any) -> Optional[str]:
|
|
52
|
+
"""Open the system gallery picker.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
The selected image path, or ``None`` if the user cancelled.
|
|
56
|
+
|
|
57
|
+
Raises:
|
|
58
|
+
NativeModuleError: If the picker can't be presented.
|
|
59
|
+
"""
|
|
60
|
+
return await _launch("pick_from_gallery", options)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
async def _launch(method: str, options: Any) -> Optional[str]:
|
|
64
|
+
result = await native_module("Camera").call_async(method, **options)
|
|
65
|
+
return str(result) if result else None
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Cross-platform clipboard access.
|
|
2
|
+
|
|
3
|
+
[`Clipboard`][pythonnative.Clipboard] reads and writes the system
|
|
4
|
+
pasteboard through the native ``Clipboard`` module (``UIPasteboard``
|
|
5
|
+
on iOS, ``ClipboardManager`` on Android). The pasteboard lives in
|
|
6
|
+
process memory on both platforms, so every method is synchronous.
|
|
7
|
+
|
|
8
|
+
Off device the module is a process-local string buffer, which keeps it
|
|
9
|
+
usable in ``pn preview`` and unit tests.
|
|
10
|
+
|
|
11
|
+
Example:
|
|
12
|
+
```python
|
|
13
|
+
import pythonnative as pn
|
|
14
|
+
|
|
15
|
+
pn.Clipboard.set_string("hello")
|
|
16
|
+
assert pn.Clipboard.get_string() == "hello"
|
|
17
|
+
```
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from .registry import native_module
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Clipboard:
|
|
26
|
+
"""System clipboard interface (synchronous).
|
|
27
|
+
|
|
28
|
+
Raises:
|
|
29
|
+
NativeModuleError: If the native module reports a failure.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
@staticmethod
|
|
33
|
+
def set_string(text: str) -> None:
|
|
34
|
+
"""Copy ``text`` onto the system clipboard."""
|
|
35
|
+
native_module("Clipboard").call("set_string", text="" if text is None else str(text))
|
|
36
|
+
|
|
37
|
+
@staticmethod
|
|
38
|
+
def get_string() -> str:
|
|
39
|
+
"""Return the current clipboard string (``""`` when empty)."""
|
|
40
|
+
value = native_module("Clipboard").call("get_string")
|
|
41
|
+
return "" if value is None else str(value)
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def has_string() -> bool:
|
|
45
|
+
"""Return ``True`` when the clipboard holds non-empty text."""
|
|
46
|
+
return bool(Clipboard.get_string())
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
"""Pure-Python fallbacks for the built-in native modules.
|
|
2
|
+
|
|
3
|
+
On iOS and Android every module in this package is implemented in
|
|
4
|
+
Swift and Kotlin (``PythonNativeKit`` and the ``pythonnative`` Gradle
|
|
5
|
+
module). Off device the same module names resolve to the plain Python
|
|
6
|
+
classes below, which keep the API usable without a device: in-memory
|
|
7
|
+
buffers, ``"unknown"`` states, and no-op feedback. Unit tests use them
|
|
8
|
+
directly; the browser preview implements a handful of modules in the
|
|
9
|
+
page (``Alert``, ``Clipboard``, ``Device``, ...) and routes the rest
|
|
10
|
+
here.
|
|
11
|
+
|
|
12
|
+
Each class has the same method names and argument shapes as its native
|
|
13
|
+
counterpart, so a facade in this package never branches on platform.
|
|
14
|
+
Apps and tests may swap any of these with
|
|
15
|
+
[`register_python_module`][pythonnative.native_modules.registry.register_python_module].
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import json
|
|
21
|
+
import os
|
|
22
|
+
import platform as _platform
|
|
23
|
+
import sys
|
|
24
|
+
import threading
|
|
25
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
26
|
+
|
|
27
|
+
__all__ = ["default_implementation"]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# ======================================================================
|
|
31
|
+
# Device / host information
|
|
32
|
+
# ======================================================================
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class FallbackDevice:
|
|
36
|
+
"""Static device information for the host machine."""
|
|
37
|
+
|
|
38
|
+
def info(self) -> Dict[str, Any]:
|
|
39
|
+
home = os.path.expanduser("~")
|
|
40
|
+
app_dir = os.path.join(home, ".pythonnative_data")
|
|
41
|
+
return {
|
|
42
|
+
"platform": "test",
|
|
43
|
+
"os": sys.platform,
|
|
44
|
+
"os_version": _platform.release(),
|
|
45
|
+
"model": _platform.machine(),
|
|
46
|
+
"app_dir": app_dir,
|
|
47
|
+
"cache_dir": os.path.join(app_dir, "cache"),
|
|
48
|
+
"temp_dir": os.path.join(app_dir, "tmp"),
|
|
49
|
+
"locale": os.environ.get("LANG", "en_US").split(".")[0],
|
|
50
|
+
"app_version": "0.0.0",
|
|
51
|
+
"build_number": "0",
|
|
52
|
+
"bundle_id": "com.pythonnative.preview",
|
|
53
|
+
"python_version": _platform.python_version(),
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class FallbackAppState:
|
|
58
|
+
def current_state(self) -> str:
|
|
59
|
+
return "active"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# ======================================================================
|
|
63
|
+
# Storage
|
|
64
|
+
# ======================================================================
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class FallbackStorage:
|
|
68
|
+
"""Dict-backed ``AsyncStorage`` with optional JSON persistence.
|
|
69
|
+
|
|
70
|
+
Set ``PN_STORAGE_DIR`` to persist between runs (``pn preview`` does
|
|
71
|
+
this); leave it unset in tests for a purely in-memory store.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def __init__(self) -> None:
|
|
75
|
+
self._lock = threading.Lock()
|
|
76
|
+
self._store: Dict[str, str] = {}
|
|
77
|
+
self._loaded = False
|
|
78
|
+
|
|
79
|
+
def _path(self) -> Optional[str]:
|
|
80
|
+
base = os.environ.get("PN_STORAGE_DIR")
|
|
81
|
+
if not base:
|
|
82
|
+
return None
|
|
83
|
+
try:
|
|
84
|
+
os.makedirs(base, exist_ok=True)
|
|
85
|
+
except OSError:
|
|
86
|
+
return None
|
|
87
|
+
return os.path.join(base, "pn_async_storage.json")
|
|
88
|
+
|
|
89
|
+
def _load(self) -> None:
|
|
90
|
+
if self._loaded:
|
|
91
|
+
return
|
|
92
|
+
self._loaded = True
|
|
93
|
+
path = self._path()
|
|
94
|
+
if path is None or not os.path.exists(path):
|
|
95
|
+
return
|
|
96
|
+
try:
|
|
97
|
+
with open(path, encoding="utf-8") as f:
|
|
98
|
+
data = json.load(f)
|
|
99
|
+
except (OSError, json.JSONDecodeError):
|
|
100
|
+
return
|
|
101
|
+
if isinstance(data, dict):
|
|
102
|
+
self._store.update({str(k): str(v) for k, v in data.items()})
|
|
103
|
+
|
|
104
|
+
def _persist(self) -> None:
|
|
105
|
+
path = self._path()
|
|
106
|
+
if path is None:
|
|
107
|
+
return
|
|
108
|
+
try:
|
|
109
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
110
|
+
json.dump(self._store, f)
|
|
111
|
+
except OSError:
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
def get(self, key: str) -> Optional[str]:
|
|
115
|
+
with self._lock:
|
|
116
|
+
self._load()
|
|
117
|
+
return self._store.get(key)
|
|
118
|
+
|
|
119
|
+
def set(self, key: str, value: str) -> None:
|
|
120
|
+
with self._lock:
|
|
121
|
+
self._load()
|
|
122
|
+
self._store[key] = value
|
|
123
|
+
self._persist()
|
|
124
|
+
|
|
125
|
+
def delete(self, key: str) -> None:
|
|
126
|
+
with self._lock:
|
|
127
|
+
self._load()
|
|
128
|
+
self._store.pop(key, None)
|
|
129
|
+
self._persist()
|
|
130
|
+
|
|
131
|
+
def all_keys(self) -> List[str]:
|
|
132
|
+
with self._lock:
|
|
133
|
+
self._load()
|
|
134
|
+
return list(self._store.keys())
|
|
135
|
+
|
|
136
|
+
def clear(self) -> None:
|
|
137
|
+
with self._lock:
|
|
138
|
+
self._store.clear()
|
|
139
|
+
self._persist()
|
|
140
|
+
|
|
141
|
+
def _reset(self) -> None:
|
|
142
|
+
"""Forget everything, including the on-disk snapshot (tests)."""
|
|
143
|
+
with self._lock:
|
|
144
|
+
self._store.clear()
|
|
145
|
+
self._loaded = False
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class FallbackSecureStore:
|
|
149
|
+
def __init__(self) -> None:
|
|
150
|
+
self._store: Dict[str, str] = {}
|
|
151
|
+
|
|
152
|
+
def set_item(self, key: str, value: str) -> bool:
|
|
153
|
+
self._store[key] = value
|
|
154
|
+
return True
|
|
155
|
+
|
|
156
|
+
def get_item(self, key: str) -> Optional[str]:
|
|
157
|
+
return self._store.get(key)
|
|
158
|
+
|
|
159
|
+
def delete_item(self, key: str) -> bool:
|
|
160
|
+
return self._store.pop(key, None) is not None
|
|
161
|
+
|
|
162
|
+
def clear(self) -> None:
|
|
163
|
+
self._store.clear()
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class FallbackClipboard:
|
|
167
|
+
def __init__(self) -> None:
|
|
168
|
+
self._buffer = ""
|
|
169
|
+
|
|
170
|
+
def set_string(self, text: str) -> None:
|
|
171
|
+
self._buffer = "" if text is None else str(text)
|
|
172
|
+
|
|
173
|
+
def get_string(self) -> str:
|
|
174
|
+
return self._buffer
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
# ======================================================================
|
|
178
|
+
# System integration
|
|
179
|
+
# ======================================================================
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class FallbackAlert:
|
|
183
|
+
"""Records alerts and answers with scripted responses.
|
|
184
|
+
|
|
185
|
+
The log and response queue live on
|
|
186
|
+
[`Alert`][pythonnative.alerts.Alert] (``Alert._test_log`` and
|
|
187
|
+
``Alert.set_test_response``) so tests have one place to look.
|
|
188
|
+
"""
|
|
189
|
+
|
|
190
|
+
@staticmethod
|
|
191
|
+
def _record(title: str, message: Optional[str], buttons: List[Dict[str, Any]], style: str) -> Any:
|
|
192
|
+
from ..alerts import Alert
|
|
193
|
+
|
|
194
|
+
Alert._test_log.append({"title": title, "message": message, "buttons": list(buttons), "style": style})
|
|
195
|
+
return Alert
|
|
196
|
+
|
|
197
|
+
def show(self, title: str, message: Optional[str], buttons: List[Dict[str, Any]], style: str) -> None:
|
|
198
|
+
self._record(title, message, buttons, style)
|
|
199
|
+
|
|
200
|
+
def present(self, title: str, message: Optional[str], buttons: List[Dict[str, Any]], style: str) -> int:
|
|
201
|
+
return int(self._record(title, message, buttons, style)._next_test_response())
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class FallbackShare:
|
|
205
|
+
def share(self, message: Optional[str] = None, url: Optional[str] = None, title: Optional[str] = None) -> bool:
|
|
206
|
+
del message, url, title
|
|
207
|
+
return False
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class FallbackLinking:
|
|
211
|
+
def open_url(self, url: str) -> bool:
|
|
212
|
+
del url
|
|
213
|
+
return False
|
|
214
|
+
|
|
215
|
+
def can_open_url(self, url: str) -> bool:
|
|
216
|
+
del url
|
|
217
|
+
return False
|
|
218
|
+
|
|
219
|
+
def open_settings(self) -> bool:
|
|
220
|
+
return False
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class FallbackHaptics:
|
|
224
|
+
def impact(self, style: str = "medium") -> None:
|
|
225
|
+
del style
|
|
226
|
+
|
|
227
|
+
def notification(self, type: str = "success") -> None: # noqa: A002 - wire name
|
|
228
|
+
del type
|
|
229
|
+
|
|
230
|
+
def selection(self) -> None:
|
|
231
|
+
pass
|
|
232
|
+
|
|
233
|
+
def vibrate(self, duration_ms: int = 400) -> None:
|
|
234
|
+
del duration_ms
|
|
235
|
+
|
|
236
|
+
def cancel(self) -> None:
|
|
237
|
+
pass
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
class FallbackBattery:
|
|
241
|
+
def get_level(self) -> float:
|
|
242
|
+
return -1.0
|
|
243
|
+
|
|
244
|
+
def get_state(self) -> str:
|
|
245
|
+
return "unknown"
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
class FallbackNetInfo:
|
|
249
|
+
def fetch(self) -> Optional[Dict[str, Any]]:
|
|
250
|
+
# ``None`` means "no fresh reading"; the facade keeps whatever
|
|
251
|
+
# snapshot was last dispatched (tests push those directly).
|
|
252
|
+
return None
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class FallbackPermissions:
|
|
256
|
+
def check(self, permission: str) -> str:
|
|
257
|
+
del permission
|
|
258
|
+
return "undetermined"
|
|
259
|
+
|
|
260
|
+
def request(self, permission: str) -> str:
|
|
261
|
+
del permission
|
|
262
|
+
return "undetermined"
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
class FallbackNotifications:
|
|
266
|
+
def request_permission(self) -> bool:
|
|
267
|
+
return False
|
|
268
|
+
|
|
269
|
+
def schedule(self, title: str, body: str = "", delay_seconds: float = 0, identifier: str = "default") -> bool:
|
|
270
|
+
del title, body, delay_seconds, identifier
|
|
271
|
+
return False
|
|
272
|
+
|
|
273
|
+
def cancel(self, identifier: str = "default") -> None:
|
|
274
|
+
del identifier
|
|
275
|
+
|
|
276
|
+
def get_device_token(self) -> Optional[str]:
|
|
277
|
+
return None
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
class FallbackCamera:
|
|
281
|
+
def take_photo(self) -> Optional[str]:
|
|
282
|
+
return None
|
|
283
|
+
|
|
284
|
+
def pick_from_gallery(self) -> Optional[str]:
|
|
285
|
+
return None
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
class FallbackLocation:
|
|
289
|
+
def get_current(self) -> Optional[Dict[str, float]]:
|
|
290
|
+
return None
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
class FallbackBiometrics:
|
|
294
|
+
def is_available(self) -> bool:
|
|
295
|
+
return False
|
|
296
|
+
|
|
297
|
+
def authenticate(self, reason: str = "Authenticate") -> bool:
|
|
298
|
+
del reason
|
|
299
|
+
return False
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
_DEFAULTS: Dict[str, Callable[[], Any]] = {
|
|
303
|
+
"Device": FallbackDevice,
|
|
304
|
+
"AppState": FallbackAppState,
|
|
305
|
+
"Storage": FallbackStorage,
|
|
306
|
+
"SecureStore": FallbackSecureStore,
|
|
307
|
+
"Clipboard": FallbackClipboard,
|
|
308
|
+
"Alert": FallbackAlert,
|
|
309
|
+
"Share": FallbackShare,
|
|
310
|
+
"Linking": FallbackLinking,
|
|
311
|
+
"Haptics": FallbackHaptics,
|
|
312
|
+
"Battery": FallbackBattery,
|
|
313
|
+
"NetInfo": FallbackNetInfo,
|
|
314
|
+
"Permissions": FallbackPermissions,
|
|
315
|
+
"Notifications": FallbackNotifications,
|
|
316
|
+
"Camera": FallbackCamera,
|
|
317
|
+
"Location": FallbackLocation,
|
|
318
|
+
"Biometrics": FallbackBiometrics,
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def default_implementation(name: str) -> Optional[Callable[[], Any]]:
|
|
323
|
+
"""Return the factory for the built-in Python fallback implementation of ``name``."""
|
|
324
|
+
return _DEFAULTS.get(name)
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""App-scoped file I/O.
|
|
2
|
+
|
|
3
|
+
[`FileSystem`][pythonnative.FileSystem] answers one question the
|
|
4
|
+
standard library can't, "where may this app write?", and then gets out
|
|
5
|
+
of the way: [`app_dir`][pythonnative.native_modules.file_system.FileSystem.app_dir]
|
|
6
|
+
comes from the native ``Device`` module, and
|
|
7
|
+
[`path`][pythonnative.native_modules.file_system.FileSystem.path] turns
|
|
8
|
+
an app-relative name into a ``pathlib.Path`` you use like any other.
|
|
9
|
+
The read/write helpers are thin conveniences over that path; they
|
|
10
|
+
raise the same ``OSError`` subclasses ``open`` and ``os`` do
|
|
11
|
+
(``FileNotFoundError``, ``PermissionError``, ...) rather than hiding
|
|
12
|
+
them behind ``None`` and ``False``.
|
|
13
|
+
|
|
14
|
+
Relative paths are resolved against ``app_dir``; absolute paths are used
|
|
15
|
+
as-is. Everything here is synchronous: it is local disk I/O on the
|
|
16
|
+
calling thread, exactly like the standard library.
|
|
17
|
+
|
|
18
|
+
Example:
|
|
19
|
+
```python
|
|
20
|
+
from pythonnative import FileSystem
|
|
21
|
+
|
|
22
|
+
FileSystem.write_text("notes/today.txt", "Hello, file system!")
|
|
23
|
+
print(FileSystem.read_text("notes/today.txt"))
|
|
24
|
+
|
|
25
|
+
# Or work with the Path directly:
|
|
26
|
+
notes = FileSystem.path("notes")
|
|
27
|
+
for entry in sorted(notes.iterdir()):
|
|
28
|
+
print(entry.name)
|
|
29
|
+
```
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
import os
|
|
35
|
+
from pathlib import Path
|
|
36
|
+
from typing import Any, List, Optional, Union
|
|
37
|
+
|
|
38
|
+
from .registry import native_module
|
|
39
|
+
|
|
40
|
+
PathLike = Union[str, "os.PathLike[str]"]
|
|
41
|
+
|
|
42
|
+
_app_dir_cache: Optional[str] = None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class FileSystem:
|
|
46
|
+
"""App-scoped file I/O.
|
|
47
|
+
|
|
48
|
+
Every helper accepts an absolute path or a path relative to
|
|
49
|
+
[`app_dir`][pythonnative.native_modules.file_system.FileSystem.app_dir],
|
|
50
|
+
and raises ``OSError`` (or a subclass) when the operation fails,
|
|
51
|
+
like the standard library it wraps.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def app_dir() -> str:
|
|
56
|
+
"""Return the app's writable data directory.
|
|
57
|
+
|
|
58
|
+
On Android the result is ``Context.getFilesDir()``. On iOS it is
|
|
59
|
+
the app's Documents directory. Off device, without
|
|
60
|
+
either runtime, a ``.pythonnative_data`` directory under the
|
|
61
|
+
user's home folder is used. The value comes from the native
|
|
62
|
+
``Device`` module's ``info()`` and is cached after the first
|
|
63
|
+
call.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Absolute path to the app's data directory.
|
|
67
|
+
"""
|
|
68
|
+
global _app_dir_cache
|
|
69
|
+
if _app_dir_cache is None:
|
|
70
|
+
info = native_module("Device").call("info")
|
|
71
|
+
path = info.get("app_dir") if isinstance(info, dict) else None
|
|
72
|
+
_app_dir_cache = str(path) if path else os.path.join(os.path.expanduser("~"), ".pythonnative_data")
|
|
73
|
+
return _app_dir_cache
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
def path(path: PathLike = "") -> Path:
|
|
77
|
+
"""Resolve ``path`` against ``app_dir`` and return it as a ``pathlib.Path``.
|
|
78
|
+
|
|
79
|
+
Absolute paths are returned unchanged. With no argument, returns
|
|
80
|
+
``app_dir`` itself.
|
|
81
|
+
"""
|
|
82
|
+
candidate = Path(path)
|
|
83
|
+
return candidate if candidate.is_absolute() else Path(FileSystem.app_dir()) / candidate
|
|
84
|
+
|
|
85
|
+
@staticmethod
|
|
86
|
+
def read_text(path: PathLike, encoding: str = "utf-8") -> str:
|
|
87
|
+
"""Read a text file; raises ``FileNotFoundError`` and friends like ``open`` does."""
|
|
88
|
+
return FileSystem.path(path).read_text(encoding=encoding)
|
|
89
|
+
|
|
90
|
+
@staticmethod
|
|
91
|
+
def write_text(path: PathLike, content: str, encoding: str = "utf-8") -> None:
|
|
92
|
+
"""Write a text file, creating parent directories as needed."""
|
|
93
|
+
target = FileSystem.path(path)
|
|
94
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
95
|
+
target.write_text(content, encoding=encoding)
|
|
96
|
+
|
|
97
|
+
@staticmethod
|
|
98
|
+
def read_bytes(path: PathLike) -> bytes:
|
|
99
|
+
"""Read a binary file."""
|
|
100
|
+
return FileSystem.path(path).read_bytes()
|
|
101
|
+
|
|
102
|
+
@staticmethod
|
|
103
|
+
def write_bytes(path: PathLike, data: bytes) -> None:
|
|
104
|
+
"""Write a binary file, creating parent directories as needed."""
|
|
105
|
+
target = FileSystem.path(path)
|
|
106
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
107
|
+
target.write_bytes(data)
|
|
108
|
+
|
|
109
|
+
@staticmethod
|
|
110
|
+
def exists(path: PathLike) -> bool:
|
|
111
|
+
"""Return whether a file or directory exists."""
|
|
112
|
+
return FileSystem.path(path).exists()
|
|
113
|
+
|
|
114
|
+
@staticmethod
|
|
115
|
+
def delete(path: PathLike, *, missing_ok: bool = False) -> None:
|
|
116
|
+
"""Delete a single file.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
path: Absolute or ``app_dir``-relative path.
|
|
120
|
+
missing_ok: Ignore a missing file instead of raising
|
|
121
|
+
``FileNotFoundError`` (same as ``Path.unlink``).
|
|
122
|
+
"""
|
|
123
|
+
FileSystem.path(path).unlink(missing_ok=missing_ok)
|
|
124
|
+
|
|
125
|
+
@staticmethod
|
|
126
|
+
def list_dir(path: PathLike = "") -> List[str]:
|
|
127
|
+
"""Return the entry names in a directory (``app_dir`` by default), sorted."""
|
|
128
|
+
return sorted(entry.name for entry in FileSystem.path(path).iterdir())
|
|
129
|
+
|
|
130
|
+
@staticmethod
|
|
131
|
+
def get_size(path: PathLike) -> int:
|
|
132
|
+
"""Return a file's size in bytes."""
|
|
133
|
+
return FileSystem.path(path).stat().st_size
|
|
134
|
+
|
|
135
|
+
@staticmethod
|
|
136
|
+
def ensure_dir(path: PathLike) -> Path:
|
|
137
|
+
"""Create a directory (and any missing parents) if needed; returns its ``Path``."""
|
|
138
|
+
target = FileSystem.path(path)
|
|
139
|
+
target.mkdir(parents=True, exist_ok=True)
|
|
140
|
+
return target
|
|
141
|
+
|
|
142
|
+
@staticmethod
|
|
143
|
+
def join(*parts: Any) -> str:
|
|
144
|
+
"""Join path components with the OS separator (``os.path.join`` over ``str(part)``)."""
|
|
145
|
+
return os.path.join(*[str(p) for p in parts])
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Haptic feedback and raw vibration.
|
|
2
|
+
|
|
3
|
+
Two interfaces live here, both backed by the native ``Haptics`` module:
|
|
4
|
+
|
|
5
|
+
- [`Haptics`][pythonnative.Haptics]: semantic, iOS-style feedback
|
|
6
|
+
(impact / notification / selection) backed by
|
|
7
|
+
``UIFeedbackGenerator`` on iOS and ``VibrationEffect`` patterns on
|
|
8
|
+
Android.
|
|
9
|
+
- [`Vibration`][pythonnative.Vibration]: a blunt "buzz for N
|
|
10
|
+
milliseconds" interface for cases where you want an explicit
|
|
11
|
+
duration.
|
|
12
|
+
|
|
13
|
+
Every method is synchronous (the OS queues the effect and returns).
|
|
14
|
+
Missing hardware is not an error: on a device without a Taptic Engine
|
|
15
|
+
or vibrator, and off device the native module simply does nothing.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
from .registry import native_module
|
|
23
|
+
|
|
24
|
+
ImpactStyle = str # "light" | "medium" | "heavy" | "soft" | "rigid"
|
|
25
|
+
NotificationType = str # "success" | "warning" | "error"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _call(method: str, **args: Any) -> None:
|
|
29
|
+
native_module("Haptics").call(method, **args)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Haptics:
|
|
33
|
+
"""Semantic haptic feedback (synchronous).
|
|
34
|
+
|
|
35
|
+
Raises:
|
|
36
|
+
NativeModuleError: If the native module fails.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
@staticmethod
|
|
40
|
+
def impact(style: ImpactStyle = "medium") -> None:
|
|
41
|
+
"""Play a physical "impact" tap of the given ``style``."""
|
|
42
|
+
_call("impact", style=style)
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
def notification(type_: NotificationType = "success") -> None:
|
|
46
|
+
"""Play a success / warning / error notification pattern."""
|
|
47
|
+
_call("notification", type=type_)
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def selection() -> None:
|
|
51
|
+
"""Play the light "selection changed" tick."""
|
|
52
|
+
_call("selection")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class Vibration:
|
|
56
|
+
"""Raw vibration control (synchronous).
|
|
57
|
+
|
|
58
|
+
Raises:
|
|
59
|
+
NativeModuleError: If the native module fails.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
@staticmethod
|
|
63
|
+
def vibrate(duration_ms: int = 400) -> None:
|
|
64
|
+
"""Vibrate for ``duration_ms`` milliseconds.
|
|
65
|
+
|
|
66
|
+
iOS has no arbitrary-duration API; the native module
|
|
67
|
+
approximates short buzzes with a heavy impact and longer ones
|
|
68
|
+
with the legacy system vibration sound.
|
|
69
|
+
"""
|
|
70
|
+
_call("vibrate", duration_ms=int(duration_ms))
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def cancel() -> None:
|
|
74
|
+
"""Cancel an in-progress vibration (Android only)."""
|
|
75
|
+
_call("cancel")
|