pythonnative 0.15.0__tar.gz → 0.17.0__tar.gz

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.
Files changed (126) hide show
  1. {pythonnative-0.15.0/src/pythonnative.egg-info → pythonnative-0.17.0}/PKG-INFO +3 -1
  2. {pythonnative-0.15.0 → pythonnative-0.17.0}/pyproject.toml +8 -1
  3. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/__init__.py +69 -3
  4. pythonnative-0.17.0/src/pythonnative/alerts.py +298 -0
  5. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/animated.py +225 -169
  6. pythonnative-0.17.0/src/pythonnative/components.py +1515 -0
  7. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/hooks.py +345 -21
  8. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/native_modules/camera.py +118 -121
  9. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/native_modules/file_system.py +3 -3
  10. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/native_modules/location.py +90 -109
  11. pythonnative-0.17.0/src/pythonnative/native_modules/notifications.py +206 -0
  12. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/native_views/android.py +189 -62
  13. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/native_views/base.py +3 -92
  14. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/native_views/ios.py +186 -38
  15. pythonnative-0.17.0/src/pythonnative/net.py +244 -0
  16. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/reconciler.py +123 -14
  17. pythonnative-0.17.0/src/pythonnative/runtime.py +487 -0
  18. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/sdk/__init__.py +1 -2
  19. pythonnative-0.17.0/src/pythonnative/storage.py +400 -0
  20. {pythonnative-0.15.0 → pythonnative-0.17.0/src/pythonnative.egg-info}/PKG-INFO +3 -1
  21. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative.egg-info/SOURCES.txt +7 -0
  22. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative.egg-info/requires.txt +2 -0
  23. pythonnative-0.17.0/tests/test_alert.py +115 -0
  24. pythonnative-0.17.0/tests/test_animated.py +268 -0
  25. pythonnative-0.17.0/tests/test_async_hooks.py +339 -0
  26. {pythonnative-0.15.0 → pythonnative-0.17.0}/tests/test_components.py +34 -0
  27. {pythonnative-0.15.0 → pythonnative-0.17.0}/tests/test_hooks.py +113 -0
  28. {pythonnative-0.15.0 → pythonnative-0.17.0}/tests/test_native_views.py +0 -59
  29. pythonnative-0.17.0/tests/test_net.py +189 -0
  30. {pythonnative-0.15.0 → pythonnative-0.17.0}/tests/test_new_components.py +21 -16
  31. {pythonnative-0.15.0 → pythonnative-0.17.0}/tests/test_reconciler.py +62 -0
  32. pythonnative-0.17.0/tests/test_runtime.py +146 -0
  33. pythonnative-0.17.0/tests/test_storage.py +215 -0
  34. pythonnative-0.15.0/src/pythonnative/alerts.py +0 -112
  35. pythonnative-0.15.0/src/pythonnative/components.py +0 -1223
  36. pythonnative-0.15.0/src/pythonnative/native_modules/notifications.py +0 -184
  37. pythonnative-0.15.0/tests/test_alert.py +0 -68
  38. pythonnative-0.15.0/tests/test_animated.py +0 -168
  39. {pythonnative-0.15.0 → pythonnative-0.17.0}/LICENSE +0 -0
  40. {pythonnative-0.15.0 → pythonnative-0.17.0}/README.md +0 -0
  41. {pythonnative-0.15.0 → pythonnative-0.17.0}/setup.cfg +0 -0
  42. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/_ios_log.py +0 -0
  43. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/cli/__init__.py +0 -0
  44. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/cli/pn.py +0 -0
  45. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/element.py +0 -0
  46. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/hot_reload.py +0 -0
  47. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/layout.py +0 -0
  48. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/native_modules/__init__.py +0 -0
  49. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/native_views/__init__.py +0 -0
  50. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/navigation.py +0 -0
  51. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/platform.py +0 -0
  52. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/platform_metrics.py +0 -0
  53. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/screen.py +0 -0
  54. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/sdk/_components.py +0 -0
  55. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/style.py +0 -0
  56. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/build.gradle +0 -0
  57. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/proguard-rules.pro +0 -0
  58. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/androidTest/java/com/pythonnative/android_template/ExampleInstrumentedTest.kt +0 -0
  59. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/AndroidManifest.xml +0 -0
  60. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +0 -0
  61. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/Navigator.kt +0 -0
  62. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNVirtualListView.java +0 -0
  63. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt +0 -0
  64. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/res/drawable/ic_launcher_background.xml +0 -0
  65. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +0 -0
  66. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/res/layout/activity_main.xml +0 -0
  67. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +0 -0
  68. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +0 -0
  69. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
  70. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
  71. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
  72. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
  73. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
  74. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
  75. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
  76. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
  77. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
  78. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
  79. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/res/navigation/nav_graph.xml +0 -0
  80. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/res/values/colors.xml +0 -0
  81. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/res/values/strings.xml +0 -0
  82. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/res/values/themes.xml +0 -0
  83. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/res/values-night/themes.xml +0 -0
  84. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/res/xml/backup_rules.xml +0 -0
  85. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/main/res/xml/data_extraction_rules.xml +0 -0
  86. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/app/src/test/java/com/pythonnative/android_template/ExampleUnitTest.kt +0 -0
  87. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/build.gradle +0 -0
  88. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.jar +0 -0
  89. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties +0 -0
  90. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/gradle.properties +0 -0
  91. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/gradlew +0 -0
  92. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/gradlew.bat +0 -0
  93. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/android_template/settings.gradle +0 -0
  94. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/ios_template/ios_template/AppDelegate.swift +0 -0
  95. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/AccentColor.colorset/Contents.json +0 -0
  96. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -0
  97. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/Contents.json +0 -0
  98. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/ios_template/ios_template/Base.lproj/LaunchScreen.storyboard +0 -0
  99. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/ios_template/ios_template/Base.lproj/Main.storyboard +0 -0
  100. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/ios_template/ios_template/Info.plist +0 -0
  101. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +0 -0
  102. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/ios_template/ios_template/ViewController.swift +0 -0
  103. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +0 -0
  104. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/ios_template/ios_template.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -0
  105. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/ios_template/ios_templateTests/ios_templateTests.swift +0 -0
  106. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITests.swift +0 -0
  107. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITestsLaunchTests.swift +0 -0
  108. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative/utils.py +0 -0
  109. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative.egg-info/dependency_links.txt +0 -0
  110. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative.egg-info/entry_points.txt +0 -0
  111. {pythonnative-0.15.0 → pythonnative-0.17.0}/src/pythonnative.egg-info/top_level.txt +0 -0
  112. {pythonnative-0.15.0 → pythonnative-0.17.0}/tests/test_cli.py +0 -0
  113. {pythonnative-0.15.0 → pythonnative-0.17.0}/tests/test_element.py +0 -0
  114. {pythonnative-0.15.0 → pythonnative-0.17.0}/tests/test_hot_reload.py +0 -0
  115. {pythonnative-0.15.0 → pythonnative-0.17.0}/tests/test_ios_log.py +0 -0
  116. {pythonnative-0.15.0 → pythonnative-0.17.0}/tests/test_layout.py +0 -0
  117. {pythonnative-0.15.0 → pythonnative-0.17.0}/tests/test_metric_hooks.py +0 -0
  118. {pythonnative-0.15.0 → pythonnative-0.17.0}/tests/test_navigation.py +0 -0
  119. {pythonnative-0.15.0 → pythonnative-0.17.0}/tests/test_platform.py +0 -0
  120. {pythonnative-0.15.0 → pythonnative-0.17.0}/tests/test_platform_metrics.py +0 -0
  121. {pythonnative-0.15.0 → pythonnative-0.17.0}/tests/test_ref.py +0 -0
  122. {pythonnative-0.15.0 → pythonnative-0.17.0}/tests/test_screen.py +0 -0
  123. {pythonnative-0.15.0 → pythonnative-0.17.0}/tests/test_sdk.py +0 -0
  124. {pythonnative-0.15.0 → pythonnative-0.17.0}/tests/test_smoke.py +0 -0
  125. {pythonnative-0.15.0 → pythonnative-0.17.0}/tests/test_style.py +0 -0
  126. {pythonnative-0.15.0 → pythonnative-0.17.0}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythonnative
3
- Version: 0.15.0
3
+ Version: 0.17.0
4
4
  Summary: Cross-platform native UI toolkit for Android and iOS
5
5
  Author: Owen Carey
6
6
  License: MIT License
@@ -55,11 +55,13 @@ Requires-Dist: black>=24.0; extra == "dev"
55
55
  Requires-Dist: ruff>=0.5; extra == "dev"
56
56
  Requires-Dist: mypy>=1.10; extra == "dev"
57
57
  Requires-Dist: pytest>=8.0; extra == "dev"
58
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
58
59
  Provides-Extra: ci
59
60
  Requires-Dist: black>=24.0; extra == "ci"
60
61
  Requires-Dist: ruff>=0.5; extra == "ci"
61
62
  Requires-Dist: mypy>=1.10; extra == "ci"
62
63
  Requires-Dist: pytest>=8.0; extra == "ci"
64
+ Requires-Dist: pytest-asyncio>=0.23; extra == "ci"
63
65
  Dynamic: license-file
64
66
 
65
67
  <p align="center">
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pythonnative"
7
- version = "0.15.0"
7
+ version = "0.17.0"
8
8
  description = "Cross-platform native UI toolkit for Android and iOS"
9
9
  authors = [
10
10
  { name = "Owen Carey" }
@@ -43,12 +43,14 @@ dev = [
43
43
  "ruff>=0.5",
44
44
  "mypy>=1.10",
45
45
  "pytest>=8.0",
46
+ "pytest-asyncio>=0.23",
46
47
  ]
47
48
  ci = [
48
49
  "black>=24.0",
49
50
  "ruff>=0.5",
50
51
  "mypy>=1.10",
51
52
  "pytest>=8.0",
53
+ "pytest-asyncio>=0.23",
52
54
  ]
53
55
 
54
56
  [project.scripts]
@@ -65,6 +67,8 @@ Documentation = "https://docs.pythonnative.com/"
65
67
 
66
68
 
67
69
 
70
+
71
+
68
72
  [tool.setuptools.packages.find]
69
73
  where = ["src"]
70
74
 
@@ -119,6 +123,9 @@ convention = "google"
119
123
  line-length = 120
120
124
  target-version = ['py39']
121
125
 
126
+ [tool.pytest.ini_options]
127
+ asyncio_mode = "auto"
128
+
122
129
  # ── Semantic Release ────────────────────────────────────────────────
123
130
 
124
131
  [tool.semantic_release]
@@ -51,49 +51,75 @@ Example:
51
51
  ```
52
52
  """
53
53
 
54
- __version__ = "0.15.0"
54
+ __version__ = "0.17.0"
55
55
 
56
- from . import sdk
56
+ from . import runtime, sdk
57
57
  from .alerts import Alert
58
- from .animated import Animated, AnimatedValue
58
+ from .animated import Animated, AnimatedValue, use_animated_value
59
59
  from .components import (
60
60
  ActivityIndicator,
61
+ ActivityIndicatorProps,
61
62
  Button,
63
+ ButtonProps,
62
64
  Column,
63
65
  ErrorBoundary,
64
66
  FlatList,
67
+ Fragment,
65
68
  Image,
69
+ ImageProps,
66
70
  KeyboardAvoidingView,
71
+ KeyboardAvoidingViewProps,
67
72
  Modal,
73
+ ModalProps,
68
74
  Picker,
75
+ PickerProps,
69
76
  Pressable,
77
+ PressableProps,
70
78
  ProgressBar,
79
+ ProgressBarProps,
71
80
  RefreshControl,
72
81
  Row,
73
82
  SafeAreaView,
83
+ SafeAreaViewProps,
74
84
  ScrollView,
85
+ ScrollViewProps,
75
86
  SectionList,
76
87
  Slider,
88
+ SliderProps,
77
89
  Spacer,
90
+ SpacerProps,
78
91
  StatusBar,
92
+ StatusBarProps,
79
93
  Switch,
94
+ SwitchProps,
80
95
  Text,
81
96
  TextInput,
97
+ TextInputProps,
98
+ TextProps,
82
99
  View,
100
+ ViewProps,
83
101
  WebView,
102
+ WebViewProps,
84
103
  )
85
104
  from .element import Element
86
105
  from .hooks import (
106
+ MutationCall,
107
+ MutationState,
87
108
  Provider,
109
+ QueryResult,
88
110
  batch_updates,
89
111
  component,
90
112
  create_context,
113
+ memo,
114
+ use_async_effect,
91
115
  use_callback,
92
116
  use_context,
93
117
  use_effect,
94
118
  use_keyboard_height,
95
119
  use_memo,
120
+ use_mutation,
96
121
  use_navigation,
122
+ use_query,
97
123
  use_reducer,
98
124
  use_ref,
99
125
  use_safe_area_insets,
@@ -109,7 +135,9 @@ from .navigation import (
109
135
  use_focus_effect,
110
136
  use_route,
111
137
  )
138
+ from .net import HTTPError, Response, fetch
112
139
  from .platform import Platform
140
+ from .runtime import run_async
113
141
  from .screen import create_screen
114
142
  from .sdk import (
115
143
  Props,
@@ -118,6 +146,7 @@ from .sdk import (
118
146
  native_component,
119
147
  register_component,
120
148
  )
149
+ from .storage import AsyncStorage, use_persisted_state
121
150
  from .style import (
122
151
  AlignItems,
123
152
  AlignSelf,
@@ -152,6 +181,7 @@ __all__ = [
152
181
  "Column",
153
182
  "ErrorBoundary",
154
183
  "FlatList",
184
+ "Fragment",
155
185
  "Image",
156
186
  "KeyboardAvoidingView",
157
187
  "Modal",
@@ -171,6 +201,25 @@ __all__ = [
171
201
  "TextInput",
172
202
  "View",
173
203
  "WebView",
204
+ # Built-in Props dataclasses
205
+ "ActivityIndicatorProps",
206
+ "ButtonProps",
207
+ "ImageProps",
208
+ "KeyboardAvoidingViewProps",
209
+ "ModalProps",
210
+ "PickerProps",
211
+ "PressableProps",
212
+ "ProgressBarProps",
213
+ "SafeAreaViewProps",
214
+ "ScrollViewProps",
215
+ "SliderProps",
216
+ "SpacerProps",
217
+ "StatusBarProps",
218
+ "SwitchProps",
219
+ "TextInputProps",
220
+ "TextProps",
221
+ "ViewProps",
222
+ "WebViewProps",
174
223
  # Core
175
224
  "Element",
176
225
  "create_screen",
@@ -178,13 +227,21 @@ __all__ = [
178
227
  "batch_updates",
179
228
  "component",
180
229
  "create_context",
230
+ "memo",
231
+ "MutationCall",
232
+ "MutationState",
233
+ "QueryResult",
234
+ "use_async_effect",
181
235
  "use_callback",
182
236
  "use_context",
183
237
  "use_effect",
184
238
  "use_focus_effect",
185
239
  "use_keyboard_height",
186
240
  "use_memo",
241
+ "use_mutation",
187
242
  "use_navigation",
243
+ "use_persisted_state",
244
+ "use_query",
188
245
  "use_reducer",
189
246
  "use_ref",
190
247
  "use_route",
@@ -225,6 +282,7 @@ __all__ = [
225
282
  # Animation
226
283
  "Animated",
227
284
  "AnimatedValue",
285
+ "use_animated_value",
228
286
  # Imperative
229
287
  "Alert",
230
288
  # Native modules
@@ -232,6 +290,14 @@ __all__ = [
232
290
  "FileSystem",
233
291
  "Location",
234
292
  "Notifications",
293
+ # Networking + persistence
294
+ "AsyncStorage",
295
+ "fetch",
296
+ "HTTPError",
297
+ "Response",
298
+ # Runtime
299
+ "run_async",
300
+ "runtime",
235
301
  # Platform
236
302
  "Platform",
237
303
  # Custom-component SDK
@@ -0,0 +1,298 @@
1
+ """Imperative, awaitable system alerts.
2
+
3
+ Inspired by React Native's ``Alert.alert()`` but designed around
4
+ ``async`` / ``await`` instead of per-button callbacks. There are three
5
+ entry points:
6
+
7
+ - [`Alert.show`][pythonnative.alerts.Alert.show]: fire-and-forget
8
+ one-button notice (no return value).
9
+ - [`Alert.confirm`][pythonnative.alerts.Alert.confirm]: awaitable
10
+ two-button yes/no, resolves to a ``bool``.
11
+ - [`Alert.choose`][pythonnative.alerts.Alert.choose]: awaitable
12
+ multi-button picker / action sheet, resolves to the selected
13
+ label (or ``None`` if dismissed).
14
+
15
+ Example:
16
+ ```python
17
+ import pythonnative as pn
18
+
19
+
20
+ async def maybe_delete():
21
+ if await pn.Alert.confirm(
22
+ title="Delete item?",
23
+ message="This action cannot be undone.",
24
+ confirm_label="Delete",
25
+ cancel_label="Keep",
26
+ ):
27
+ await delete_item()
28
+ ```
29
+ """
30
+
31
+ from __future__ import annotations
32
+
33
+ import asyncio
34
+ from typing import Any, Dict, List, Optional, Sequence
35
+
36
+ from .platform import Platform
37
+ from .runtime import resolve_future
38
+
39
+ # ======================================================================
40
+ # Internal dispatch helpers
41
+ # ======================================================================
42
+
43
+
44
+ def _dispatch_alert(
45
+ *,
46
+ title: str,
47
+ message: Optional[str],
48
+ buttons: List[Dict[str, Any]],
49
+ style: str,
50
+ on_result: Any,
51
+ ) -> None:
52
+ """Route an alert request to the active platform presenter.
53
+
54
+ ``buttons`` is a list of ``{"label": str, "style":
55
+ "default"|"cancel"|"destructive"}`` dicts. The presenter must
56
+ invoke ``on_result(index)`` exactly once when the user picks a
57
+ button, or ``on_result(-1)`` if the dialog is dismissed without a
58
+ selection. ``on_result`` may run on any thread.
59
+ """
60
+ if Platform.is_ios:
61
+ try:
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
+ }
101
+ )
102
+ response = Alert._next_test_response()
103
+ on_result(response)
104
+
105
+
106
+ # ======================================================================
107
+ # Public Alert API
108
+ # ======================================================================
109
+
110
+
111
+ class Alert:
112
+ """Imperative alert / action-sheet helper.
113
+
114
+ All methods are static. Use [`show`][pythonnative.alerts.Alert.show]
115
+ for a fire-and-forget single-button notice,
116
+ [`confirm`][pythonnative.alerts.Alert.confirm] for an awaitable
117
+ yes/no dialog, and
118
+ [`choose`][pythonnative.alerts.Alert.choose] for a multi-option
119
+ picker.
120
+ """
121
+
122
+ #: Records every alert call when running off-device. Tests reset
123
+ #: this between cases via ``Alert._test_log.clear()``. Each entry
124
+ #: contains ``title``, ``message``, ``buttons``, and ``style``.
125
+ _test_log: List[Dict[str, Any]] = []
126
+
127
+ #: Queue of indices to deliver to upcoming alerts in tests. Set via
128
+ #: [`Alert.set_test_response`][pythonnative.alerts.Alert.set_test_response].
129
+ #: A negative value (or empty queue) simulates a dismiss.
130
+ _test_responses: List[int] = []
131
+
132
+ @staticmethod
133
+ def set_test_response(*indices: int) -> None:
134
+ """Queue indices to return from upcoming test alerts.
135
+
136
+ Use in async tests to script the user's choices: each pending
137
+ call to [`confirm`][pythonnative.alerts.Alert.confirm] or
138
+ [`choose`][pythonnative.alerts.Alert.choose] pops the next
139
+ queued index. Pass ``-1`` to simulate a dismiss.
140
+
141
+ Args:
142
+ *indices: Sequence of button indices to deliver, oldest
143
+ first. Calls beyond the queue length resolve to ``-1``.
144
+ """
145
+ Alert._test_responses[:] = list(indices)
146
+
147
+ @staticmethod
148
+ def _next_test_response() -> int:
149
+ if Alert._test_responses:
150
+ return Alert._test_responses.pop(0)
151
+ return -1
152
+
153
+ @staticmethod
154
+ def show(
155
+ title: str,
156
+ message: Optional[str] = None,
157
+ *,
158
+ button: str = "OK",
159
+ ) -> None:
160
+ """Display a simple, one-button alert and return immediately.
161
+
162
+ Args:
163
+ title: Dialog title.
164
+ message: Optional body text.
165
+ button: Label for the single dismiss button (default
166
+ ``"OK"``).
167
+
168
+ This is fire-and-forget. To know what the user did, use
169
+ [`confirm`][pythonnative.alerts.Alert.confirm] or
170
+ [`choose`][pythonnative.alerts.Alert.choose] and ``await``
171
+ the result.
172
+ """
173
+ _dispatch_alert(
174
+ title=title,
175
+ message=message,
176
+ buttons=[{"label": button, "style": "default"}],
177
+ style="alert",
178
+ on_result=lambda _idx: None,
179
+ )
180
+
181
+ @staticmethod
182
+ async def confirm(
183
+ title: str,
184
+ message: Optional[str] = None,
185
+ *,
186
+ confirm_label: str = "OK",
187
+ cancel_label: str = "Cancel",
188
+ ) -> bool:
189
+ """Present a two-button yes/no dialog and wait for the choice.
190
+
191
+ Args:
192
+ title: Dialog title.
193
+ message: Optional body text.
194
+ confirm_label: Label for the "yes" button (default
195
+ ``"OK"``).
196
+ cancel_label: Label for the "no" button (default
197
+ ``"Cancel"``).
198
+
199
+ Returns:
200
+ ``True`` if the user pressed the confirm button, ``False``
201
+ for the cancel button or a dismiss.
202
+
203
+ Example:
204
+ ```python
205
+ if await pn.Alert.confirm("Save changes?"):
206
+ await save()
207
+ ```
208
+ """
209
+ loop = asyncio.get_running_loop()
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(
216
+ title=title,
217
+ message=message,
218
+ buttons=[
219
+ {"label": cancel_label, "style": "cancel"},
220
+ {"label": confirm_label, "style": "default"},
221
+ ],
222
+ style="alert",
223
+ on_result=_on_result,
224
+ )
225
+ return await future
226
+
227
+ @staticmethod
228
+ async def choose(
229
+ title: str,
230
+ options: Sequence[str],
231
+ *,
232
+ message: Optional[str] = None,
233
+ cancel_label: Optional[str] = None,
234
+ style: str = "action_sheet",
235
+ destructive_labels: Sequence[str] = (),
236
+ ) -> Optional[str]:
237
+ """Present a multi-option picker and wait for the user's choice.
238
+
239
+ Args:
240
+ title: Dialog title.
241
+ options: Sequence of option labels (in display order).
242
+ message: Optional body text.
243
+ cancel_label: If provided, adds a "cancel" button with
244
+ this label. Selecting it resolves to ``None``.
245
+ style: ``"action_sheet"`` (default) for an iOS-style
246
+ sheet, or ``"alert"`` for a stacked alert dialog.
247
+ destructive_labels: Labels in ``options`` that should be
248
+ styled destructively (red on iOS).
249
+
250
+ Returns:
251
+ The selected label, or ``None`` if the user dismissed or
252
+ tapped the cancel button.
253
+
254
+ Example:
255
+ ```python
256
+ choice = await pn.Alert.choose(
257
+ "Photo source",
258
+ options=["Camera", "Gallery"],
259
+ cancel_label="Cancel",
260
+ )
261
+ if choice == "Camera":
262
+ ...
263
+ ```
264
+ """
265
+ if not options:
266
+ raise ValueError("Alert.choose requires at least one option")
267
+
268
+ loop = asyncio.get_running_loop()
269
+ future: asyncio.Future[Optional[str]] = loop.create_future()
270
+
271
+ destructive = set(destructive_labels)
272
+ buttons: List[Dict[str, Any]] = [
273
+ {
274
+ "label": opt,
275
+ "style": "destructive" if opt in destructive else "default",
276
+ }
277
+ for opt in options
278
+ ]
279
+ if cancel_label is not None:
280
+ buttons.append({"label": cancel_label, "style": "cancel"})
281
+
282
+ def _on_result(index: int) -> None:
283
+ if 0 <= index < len(options):
284
+ resolve_future(future, options[index])
285
+ else:
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
296
+
297
+
298
+ __all__ = ["Alert"]