pythonnative 0.35.0__py3-none-any.whl → 0.37.0__py3-none-any.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 +6 -4
- pythonnative/_ios_log.py +1 -1
- pythonnative/alerts.py +34 -88
- pythonnative/bootstrap.py +90 -0
- pythonnative/bridge/__init__.py +306 -0
- pythonnative/bridge/android.py +78 -0
- pythonnative/bridge/codec.py +164 -0
- pythonnative/bridge/fake.py +236 -0
- pythonnative/bridge/ios.py +138 -0
- pythonnative/cli/pn.py +24 -6
- pythonnative/components/layout.py +65 -3
- pythonnative/gestures.py +13 -10
- pythonnative/hooks.py +41 -26
- pythonnative/hosts/__init__.py +13 -36
- pythonnative/hosts/native.py +348 -0
- pythonnative/images.py +6 -5
- pythonnative/mutations.py +21 -0
- pythonnative/native_modules/__init__.py +30 -29
- pythonnative/native_modules/app_state.py +10 -6
- pythonnative/native_modules/battery.py +20 -84
- pythonnative/native_modules/biometrics.py +11 -124
- pythonnative/native_modules/camera.py +15 -291
- pythonnative/native_modules/clipboard.py +14 -109
- pythonnative/native_modules/desktop.py +322 -0
- pythonnative/native_modules/file_system.py +15 -21
- pythonnative/native_modules/haptics.py +19 -129
- pythonnative/native_modules/linking.py +24 -137
- pythonnative/native_modules/location.py +28 -180
- pythonnative/native_modules/net_info.py +25 -135
- pythonnative/native_modules/notifications.py +36 -257
- pythonnative/native_modules/permissions.py +20 -184
- pythonnative/native_modules/registry.py +513 -0
- pythonnative/native_modules/secure_store.py +18 -155
- pythonnative/native_modules/share.py +8 -113
- pythonnative/native_views/__init__.py +67 -86
- pythonnative/native_views/bridge_backend.py +264 -0
- pythonnative/platform.py +19 -15
- pythonnative/platform_metrics.py +3 -3
- pythonnative/project/android.py +0 -3
- pythonnative/project/builder.py +38 -16
- pythonnative/project/config.py +12 -2
- pythonnative/project/plugins.py +366 -0
- pythonnative/py.typed +0 -0
- pythonnative/runtime.py +35 -203
- pythonnative/sdk/__init__.py +48 -33
- pythonnative/sdk/_components.py +57 -76
- pythonnative/storage.py +18 -243
- pythonnative/templates/android_template/app/build.gradle +6 -9
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +34 -166
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt +13 -151
- pythonnative/templates/android_template/pythonnative/build.gradle +48 -0
- pythonnative/templates/android_template/pythonnative/src/main/AndroidManifest.xml +11 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/PNBridge.kt +214 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/PythonHost.kt +20 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/animation/AnimationSpecs.kt +141 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/animation/PNAnimator.kt +249 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/JsonUtil.kt +175 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/MainThread.kt +32 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/PNLog.kt +33 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/PNRegistry.kt +82 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/PNTransaction.kt +80 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/TransactionApplier.kt +115 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/ViewRegistry.kt +74 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/BuiltinComponents.kt +35 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ButtonManager.kt +33 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ComponentManager.kt +213 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ContainerManagers.kt +69 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ControlManagers.kt +186 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/DatePickerManager.kt +123 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ImageLoader.kt +141 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ImageManager.kt +156 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ModalManager.kt +134 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PNColor.kt +201 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PickerManager.kt +87 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PortalManager.kt +55 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PressableManager.kt +122 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ScrollViewManager.kt +214 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/SegmentedControlManager.kt +113 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/StatusBarManager.kt +52 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/TabBarManager.kt +131 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/TextInputManager.kt +245 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/TextManager.kt +233 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ViewStyler.kt +355 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/VirtualListManager.kt +179 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/WebViewManager.kt +149 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/gestures/GestureArbiter.kt +233 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/gestures/GestureCoordinator.kt +127 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/gestures/GestureRecognizers.kt +527 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/BuiltinModules.kt +73 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/DeviceModules.kt +316 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/HostModule.kt +97 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/MediaModules.kt +200 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/NativeModule.kt +135 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/NotificationsModule.kt +100 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/PermissionsModule.kt +88 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/StorageModules.kt +89 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/SystemModules.kt +178 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/plugins/GeneratedPlugins.kt +16 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/screens/Navigator.kt +116 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/screens/PNScreenFragment.kt +223 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/screens/ScreenRegistry.kt +80 -0
- pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNAccessibilityDelegate.kt +5 -4
- pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNBorderDrawable.kt +12 -3
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/views/PNEditText.kt +18 -0
- pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNFrameLayout.kt +9 -10
- pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNVirtualListView.java +9 -12
- pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/AnimationSpecsTest.kt +75 -0
- pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/GestureArbiterTest.kt +197 -0
- pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/PNColorTest.kt +47 -0
- pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/PNTransactionTest.kt +78 -0
- pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/PromiseTest.kt +75 -0
- pythonnative/templates/android_template/settings.gradle +1 -0
- pythonnative/templates/ios_template/PythonNativeKit/Package.swift +21 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Animation/PNAnimator.swift +301 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNBridge.swift +191 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNRegistry.swift +140 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNTransaction.swift +165 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNViewRegistry.swift +53 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNButtonManagers.swift +206 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNColor.swift +173 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNComponentManager.swift +183 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNContainerView.swift +56 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNControlManagers.swift +281 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNImageManager.swift +221 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNOverlayManagers.swift +243 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNPressableManager.swift +114 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNScrollViewManager.swift +197 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTabBarManager.swift +106 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTextInputManager.swift +289 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTextManager.swift +230 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTransform.swift +82 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNViewManager.swift +86 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNViewState.swift +87 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNViewStyler.swift +340 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNVirtualListManager.swift +257 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNWebViewManager.swift +124 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Gestures/PNGestureCoordinator.swift +260 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/AlertModule.swift +92 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/HostModule.swift +158 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/LifecycleModules.swift +174 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/MediaModules.swift +177 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/PNNativeModule.swift +125 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/PermissionModules.swift +251 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/SystemModules.swift +251 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Plugins/PNPluginRegistration.swift +8 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Screens/PNScreenRegistry.swift +46 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Screens/PNViewController.swift +251 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNCompat.swift +46 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNJSON.swift +173 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNLog.swift +47 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNWindow.swift +51 -0
- pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNColorTests.swift +50 -0
- pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNGestureTests.swift +101 -0
- pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNManagerTests.swift +124 -0
- pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNModuleTests.swift +98 -0
- pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNTransactionTests.swift +91 -0
- pythonnative/templates/ios_template/ios_template/AppDelegate.swift +8 -50
- pythonnative/templates/ios_template/ios_template/Info.plist +4 -4
- pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +38 -46
- pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +11 -16
- pythonnative/templates/ios_template/ios_template/ViewController.swift +12 -142
- pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +47 -1
- pythonnative/utils.py +4 -82
- pythonnative/virtual_rows.py +3 -3
- {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/METADATA +3 -5
- pythonnative-0.37.0.dist-info/RECORD +263 -0
- pythonnative/hosts/android.py +0 -269
- pythonnative/hosts/ios.py +0 -404
- pythonnative/native_views/android.py +0 -3702
- pythonnative/native_views/ios.py +0 -5253
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/Navigator.kt +0 -43
- pythonnative-0.35.0.dist-info/RECORD +0 -155
- {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/WHEEL +0 -0
- {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/entry_points.txt +0 -0
- {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/licenses/LICENSE +0 -0
- {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/top_level.txt +0 -0
pythonnative/sdk/__init__.py
CHANGED
|
@@ -2,46 +2,51 @@
|
|
|
2
2
|
|
|
3
3
|
The ``pythonnative.sdk`` package collects the *stable* extension
|
|
4
4
|
contract that third-party packages rely on: the
|
|
5
|
-
[`ViewHandler`][pythonnative.sdk.ViewHandler] protocol, the
|
|
6
5
|
[`Style`][pythonnative.sdk.Style] type, the
|
|
7
|
-
[`@native_component`][pythonnative.sdk.native_component]
|
|
8
|
-
|
|
6
|
+
[`@native_component`][pythonnative.sdk.native_component] /
|
|
7
|
+
[`register_component`][pythonnative.sdk.register_component]
|
|
8
|
+
registration helpers, the
|
|
9
9
|
[`element_factory`][pythonnative.sdk.element_factory] helper for
|
|
10
|
-
producing strongly-typed element constructors
|
|
10
|
+
producing strongly-typed element constructors, the
|
|
11
|
+
[`ViewHandler`][pythonnative.sdk.ViewHandler] protocol for off-device
|
|
12
|
+
stand-ins, and the native module registry
|
|
13
|
+
([`native_module`][pythonnative.sdk.native_module],
|
|
14
|
+
[`register_python_module`][pythonnative.sdk.register_python_module]).
|
|
11
15
|
|
|
12
16
|
A custom native component is three things:
|
|
13
17
|
|
|
14
18
|
1. A typed, frozen [`Props`][pythonnative.sdk.Props] dataclass listing
|
|
15
19
|
the public properties the component accepts.
|
|
16
|
-
2.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
3. A registration call
|
|
21
|
-
[
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
2. A Swift ``PNComponentManager`` and a Kotlin ``ComponentManager``
|
|
21
|
+
registered under the component's name by the package's native
|
|
22
|
+
plugin (``pn_plugin.json`` next to ``ios/`` and ``android/`` source
|
|
23
|
+
folders; see ``docs/guides/custom-components.md``).
|
|
24
|
+
3. A registration call in Python
|
|
25
|
+
([`register_component`][pythonnative.sdk.register_component], or the
|
|
26
|
+
[`@native_component`][pythonnative.sdk.native_component] decorator
|
|
27
|
+
when you also supply a desktop
|
|
28
|
+
[`ViewHandler`][pythonnative.sdk.ViewHandler] for ``pn preview``)
|
|
29
|
+
that declares the element name and binds its props type.
|
|
26
30
|
|
|
27
31
|
Once registered, the component appears alongside the built-ins: the
|
|
28
32
|
reconciler, layout engine, and Fast Refresh treat it identically.
|
|
29
33
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
``
|
|
33
|
-
|
|
34
|
+
A native module (device API without a view) follows the same split: a
|
|
35
|
+
Swift / Kotlin class registered by name in the plugin, a Python facade
|
|
36
|
+
that calls ``native_module(name).call(...)``, and optionally a Python
|
|
37
|
+
implementation registered with ``register_python_module`` for desktop
|
|
38
|
+
and tests.
|
|
39
|
+
|
|
40
|
+
PyPI packages can ship both without users importing them explicitly
|
|
41
|
+
by declaring entry points in the ``pythonnative.handlers`` (Python
|
|
42
|
+
side) and ``pythonnative.plugins`` (native source) groups; ``pn build``
|
|
43
|
+
compiles the native sources into the app.
|
|
34
44
|
|
|
35
45
|
Example:
|
|
36
46
|
```python
|
|
37
47
|
from dataclasses import dataclass
|
|
38
48
|
import pythonnative as pn
|
|
39
|
-
from pythonnative.sdk import
|
|
40
|
-
Props,
|
|
41
|
-
ViewHandler,
|
|
42
|
-
element_factory,
|
|
43
|
-
native_component,
|
|
44
|
-
)
|
|
49
|
+
from pythonnative.sdk import Props, element_factory, register_component
|
|
45
50
|
|
|
46
51
|
|
|
47
52
|
@dataclass(frozen=True)
|
|
@@ -51,17 +56,10 @@ Example:
|
|
|
51
56
|
style: pn.StyleProp = None
|
|
52
57
|
|
|
53
58
|
|
|
54
|
-
|
|
55
|
-
class IOSBadgeHandler(ViewHandler):
|
|
56
|
-
def create(self, props):
|
|
57
|
-
...
|
|
58
|
-
|
|
59
|
-
def update(self, view, changed):
|
|
60
|
-
...
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
register_component(name="Badge", props=BadgeProps)
|
|
63
60
|
Badge = element_factory("Badge")
|
|
64
61
|
|
|
62
|
+
|
|
65
63
|
@pn.component
|
|
66
64
|
def App():
|
|
67
65
|
return pn.Column(
|
|
@@ -72,6 +70,14 @@ Example:
|
|
|
72
70
|
"""
|
|
73
71
|
|
|
74
72
|
from ..element import Element
|
|
73
|
+
from ..native_modules.registry import (
|
|
74
|
+
NativeModule,
|
|
75
|
+
NativeModuleError,
|
|
76
|
+
PythonModule,
|
|
77
|
+
emit,
|
|
78
|
+
native_module,
|
|
79
|
+
register_python_module,
|
|
80
|
+
)
|
|
75
81
|
from ..native_views.base import ViewHandler, parse_color_int
|
|
76
82
|
from ..style import (
|
|
77
83
|
Color,
|
|
@@ -91,6 +97,7 @@ from ._components import (
|
|
|
91
97
|
ENTRY_POINT_GROUP,
|
|
92
98
|
Props,
|
|
93
99
|
element_factory,
|
|
100
|
+
get_desktop_handler,
|
|
94
101
|
get_props_type,
|
|
95
102
|
install_into_registry,
|
|
96
103
|
list_components,
|
|
@@ -122,10 +129,18 @@ __all__ = [
|
|
|
122
129
|
"ENTRY_POINT_GROUP",
|
|
123
130
|
"Props",
|
|
124
131
|
"element_factory",
|
|
132
|
+
"get_desktop_handler",
|
|
125
133
|
"get_props_type",
|
|
126
134
|
"install_into_registry",
|
|
127
135
|
"list_components",
|
|
128
136
|
"native_component",
|
|
129
137
|
"register_component",
|
|
130
138
|
"unregister_component",
|
|
139
|
+
# Native-module SDK
|
|
140
|
+
"NativeModule",
|
|
141
|
+
"NativeModuleError",
|
|
142
|
+
"PythonModule",
|
|
143
|
+
"emit",
|
|
144
|
+
"native_module",
|
|
145
|
+
"register_python_module",
|
|
131
146
|
]
|
pythonnative/sdk/_components.py
CHANGED
|
@@ -4,26 +4,28 @@ Implements the [`@native_component`][pythonnative.sdk.native_component]
|
|
|
4
4
|
decorator and supporting helpers that let third-party packages contribute
|
|
5
5
|
new element types to the reconciler.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
On device a custom component is rendered by a Swift
|
|
8
|
+
``PNComponentManager`` and a Kotlin ``ComponentManager`` registered
|
|
9
|
+
under the same type name by the package's native plugin (see
|
|
10
|
+
``docs/guides/custom-components.md``). The Python side only needs to
|
|
11
|
+
know the element name and, optionally, its typed props and a
|
|
12
|
+
[`ViewHandler`][pythonnative.sdk.ViewHandler] that renders a stand-in
|
|
13
|
+
in ``pn preview`` and unit tests. Registration is therefore a
|
|
8
14
|
three-part agreement:
|
|
9
15
|
|
|
10
|
-
1. A typed, immutable
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
The decorator stores the (name, props_type, handler_instance) tuple
|
|
19
|
-
in a process-wide registry. The
|
|
20
|
-
[`NativeViewRegistry`][pythonnative.native_views.NativeViewRegistry]
|
|
21
|
-
calls
|
|
16
|
+
1. A typed, immutable [`Props`][pythonnative.sdk.Props] dataclass
|
|
17
|
+
declaring the component's public surface.
|
|
18
|
+
2. An element factory built with
|
|
19
|
+
[`element_factory`][pythonnative.sdk.element_factory].
|
|
20
|
+
3. Optionally, a desktop [`ViewHandler`][pythonnative.sdk.ViewHandler]
|
|
21
|
+
so the component also renders off device.
|
|
22
|
+
|
|
23
|
+
The registry is process-wide. The view backend calls
|
|
22
24
|
[`install_into_registry`][pythonnative.sdk.install_into_registry] on
|
|
23
25
|
first use; that helper performs entry-point discovery (importing any
|
|
24
26
|
modules registered under
|
|
25
27
|
[`ENTRY_POINT_GROUP`][pythonnative.sdk.ENTRY_POINT_GROUP]) and copies
|
|
26
|
-
every handler
|
|
28
|
+
every desktop handler into the registry.
|
|
27
29
|
|
|
28
30
|
Example:
|
|
29
31
|
```python
|
|
@@ -39,10 +41,10 @@ Example:
|
|
|
39
41
|
style: pn.StyleProp = None
|
|
40
42
|
|
|
41
43
|
|
|
42
|
-
@native_component("Badge", props=BadgeProps
|
|
43
|
-
class
|
|
44
|
+
@native_component("Badge", props=BadgeProps)
|
|
45
|
+
class BadgePreviewHandler(ViewHandler):
|
|
44
46
|
def create(self, tag, props):
|
|
45
|
-
...
|
|
47
|
+
... # Tkinter stand-in for pn preview
|
|
46
48
|
|
|
47
49
|
def update(self, view, changed):
|
|
48
50
|
...
|
|
@@ -102,8 +104,8 @@ class Props:
|
|
|
102
104
|
# Internal registry
|
|
103
105
|
# ---------------------------------------------------------------------- #
|
|
104
106
|
|
|
105
|
-
# name -> (props_type or None,
|
|
106
|
-
_REGISTRY: Dict[str, Tuple[Optional[type],
|
|
107
|
+
# name -> (props_type or None, desktop handler or None)
|
|
108
|
+
_REGISTRY: Dict[str, Tuple[Optional[type], Optional[ViewHandler]]] = {}
|
|
107
109
|
|
|
108
110
|
# Caches `_install_into_registry` runs to avoid repeated entry-point
|
|
109
111
|
# discovery once the registry has been populated for a given platform.
|
|
@@ -117,27 +119,22 @@ def native_component(
|
|
|
117
119
|
name: str,
|
|
118
120
|
*,
|
|
119
121
|
props: Optional[type] = None,
|
|
120
|
-
platforms: Optional[Tuple[str, ...]] = None,
|
|
121
122
|
) -> Callable[[Type[H]], Type[H]]:
|
|
122
|
-
"""Decorator that registers a [`ViewHandler`][pythonnative.sdk.ViewHandler] under ``name``.
|
|
123
|
+
"""Decorator that registers a desktop [`ViewHandler`][pythonnative.sdk.ViewHandler] under ``name``.
|
|
123
124
|
|
|
124
125
|
The handler class is instantiated immediately and stored in the
|
|
125
|
-
process-wide registry
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
process-wide registry as the component's off-device renderer. The
|
|
127
|
+
on-device renderers are the Swift and Kotlin component managers the
|
|
128
|
+
package's native plugin registers under the same ``name``.
|
|
128
129
|
|
|
129
130
|
Args:
|
|
130
|
-
name: Element type name (e.g., ``"Badge"``).
|
|
131
|
-
|
|
131
|
+
name: Element type name (e.g., ``"Badge"``). Used by the
|
|
132
|
+
reconciler and by native component managers at lookup time.
|
|
132
133
|
props: Optional dataclass type describing the component's
|
|
133
134
|
typed props. When supplied, the
|
|
134
135
|
[`element_factory`][pythonnative.sdk.element_factory] helper
|
|
135
136
|
uses this type to validate kwargs and produce frozen prop
|
|
136
137
|
instances.
|
|
137
|
-
platforms: Tuple of platform identifiers
|
|
138
|
-
(``"ios"`` / ``"android"``) the handler implements. Defaults
|
|
139
|
-
to ``("android", "ios")`` so a single cross-platform handler
|
|
140
|
-
registers everywhere.
|
|
141
138
|
|
|
142
139
|
Returns:
|
|
143
140
|
A decorator that, when applied to a
|
|
@@ -147,31 +144,12 @@ def native_component(
|
|
|
147
144
|
Raises:
|
|
148
145
|
TypeError: If the decorated object is not a class subclassing
|
|
149
146
|
``ViewHandler``.
|
|
150
|
-
|
|
151
|
-
Example:
|
|
152
|
-
```python
|
|
153
|
-
from dataclasses import dataclass
|
|
154
|
-
from pythonnative.sdk import Props, ViewHandler, native_component
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
@dataclass(frozen=True)
|
|
158
|
-
class BadgeProps(Props):
|
|
159
|
-
text: str = ""
|
|
160
|
-
color: str = "#FF3B30"
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
@native_component("Badge", props=BadgeProps, platforms=("ios",))
|
|
164
|
-
class IOSBadgeHandler(ViewHandler):
|
|
165
|
-
def create(self, tag, props):
|
|
166
|
-
...
|
|
167
|
-
```
|
|
168
147
|
"""
|
|
169
|
-
plats: Tuple[str, ...] = platforms if platforms is not None else ("android", "ios")
|
|
170
148
|
|
|
171
149
|
def decorator(handler_cls: Type[H]) -> Type[H]:
|
|
172
150
|
if not isinstance(handler_cls, type) or not issubclass(handler_cls, ViewHandler):
|
|
173
151
|
raise TypeError(f"@native_component({name!r}) must decorate a ViewHandler subclass; got {handler_cls!r}")
|
|
174
|
-
register_component(name=name, props=props,
|
|
152
|
+
register_component(name=name, props=props, handler=handler_cls())
|
|
175
153
|
return handler_cls
|
|
176
154
|
|
|
177
155
|
return decorator
|
|
@@ -181,42 +159,41 @@ def register_component(
|
|
|
181
159
|
*,
|
|
182
160
|
name: str,
|
|
183
161
|
props: Optional[type] = None,
|
|
184
|
-
|
|
162
|
+
handler: Optional[ViewHandler] = None,
|
|
185
163
|
) -> None:
|
|
186
164
|
"""Register a custom native component imperatively.
|
|
187
165
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
166
|
+
Declares ``name`` as an element type so
|
|
167
|
+
[`element_factory`][pythonnative.sdk.element_factory] can build it.
|
|
168
|
+
``handler`` is the optional desktop / test renderer; native
|
|
169
|
+
rendering always comes from the platform component managers.
|
|
170
|
+
Subsequent calls for the same ``name`` merge: a later ``props`` or
|
|
171
|
+
``handler`` replaces the earlier one, ``None`` leaves it alone.
|
|
194
172
|
|
|
195
173
|
Args:
|
|
196
174
|
name: Element type name.
|
|
197
175
|
props: Optional dataclass type describing the typed props.
|
|
198
|
-
|
|
199
|
-
|
|
176
|
+
handler: Optional [`ViewHandler`][pythonnative.sdk.ViewHandler]
|
|
177
|
+
instance used off device.
|
|
200
178
|
|
|
201
179
|
Raises:
|
|
202
|
-
TypeError: If
|
|
203
|
-
[`ViewHandler`][pythonnative.sdk.ViewHandler] instance, or
|
|
180
|
+
TypeError: If ``handler`` is not a ``ViewHandler`` instance, or
|
|
204
181
|
if ``props`` is not a dataclass type.
|
|
205
182
|
"""
|
|
206
183
|
if props is not None and not (isinstance(props, type) and is_dataclass(props)):
|
|
207
184
|
raise TypeError(f"register_component({name!r}): props must be a @dataclass type, got {props!r}")
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
raise TypeError(f"register_component({name!r}): handler for {plat!r} must be a ViewHandler instance")
|
|
185
|
+
if handler is not None and not isinstance(handler, ViewHandler):
|
|
186
|
+
raise TypeError(f"register_component({name!r}): handler must be a ViewHandler instance")
|
|
211
187
|
|
|
212
188
|
existing = _REGISTRY.get(name)
|
|
213
189
|
if existing is None:
|
|
214
|
-
_REGISTRY[name] = (props,
|
|
190
|
+
_REGISTRY[name] = (props, handler)
|
|
215
191
|
return
|
|
216
|
-
existing_props,
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
192
|
+
existing_props, existing_handler = existing
|
|
193
|
+
_REGISTRY[name] = (
|
|
194
|
+
props if props is not None else existing_props,
|
|
195
|
+
handler if handler is not None else existing_handler,
|
|
196
|
+
)
|
|
220
197
|
|
|
221
198
|
|
|
222
199
|
def unregister_component(name: str) -> None:
|
|
@@ -247,11 +224,17 @@ def get_props_type(name: str) -> Optional[type]:
|
|
|
247
224
|
return entry[0] if entry is not None else None
|
|
248
225
|
|
|
249
226
|
|
|
250
|
-
def
|
|
251
|
-
"""
|
|
227
|
+
def get_desktop_handler(name: str) -> Optional[ViewHandler]:
|
|
228
|
+
"""Return the registered off-device handler for ``name`` (or ``None``)."""
|
|
229
|
+
entry = _REGISTRY.get(name)
|
|
230
|
+
return entry[1] if entry is not None else None
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def install_into_registry(registry: Any) -> None:
|
|
234
|
+
"""Copy registered desktop handlers into a view registry.
|
|
252
235
|
|
|
253
236
|
Called once by the registry on first use. Triggers entry-point
|
|
254
|
-
discovery on the first call so PyPI-installed
|
|
237
|
+
discovery on the first call so PyPI-installed components register
|
|
255
238
|
themselves before the registry snapshot is taken.
|
|
256
239
|
|
|
257
240
|
Args:
|
|
@@ -259,12 +242,9 @@ def install_into_registry(registry: Any, platform_name: str) -> None:
|
|
|
259
242
|
[`NativeViewRegistry`][pythonnative.native_views.NativeViewRegistry]
|
|
260
243
|
(or duck-compatible object) with a ``register(name, handler)``
|
|
261
244
|
method.
|
|
262
|
-
platform_name: The active platform identifier
|
|
263
|
-
(``"ios"`` or ``"android"``).
|
|
264
245
|
"""
|
|
265
246
|
_discover_entry_points()
|
|
266
|
-
for name, (_props_type,
|
|
267
|
-
handler = plat_map.get(platform_name)
|
|
247
|
+
for name, (_props_type, handler) in _REGISTRY.items():
|
|
268
248
|
if handler is not None:
|
|
269
249
|
registry.register(name, handler)
|
|
270
250
|
|
|
@@ -418,6 +398,7 @@ __all__ = [
|
|
|
418
398
|
"ENTRY_POINT_GROUP",
|
|
419
399
|
"Props",
|
|
420
400
|
"element_factory",
|
|
401
|
+
"get_desktop_handler",
|
|
421
402
|
"get_props_type",
|
|
422
403
|
"install_into_registry",
|
|
423
404
|
"list_components",
|
pythonnative/storage.py
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
"""Cross-platform key/value persistence (``AsyncStorage``).
|
|
2
2
|
|
|
3
3
|
Mirrors React Native's ``AsyncStorage`` API but with native ``async``
|
|
4
|
-
coroutines and a JSON convenience layer. Values are persisted
|
|
5
|
-
|
|
4
|
+
coroutines and a JSON convenience layer. Values are persisted by the
|
|
5
|
+
native ``Storage`` module:
|
|
6
6
|
|
|
7
7
|
- **iOS**: ``NSUserDefaults`` (standard user defaults).
|
|
8
8
|
- **Android**: ``SharedPreferences`` (file ``"pn_async_storage"``).
|
|
9
9
|
- **Desktop / tests**: an in-memory dict optionally backed by a JSON
|
|
10
|
-
file under
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
file under ``PN_STORAGE_DIR`` for inter-run persistence during local
|
|
11
|
+
development (see
|
|
12
|
+
[`DesktopStorage`][pythonnative.native_modules.desktop.DesktopStorage]).
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
Every operation is a coroutine. The platform stores answer in
|
|
15
|
+
microseconds, so calls complete inline on the framework loop without
|
|
16
|
+
a thread hop.
|
|
17
17
|
|
|
18
18
|
Example:
|
|
19
19
|
```python
|
|
@@ -34,220 +34,16 @@ Example:
|
|
|
34
34
|
|
|
35
35
|
from __future__ import annotations
|
|
36
36
|
|
|
37
|
-
import asyncio
|
|
38
37
|
import json
|
|
39
|
-
import os
|
|
40
|
-
import threading
|
|
41
38
|
from typing import Any, Callable, List, Optional, Tuple, TypeVar
|
|
42
39
|
|
|
43
|
-
from .
|
|
40
|
+
from .native_modules.registry import NativeModule, native_module
|
|
44
41
|
|
|
45
42
|
T = TypeVar("T")
|
|
46
43
|
|
|
47
|
-
# ======================================================================
|
|
48
|
-
# Backend selection
|
|
49
|
-
# ======================================================================
|
|
50
44
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
# Cache the NSUserDefaults class lookup. rubicon.objc's
|
|
56
|
-
# ``ObjCClass("NSUserDefaults")`` walks the ObjC runtime metadata which
|
|
57
|
-
# takes hundreds of milliseconds on first call; resolving once at module
|
|
58
|
-
# import keeps every later get/set/delete in the sub-millisecond range.
|
|
59
|
-
_ios_defaults: Any = None
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def _ios_get_defaults() -> Any:
|
|
63
|
-
global _ios_defaults
|
|
64
|
-
if _ios_defaults is None:
|
|
65
|
-
from rubicon.objc import ObjCClass
|
|
66
|
-
|
|
67
|
-
_ios_defaults = ObjCClass("NSUserDefaults").standardUserDefaults
|
|
68
|
-
return _ios_defaults
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def _ios_set(key: str, value: str) -> None:
|
|
72
|
-
defaults = _ios_get_defaults()
|
|
73
|
-
defaults.setObject_forKey_(value, key)
|
|
74
|
-
# ``synchronize()`` is documented as unnecessary on modern iOS and
|
|
75
|
-
# can block for seconds while it flushes to disk on a busy system;
|
|
76
|
-
# NSUserDefaults already coalesces writes asynchronously.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def _ios_get(key: str) -> Optional[str]:
|
|
80
|
-
defaults = _ios_get_defaults()
|
|
81
|
-
val = defaults.stringForKey_(key)
|
|
82
|
-
if val is None:
|
|
83
|
-
return None
|
|
84
|
-
try:
|
|
85
|
-
return str(val)
|
|
86
|
-
except Exception:
|
|
87
|
-
return None
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def _ios_delete(key: str) -> None:
|
|
91
|
-
defaults = _ios_get_defaults()
|
|
92
|
-
defaults.removeObjectForKey_(key)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
def _ios_all_keys() -> List[str]:
|
|
96
|
-
defaults = _ios_get_defaults()
|
|
97
|
-
rep = defaults.dictionaryRepresentation()
|
|
98
|
-
if rep is None:
|
|
99
|
-
return []
|
|
100
|
-
try:
|
|
101
|
-
keys = rep.allKeys
|
|
102
|
-
return [str(keys.objectAtIndex_(i)) for i in range(keys.count)]
|
|
103
|
-
except Exception:
|
|
104
|
-
return []
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def _ios_clear() -> None:
|
|
108
|
-
for key in _ios_all_keys():
|
|
109
|
-
_ios_delete(key)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
def _android_prefs() -> Any:
|
|
113
|
-
from java import jclass
|
|
114
|
-
|
|
115
|
-
from .utils import get_android_context
|
|
116
|
-
|
|
117
|
-
Context = jclass("android.content.Context")
|
|
118
|
-
ctx = get_android_context()
|
|
119
|
-
return ctx.getSharedPreferences(_DEFAULTS_SUITE, Context.MODE_PRIVATE)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def _android_set(key: str, value: str) -> None:
|
|
123
|
-
prefs = _android_prefs()
|
|
124
|
-
editor = prefs.edit()
|
|
125
|
-
editor.putString(key, value)
|
|
126
|
-
editor.apply()
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
def _android_get(key: str) -> Optional[str]:
|
|
130
|
-
prefs = _android_prefs()
|
|
131
|
-
if not prefs.contains(key):
|
|
132
|
-
return None
|
|
133
|
-
try:
|
|
134
|
-
return str(prefs.getString(key, None))
|
|
135
|
-
except Exception:
|
|
136
|
-
return None
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def _android_delete(key: str) -> None:
|
|
140
|
-
prefs = _android_prefs()
|
|
141
|
-
editor = prefs.edit()
|
|
142
|
-
editor.remove(key)
|
|
143
|
-
editor.apply()
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
def _android_all_keys() -> List[str]:
|
|
147
|
-
prefs = _android_prefs()
|
|
148
|
-
all_map = prefs.getAll()
|
|
149
|
-
keys = all_map.keySet()
|
|
150
|
-
it = keys.iterator()
|
|
151
|
-
out: List[str] = []
|
|
152
|
-
while it.hasNext():
|
|
153
|
-
out.append(str(it.next()))
|
|
154
|
-
return out
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
def _android_clear() -> None:
|
|
158
|
-
prefs = _android_prefs()
|
|
159
|
-
editor = prefs.edit()
|
|
160
|
-
editor.clear()
|
|
161
|
-
editor.apply()
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
# ======================================================================
|
|
165
|
-
# Desktop / test backend
|
|
166
|
-
# ======================================================================
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
_desktop_lock = threading.Lock()
|
|
170
|
-
_desktop_store: dict = {}
|
|
171
|
-
_desktop_loaded = False
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
def _desktop_path() -> Optional[str]:
|
|
175
|
-
# Tests can opt out of disk persistence by leaving the env var unset.
|
|
176
|
-
base = os.environ.get("PN_STORAGE_DIR")
|
|
177
|
-
if not base:
|
|
178
|
-
return None
|
|
179
|
-
try:
|
|
180
|
-
os.makedirs(base, exist_ok=True)
|
|
181
|
-
except OSError:
|
|
182
|
-
return None
|
|
183
|
-
return os.path.join(base, "pn_async_storage.json")
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
def _desktop_load() -> None:
|
|
187
|
-
global _desktop_loaded
|
|
188
|
-
if _desktop_loaded:
|
|
189
|
-
return
|
|
190
|
-
_desktop_loaded = True
|
|
191
|
-
path = _desktop_path()
|
|
192
|
-
if path is None or not os.path.exists(path):
|
|
193
|
-
return
|
|
194
|
-
try:
|
|
195
|
-
with open(path, encoding="utf-8") as f:
|
|
196
|
-
data = json.load(f)
|
|
197
|
-
if isinstance(data, dict):
|
|
198
|
-
_desktop_store.update({str(k): str(v) for k, v in data.items()})
|
|
199
|
-
except OSError:
|
|
200
|
-
return
|
|
201
|
-
except json.JSONDecodeError:
|
|
202
|
-
return
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
def _desktop_persist() -> None:
|
|
206
|
-
path = _desktop_path()
|
|
207
|
-
if path is None:
|
|
208
|
-
return
|
|
209
|
-
try:
|
|
210
|
-
with open(path, "w", encoding="utf-8") as f:
|
|
211
|
-
json.dump(_desktop_store, f)
|
|
212
|
-
except OSError:
|
|
213
|
-
pass
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
def _desktop_set(key: str, value: str) -> None:
|
|
217
|
-
with _desktop_lock:
|
|
218
|
-
_desktop_load()
|
|
219
|
-
_desktop_store[key] = value
|
|
220
|
-
_desktop_persist()
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
def _desktop_get(key: str) -> Optional[str]:
|
|
224
|
-
with _desktop_lock:
|
|
225
|
-
_desktop_load()
|
|
226
|
-
return _desktop_store.get(key)
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
def _desktop_delete(key: str) -> None:
|
|
230
|
-
with _desktop_lock:
|
|
231
|
-
_desktop_load()
|
|
232
|
-
_desktop_store.pop(key, None)
|
|
233
|
-
_desktop_persist()
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
def _desktop_all_keys() -> List[str]:
|
|
237
|
-
with _desktop_lock:
|
|
238
|
-
_desktop_load()
|
|
239
|
-
return list(_desktop_store.keys())
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
def _desktop_clear() -> None:
|
|
243
|
-
with _desktop_lock:
|
|
244
|
-
_desktop_store.clear()
|
|
245
|
-
_desktop_persist()
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
# ======================================================================
|
|
249
|
-
# Public API
|
|
250
|
-
# ======================================================================
|
|
45
|
+
def _storage() -> NativeModule:
|
|
46
|
+
return native_module("Storage")
|
|
251
47
|
|
|
252
48
|
|
|
253
49
|
class AsyncStorage:
|
|
@@ -265,11 +61,8 @@ class AsyncStorage:
|
|
|
265
61
|
@staticmethod
|
|
266
62
|
async def get(key: str) -> Optional[str]:
|
|
267
63
|
"""Return the string stored at ``key``, or ``None`` if missing."""
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
if IS_ANDROID:
|
|
271
|
-
return await asyncio.to_thread(_android_get, key)
|
|
272
|
-
return await asyncio.to_thread(_desktop_get, key)
|
|
64
|
+
value = _storage().call("get", key=key)
|
|
65
|
+
return None if value is None else str(value)
|
|
273
66
|
|
|
274
67
|
@staticmethod
|
|
275
68
|
async def set(key: str, value: str) -> None:
|
|
@@ -280,41 +73,23 @@ class AsyncStorage:
|
|
|
280
73
|
"""
|
|
281
74
|
if not isinstance(value, str):
|
|
282
75
|
raise TypeError("AsyncStorage.set requires a str value; use set_json for richer types")
|
|
283
|
-
|
|
284
|
-
await asyncio.to_thread(_ios_set, key, value)
|
|
285
|
-
elif IS_ANDROID:
|
|
286
|
-
await asyncio.to_thread(_android_set, key, value)
|
|
287
|
-
else:
|
|
288
|
-
await asyncio.to_thread(_desktop_set, key, value)
|
|
76
|
+
_storage().call("set", key=key, value=value)
|
|
289
77
|
|
|
290
78
|
@staticmethod
|
|
291
79
|
async def delete(key: str) -> None:
|
|
292
80
|
"""Remove the entry at ``key`` if present (no-op otherwise)."""
|
|
293
|
-
|
|
294
|
-
await asyncio.to_thread(_ios_delete, key)
|
|
295
|
-
elif IS_ANDROID:
|
|
296
|
-
await asyncio.to_thread(_android_delete, key)
|
|
297
|
-
else:
|
|
298
|
-
await asyncio.to_thread(_desktop_delete, key)
|
|
81
|
+
_storage().call("delete", key=key)
|
|
299
82
|
|
|
300
83
|
@staticmethod
|
|
301
84
|
async def all_keys() -> List[str]:
|
|
302
85
|
"""Return every persisted key (order is platform-dependent)."""
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
if IS_ANDROID:
|
|
306
|
-
return await asyncio.to_thread(_android_all_keys)
|
|
307
|
-
return await asyncio.to_thread(_desktop_all_keys)
|
|
86
|
+
keys = _storage().call("all_keys")
|
|
87
|
+
return [str(k) for k in keys] if isinstance(keys, list) else []
|
|
308
88
|
|
|
309
89
|
@staticmethod
|
|
310
90
|
async def clear() -> None:
|
|
311
91
|
"""Remove every entry written through ``AsyncStorage``."""
|
|
312
|
-
|
|
313
|
-
await asyncio.to_thread(_ios_clear)
|
|
314
|
-
elif IS_ANDROID:
|
|
315
|
-
await asyncio.to_thread(_android_clear)
|
|
316
|
-
else:
|
|
317
|
-
await asyncio.to_thread(_desktop_clear)
|
|
92
|
+
_storage().call("clear")
|
|
318
93
|
|
|
319
94
|
@staticmethod
|
|
320
95
|
async def get_json(key: str) -> Any:
|