pythonnative 0.36.0__py3-none-any.whl → 0.38.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 +14 -4
- pythonnative/_ios_log.py +1 -1
- pythonnative/alerts.py +24 -86
- 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 +96 -10
- pythonnative/components/_base.py +31 -0
- pythonnative/components/controls.py +19 -16
- pythonnative/components/layout.py +70 -11
- pythonnative/components/lists.py +6 -6
- pythonnative/gestures.py +40 -16
- pythonnative/hooks.py +41 -26
- pythonnative/hosts/__init__.py +13 -36
- pythonnative/hosts/native.py +348 -0
- pythonnative/images.py +6 -5
- pythonnative/native_modules/__init__.py +49 -29
- pythonnative/native_modules/app_state.py +10 -6
- pythonnative/native_modules/battery.py +22 -87
- pythonnative/native_modules/biometrics.py +15 -130
- pythonnative/native_modules/camera.py +24 -296
- pythonnative/native_modules/clipboard.py +14 -111
- pythonnative/native_modules/desktop.py +322 -0
- pythonnative/native_modules/file_system.py +90 -187
- pythonnative/native_modules/haptics.py +29 -135
- pythonnative/native_modules/linking.py +29 -141
- pythonnative/native_modules/location.py +32 -186
- pythonnative/native_modules/net_info.py +22 -135
- pythonnative/native_modules/notifications.py +37 -265
- pythonnative/native_modules/permissions.py +78 -194
- pythonnative/native_modules/registry.py +513 -0
- pythonnative/native_modules/secure_store.py +23 -164
- pythonnative/native_modules/share.py +9 -114
- pythonnative/native_views/__init__.py +67 -86
- pythonnative/native_views/bridge_backend.py +264 -0
- pythonnative/navigation/__init__.py +2 -1
- pythonnative/navigation/hooks.py +46 -7
- pythonnative/navigation/state.py +30 -9
- pythonnative/platform.py +19 -15
- pythonnative/platform_metrics.py +3 -3
- pythonnative/project/android.py +23 -3
- pythonnative/project/builder.py +99 -48
- pythonnative/project/config.py +96 -26
- pythonnative/project/deps.py +703 -0
- pythonnative/project/doctor.py +53 -4
- pythonnative/project/plugins.py +366 -0
- pythonnative/project/runtime_assets.py +13 -14
- 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/style.py +94 -44
- pythonnative/templates/android_template/app/build.gradle +11 -11
- 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/build.gradle +7 -4
- pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties +1 -1
- 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 +99 -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 +266 -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 +41 -48
- 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 +82 -10
- pythonnative/utils.py +26 -109
- pythonnative/virtual_rows.py +13 -23
- {pythonnative-0.36.0.dist-info → pythonnative-0.38.0.dist-info}/METADATA +10 -12
- pythonnative-0.38.0.dist-info/RECORD +264 -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.36.0.dist-info/RECORD +0 -155
- {pythonnative-0.36.0.dist-info → pythonnative-0.38.0.dist-info}/WHEEL +0 -0
- {pythonnative-0.36.0.dist-info → pythonnative-0.38.0.dist-info}/entry_points.txt +0 -0
- {pythonnative-0.36.0.dist-info → pythonnative-0.38.0.dist-info}/licenses/LICENSE +0 -0
- {pythonnative-0.36.0.dist-info → pythonnative-0.38.0.dist-info}/top_level.txt +0 -0
pythonnative/__init__.py
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
PythonNative is a cross-platform toolkit that turns Python ``@component``
|
|
4
4
|
functions into real, native Android and iOS views. The component model
|
|
5
|
-
is React-like (function components plus hooks)
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
is React-like (function components plus hooks). Python owns the
|
|
6
|
+
component tree, reconciliation, and layout; each commit crosses once
|
|
7
|
+
into a native rendering core (Swift ``PythonNativeKit`` on iOS, the
|
|
8
|
+
Kotlin ``pythonnative`` module on Android) that owns every platform
|
|
9
|
+
view, gesture, animation, and device API. There is no JavaScript.
|
|
8
10
|
|
|
9
11
|
Key building blocks:
|
|
10
12
|
|
|
@@ -60,7 +62,7 @@ Example:
|
|
|
60
62
|
```
|
|
61
63
|
"""
|
|
62
64
|
|
|
63
|
-
__version__ = "0.
|
|
65
|
+
__version__ = "0.38.0"
|
|
64
66
|
|
|
65
67
|
from . import appearance, diagnostics, gestures, images, runtime, sdk
|
|
66
68
|
from .alerts import Alert
|
|
@@ -188,6 +190,7 @@ from .style import (
|
|
|
188
190
|
AutoCapitalize,
|
|
189
191
|
Color,
|
|
190
192
|
Dimension,
|
|
193
|
+
Display,
|
|
191
194
|
EdgeInsets,
|
|
192
195
|
FlexDirection,
|
|
193
196
|
FlexWrap,
|
|
@@ -196,6 +199,7 @@ from .style import (
|
|
|
196
199
|
KeyboardType,
|
|
197
200
|
LayoutDirection,
|
|
198
201
|
Overflow,
|
|
202
|
+
PointerEvents,
|
|
199
203
|
Position,
|
|
200
204
|
ReturnKeyType,
|
|
201
205
|
ScaleType,
|
|
@@ -205,6 +209,8 @@ from .style import (
|
|
|
205
209
|
StyleSheet,
|
|
206
210
|
TextAlign,
|
|
207
211
|
TextDecoration,
|
|
212
|
+
TextTransform,
|
|
213
|
+
Theme,
|
|
208
214
|
ThemeContext,
|
|
209
215
|
TransformSpec,
|
|
210
216
|
default_theme,
|
|
@@ -312,6 +318,7 @@ __all__ = [
|
|
|
312
318
|
"DEFAULT_DARK_THEME",
|
|
313
319
|
"DEFAULT_LIGHT_THEME",
|
|
314
320
|
"Dimension",
|
|
321
|
+
"Display",
|
|
315
322
|
"EdgeInsets",
|
|
316
323
|
"FlexDirection",
|
|
317
324
|
"FlexWrap",
|
|
@@ -320,6 +327,7 @@ __all__ = [
|
|
|
320
327
|
"KeyboardType",
|
|
321
328
|
"LayoutDirection",
|
|
322
329
|
"Overflow",
|
|
330
|
+
"PointerEvents",
|
|
323
331
|
"Position",
|
|
324
332
|
"ReturnKeyType",
|
|
325
333
|
"ScaleType",
|
|
@@ -329,6 +337,8 @@ __all__ = [
|
|
|
329
337
|
"StyleSheet",
|
|
330
338
|
"TextAlign",
|
|
331
339
|
"TextDecoration",
|
|
340
|
+
"TextTransform",
|
|
341
|
+
"Theme",
|
|
332
342
|
"ThemeContext",
|
|
333
343
|
"TransformSpec",
|
|
334
344
|
"default_theme",
|
pythonnative/_ios_log.py
CHANGED
|
@@ -14,7 +14,7 @@ straight to fd 2 is a small, reliable fix: fd 2 *is* visible to
|
|
|
14
14
|
`simctl` (that is exactly how `NSLog` reaches the terminal), so
|
|
15
15
|
Python output lands next to the Swift logs with correct ordering.
|
|
16
16
|
|
|
17
|
-
This module is intentionally self-contained (no
|
|
17
|
+
This module is intentionally self-contained (no bridge or
|
|
18
18
|
platform-specific C bindings required), so it is safe to import
|
|
19
19
|
early during `pythonnative` package initialization.
|
|
20
20
|
"""
|
pythonnative/alerts.py
CHANGED
|
@@ -30,77 +30,39 @@ Example:
|
|
|
30
30
|
|
|
31
31
|
from __future__ import annotations
|
|
32
32
|
|
|
33
|
-
import asyncio
|
|
34
33
|
from typing import Any, Dict, List, Optional, Sequence
|
|
35
34
|
|
|
36
|
-
from .
|
|
37
|
-
from .runtime import resolve_future
|
|
35
|
+
from .native_modules.registry import native_module
|
|
38
36
|
|
|
39
37
|
# ======================================================================
|
|
40
38
|
# Internal dispatch helpers
|
|
41
39
|
# ======================================================================
|
|
42
40
|
|
|
43
41
|
|
|
44
|
-
def
|
|
42
|
+
async def _present(
|
|
45
43
|
*,
|
|
46
44
|
title: str,
|
|
47
45
|
message: Optional[str],
|
|
48
46
|
buttons: List[Dict[str, Any]],
|
|
49
47
|
style: str,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"""Route an alert request to the active platform presenter.
|
|
48
|
+
) -> int:
|
|
49
|
+
"""Ask the native ``Alert`` module to present and await the chosen index.
|
|
53
50
|
|
|
54
51
|
``buttons`` is a list of ``{"label": str, "style":
|
|
55
|
-
"default"|"cancel"|"destructive"}`` dicts. The
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
"default"|"cancel"|"destructive"}`` dicts. The module resolves with
|
|
53
|
+
the index the user picked, or ``-1`` if the dialog was dismissed
|
|
54
|
+
without a selection. Off device the
|
|
55
|
+
[`DesktopAlert`][pythonnative.native_modules.desktop.DesktopAlert]
|
|
56
|
+
implementation records the call and answers from the queue set by
|
|
57
|
+
[`Alert.set_test_response`][pythonnative.alerts.Alert.set_test_response].
|
|
59
58
|
"""
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
from .native_views.ios import _present_alert as _ios_present_alert
|
|
63
|
-
|
|
64
|
-
_ios_present_alert(
|
|
65
|
-
title=title,
|
|
66
|
-
message=message,
|
|
67
|
-
buttons=buttons,
|
|
68
|
-
style=style,
|
|
69
|
-
on_result=on_result,
|
|
70
|
-
)
|
|
71
|
-
return
|
|
72
|
-
except Exception:
|
|
73
|
-
on_result(-1)
|
|
74
|
-
return
|
|
75
|
-
|
|
76
|
-
if Platform.is_android:
|
|
77
|
-
try:
|
|
78
|
-
from .native_views.android import _present_alert as _android_present_alert
|
|
79
|
-
|
|
80
|
-
_android_present_alert(
|
|
81
|
-
title=title,
|
|
82
|
-
message=message,
|
|
83
|
-
buttons=buttons,
|
|
84
|
-
style=style,
|
|
85
|
-
on_result=on_result,
|
|
86
|
-
)
|
|
87
|
-
return
|
|
88
|
-
except Exception:
|
|
89
|
-
on_result(-1)
|
|
90
|
-
return
|
|
91
|
-
|
|
92
|
-
# Test backend: record the call so unit tests can assert on it,
|
|
93
|
-
# then deliver the configured response.
|
|
94
|
-
Alert._test_log.append(
|
|
95
|
-
{
|
|
96
|
-
"title": title,
|
|
97
|
-
"message": message,
|
|
98
|
-
"buttons": list(buttons),
|
|
99
|
-
"style": style,
|
|
100
|
-
}
|
|
59
|
+
result = await native_module("Alert").call_async(
|
|
60
|
+
"present", title=title, message=message, buttons=buttons, style=style
|
|
101
61
|
)
|
|
102
|
-
|
|
103
|
-
|
|
62
|
+
try:
|
|
63
|
+
return int(result)
|
|
64
|
+
except (TypeError, ValueError):
|
|
65
|
+
return -1
|
|
104
66
|
|
|
105
67
|
|
|
106
68
|
# ======================================================================
|
|
@@ -170,12 +132,8 @@ class Alert:
|
|
|
170
132
|
[`choose`][pythonnative.alerts.Alert.choose] and ``await``
|
|
171
133
|
the result.
|
|
172
134
|
"""
|
|
173
|
-
|
|
174
|
-
title=title,
|
|
175
|
-
message=message,
|
|
176
|
-
buttons=[{"label": button, "style": "default"}],
|
|
177
|
-
style="alert",
|
|
178
|
-
on_result=lambda _idx: None,
|
|
135
|
+
native_module("Alert").call(
|
|
136
|
+
"show", title=title, message=message, buttons=[{"label": button, "style": "default"}], style="alert"
|
|
179
137
|
)
|
|
180
138
|
|
|
181
139
|
@staticmethod
|
|
@@ -206,13 +164,7 @@ class Alert:
|
|
|
206
164
|
await save()
|
|
207
165
|
```
|
|
208
166
|
"""
|
|
209
|
-
|
|
210
|
-
future: asyncio.Future[bool] = loop.create_future()
|
|
211
|
-
|
|
212
|
-
def _on_result(index: int) -> None:
|
|
213
|
-
resolve_future(future, index == 1)
|
|
214
|
-
|
|
215
|
-
_dispatch_alert(
|
|
167
|
+
index = await _present(
|
|
216
168
|
title=title,
|
|
217
169
|
message=message,
|
|
218
170
|
buttons=[
|
|
@@ -220,9 +172,8 @@ class Alert:
|
|
|
220
172
|
{"label": confirm_label, "style": "default"},
|
|
221
173
|
],
|
|
222
174
|
style="alert",
|
|
223
|
-
on_result=_on_result,
|
|
224
175
|
)
|
|
225
|
-
return
|
|
176
|
+
return index == 1
|
|
226
177
|
|
|
227
178
|
@staticmethod
|
|
228
179
|
async def choose(
|
|
@@ -265,9 +216,6 @@ class Alert:
|
|
|
265
216
|
if not options:
|
|
266
217
|
raise ValueError("Alert.choose requires at least one option")
|
|
267
218
|
|
|
268
|
-
loop = asyncio.get_running_loop()
|
|
269
|
-
future: asyncio.Future[Optional[str]] = loop.create_future()
|
|
270
|
-
|
|
271
219
|
destructive = set(destructive_labels)
|
|
272
220
|
buttons: List[Dict[str, Any]] = [
|
|
273
221
|
{
|
|
@@ -279,20 +227,10 @@ class Alert:
|
|
|
279
227
|
if cancel_label is not None:
|
|
280
228
|
buttons.append({"label": cancel_label, "style": "cancel"})
|
|
281
229
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
resolve_future(future, None)
|
|
287
|
-
|
|
288
|
-
_dispatch_alert(
|
|
289
|
-
title=title,
|
|
290
|
-
message=message,
|
|
291
|
-
buttons=buttons,
|
|
292
|
-
style=style,
|
|
293
|
-
on_result=_on_result,
|
|
294
|
-
)
|
|
295
|
-
return await future
|
|
230
|
+
index = await _present(title=title, message=message, buttons=buttons, style=style)
|
|
231
|
+
if 0 <= index < len(options):
|
|
232
|
+
return options[index]
|
|
233
|
+
return None
|
|
296
234
|
|
|
297
235
|
|
|
298
236
|
__all__ = ["Alert"]
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Entry point the native app templates run right after Python starts.
|
|
2
|
+
|
|
3
|
+
Both templates execute one line of Python once the interpreter is up:
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
import pythonnative.bootstrap; pythonnative.bootstrap.start()
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
[`start`][pythonnative.bootstrap.start] connects the two halves of the
|
|
10
|
+
bridge (installing the native -> Python callback on iOS), verifies the
|
|
11
|
+
protocol version, routes ``print()`` to the console on iOS, and warms
|
|
12
|
+
the asyncio runtime. From then on the native runtime drives everything
|
|
13
|
+
through ``callback("host", ...)``; see ``docs/concepts/bridge.md``.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
20
|
+
import traceback
|
|
21
|
+
from typing import Any, Dict
|
|
22
|
+
|
|
23
|
+
__all__ = ["start", "status"]
|
|
24
|
+
|
|
25
|
+
_started: Dict[str, Any] = {}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def start(dev: bool = False, strict: bool = False) -> Dict[str, Any]:
|
|
29
|
+
"""Connect the bridge and prepare the runtime.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
dev: Enable dev mode (RedBox, validation warnings). Debug
|
|
33
|
+
templates pass ``True``; hot reload turns it on as well.
|
|
34
|
+
strict: Re-raise the failure after recording it. The templates
|
|
35
|
+
pass ``True`` so a broken bridge surfaces as a bootstrap
|
|
36
|
+
error screen with the full traceback.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
A status dict (``{"protocol": 1, "platform": "ios"}``) that the
|
|
40
|
+
template logs. Unless ``strict`` is set this never raises:
|
|
41
|
+
failures are printed and reported in the dict under
|
|
42
|
+
``"error"``.
|
|
43
|
+
"""
|
|
44
|
+
global _started
|
|
45
|
+
if _started and "error" not in _started:
|
|
46
|
+
return dict(_started)
|
|
47
|
+
status_: Dict[str, Any] = {"platform": None, "protocol": None}
|
|
48
|
+
try:
|
|
49
|
+
from .utils import IS_ANDROID, IS_IOS
|
|
50
|
+
|
|
51
|
+
status_["platform"] = "ios" if IS_IOS else "android" if IS_ANDROID else "off-device"
|
|
52
|
+
if IS_IOS:
|
|
53
|
+
try:
|
|
54
|
+
from . import _ios_log
|
|
55
|
+
|
|
56
|
+
_ios_log.install()
|
|
57
|
+
except Exception:
|
|
58
|
+
pass
|
|
59
|
+
from . import bridge
|
|
60
|
+
|
|
61
|
+
status_["protocol"] = bridge.handshake()
|
|
62
|
+
if dev or os.environ.get("PN_DEV") in ("1", "true"):
|
|
63
|
+
from . import diagnostics
|
|
64
|
+
|
|
65
|
+
diagnostics.set_dev_mode(True)
|
|
66
|
+
# Create the guest loop now so the first pump request has a
|
|
67
|
+
# loop to drive and effects can schedule work during mount.
|
|
68
|
+
from .runtime import get_loop
|
|
69
|
+
|
|
70
|
+
get_loop()
|
|
71
|
+
# Import the view backend eagerly: on a slow device the first
|
|
72
|
+
# commit shouldn't also pay for importing the reconciler.
|
|
73
|
+
from .native_views import get_registry
|
|
74
|
+
|
|
75
|
+
get_registry()
|
|
76
|
+
except Exception as exc:
|
|
77
|
+
status_["error"] = f"{type(exc).__name__}: {exc}"
|
|
78
|
+
print(f"[pn.bootstrap] start failed: {exc!r}", file=sys.stderr)
|
|
79
|
+
traceback.print_exc()
|
|
80
|
+
_started = status_
|
|
81
|
+
if strict:
|
|
82
|
+
raise
|
|
83
|
+
return dict(status_)
|
|
84
|
+
_started = status_
|
|
85
|
+
return dict(status_)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def status() -> Dict[str, Any]:
|
|
89
|
+
"""Return the result of the last [`start`][pythonnative.bootstrap.start] (empty before it ran)."""
|
|
90
|
+
return dict(_started)
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"""The native bridge: one channel between Python and Swift / Kotlin.
|
|
2
|
+
|
|
3
|
+
Everything that crosses into native code goes through a
|
|
4
|
+
[`Transport`][pythonnative.bridge.Transport] (``apply`` a transaction,
|
|
5
|
+
``measure`` a view, run a ``command``, drive an ``animate`` request, or
|
|
6
|
+
``call`` a native module), and everything native sends back arrives at
|
|
7
|
+
[`native_callback`][pythonnative.bridge.native_callback]. The protocol
|
|
8
|
+
is documented in ``docs/concepts/bridge.md``.
|
|
9
|
+
|
|
10
|
+
Off-device (tests, ``pn preview``) there is no transport; the desktop
|
|
11
|
+
registry renders with Tkinter and native modules fall back to their
|
|
12
|
+
Python implementations. Tests that want to exercise the bridge itself
|
|
13
|
+
install a [`FakeTransport`][pythonnative.bridge.fake.FakeTransport]
|
|
14
|
+
with [`set_transport`][pythonnative.bridge.set_transport].
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import threading
|
|
20
|
+
from collections import deque
|
|
21
|
+
from typing import Any, Callable, Deque, Dict, Optional, Protocol, Tuple
|
|
22
|
+
|
|
23
|
+
from . import codec
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"PROTOCOL_VERSION",
|
|
27
|
+
"Transport",
|
|
28
|
+
"get_transport",
|
|
29
|
+
"has_transport",
|
|
30
|
+
"handshake",
|
|
31
|
+
"native_callback",
|
|
32
|
+
"post_to_main",
|
|
33
|
+
"set_transport",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
PROTOCOL_VERSION = 1
|
|
37
|
+
"""Bridge protocol version this Python package speaks."""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Transport(Protocol):
|
|
41
|
+
"""The Python -> native half of the bridge."""
|
|
42
|
+
|
|
43
|
+
name: str
|
|
44
|
+
|
|
45
|
+
def protocol_version(self) -> int:
|
|
46
|
+
"""Return the protocol version compiled into the native library."""
|
|
47
|
+
|
|
48
|
+
def apply(self, transaction_json: str) -> None:
|
|
49
|
+
"""Apply one serialized transaction (a JSON array of ops)."""
|
|
50
|
+
|
|
51
|
+
def measure(self, tag: int, max_width: float, max_height: float) -> Tuple[float, float]:
|
|
52
|
+
"""Return the intrinsic ``(width, height)`` of the view ``tag`` under the constraints."""
|
|
53
|
+
|
|
54
|
+
def command(self, tag: int, name: str, args_json: str) -> Optional[str]:
|
|
55
|
+
"""Run an imperative command on one view; returns its JSON result or ``None``."""
|
|
56
|
+
|
|
57
|
+
def animate(self, tag: int, request_json: str) -> Optional[str]:
|
|
58
|
+
"""Handle an animation request (``set`` / ``start`` / ``cancel``) for one view."""
|
|
59
|
+
|
|
60
|
+
def call(self, module: str, method: str, args_json: str) -> Optional[str]:
|
|
61
|
+
"""Call a native module method with a ``{"call_id", "args"}`` envelope."""
|
|
62
|
+
|
|
63
|
+
def set_callback(self, callback: Callable[[str, int, str, str], Optional[str]]) -> None:
|
|
64
|
+
"""Install ``callback`` as the native -> Python entry point."""
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# ======================================================================
|
|
68
|
+
# Transport selection
|
|
69
|
+
# ======================================================================
|
|
70
|
+
|
|
71
|
+
_transport: Optional[Transport] = None
|
|
72
|
+
_transport_lock = threading.Lock()
|
|
73
|
+
_explicit = False
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _create_platform_transport() -> Optional[Transport]:
|
|
77
|
+
from ..utils import IS_ANDROID, IS_IOS
|
|
78
|
+
|
|
79
|
+
if IS_IOS:
|
|
80
|
+
from .ios import IOSTransport
|
|
81
|
+
|
|
82
|
+
transport: Transport = IOSTransport()
|
|
83
|
+
transport.set_callback(native_callback)
|
|
84
|
+
return transport
|
|
85
|
+
if IS_ANDROID:
|
|
86
|
+
from .android import AndroidTransport
|
|
87
|
+
|
|
88
|
+
return AndroidTransport()
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def get_transport() -> Transport:
|
|
93
|
+
"""Return the active transport, creating the platform one on first use.
|
|
94
|
+
|
|
95
|
+
Raises:
|
|
96
|
+
RuntimeError: Off-device, where no native runtime exists.
|
|
97
|
+
"""
|
|
98
|
+
global _transport
|
|
99
|
+
if _transport is not None:
|
|
100
|
+
return _transport
|
|
101
|
+
with _transport_lock:
|
|
102
|
+
if _transport is None:
|
|
103
|
+
created = _create_platform_transport()
|
|
104
|
+
if created is None:
|
|
105
|
+
raise RuntimeError(
|
|
106
|
+
"No native bridge is available on this platform (running off-device). "
|
|
107
|
+
"Use `pn preview` for the desktop renderer or install a FakeTransport in tests."
|
|
108
|
+
)
|
|
109
|
+
_transport = created
|
|
110
|
+
return _transport
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def has_transport() -> bool:
|
|
114
|
+
"""Whether a transport exists or can be created without raising."""
|
|
115
|
+
if _transport is not None:
|
|
116
|
+
return True
|
|
117
|
+
if _explicit:
|
|
118
|
+
return False
|
|
119
|
+
from ..utils import IS_ANDROID, IS_IOS
|
|
120
|
+
|
|
121
|
+
return bool(IS_IOS or IS_ANDROID)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def set_transport(transport: Optional[Transport]) -> None:
|
|
125
|
+
"""Install a transport explicitly (tests) or reset with ``None``."""
|
|
126
|
+
global _transport, _explicit
|
|
127
|
+
with _transport_lock:
|
|
128
|
+
_transport = transport
|
|
129
|
+
_explicit = transport is not None
|
|
130
|
+
if transport is not None:
|
|
131
|
+
transport.set_callback(native_callback)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def handshake() -> int:
|
|
135
|
+
"""Verify the native library speaks our protocol version.
|
|
136
|
+
|
|
137
|
+
Called by the native templates right after Python starts. Returns
|
|
138
|
+
the negotiated version.
|
|
139
|
+
|
|
140
|
+
Raises:
|
|
141
|
+
RuntimeError: On a version mismatch, with a hint to rebuild.
|
|
142
|
+
"""
|
|
143
|
+
version = get_transport().protocol_version()
|
|
144
|
+
if version != PROTOCOL_VERSION:
|
|
145
|
+
raise RuntimeError(
|
|
146
|
+
f"Bridge protocol mismatch: the native runtime speaks v{version} but pythonnative "
|
|
147
|
+
f"expects v{PROTOCOL_VERSION}. Re-run 'pn build' so the staged template matches the "
|
|
148
|
+
"installed pythonnative package."
|
|
149
|
+
)
|
|
150
|
+
return version
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
# ======================================================================
|
|
154
|
+
# Main-queue posting
|
|
155
|
+
# ======================================================================
|
|
156
|
+
#
|
|
157
|
+
# The asyncio guest loop and ``call_on_main_thread`` need a way to run
|
|
158
|
+
# a callable on the platform main thread's next turn. Native provides
|
|
159
|
+
# it: ``Host.post()`` schedules ``callback("pump")``, which drains this
|
|
160
|
+
# queue. Keeping the queue in Python means one native crossing per
|
|
161
|
+
# batch of callables rather than one per callable.
|
|
162
|
+
|
|
163
|
+
_main_queue: Deque[Callable[[], None]] = deque()
|
|
164
|
+
_main_queue_lock = threading.Lock()
|
|
165
|
+
_pump_requested = False
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def post_to_main(fn: Callable[[], None]) -> None:
|
|
169
|
+
"""Queue ``fn`` for the next main-thread turn (never runs inline)."""
|
|
170
|
+
global _pump_requested
|
|
171
|
+
with _main_queue_lock:
|
|
172
|
+
_main_queue.append(fn)
|
|
173
|
+
if _pump_requested:
|
|
174
|
+
return
|
|
175
|
+
_pump_requested = True
|
|
176
|
+
try:
|
|
177
|
+
get_transport().call("Host", "post", codec.dumps({"call_id": 0, "args": {}}))
|
|
178
|
+
except Exception as exc:
|
|
179
|
+
with _main_queue_lock:
|
|
180
|
+
_pump_requested = False
|
|
181
|
+
print(f"[pn.bridge] Host.post failed; running {len(_main_queue)} queued callable(s) inline: {exc!r}")
|
|
182
|
+
_drain_main_queue()
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _drain_main_queue() -> None:
|
|
186
|
+
global _pump_requested
|
|
187
|
+
while True:
|
|
188
|
+
with _main_queue_lock:
|
|
189
|
+
if not _main_queue:
|
|
190
|
+
_pump_requested = False
|
|
191
|
+
return
|
|
192
|
+
fn = _main_queue.popleft()
|
|
193
|
+
try:
|
|
194
|
+
fn()
|
|
195
|
+
except Exception as exc:
|
|
196
|
+
print(f"[pn.bridge] main-thread callable raised: {exc!r}")
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
# ======================================================================
|
|
200
|
+
# native -> Python
|
|
201
|
+
# ======================================================================
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def native_callback(kind: str, tag: int, name: str, payload: str) -> Optional[str]:
|
|
205
|
+
"""Single entry point for every native -> Python message.
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
kind: ``"event"``, ``"module"``, ``"host"``, ``"animation"``,
|
|
209
|
+
or ``"pump"``.
|
|
210
|
+
tag: View tag (events), screen id (host), otherwise ``0``.
|
|
211
|
+
name: Event name, module name, or host event.
|
|
212
|
+
payload: JSON text whose shape depends on ``kind``.
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
A JSON string for request-style messages (a handler's return
|
|
216
|
+
value, ``"true"`` / ``"false"`` for ``back_pressed``), else
|
|
217
|
+
``None``. Never raises: failures are reported through
|
|
218
|
+
``diagnostics`` so nothing propagates into UIKit or the
|
|
219
|
+
Android looper.
|
|
220
|
+
"""
|
|
221
|
+
try:
|
|
222
|
+
if kind == "event":
|
|
223
|
+
return _on_event(int(tag), name, payload)
|
|
224
|
+
if kind == "module":
|
|
225
|
+
from ..native_modules.registry import dispatch_module_message
|
|
226
|
+
|
|
227
|
+
dispatch_module_message(name, codec.loads(payload) or {})
|
|
228
|
+
return None
|
|
229
|
+
if kind == "host":
|
|
230
|
+
from ..hosts.native import dispatch_host_event
|
|
231
|
+
|
|
232
|
+
return dispatch_host_event(int(tag), name, codec.loads(payload))
|
|
233
|
+
if kind == "animation":
|
|
234
|
+
from ..animated import native_animation_completed
|
|
235
|
+
|
|
236
|
+
data = codec.loads(payload) or {}
|
|
237
|
+
native_animation_completed(int(data.get("id", 0)), bool(data.get("finished", True)))
|
|
238
|
+
return None
|
|
239
|
+
if kind == "pump":
|
|
240
|
+
_drain_main_queue()
|
|
241
|
+
return None
|
|
242
|
+
print(f"[pn.bridge] unknown callback kind {kind!r}")
|
|
243
|
+
except Exception as exc:
|
|
244
|
+
from .. import diagnostics
|
|
245
|
+
|
|
246
|
+
if not diagnostics.report_error(exc, phase=f"bridge {kind}:{name}"):
|
|
247
|
+
import traceback
|
|
248
|
+
|
|
249
|
+
traceback.print_exc()
|
|
250
|
+
return None
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _on_event(tag: int, name: str, payload: str) -> Optional[str]:
|
|
254
|
+
from ..events import get_event_registry
|
|
255
|
+
|
|
256
|
+
args = codec.loads(payload)
|
|
257
|
+
if args is None:
|
|
258
|
+
args = []
|
|
259
|
+
elif not isinstance(args, list):
|
|
260
|
+
args = [args]
|
|
261
|
+
callback = get_event_registry().get(tag, name)
|
|
262
|
+
if callback is None:
|
|
263
|
+
from ..native_views import get_registry
|
|
264
|
+
|
|
265
|
+
backend = get_registry()
|
|
266
|
+
internal = getattr(backend, "handle_internal_event", None)
|
|
267
|
+
if internal is not None:
|
|
268
|
+
result = internal(tag, name, args)
|
|
269
|
+
return None if result is None else codec.dumps(codec.to_jsonable(result))
|
|
270
|
+
return None
|
|
271
|
+
try:
|
|
272
|
+
result = callback(*args)
|
|
273
|
+
except Exception as exc:
|
|
274
|
+
from .. import diagnostics
|
|
275
|
+
|
|
276
|
+
if not diagnostics.report_error(exc, phase=f"event {name!r}"):
|
|
277
|
+
import traceback
|
|
278
|
+
|
|
279
|
+
traceback.print_exc()
|
|
280
|
+
return None
|
|
281
|
+
if result is None:
|
|
282
|
+
return None
|
|
283
|
+
try:
|
|
284
|
+
return codec.dumps(codec.to_jsonable(result))
|
|
285
|
+
except (TypeError, ValueError):
|
|
286
|
+
return None
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def _reset_for_tests() -> None:
|
|
290
|
+
"""Drop the transport and any queued main-thread work (test isolation)."""
|
|
291
|
+
global _transport, _explicit, _pump_requested
|
|
292
|
+
with _transport_lock:
|
|
293
|
+
_transport = None
|
|
294
|
+
_explicit = False
|
|
295
|
+
with _main_queue_lock:
|
|
296
|
+
_main_queue.clear()
|
|
297
|
+
_pump_requested = False
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def transport_state() -> Dict[str, Any]:
|
|
301
|
+
"""Diagnostics snapshot (used by ``pn doctor`` and tests)."""
|
|
302
|
+
return {
|
|
303
|
+
"transport": None if _transport is None else _transport.name,
|
|
304
|
+
"protocol_version": PROTOCOL_VERSION,
|
|
305
|
+
"queued_main_callables": len(_main_queue),
|
|
306
|
+
}
|