pythonnative 0.24.0__py3-none-any.whl → 0.25.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/screen.py CHANGED
@@ -1587,6 +1587,19 @@ else:
1587
1587
  return True
1588
1588
  _log_pn("_request_render: native iOS scheduler unavailable; render remains queued")
1589
1589
  return True
1590
+ # Defer via the main dispatch queue rather than an NSTimer.
1591
+ # GCD main-queue blocks are serviced in the common run-loop
1592
+ # modes, so they still run while UIKit is in tracking mode
1593
+ # (scrolls, synthesized test touches). A default-mode NSTimer
1594
+ # is starved for the whole gesture, which delayed renders
1595
+ # long enough on slow CI simulators for Maestro asserts to
1596
+ # time out, and let stale flushes land mid-touch.
1597
+ from .runtime import _ensure_libdispatch_loaded, _ios_dispatch_async
1598
+
1599
+ if _ensure_libdispatch_loaded():
1600
+ _ios_dispatch_async(_flush_ios_scheduled_renders)
1601
+ _log_pn("_request_render: deferred iOS render to next main-queue turn")
1602
+ return True
1590
1603
  try:
1591
1604
  NSTimer = ObjCClass("NSTimer")
1592
1605
  target = _ensure_ios_render_scheduler_target()
@@ -1719,22 +1732,55 @@ else:
1719
1732
  raise RuntimeError(
1720
1733
  "No UINavigationController available; ensure template embeds root in navigation controller"
1721
1734
  )
1722
- nav.pushViewController_animated_(next_vc, True)
1735
+ self._run_nav_op(lambda n: n.pushViewController_animated_(next_vc, True))
1723
1736
 
1724
1737
  def _pop(self) -> None:
1725
- nav = getattr(self.native_instance, "navigationController", None)
1726
- if nav is not None:
1727
- nav.popViewControllerAnimated_(True)
1738
+ self._run_nav_op(lambda n: n.popViewControllerAnimated_(True))
1728
1739
 
1729
1740
  def _reset_to_root(self) -> None:
1730
1741
  """Pop everything above the root view-controller."""
1731
- nav = getattr(self.native_instance, "navigationController", None)
1732
- if nav is not None:
1742
+
1743
+ def op(n: Any) -> None:
1733
1744
  try:
1734
- nav.popToRootViewControllerAnimated_(True)
1745
+ n.popToRootViewControllerAnimated_(True)
1735
1746
  except Exception:
1736
1747
  pass
1737
1748
 
1749
+ self._run_nav_op(op)
1750
+
1751
+ def _run_nav_op(self, op: Any) -> None:
1752
+ """Run ``op(nav)`` unless a stack transition is mid-flight.
1753
+
1754
+ UIKit does not queue navigation calls: a
1755
+ ``pushViewController`` / ``popViewController`` issued
1756
+ while the previous transition is still animating is
1757
+ silently ignored or, worse, misapplies and pops past
1758
+ the intended screen. A fast tap right after a push
1759
+ (easy for a user, common for a UI test on a slow
1760
+ machine) lands exactly in that window. Dropping the
1761
+ operation keeps the stack consistent: the tap simply
1762
+ does nothing, and a retry lands on a settled stack.
1763
+ (Deferring the operation instead was tried and is
1764
+ worse: the user retries, then the deferred operation
1765
+ also fires, popping one screen too many.)
1766
+ """
1767
+ nav = getattr(self.native_instance, "navigationController", None)
1768
+ if nav is None:
1769
+ return
1770
+ try:
1771
+ # rubicon resolves ``transitionCoordinator`` as a bound
1772
+ # method rather than a property on some classes; call
1773
+ # it to get the actual (possibly nil -> None) value.
1774
+ coord = nav.transitionCoordinator
1775
+ if callable(coord):
1776
+ coord = coord()
1777
+ if coord is not None:
1778
+ _log_pn("_run_nav_op: transition in flight; dropping nav op")
1779
+ return
1780
+ except Exception:
1781
+ pass
1782
+ op(nav)
1783
+
1738
1784
  def _set_screen_options(self, options: Dict[str, Any]) -> None:
1739
1785
  """Bind screen options (e.g. title) to the native nav bar.
1740
1786
 
pythonnative/storage.py CHANGED
@@ -379,7 +379,7 @@ def use_persisted_state(
379
379
  )
380
380
  ```
381
381
  """
382
- from .hooks import use_async_effect, use_callback, use_ref, use_state
382
+ from .hooks import use_callback, use_effect, use_ref, use_state
383
383
  from .runtime import run_async
384
384
 
385
385
  state, set_state = use_state(initial)
@@ -391,7 +391,7 @@ def use_persisted_state(
391
391
  set_state(stored)
392
392
  loaded.current = True
393
393
 
394
- use_async_effect(_load, [key])
394
+ use_effect(_load, [key])
395
395
 
396
396
  def setter(value_or_updater: Any) -> None:
397
397
  def _reducer(current: Any) -> Any:
@@ -0,0 +1,417 @@
1
+ """Suspense primitives: suspension signals, resources, and lazy loading.
2
+
3
+ This module implements the machinery behind PythonNative's async
4
+ rendering model:
5
+
6
+ - ``async def`` components: a component body may be a coroutine. The
7
+ reconciler drives it synchronously as far as possible (awaits on
8
+ already-resolved futures complete inline); if it blocks on pending
9
+ work, the render **suspends** and the nearest
10
+ [`Suspense`][pythonnative.Suspense] boundary shows its fallback
11
+ until the coroutine finishes.
12
+ - [`use_resource`][pythonnative.use_resource]: a hook that starts an
13
+ async fetch and caches it across renders. Reading an unresolved
14
+ [`Resource`][pythonnative.Resource] suspends the render; awaiting it
15
+ inside an ``async def`` component does the same thing without the
16
+ boundary having to re-run anything by hand.
17
+ - [`lazy`][pythonnative.lazy]: code-splitting for components. The
18
+ loader runs once, and renders suspend until it resolves.
19
+
20
+ The core building block is [`CoroDriver`][pythonnative.suspense.CoroDriver],
21
+ a miniature task that steps a coroutine **synchronously** until it
22
+ either finishes or blocks on a pending :class:`asyncio.Future`. This is
23
+ what lets an ``async def`` component whose awaits are all cached
24
+ (resolved resources, completed tasks) render in a single synchronous
25
+ pass with zero event-loop round trips, while genuinely pending awaits
26
+ suspend cleanly.
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ import asyncio
32
+ import contextvars
33
+ import inspect
34
+ from typing import Any, Callable, Generic, List, Optional, Tuple, TypeVar
35
+
36
+ T = TypeVar("T")
37
+
38
+ _UNSET = object()
39
+
40
+
41
+ class Suspend(BaseException):
42
+ """Signal that a render is blocked on pending async work.
43
+
44
+ Raised while a component renders, either by
45
+ [`Resource.read`][pythonnative.suspense.Resource.read] or by an
46
+ ``async def`` component body blocking on a pending await. The
47
+ reconciler catches it: the nearest
48
+ [`Suspense`][pythonnative.Suspense] boundary shows its fallback
49
+ (initial mounts), or the component keeps its previous content and
50
+ re-renders when the work finishes (updates).
51
+
52
+ Derives from :class:`BaseException` so
53
+ [`ErrorBoundary`][pythonnative.ErrorBoundary] (which catches
54
+ :class:`Exception`) never mistakes a suspension for a crash.
55
+
56
+ Attributes:
57
+ waitable: The pending work; exposes ``done()`` and
58
+ ``add_done_callback(cb)``.
59
+ hook_state: The suspended component's hook state, carried so
60
+ a Suspense boundary can preserve it across retries (its
61
+ cached resources survive, so the retry doesn't refetch).
62
+ key: ``(component identity, element key)`` used to re-match
63
+ ``hook_state`` on retry.
64
+ label: Component name for diagnostics.
65
+ """
66
+
67
+ def __init__(self, waitable: Any, hook_state: Any = None, label: str = "") -> None:
68
+ super().__init__(label or "render suspended")
69
+ self.waitable = waitable
70
+ self.hook_state = hook_state
71
+ self.key: Optional[Tuple[int, Any]] = None
72
+ self.label = label
73
+
74
+
75
+ class CoroDriver:
76
+ """Drive a coroutine synchronously until it blocks on pending work.
77
+
78
+ A miniature :class:`asyncio.Task`: it steps the coroutine with
79
+ ``send()``, resolving awaits on already-done futures inline. When
80
+ the coroutine blocks on a *pending* future, the driver parks and
81
+ resumes from that future's done callback (on the framework loop's
82
+ thread). Each step runs inside the :mod:`contextvars` context
83
+ captured at creation, so hooks called after an ``await`` still see
84
+ the owning component's hook state.
85
+
86
+ Attributes:
87
+ done: Whether the coroutine finished (returned, raised, or was
88
+ cancelled).
89
+ """
90
+
91
+ __slots__ = (
92
+ "_coro",
93
+ "_context",
94
+ "done",
95
+ "_cancelled",
96
+ "_result",
97
+ "_error",
98
+ "_callbacks",
99
+ "_waiting_on",
100
+ "_stepping",
101
+ )
102
+
103
+ def __init__(self, coro: Any, context: Optional[contextvars.Context] = None) -> None:
104
+ self._coro = coro
105
+ self._context = context if context is not None else contextvars.copy_context()
106
+ self.done = False
107
+ self._cancelled = False
108
+ self._result: Any = _UNSET
109
+ self._error: Optional[BaseException] = None
110
+ self._callbacks: List[Callable[["CoroDriver"], None]] = []
111
+ self._waiting_on: Optional[Any] = None
112
+ self._stepping = False
113
+
114
+ # -- inspection ------------------------------------------------------
115
+
116
+ def cancelled(self) -> bool:
117
+ """Whether the driver was cancelled before finishing normally."""
118
+ return self._cancelled
119
+
120
+ def result(self) -> Any:
121
+ """Return the coroutine's return value (raises if not done or failed)."""
122
+ if not self.done:
123
+ raise RuntimeError("CoroDriver is not done")
124
+ if self._error is not None:
125
+ raise self._error
126
+ return self._result
127
+
128
+ def exception(self) -> Optional[BaseException]:
129
+ """Return the coroutine's exception, or ``None``."""
130
+ if not self.done:
131
+ raise RuntimeError("CoroDriver is not done")
132
+ return self._error
133
+
134
+ def add_done_callback(self, callback: Callable[["CoroDriver"], None]) -> None:
135
+ """Invoke ``callback(self)`` when the coroutine finishes.
136
+
137
+ Fires immediately when already done. Callbacks run on whatever
138
+ thread finished the coroutine (the framework loop's thread for
139
+ suspended coroutines).
140
+ """
141
+ if self.done:
142
+ callback(self)
143
+ return
144
+ self._callbacks.append(callback)
145
+
146
+ # -- driving ---------------------------------------------------------
147
+
148
+ def start(self) -> None:
149
+ """Run the coroutine as far as it can go without blocking."""
150
+ self._step(("send", None))
151
+
152
+ def cancel(self) -> bool:
153
+ """Throw :class:`asyncio.CancelledError` into the coroutine.
154
+
155
+ Returns:
156
+ ``False`` when the coroutine had already finished,
157
+ ``True`` otherwise.
158
+ """
159
+ if self.done:
160
+ return False
161
+ waiting = self._waiting_on
162
+ self._waiting_on = None
163
+ if waiting is not None:
164
+ try:
165
+ waiting.remove_done_callback(self._resume)
166
+ except Exception:
167
+ pass
168
+ self._cancelled = True
169
+ self._step(("throw", asyncio.CancelledError()))
170
+ return True
171
+
172
+ def _resume(self, _fut: Any) -> None:
173
+ self._waiting_on = None
174
+ self._step(("send", None))
175
+
176
+ def _step(self, action: Tuple[str, Any]) -> None:
177
+ if self.done or self._stepping:
178
+ return
179
+ self._stepping = True
180
+ # When a step runs outside a loop iteration (a synchronous
181
+ # render), mark the framework loop as the running loop for the
182
+ # duration, exactly as asyncio.Task steps do. Without this,
183
+ # anything the coroutine calls that uses
184
+ # ``asyncio.get_running_loop()`` internally (``asyncio.sleep``,
185
+ # ``asyncio.create_task``, ...) raises "no running event loop".
186
+ from . import runtime
187
+
188
+ events = asyncio.events
189
+ installed_loop = None
190
+ if events._get_running_loop() is None:
191
+ installed_loop = runtime.get_loop()
192
+ events._set_running_loop(installed_loop)
193
+ try:
194
+ self._context.run(self._advance, action)
195
+ finally:
196
+ if installed_loop is not None:
197
+ events._set_running_loop(None)
198
+ self._stepping = False
199
+
200
+ def _advance(self, action: Tuple[str, Any]) -> None:
201
+ kind, value = action
202
+ while True:
203
+ try:
204
+ if kind == "throw":
205
+ yielded = self._coro.throw(value)
206
+ else:
207
+ yielded = self._coro.send(value)
208
+ except StopIteration as stop:
209
+ self._finish(stop.value, None)
210
+ return
211
+ except asyncio.CancelledError as exc:
212
+ self._cancelled = True
213
+ self._finish(None, exc)
214
+ return
215
+ except BaseException as exc:
216
+ self._finish(None, exc)
217
+ return
218
+
219
+ kind, value = "send", None
220
+ if yielded is None:
221
+ # ``await asyncio.sleep(0)`` and bare yields: resume on
222
+ # the next loop tick to preserve cooperative yielding.
223
+ from . import runtime
224
+
225
+ runtime.get_loop().call_soon(self._resume, None)
226
+ return
227
+ if isinstance(yielded, asyncio.Future):
228
+ # Mirror asyncio.Task's handshake with Future.__await__.
229
+ try:
230
+ yielded._asyncio_future_blocking = False
231
+ except AttributeError:
232
+ pass
233
+ if yielded.done():
234
+ continue
235
+ self._waiting_on = yielded
236
+ yielded.add_done_callback(self._resume)
237
+ return
238
+ self._finish(
239
+ None,
240
+ RuntimeError(
241
+ f"async component awaited an unsupported object: {yielded!r}. "
242
+ "Await asyncio futures, tasks, or PythonNative awaitables."
243
+ ),
244
+ )
245
+ return
246
+
247
+ def _finish(self, result: Any, error: Optional[BaseException]) -> None:
248
+ self.done = True
249
+ self._result = result
250
+ self._error = error
251
+ self._waiting_on = None
252
+ callbacks = self._callbacks
253
+ self._callbacks = []
254
+ for callback in callbacks:
255
+ try:
256
+ callback(self)
257
+ except Exception as exc:
258
+ print(f"[pn.suspense] done callback raised: {exc!r}")
259
+
260
+
261
+ class Resource(Generic[T]):
262
+ """A cached async value with Suspense integration.
263
+
264
+ Returned by [`use_resource`][pythonnative.use_resource]. A resource
265
+ starts fetching as soon as the hook runs and remembers its result
266
+ across renders (until its dependencies change), so re-renders never
267
+ refetch.
268
+
269
+ Two ways to consume it:
270
+
271
+ - ``resource.read()`` in a regular component: returns the value
272
+ when ready, re-raises the fetcher's error if it failed, and
273
+ **suspends the render** while pending.
274
+ - ``await resource`` in an ``async def`` component: same
275
+ semantics, expressed as a plain await.
276
+ """
277
+
278
+ __slots__ = ("_driver",)
279
+
280
+ def __init__(self, driver: CoroDriver) -> None:
281
+ self._driver = driver
282
+
283
+ @property
284
+ def ready(self) -> bool:
285
+ """Whether the fetch has finished (successfully or not)."""
286
+ return self._driver.done
287
+
288
+ def read(self) -> T:
289
+ """Return the fetched value, or suspend the render while pending.
290
+
291
+ Raises:
292
+ Suspend: While the fetch is still in flight (caught by the
293
+ reconciler, never by user code).
294
+ BaseException: Whatever the fetcher raised, re-raised so an
295
+ enclosing [`ErrorBoundary`][pythonnative.ErrorBoundary]
296
+ can catch it.
297
+ """
298
+ driver = self._driver
299
+ if driver.done:
300
+ return driver.result()
301
+ raise Suspend(driver, label="Resource.read")
302
+
303
+ def __await__(self) -> Any:
304
+ driver = self._driver
305
+ if driver.done:
306
+
307
+ def _done() -> Any:
308
+ return driver.result()
309
+ yield # pragma: no cover - marks this as a generator
310
+
311
+ return _done()
312
+ from . import runtime
313
+
314
+ future = runtime.create_future()
315
+
316
+ def _transfer(d: CoroDriver) -> None:
317
+ error = d.exception() if not d.cancelled() else asyncio.CancelledError()
318
+ if error is not None:
319
+ runtime.reject_future(future, error)
320
+ else:
321
+ runtime.resolve_future(future, d.result())
322
+
323
+ driver.add_done_callback(_transfer)
324
+ return future.__await__()
325
+
326
+ def cancel(self) -> None:
327
+ """Cancel the in-flight fetch (no-op when already done)."""
328
+ self._driver.cancel()
329
+
330
+
331
+ def start_resource(fetcher: Callable[[], Any]) -> Resource[Any]:
332
+ """Start ``fetcher`` immediately and wrap it in a [`Resource`][pythonnative.Resource].
333
+
334
+ The fetcher may be an ``async def`` (typical) or a plain function;
335
+ synchronous results resolve the resource immediately, so reading
336
+ it never suspends.
337
+
338
+ This is the non-hook constructor used for module-level resources
339
+ (preloading data before a screen mounts) and by
340
+ [`lazy`][pythonnative.lazy]. Inside components, prefer
341
+ [`use_resource`][pythonnative.use_resource], which caches per
342
+ component instance and re-fetches when dependencies change.
343
+ """
344
+
345
+ async def _run() -> Any:
346
+ value = fetcher()
347
+ if inspect.isawaitable(value):
348
+ return await value
349
+ return value
350
+
351
+ driver = CoroDriver(_run())
352
+ driver.start()
353
+ return Resource(driver)
354
+
355
+
356
+ def lazy(loader: Callable[[], Any]) -> Callable[..., Any]:
357
+ """Define a component that loads its implementation on first render.
358
+
359
+ ``loader`` runs once, the first time the returned component
360
+ renders; until it resolves, renders suspend (so wrap usages in a
361
+ [`Suspense`][pythonnative.Suspense] boundary to show a loading
362
+ state). The loaded value must be a component (a ``@component``
363
+ function or any element factory).
364
+
365
+ Args:
366
+ loader: A zero-arg callable returning the component, or an
367
+ ``async def`` resolving to it. Synchronous loaders (a
368
+ deferred ``import``) resolve immediately and never
369
+ suspend.
370
+
371
+ Returns:
372
+ A component. Props (and children) pass through to the loaded
373
+ component unchanged.
374
+
375
+ Example:
376
+ ```python
377
+ import pythonnative as pn
378
+
379
+ Chart = pn.lazy(lambda: __import__("app.chart", fromlist=["Chart"]).Chart)
380
+
381
+ @pn.component
382
+ def Dashboard():
383
+ return pn.Suspense(
384
+ Chart(points=[1, 2, 3]),
385
+ fallback=pn.ActivityIndicator(),
386
+ )
387
+ ```
388
+ """
389
+ from .hooks import component
390
+
391
+ state: dict = {}
392
+
393
+ @component
394
+ def Lazy(**props: Any) -> Any:
395
+ resource = state.get("resource")
396
+ if resource is None:
397
+ resource = start_resource(loader)
398
+ state["resource"] = resource
399
+ loaded = resource.read()
400
+ children = props.pop("children", None)
401
+ if children:
402
+ return loaded(*children, **props)
403
+ return loaded(**props)
404
+
405
+ Lazy.__name__ = getattr(loader, "__name__", "Lazy")
406
+ if Lazy.__name__ == "<lambda>":
407
+ Lazy.__name__ = "Lazy"
408
+ return Lazy
409
+
410
+
411
+ __all__ = [
412
+ "CoroDriver",
413
+ "Resource",
414
+ "Suspend",
415
+ "lazy",
416
+ "start_resource",
417
+ ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythonnative
3
- Version: 0.24.0
3
+ Version: 0.25.0
4
4
  Summary: Cross-platform native UI toolkit for Android and iOS
5
5
  Author: Owen Carey
6
6
  License: MIT License
@@ -105,6 +105,7 @@ PythonNative is a cross-platform toolkit for building native Android and iOS app
105
105
  - **Rich component library:** 25+ built-in components backed by real native widgets: `TextInput`, `Image` / `ImageBackground`, `ScrollView`, `FlatList` / `SectionList`, `Modal`, `Pressable` / `TouchableOpacity`, `Switch` / `Checkbox`, `Slider`, `SegmentedControl`, `Picker`, `DatePicker`, `ProgressBar` / `ActivityIndicator`, `WebView`, and more.
106
106
  - **Device APIs:** Cross-platform modules for `Camera`, `Location`, `FileSystem`, `Notifications`, `Clipboard`, `Share`, `Linking`, `Permissions`, `AppState`, `NetInfo`, `SecureStore`, `Battery`, `Haptics` / `Vibration`, and `Biometrics`, plus reactive `use_app_state` and `use_net_info` hooks.
107
107
  - **Hooks and function components:** Manage state with `use_state`, side effects with `use_effect` / `use_layout_effect`, refs and imperative handles with `use_ref` / `use_imperative_handle`, and navigation with `use_navigation`, all through one consistent pattern. Components can return a single element, a list of siblings, or `None`, and `Fragment`, `Portal`, and reactive `Provider` context work the way they do in React.
108
+ - **Async-first rendering:** One `asyncio` event loop runs the whole framework on the platform's main thread. Components can be `async def` and `await` data directly in the body; `Suspense` boundaries show declarative loading states while they wait. `use_resource` fetches during render, `use_effect` accepts coroutine callbacks (cancelled automatically on unmount), `lazy` code-splits components, and `use_transition` / `use_deferred_value` keep the UI responsive during expensive updates.
108
109
  - **Developer feedback that finds your bugs:** In dev mode (`pn preview`, hot reload, or `PN_DEV=1`), uncaught errors from renders, effects, and event handlers show a full-screen RedBox with the traceback; unknown style keys and duplicate list keys print "did you mean" warnings; and conditional hooks raise a `HookOrderError` at the source instead of silently cross-wiring state.
109
110
  - **Typed `style` prop:** Pass all visual and layout properties through a single `style` dict, fully described by the `pn.Style` `TypedDict` and the ergonomic `pn.style(...)` helper for IDE autocomplete and static checking. Compose reusable styles with `StyleSheet`.
110
111
  - **Cross-platform flexbox engine:** A pure-Python, Yoga-style layout engine computes frames once and applies them to native views, so `flex`, `padding`, `aspect_ratio`, and `position: "absolute"` produce the same geometry on Android and iOS.
@@ -1,14 +1,14 @@
1
- pythonnative/__init__.py,sha256=HcUCPSMZBt7N1-AFt4u6doqhgt_VOgImWEASJk5Tj6A,8394
1
+ pythonnative/__init__.py,sha256=4WYUg8cAV8_JboVBvwc0SR5sRvY4SW7CR4qKrflOBm0,8680
2
2
  pythonnative/_ios_log.py,sha256=Oi7V28VxcVoZyrpAirvLeEmUW18McqnU87V4d37Zzlw,2582
3
3
  pythonnative/alerts.py,sha256=mIANysFlaHwL5EqKnvNcyiJN9rGiZi9XDrD9Jpz1RFM,9340
4
- pythonnative/animated.py,sha256=2t6thUzmmapccP6ziW-x2tr8A2LCrWQoF8KqoETdSDY,36226
4
+ pythonnative/animated.py,sha256=OzDqG9ijkxF0rfU0glmgmGikbVB1s5ExoWndy74EhBw,36214
5
5
  pythonnative/appearance.py,sha256=vMkDRIz9ptaprMmlPmiYJLVwrDslfnc1bhwyE27M2ZI,3891
6
- pythonnative/components.py,sha256=61DKYxdcfaw1B4LqS5ho0aUksxjkA2zq4BB1cJiQHzA,106286
6
+ pythonnative/components.py,sha256=xmJFHw88xPzUQQGeVVhujrIiNLQiQTBZZkPxOD8iWoA,108436
7
7
  pythonnative/diagnostics.py,sha256=F6Ym163wSZI93j_ScwRWULIuxRxQ39f5lqvXRDP93po,7078
8
8
  pythonnative/element.py,sha256=jbCMPiVq4oRgbSAbKdltBJ1BU6Aom1qKoc6-FsbpJ74,2902
9
9
  pythonnative/events.py,sha256=EFYz8HxVOTA3soAe0Z9P2gZN9ShLjXhKH7I34DmnNbM,7895
10
10
  pythonnative/gestures.py,sha256=6qe_s3Cv2M5_V-_PgAQ5y9QbYV812B0xVmh8dRDHYMY,29940
11
- pythonnative/hooks.py,sha256=iOngNAtCeQPwdL0zwvgJ1TOKSarObVSG_zIhSAyKhPo,54439
11
+ pythonnative/hooks.py,sha256=tpMHxESLndaI_PXHNOcV_4B_u7ZzijQtSXH8_RU5kko,65422
12
12
  pythonnative/hot_reload.py,sha256=2HI6u-fc4JRudc35BAe8RXmPIXLWh1imoH4fMVqu_Ks,25502
13
13
  pythonnative/images.py,sha256=JFjBJdZzZoQmAOfQ7QHqi7dYJ75XTs3u2lweQHGbdPc,6466
14
14
  pythonnative/layout.py,sha256=Ck_FCLSk0g4hdOzHo0c50cbvLe8Q8caQAxywXgnFhKM,49077
@@ -18,11 +18,12 @@ pythonnative/net.py,sha256=8D7EyEC8-JkYxlrtVfTDApOGzzdcA4P4kaCdYBBZI8E,8082
18
18
  pythonnative/platform.py,sha256=WtjxR04CqVhSnjJu-3upk8bwXYaSWr2N0MOVoNov-h0,4994
19
19
  pythonnative/platform_metrics.py,sha256=1r1kMbgS9LcVTbdpP5qRD4oHEvw70te5BXEyd_16Ihc,8961
20
20
  pythonnative/preview.py,sha256=RoZv1s142GWj9xpg9v5Kh-0YuuhpEfwwb_JdnzboRLw,19355
21
- pythonnative/reconciler.py,sha256=_3-TZ7cYFvcF9Jeu3_lHa33qwfXy4OkWAVfSaYpRpNY,74426
22
- pythonnative/runtime.py,sha256=Ij3xxr_RyWESX7jP7b-RpsYjqjsJi1OpoV3WL3WTBWU,18245
23
- pythonnative/screen.py,sha256=_ylaGFviZGwllEd1akQRNJIuXIsEQf_81QkFkjblu7k,80851
24
- pythonnative/storage.py,sha256=3brJoo0X6YuxXiAv67fHwDotPRazVP3l8dxiRiEnNEo,11995
21
+ pythonnative/reconciler.py,sha256=6tzn5CsGvGypTnQ7sglEimfrdl7K3dP9W464n9naWZU,96412
22
+ pythonnative/runtime.py,sha256=m4MllJcApoor822LrceIWNLa2YPbd5lgeijQd3yqp0c,25701
23
+ pythonnative/screen.py,sha256=Z0jYl_ZfZkde_Kel2m7kGMNEQesHQudshvI5WmXLvhU,83246
24
+ pythonnative/storage.py,sha256=DNhRJWkT_iwb8lb-8YgTx2P_BHnYkOePLeOPPxcvREc,11983
25
25
  pythonnative/style.py,sha256=-XP1vbqiTEkQraFF0JknZLHIzVsou-GUhrKz5zQ7vVk,20030
26
+ pythonnative/suspense.py,sha256=IxWGXFP3Odriy0uQe9zhrNrObdKer0_WT6v9Vi8Vatg,14665
26
27
  pythonnative/utils.py,sha256=-hwe_YS19ebpjeygdl3dGeVsYzO4G74rYD53svSi0rI,7593
27
28
  pythonnative/virtual_rows.py,sha256=4YK3CeZobW-6F6GCXzGNFfmOaAiigmH4HXtqjYDRbyU,5056
28
29
  pythonnative/cli/__init__.py,sha256=NM1psvKe8jT0vzp8Ak4MMoygZz4P_msk5g-YEsY8xLk,232
@@ -38,15 +39,15 @@ pythonnative/native_modules/haptics.py,sha256=YklgquXUFyQS2a6AizflJj-dM2LHyS86Rd
38
39
  pythonnative/native_modules/linking.py,sha256=dny2geVaO975JYxoc7DD_PvGs2FyZ1QvZ7JySxbv5dY,5044
39
40
  pythonnative/native_modules/location.py,sha256=K_zXUP6XWqJt3qqUMkop_TdMn-yOQB_2TZ18eEQ9Rdc,7606
40
41
  pythonnative/native_modules/net_info.py,sha256=QGoi4xZqk9IMyKR9m-delWNRDjEgYRDmxK4lGhZpLoE,5104
41
- pythonnative/native_modules/notifications.py,sha256=WVtzdimc_aGfnxU6syCFPkjHF9YRRc97UVK3--TBwoU,7115
42
- pythonnative/native_modules/permissions.py,sha256=ABIWiCuLIiZJmfA-B7dnw56_Iz7ApGzdtLAs6X694QU,6704
42
+ pythonnative/native_modules/notifications.py,sha256=w2_ChU8Ae-QGnijIxtCOGbBFxR68GlMdDP-LMYmiTyw,8448
43
+ pythonnative/native_modules/permissions.py,sha256=m2kPUiKYZ3ws1bRY4DGgAzmRCzBnu-zQ905z8A6C6UA,7736
43
44
  pythonnative/native_modules/secure_store.py,sha256=PsnUwHzUPt-CzCAwM6BhidOBi9yofRD2uNtQIqAGofU,5953
44
45
  pythonnative/native_modules/share.py,sha256=B9ovknmnjeQvGQ8MQ14Hmq6E1x-JA469APMburJ0KUg,4727
45
46
  pythonnative/native_views/__init__.py,sha256=93v7pt0_7f8BWqOiwSvlq20F1oJLfCnmQBvLlCDA-Xk,15340
46
- pythonnative/native_views/android.py,sha256=YKokykZUkMT7N2f2Ug0uShquCdwI5joYmdVp5DzATFA,133786
47
+ pythonnative/native_views/android.py,sha256=vAS1BIZG_gX0nuI4ibEum2XNJc4bv3_uGiHj4dsc_PY,134114
47
48
  pythonnative/native_views/base.py,sha256=NFDlEIoKCINzcVkf2uobT9TgujPb1RClLS3TzwH72MY,9668
48
49
  pythonnative/native_views/desktop.py,sha256=ZZZ1aw9iRswAfkVoYMUsFz2ztppRgFpMxxwrBkNS7lw,61192
49
- pythonnative/native_views/ios.py,sha256=zLyaCGFSpcwgtpZd2daCv7TPjCBNPL-F23byLOflnYA,178536
50
+ pythonnative/native_views/ios.py,sha256=1QhEx9FoHkar3DyuItVWGwzO3oEzrW-TGTPyxtsQDtI,179200
50
51
  pythonnative/project/__init__.py,sha256=kAhWLONs53H1L7xVtczLmzYcQSOOkdRZTAJnIMhpBts,2064
51
52
  pythonnative/project/android.py,sha256=SX0uu4-tz4WdYAf9sbycQPSmoQ8fbtAADeUWk9G8ybM,19127
52
53
  pythonnative/project/builder.py,sha256=DokY-YidsDeWYgaixrpD3Kzp7_XudibltI8X_WdBIcE,19096
@@ -112,9 +113,9 @@ pythonnative/templates/ios_template/ios_template.xcodeproj/project.xcworkspace/x
112
113
  pythonnative/templates/ios_template/ios_templateTests/ios_templateTests.swift,sha256=YnwzZx7yXB13xKAXEGNgz17VuhWeqkHTRTtBJ2Vu3_E,1238
113
114
  pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITests.swift,sha256=HLRr3cmouRqQeXG13WrZ2XSAmgMgCsNXfZpbwkxeFcs,1385
114
115
  pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITestsLaunchTests.swift,sha256=f5JrG0uVtLMeJQy26Yyz7Om-JUkT220osqcbeIVkj2g,815
115
- pythonnative-0.24.0.dist-info/licenses/LICENSE,sha256=A69iG7TIAe6KkGQf6xoVHkc5JSZtOr5eRSvC5iuivnI,1067
116
- pythonnative-0.24.0.dist-info/METADATA,sha256=ad8csbo8lKOGtl9VfjTwTv4k5rx8E8UoKVHKbgPpmGE,11050
117
- pythonnative-0.24.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
118
- pythonnative-0.24.0.dist-info/entry_points.txt,sha256=iUtDawWSAJAEyWTycpZxDuYz73ol31butpzDIEAgPO0,48
119
- pythonnative-0.24.0.dist-info/top_level.txt,sha256=kT4SEATY2ywzrZ2Pgea6_zxyym44Q_PbOsUoOYjJLFE,13
120
- pythonnative-0.24.0.dist-info/RECORD,,
116
+ pythonnative-0.25.0.dist-info/licenses/LICENSE,sha256=A69iG7TIAe6KkGQf6xoVHkc5JSZtOr5eRSvC5iuivnI,1067
117
+ pythonnative-0.25.0.dist-info/METADATA,sha256=JiIpbQJggQXhQhjk_ky3Rn-W1EhWc3yP30-6rvqpk2U,11541
118
+ pythonnative-0.25.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
119
+ pythonnative-0.25.0.dist-info/entry_points.txt,sha256=iUtDawWSAJAEyWTycpZxDuYz73ol31butpzDIEAgPO0,48
120
+ pythonnative-0.25.0.dist-info/top_level.txt,sha256=kT4SEATY2ywzrZ2Pgea6_zxyym44Q_PbOsUoOYjJLFE,13
121
+ pythonnative-0.25.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (83.0.0)
2
+ Generator: setuptools (84.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5