pythonnative 0.32.0__py3-none-any.whl → 0.34.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.
Files changed (78) hide show
  1. pythonnative/__init__.py +27 -15
  2. pythonnative/animated.py +7 -13
  3. pythonnative/cli/pn.py +1 -2
  4. pythonnative/component.py +255 -0
  5. pythonnative/components/__init__.py +106 -0
  6. pythonnative/components/_base.py +132 -0
  7. pythonnative/components/controls.py +515 -0
  8. pythonnative/components/layout.py +459 -0
  9. pythonnative/components/lists.py +809 -0
  10. pythonnative/components/media.py +204 -0
  11. pythonnative/components/overlays.py +107 -0
  12. pythonnative/components/pressable.py +252 -0
  13. pythonnative/components/structural.py +167 -0
  14. pythonnative/components/text.py +291 -0
  15. pythonnative/diagnostics.py +1 -1
  16. pythonnative/element.py +108 -29
  17. pythonnative/gestures.py +1 -1
  18. pythonnative/hooks.py +366 -671
  19. pythonnative/hosts/__init__.py +85 -0
  20. pythonnative/hosts/android.py +269 -0
  21. pythonnative/hosts/base.py +665 -0
  22. pythonnative/hosts/desktop.py +107 -0
  23. pythonnative/hosts/ios.py +404 -0
  24. pythonnative/hot_reload.py +16 -24
  25. pythonnative/layout.py +372 -66
  26. pythonnative/native_modules/__init__.py +23 -0
  27. pythonnative/native_modules/net_info.py +5 -1
  28. pythonnative/native_views/__init__.py +1 -1
  29. pythonnative/native_views/android.py +50 -6
  30. pythonnative/native_views/base.py +64 -1
  31. pythonnative/native_views/desktop.py +11 -3
  32. pythonnative/native_views/ios.py +72 -12
  33. pythonnative/navigation/__init__.py +102 -0
  34. pythonnative/navigation/container.py +154 -0
  35. pythonnative/navigation/handle.py +573 -0
  36. pythonnative/navigation/hooks.py +94 -0
  37. pythonnative/navigation/host.py +58 -0
  38. pythonnative/navigation/linking.py +200 -0
  39. pythonnative/navigation/navigators.py +637 -0
  40. pythonnative/navigation/screen.py +149 -0
  41. pythonnative/navigation/state.py +248 -0
  42. pythonnative/net.py +4 -0
  43. pythonnative/platform_metrics.py +1 -1
  44. pythonnative/preview.py +21 -19
  45. pythonnative/project/android.py +7 -1
  46. pythonnative/project/doctor.py +1 -1
  47. pythonnative/project/ios.py +1 -0
  48. pythonnative/project/runtime_assets.py +1 -1
  49. pythonnative/reconciler/__init__.py +29 -0
  50. pythonnative/reconciler/boundaries.py +365 -0
  51. pythonnative/reconciler/children.py +88 -0
  52. pythonnative/reconciler/core.py +1153 -0
  53. pythonnative/reconciler/layout_pass.py +359 -0
  54. pythonnative/reconciler/vnode.py +266 -0
  55. pythonnative/scheduler.py +159 -0
  56. pythonnative/sdk/_components.py +2 -4
  57. pythonnative/style.py +95 -13
  58. pythonnative/suspense.py +8 -12
  59. pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +85 -1
  60. pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt +6 -7
  61. pythonnative/templates/android_template/app/src/main/res/navigation/nav_graph.xml +1 -1
  62. pythonnative/templates/android_template/app/src/main/res/values/strings.xml +3 -1
  63. pythonnative/templates/ios_template/ios_template/AppDelegate.swift +33 -0
  64. pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +1 -1
  65. pythonnative/templates/ios_template/ios_template/ViewController.swift +12 -10
  66. pythonnative/testing/__init__.py +53 -0
  67. pythonnative/testing/backend.py +276 -0
  68. pythonnative/testing/harness.py +391 -0
  69. {pythonnative-0.32.0.dist-info → pythonnative-0.34.0.dist-info}/METADATA +2 -21
  70. {pythonnative-0.32.0.dist-info → pythonnative-0.34.0.dist-info}/RECORD +74 -43
  71. pythonnative/components.py +0 -2856
  72. pythonnative/navigation.py +0 -1031
  73. pythonnative/reconciler.py +0 -2298
  74. pythonnative/screen.py +0 -2068
  75. {pythonnative-0.32.0.dist-info → pythonnative-0.34.0.dist-info}/WHEEL +0 -0
  76. {pythonnative-0.32.0.dist-info → pythonnative-0.34.0.dist-info}/entry_points.txt +0 -0
  77. {pythonnative-0.32.0.dist-info → pythonnative-0.34.0.dist-info}/licenses/LICENSE +0 -0
  78. {pythonnative-0.32.0.dist-info → pythonnative-0.34.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,391 @@
1
+ """Render components into a fake backend and query the result.
2
+
3
+ ```python
4
+ import pythonnative as pn
5
+ from pythonnative.testing import render
6
+
7
+ def test_counter():
8
+ result = render(Counter())
9
+ assert result.get_by_text("Count: 0")
10
+ result.press(result.get_by_text("+"))
11
+ assert result.get_by_text("Count: 1")
12
+ ```
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import re
18
+ from typing import Any, Callable, Dict, Generic, List, Optional, Pattern, Tuple, TypeVar, Union
19
+
20
+ from ..element import Element, Node
21
+ from ..events import dispatch_event, get_event_registry
22
+ from .backend import FakeBackend, FakeView
23
+
24
+ __all__ = ["FakeHost", "HookResult", "RenderResult", "render", "render_hook", "settle"]
25
+
26
+ T = TypeVar("T")
27
+ Matcher = Union[str, Pattern[str], Callable[[Optional[str]], bool]]
28
+ Target = Union[FakeView, int]
29
+
30
+ DEFAULT_VIEWPORT: Tuple[float, float] = (390.0, 844.0)
31
+
32
+
33
+ def _matches(value: Optional[str], matcher: Matcher, *, exact: bool) -> bool:
34
+ if callable(matcher) and not isinstance(matcher, (str, re.Pattern)):
35
+ return bool(matcher(value))
36
+ if value is None:
37
+ return False
38
+ if isinstance(matcher, re.Pattern):
39
+ return matcher.search(value) is not None
40
+ return value == matcher if exact else matcher in value
41
+
42
+
43
+ def settle(reconciler: Any = None, timeout: float = 1.0) -> None:
44
+ """Pump the framework loop and flush pending renders until everything is idle.
45
+
46
+ Use after triggering async work (``use_resource``, ``use_query``,
47
+ transitions, coroutines started by handlers) so assertions see the
48
+ settled tree.
49
+ """
50
+ from .. import runtime
51
+
52
+ for _ in range(50):
53
+ idle = runtime.drain(timeout)
54
+ if reconciler is not None:
55
+ reconciler.flush_dirty()
56
+ if reconciler.transitions.pending:
57
+ reconciler.transitions.flush()
58
+ continue
59
+ if idle and (reconciler is None or not reconciler._has_dirty_work()):
60
+ return
61
+
62
+
63
+ class FakeHost:
64
+ """A [`HostNavigator`][pythonnative.navigation.HostNavigator] that records native screen operations.
65
+
66
+ Pass as ``render(..., host=FakeHost())`` to render a root stack the
67
+ way a device would: pushes are recorded in ``pushed`` instead of
68
+ creating screens in-tree. ``set_focused`` simulates the platform
69
+ covering / revealing the screen.
70
+ """
71
+
72
+ def __init__(self, initial_state: Optional[Dict[str, Any]] = None) -> None:
73
+ self.is_focused = True
74
+ self.initial_state = initial_state
75
+ self.pushed: List[Tuple[Dict[str, Any], Dict[str, Any]]] = []
76
+ self.popped: List[int] = []
77
+ self.replaced: List[Tuple[Dict[str, Any], Dict[str, Any]]] = []
78
+ self.resets: List[Tuple[Dict[str, Any], Dict[str, Any]]] = []
79
+ self.options: List[Dict[str, Any]] = []
80
+ self._focus_listeners: List[Callable[[bool], None]] = []
81
+
82
+ def initial_navigation_state(self) -> Optional[Dict[str, Any]]:
83
+ """Return the ``initial_state`` the host was constructed with (``None`` for a fresh root)."""
84
+ return self.initial_state
85
+
86
+ def push_screen(self, state: Dict[str, Any], options: Dict[str, Any]) -> None:
87
+ """Record the push in ``pushed`` instead of creating a native screen."""
88
+ self.pushed.append((state, dict(options)))
89
+
90
+ def pop_screens(self, count: int) -> None:
91
+ """Record the requested pop ``count`` in ``popped``."""
92
+ self.popped.append(count)
93
+
94
+ def replace_screen(self, state: Dict[str, Any], options: Dict[str, Any]) -> None:
95
+ """Record the replacement in ``replaced`` instead of swapping a native screen."""
96
+ self.replaced.append((state, dict(options)))
97
+
98
+ def reset_screens(self, state: Dict[str, Any], options: Dict[str, Any]) -> None:
99
+ """Record the stack reset in ``resets`` instead of rebuilding native screens."""
100
+ self.resets.append((state, dict(options)))
101
+
102
+ def set_screen_options(self, options: Dict[str, Any]) -> None:
103
+ """Append a copy of ``options`` to ``options`` (see the ``title`` property)."""
104
+ self.options.append(dict(options))
105
+
106
+ def add_focus_listener(self, callback: Callable[[bool], None]) -> Callable[[], None]:
107
+ """Register a focus callback fired by ``set_focused``; returns an unsubscribe callable."""
108
+ self._focus_listeners.append(callback)
109
+ return lambda: self._focus_listeners.remove(callback)
110
+
111
+ def set_focused(self, focused: bool) -> None:
112
+ """Simulate the platform covering (``False``) or revealing (``True``) the screen."""
113
+ self.is_focused = focused
114
+ for cb in list(self._focus_listeners):
115
+ cb(focused)
116
+
117
+ @property
118
+ def title(self) -> Optional[str]:
119
+ """The most recent ``title`` passed to ``set_screen_options``."""
120
+ for options in reversed(self.options):
121
+ if "title" in options:
122
+ return options["title"]
123
+ return None
124
+
125
+
126
+ class RenderResult:
127
+ """Handle to a mounted tree: queries, events, re-render, unmount.
128
+
129
+ Query methods come in three flavors, mirroring Testing Library:
130
+ ``get_by_*`` returns exactly one match or raises ``LookupError``
131
+ (with the tree dumped in the message), ``query_by_*`` returns the
132
+ match or ``None``, ``get_all_by_*`` returns every match. Matchers
133
+ are exact strings, compiled regexes, or predicates. Views inside a
134
+ ``display: "none"`` subtree (inactive tabs, covered stack screens)
135
+ are skipped unless ``hidden=True`` is passed.
136
+ """
137
+
138
+ def __init__(self, reconciler: Any, backend: FakeBackend, wrap: Callable[[Node], Element]) -> None:
139
+ self.reconciler = reconciler
140
+ self.backend = backend
141
+ self._wrap = wrap
142
+
143
+ # -- tree -----------------------------------------------------------
144
+
145
+ @property
146
+ def root(self) -> Optional[FakeView]:
147
+ """The root native view (``None`` after unmount)."""
148
+ return self.reconciler.root_view()
149
+
150
+ def views(self, *, hidden: bool = False) -> List[FakeView]:
151
+ """Every live view, root first (includes detached ``Portal`` overlays)."""
152
+ root = self.root
153
+ out = list(root.walk(include_hidden=hidden)) if root is not None else []
154
+ seen = {id(v) for v in out}
155
+ for view in self.backend.views.values():
156
+ if view.parent is None and view is not root and id(view) not in seen:
157
+ out.extend(view.walk(include_hidden=hidden))
158
+ return out
159
+
160
+ def dump(self) -> str:
161
+ """Indented text rendering of the live tree."""
162
+ root = self.root
163
+ return root.dump() if root is not None else "<unmounted>"
164
+
165
+ def text(self, *, hidden: bool = False) -> List[str]:
166
+ """Visible strings in document order."""
167
+ return [v.text for v in self.views(hidden=hidden) if v.text is not None]
168
+
169
+ # -- queries --------------------------------------------------------
170
+
171
+ def _all(self, predicate: Callable[[FakeView], bool], hidden: bool = False) -> List[FakeView]:
172
+ return [v for v in self.views(hidden=hidden) if predicate(v)]
173
+
174
+ def _one(self, kind: str, matcher: Any, found: List[FakeView]) -> FakeView:
175
+ if len(found) == 1:
176
+ return found[0]
177
+ detail = "no matches" if not found else f"{len(found)} matches: {found}"
178
+ raise LookupError(f"get_by_{kind}({matcher!r}): {detail}\n\n{self.dump()}")
179
+
180
+ def get_all_by_text(self, matcher: Matcher, *, exact: bool = True, hidden: bool = False) -> List[FakeView]:
181
+ """Return every view whose visible text matches ``matcher`` (``exact=False`` matches substrings)."""
182
+ return self._all(lambda v: _matches(v.text, matcher, exact=exact), hidden)
183
+
184
+ def get_by_text(self, matcher: Matcher, *, exact: bool = True, hidden: bool = False) -> FakeView:
185
+ """Return the single view whose visible text matches ``matcher``; raise ``LookupError`` otherwise."""
186
+ return self._one("text", matcher, self.get_all_by_text(matcher, exact=exact, hidden=hidden))
187
+
188
+ def query_by_text(self, matcher: Matcher, *, exact: bool = True, hidden: bool = False) -> Optional[FakeView]:
189
+ """Return the first view whose visible text matches ``matcher``, or ``None``."""
190
+ found = self.get_all_by_text(matcher, exact=exact, hidden=hidden)
191
+ return found[0] if found else None
192
+
193
+ def get_all_by_test_id(self, matcher: Matcher, *, hidden: bool = False) -> List[FakeView]:
194
+ """Return every view whose ``test_id`` prop matches ``matcher``."""
195
+ return self._all(lambda v: _matches(v.test_id, matcher, exact=True), hidden)
196
+
197
+ def get_by_test_id(self, matcher: Matcher, *, hidden: bool = False) -> FakeView:
198
+ """Return the single view whose ``test_id`` prop matches ``matcher``; raise ``LookupError`` otherwise."""
199
+ return self._one("test_id", matcher, self.get_all_by_test_id(matcher, hidden=hidden))
200
+
201
+ def query_by_test_id(self, matcher: Matcher, *, hidden: bool = False) -> Optional[FakeView]:
202
+ """Return the first view whose ``test_id`` prop matches ``matcher``, or ``None``."""
203
+ found = self.get_all_by_test_id(matcher, hidden=hidden)
204
+ return found[0] if found else None
205
+
206
+ def get_all_by_label(self, matcher: Matcher, *, hidden: bool = False) -> List[FakeView]:
207
+ """Return every view whose ``accessibility_label`` prop matches ``matcher``."""
208
+ return self._all(lambda v: _matches(v.label, matcher, exact=True), hidden)
209
+
210
+ def get_by_label(self, matcher: Matcher, *, hidden: bool = False) -> FakeView:
211
+ """Return the single view whose ``accessibility_label`` matches ``matcher``; raise ``LookupError`` if not."""
212
+ return self._one("label", matcher, self.get_all_by_label(matcher, hidden=hidden))
213
+
214
+ def query_by_label(self, matcher: Matcher, *, hidden: bool = False) -> Optional[FakeView]:
215
+ """Return the first view whose ``accessibility_label`` prop matches ``matcher``, or ``None``."""
216
+ found = self.get_all_by_label(matcher, hidden=hidden)
217
+ return found[0] if found else None
218
+
219
+ def get_all_by_type(self, type_name: str, *, hidden: bool = False) -> List[FakeView]:
220
+ """Return every view of native type ``type_name`` (for example ``"Text"``)."""
221
+ return self._all(lambda v: v.type_name == type_name, hidden)
222
+
223
+ def get_by_type(self, type_name: str, *, hidden: bool = False) -> FakeView:
224
+ """Return the single view of native type ``type_name``; raise ``LookupError`` otherwise."""
225
+ return self._one("type", type_name, self.get_all_by_type(type_name, hidden=hidden))
226
+
227
+ def query_by_type(self, type_name: str, *, hidden: bool = False) -> Optional[FakeView]:
228
+ """Return the first view of native type ``type_name``, or ``None``."""
229
+ found = self.get_all_by_type(type_name, hidden=hidden)
230
+ return found[0] if found else None
231
+
232
+ # -- events ---------------------------------------------------------
233
+
234
+ def fire(self, target: Target, event: str, *args: Any) -> None:
235
+ """Dispatch ``event`` (an ``on_*`` prop name) to ``target`` and settle.
236
+
237
+ Raises:
238
+ LookupError: If no handler is registered for the event.
239
+ """
240
+ tag = target.tag if isinstance(target, FakeView) else int(target)
241
+ if not get_event_registry().has(tag, event):
242
+ raise LookupError(f"fire({target!r}, {event!r}): no handler registered\n\n{self.dump()}")
243
+ dispatch_event(tag, event, *args)
244
+ self.settle()
245
+
246
+ def press(self, target: Target) -> None:
247
+ """Fire ``on_press`` on ``target``."""
248
+ self.fire(target, "on_press")
249
+
250
+ def change_text(self, target: Target, value: str) -> None:
251
+ """Fire ``on_change_text`` on a ``TextInput``."""
252
+ self.fire(target, "on_change_text", value)
253
+
254
+ def back(self) -> bool:
255
+ """Simulate the system back action; returns whether a handler consumed it."""
256
+ consumed = bool(self.reconciler.dispatch_back_press())
257
+ self.settle()
258
+ return consumed
259
+
260
+ # -- lifecycle ------------------------------------------------------
261
+
262
+ def settle(self, timeout: float = 1.0) -> None:
263
+ """Flush pending renders and async work (see [`settle`][pythonnative.testing.settle])."""
264
+ settle(self.reconciler, timeout)
265
+
266
+ def rerender(self, element: Node) -> None:
267
+ """Reconcile a new root element (new props from outside the tree)."""
268
+ self.reconciler.reconcile(self._wrap(element))
269
+ self.settle()
270
+
271
+ def unmount(self) -> None:
272
+ """Unmount the tree, running effect cleanups and destroying every view."""
273
+ self.reconciler.unmount()
274
+
275
+ def __enter__(self) -> "RenderResult":
276
+ return self
277
+
278
+ def __exit__(self, *exc: Any) -> None:
279
+ self.unmount()
280
+
281
+
282
+ def render(
283
+ element: Node,
284
+ *,
285
+ viewport: Optional[Tuple[float, float]] = DEFAULT_VIEWPORT,
286
+ backend: Optional[FakeBackend] = None,
287
+ host: Optional[FakeHost] = None,
288
+ settle_first: bool = True,
289
+ ) -> RenderResult:
290
+ """Mount ``element`` into a [`FakeBackend`][pythonnative.testing.FakeBackend].
291
+
292
+ Args:
293
+ element: The element (or component call) to render.
294
+ viewport: Size for the layout pass; ``None`` skips layout.
295
+ backend: Reuse an existing backend (defaults to a fresh one).
296
+ host: Render under a [`HostRoot`][pythonnative.navigation.host.HostRoot]
297
+ with this fake host, so root stacks push native screens
298
+ into ``host.pushed`` instead of the tree.
299
+ settle_first: Drain async work and effects before returning.
300
+ """
301
+ from ..reconciler import Reconciler
302
+
303
+ backend = backend if backend is not None else FakeBackend()
304
+ reconciler = Reconciler(backend)
305
+ if viewport is not None:
306
+ reconciler.set_viewport_size(float(viewport[0]), float(viewport[1]))
307
+
308
+ def wrap(node: Node) -> Element:
309
+ if host is not None:
310
+ from ..navigation.host import HostRoot
311
+
312
+ return HostRoot(node, host=host)
313
+ if isinstance(node, Element):
314
+ return node
315
+ from ..components import Fragment
316
+
317
+ return Fragment(*(node if isinstance(node, (list, tuple)) else [node]))
318
+
319
+ reconciler.mount(wrap(element))
320
+ result = RenderResult(reconciler, backend, wrap)
321
+ if settle_first:
322
+ result.settle()
323
+ return result
324
+
325
+
326
+ class HookResult(Generic[T]):
327
+ """Handle returned by [`render_hook`][pythonnative.testing.render_hook].
328
+
329
+ Attributes:
330
+ current: The hook's most recent return value.
331
+ """
332
+
333
+ def __init__(self, result: RenderResult, box: Dict[str, Any], rerender: Callable[..., None]) -> None:
334
+ self._result = result
335
+ self._box = box
336
+ self._rerender = rerender
337
+
338
+ @property
339
+ def current(self) -> T:
340
+ """The value the hook returned on its most recent render."""
341
+ return self._box["value"]
342
+
343
+ @property
344
+ def render_count(self) -> int:
345
+ """How many times the hook has run since ``render_hook`` was called."""
346
+ return self._box["renders"]
347
+
348
+ def act(self, fn: Callable[[], Any]) -> None:
349
+ """Run ``fn`` (typically a state setter) and settle."""
350
+ fn()
351
+ self._result.settle()
352
+
353
+ def rerender(self, *args: Any, **kwargs: Any) -> None:
354
+ """Re-run the hook with new arguments."""
355
+ self._rerender(*args, **kwargs)
356
+
357
+ def settle(self) -> None:
358
+ """Flush pending renders and async work started by the hook."""
359
+ self._result.settle()
360
+
361
+ def unmount(self) -> None:
362
+ """Unmount the harness component, running the hook's effect cleanups."""
363
+ self._result.unmount()
364
+
365
+
366
+ def render_hook(hook: Callable[..., T], *args: Any, host: Optional[FakeHost] = None, **kwargs: Any) -> HookResult[T]:
367
+ """Run ``hook(*args, **kwargs)`` inside a throwaway component.
368
+
369
+ ```python
370
+ result = render_hook(lambda: pn.use_state(0))
371
+ value, set_value = result.current
372
+ result.act(lambda: set_value(5))
373
+ assert result.current[0] == 5
374
+ ```
375
+ """
376
+ from ..component import component
377
+
378
+ box: Dict[str, Any] = {"value": None, "renders": 0}
379
+
380
+ @component
381
+ def Harness(*, args: Tuple[Any, ...], kwargs: Dict[str, Any]) -> None:
382
+ box["value"] = hook(*args, **kwargs)
383
+ box["renders"] += 1
384
+ return None
385
+
386
+ result = render(Harness(args=tuple(args), kwargs=dict(kwargs)), host=host)
387
+
388
+ def rerender(*new_args: Any, **new_kwargs: Any) -> None:
389
+ result.rerender(Harness(args=tuple(new_args) or tuple(args), kwargs={**kwargs, **new_kwargs}))
390
+
391
+ return HookResult(result, box, rerender)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythonnative
3
- Version: 0.32.0
3
+ Version: 0.34.0
4
4
  Summary: Cross-platform native UI toolkit for Android and iOS
5
5
  Author: Owen Carey
6
6
  License: MIT License
@@ -43,30 +43,11 @@ Description-Content-Type: text/markdown
43
43
  License-File: LICENSE
44
44
  Requires-Dist: requests>=2.31.0
45
45
  Requires-Dist: tomli>=2.0; python_version < "3.11"
46
+ Requires-Dist: typing_extensions>=4.4; python_version < "3.11"
46
47
  Provides-Extra: ios
47
48
  Requires-Dist: rubicon-objc<0.5.0,>=0.4.6; extra == "ios"
48
49
  Provides-Extra: build
49
50
  Requires-Dist: Pillow>=10.0; extra == "build"
50
- Provides-Extra: docs
51
- Requires-Dist: mkdocs>=1.5; extra == "docs"
52
- Requires-Dist: mkdocs-material[imaging]>=9.5; extra == "docs"
53
- Requires-Dist: mkdocstrings[python]>=0.24; extra == "docs"
54
- Requires-Dist: mkdocs-autorefs>=1.0; extra == "docs"
55
- Requires-Dist: pymdown-extensions>=10.7; extra == "docs"
56
- Provides-Extra: dev
57
- Requires-Dist: black>=24.0; extra == "dev"
58
- Requires-Dist: ruff>=0.5; extra == "dev"
59
- Requires-Dist: mypy>=1.10; extra == "dev"
60
- Requires-Dist: pytest>=8.0; extra == "dev"
61
- Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
62
- Requires-Dist: Pillow>=10.0; extra == "dev"
63
- Provides-Extra: ci
64
- Requires-Dist: black>=24.0; extra == "ci"
65
- Requires-Dist: ruff>=0.5; extra == "ci"
66
- Requires-Dist: mypy>=1.10; extra == "ci"
67
- Requires-Dist: pytest>=8.0; extra == "ci"
68
- Requires-Dist: pytest-asyncio>=0.23; extra == "ci"
69
- Requires-Dist: Pillow>=10.0; extra == "ci"
70
51
  Dynamic: license-file
71
52
 
72
53
  <p align="center">
@@ -1,34 +1,47 @@
1
- pythonnative/__init__.py,sha256=C2rt9v9RJpB5FH5akknePJztOqZw1Iuok6So8NSp4Dw,8680
1
+ pythonnative/__init__.py,sha256=MTtDrM4IBTSucarWmStVhIg0QTnwQfJj-hLjlwEHgy0,8967
2
2
  pythonnative/_ios_log.py,sha256=Oi7V28VxcVoZyrpAirvLeEmUW18McqnU87V4d37Zzlw,2582
3
3
  pythonnative/alerts.py,sha256=mIANysFlaHwL5EqKnvNcyiJN9rGiZi9XDrD9Jpz1RFM,9340
4
- pythonnative/animated.py,sha256=A4IOBf_GD6lUn0H8ArFH6URf0-m0PG__8uaxcw6-ECE,60373
4
+ pythonnative/animated.py,sha256=T8xVB7uqbHLzOJAfSqjYmiaA30OikdYOcE1GX8LbqaY,60065
5
5
  pythonnative/appearance.py,sha256=vMkDRIz9ptaprMmlPmiYJLVwrDslfnc1bhwyE27M2ZI,3891
6
- pythonnative/components.py,sha256=nhUgNmM8q-rIBHTLeH5cf8AfsnSfjkdTewkWVifkOy8,111097
7
- pythonnative/diagnostics.py,sha256=x33RG_kWTInuVcfQRTQZfA1ZGDk3lkjZemIfX0IEJS4,7992
8
- pythonnative/element.py,sha256=jbCMPiVq4oRgbSAbKdltBJ1BU6Aom1qKoc6-FsbpJ74,2902
6
+ pythonnative/component.py,sha256=a7lfcd_WxEpQgYwehxDMkvphH1h960EFW8OQWrXwWnI,9668
7
+ pythonnative/diagnostics.py,sha256=S4ULu8ROsCIt2j6hsGXmGNVnyH5n9sn12nZ3WBqegiE,7991
8
+ pythonnative/element.py,sha256=j27rIFEADgCwgtNOTMZVX1mzx5R7Ejn2sb18rhUa1s4,5569
9
9
  pythonnative/events.py,sha256=EFYz8HxVOTA3soAe0Z9P2gZN9ShLjXhKH7I34DmnNbM,7895
10
- pythonnative/gestures.py,sha256=1gTiii-ROosOcmsglfffmQL8CBNq26CF-2uqQpEjdR0,46464
11
- pythonnative/hooks.py,sha256=tpMHxESLndaI_PXHNOcV_4B_u7ZzijQtSXH8_RU5kko,65422
12
- pythonnative/hot_reload.py,sha256=2HI6u-fc4JRudc35BAe8RXmPIXLWh1imoH4fMVqu_Ks,25502
10
+ pythonnative/gestures.py,sha256=MJkSz_X59L3r4wOsS-k7PYwpwxtVx9ij85LfekcVjMQ,46463
11
+ pythonnative/hooks.py,sha256=lG5WIMYt9TUBSSXKIy87CQgsepnLwuMuGio1gvvPaJM,55268
12
+ pythonnative/hot_reload.py,sha256=MQVW8jqJiOMrNlsDlINiBa_P3ulLxm1JNtkF8E0pOqc,25198
13
13
  pythonnative/images.py,sha256=JFjBJdZzZoQmAOfQ7QHqi7dYJ75XTs3u2lweQHGbdPc,6466
14
- pythonnative/layout.py,sha256=Ck_FCLSk0g4hdOzHo0c50cbvLe8Q8caQAxywXgnFhKM,49077
14
+ pythonnative/layout.py,sha256=ZWaItiNh-i7rofchnNaHK8L_YPSz487ejP4dmygZNQQ,61647
15
15
  pythonnative/mutations.py,sha256=whmxO6ENWjm-yYH0QteoCV1Gdw_v-fJrhicMQJ5A0rk,3827
16
- pythonnative/navigation.py,sha256=TCDD6MGCEW9qBDZFCJhymUVEtoeNPhQgvio8IbgL0pU,36552
17
- pythonnative/net.py,sha256=8D7EyEC8-JkYxlrtVfTDApOGzzdcA4P4kaCdYBBZI8E,8082
16
+ pythonnative/net.py,sha256=nzYi8GPnxXCSzEtE_q_U4oEjgraB_iQe3nPP_DPlxvQ,8261
18
17
  pythonnative/platform.py,sha256=WtjxR04CqVhSnjJu-3upk8bwXYaSWr2N0MOVoNov-h0,4994
19
- pythonnative/platform_metrics.py,sha256=IoWbhp-BW2ourHVOK6JCp2n8_N3weyOEkMoFGObrprM,8954
20
- pythonnative/preview.py,sha256=RoZv1s142GWj9xpg9v5Kh-0YuuhpEfwwb_JdnzboRLw,19355
21
- pythonnative/reconciler.py,sha256=DAgGS-iEDTnJ7t_GtZ41XbLiToFaHkdBIEqLhwksBXU,97689
18
+ pythonnative/platform_metrics.py,sha256=ugKVStlY-lgEzRUoOdXSqkV0M8HzZDt6SU6X5GO463g,8953
19
+ pythonnative/preview.py,sha256=aXDXclESWlfv50yGndsk1pQoOR3b8tCBNlFUs9zJ__k,19494
22
20
  pythonnative/runtime.py,sha256=m4MllJcApoor822LrceIWNLa2YPbd5lgeijQd3yqp0c,25701
23
- pythonnative/screen.py,sha256=Z0jYl_ZfZkde_Kel2m7kGMNEQesHQudshvI5WmXLvhU,83246
21
+ pythonnative/scheduler.py,sha256=PAj3ybfs6Gn1RF2PHLN7Oqbe8R7ps83RXP8lwy-0vYQ,5457
24
22
  pythonnative/storage.py,sha256=DNhRJWkT_iwb8lb-8YgTx2P_BHnYkOePLeOPPxcvREc,11983
25
- pythonnative/style.py,sha256=yL0v7V_Y7r_M22rlq3SwjqSvXPrSAgIewM7Bq9ufzY0,20791
26
- pythonnative/suspense.py,sha256=IxWGXFP3Odriy0uQe9zhrNrObdKer0_WT6v9Vi8Vatg,14665
23
+ pythonnative/style.py,sha256=32p1oRaHMiuJi3lnML82I8_77tQ5s6N6-y8bswJ3idM,24500
24
+ pythonnative/suspense.py,sha256=ojyz2yW1jTt2BJw0uLFYpnjTrCfUGsGC52lsAlKYgzg,14570
27
25
  pythonnative/utils.py,sha256=-hwe_YS19ebpjeygdl3dGeVsYzO4G74rYD53svSi0rI,7593
28
26
  pythonnative/virtual_rows.py,sha256=4YK3CeZobW-6F6GCXzGNFfmOaAiigmH4HXtqjYDRbyU,5056
29
27
  pythonnative/cli/__init__.py,sha256=NM1psvKe8jT0vzp8Ak4MMoygZz4P_msk5g-YEsY8xLk,232
30
- pythonnative/cli/pn.py,sha256=YGv8Qt5Wf_-5kaLNOhChxXPLN2WrIH2l0hp5POTP6wI,40401
31
- pythonnative/native_modules/__init__.py,sha256=pgigpHuzT-rqwcjlwJvu93_4L8Nozal1HJid0S_JlmM,2636
28
+ pythonnative/cli/pn.py,sha256=PPPpucsS_aqF2BQf1Ehw4nnQK-bpwR3pRtbthzNcKa4,40386
29
+ pythonnative/components/__init__.py,sha256=6xbkZYU3M4Jvv4wOLSiL8pj5HGkEYbOHXcyC0gj4oZU,2640
30
+ pythonnative/components/_base.py,sha256=avUjUcEpODVJSsOI845w7A0U85WAhp2fTbYXYjJsM8U,4744
31
+ pythonnative/components/controls.py,sha256=qd3Ir1_W-jliI7_BTM0AJB4RS-cxnj0QYQA5QKyHOZQ,18462
32
+ pythonnative/components/layout.py,sha256=-rZdMs_q53GhX49VL1DVPibWvLVGq6Yro-P2J63RLVc,17931
33
+ pythonnative/components/lists.py,sha256=uofhliTd14k8S1Kt8_jsENwiRinfzBKUPjah0CR2D2k,33028
34
+ pythonnative/components/media.py,sha256=g6bf8bidJlrASJ3bxEaFpuW9WegstkzvFgysLzALjC8,8167
35
+ pythonnative/components/overlays.py,sha256=n8wkOT2TST9aLD7Yrx8dW72qv5vXdx7DuuXHbM4C9wM,4124
36
+ pythonnative/components/pressable.py,sha256=vLPDOblFkWIErera0Cj8p3FQUAyB0xhK3Ut50-WWRUA,10214
37
+ pythonnative/components/structural.py,sha256=GYW-JoK5p9yJXXxIejQSF1K1Y4U3xoG3SoSpTNpFGUg,6145
38
+ pythonnative/components/text.py,sha256=kGHu7kc00T5LAQzgAKfOxxhY_ArO5fr4Hs5SEtQl9NA,11715
39
+ pythonnative/hosts/__init__.py,sha256=i8z2RQEOpWTr4pXb0M567LJ6MrN_DaFbUYAnDoK2OUk,2794
40
+ pythonnative/hosts/android.py,sha256=LnAo8CMdyKzjuzfiZjMd1jEz8hVJugeZfIOVCOdPd6g,10573
41
+ pythonnative/hosts/base.py,sha256=CDXBrRsE5ZieDvN-GL9P9z4M0w7YAGVlEiTJ7VmBu7k,26657
42
+ pythonnative/hosts/desktop.py,sha256=CnhveLgGcgQaAsa8J7avJ1ufAHdm8SikLdfQbbdgOqg,3922
43
+ pythonnative/hosts/ios.py,sha256=zSAEqUWseCjlPAmsTwiuJoNp792Jbxl5bm72JH4YHgc,15162
44
+ pythonnative/native_modules/__init__.py,sha256=RBnG8T83q89CrvbsHJI0i-oxAhVCCqesCoESiTEgNwY,3516
32
45
  pythonnative/native_modules/app_state.py,sha256=CKAwDQIwoGElSLE3l7Bekk0qRbFVFOTSuSCBybumZEk,2742
33
46
  pythonnative/native_modules/battery.py,sha256=-lJu8LURw-yEObXUyhNq_jHFDa1_33lgzbJzXaGqUL4,4429
34
47
  pythonnative/native_modules/biometrics.py,sha256=Mc-FnvOPhkk5jCXPK63qpRqgiSJ5iAzIA74u4Hie_9c,5000
@@ -38,28 +51,43 @@ pythonnative/native_modules/file_system.py,sha256=ucO-Z36S1mDyZ-B0qG-HJrf17m3t1W
38
51
  pythonnative/native_modules/haptics.py,sha256=GXbwRe-lofQOjL6TCjoQAsb6L4xR1BzhxN7-oeEL5KI,5967
39
52
  pythonnative/native_modules/linking.py,sha256=kghghDXL1-rGp5q0rQgFVsMiJLo5MH32Zh6p6Txpb60,6908
40
53
  pythonnative/native_modules/location.py,sha256=0PrZ3wfjgsC2IKvGiS1GZusOPYsTojl3t2rEDrDraoU,8134
41
- pythonnative/native_modules/net_info.py,sha256=Qr2mcULW2ZHV0gfh5A42pF-t8W6w4YR-CIgEQPOSKcM,7650
54
+ pythonnative/native_modules/net_info.py,sha256=wYt7sXkX-4meZop9sEBUrI2B6x0DV5qrN2lYU6t58Fw,7739
42
55
  pythonnative/native_modules/notifications.py,sha256=vbcYM89CjbftNdFtP2IlPAGBTx3LVK0jXgWLGrdn5bY,13250
43
56
  pythonnative/native_modules/permissions.py,sha256=yP627uvGzqg21LiV0WNvn-HOfGDDGtCj6xvf8W7O4l4,7874
44
57
  pythonnative/native_modules/secure_store.py,sha256=PsnUwHzUPt-CzCAwM6BhidOBi9yofRD2uNtQIqAGofU,5953
45
58
  pythonnative/native_modules/share.py,sha256=I20y7WBMAak8yPGxnU_S4kdNLk51HHYLjlEPd5D3TVc,4803
46
- pythonnative/native_views/__init__.py,sha256=93v7pt0_7f8BWqOiwSvlq20F1oJLfCnmQBvLlCDA-Xk,15340
47
- pythonnative/native_views/android.py,sha256=V_B6wmRo8Pl-u_K4ktiomzo0-38H0bG8YdxDOXbzgNo,155152
48
- pythonnative/native_views/base.py,sha256=NFDlEIoKCINzcVkf2uobT9TgujPb1RClLS3TzwH72MY,9668
49
- pythonnative/native_views/desktop.py,sha256=UOTGmI1AdAd-PzEuQ6r1gMQk9aQoLHIScJCBoahoY7Y,94046
50
- pythonnative/native_views/ios.py,sha256=cZNc5F9q01WLWhjNJu-24EZDK5SvtSuuSc3mxJL4OOI,211334
59
+ pythonnative/native_views/__init__.py,sha256=zJ7jinnYLm6xYFL948-pXW4Cy9D4S3bwjBW_SryJpIU,15336
60
+ pythonnative/native_views/android.py,sha256=If9mNo7zxfa8stARzi8caePV7GTYTZe7kB1H05TyjrE,157285
61
+ pythonnative/native_views/base.py,sha256=5z9P_2iF1bui9_1CWOQ1cEd86D4Peo4ELN4os3AYQME,12062
62
+ pythonnative/native_views/desktop.py,sha256=0p4z_QkSYMjHyV2Q_S96NHmhOHKEbS1sSs0Kl5yLnMM,94418
63
+ pythonnative/native_views/ios.py,sha256=aeOGsjrL9uMv5m7Hd8D69hrr7RyzsHE9TihLsvvHvfc,213882
64
+ pythonnative/navigation/__init__.py,sha256=IWuHOmL4SO_PIPW9nNfPi5pF1n6t4ChFK1R4_Algguw,2845
65
+ pythonnative/navigation/container.py,sha256=eob849GyalJnq1aQxXC0Jz79MdK9Cs1i6dh7W7D3uqo,5298
66
+ pythonnative/navigation/handle.py,sha256=SYjfqMIQWCcHmd0sDQE9OQ0iaHPPhA1iOKQjdwCeWIQ,23550
67
+ pythonnative/navigation/hooks.py,sha256=bfFjdpBvlRKEjSXKPWHFQZAvOaoi9K9DmySbfntWFJw,2954
68
+ pythonnative/navigation/host.py,sha256=ZDT16pMssTwLQ48Xash887GQoNlOxijGDJK4wmcU7gk,2365
69
+ pythonnative/navigation/linking.py,sha256=A4as0oZN9kv32w_Ormr3wFGAJ5DVRzJ6e-ch3iUoV88,7972
70
+ pythonnative/navigation/navigators.py,sha256=hzjjWwUZOqAhznsiMQo2ekr3-OMK3yZ14tSDzW6umXI,22056
71
+ pythonnative/navigation/screen.py,sha256=8U7sPWrhDKjrKzDht22FiZG4eI8n_fiREWDzUegN5so,6386
72
+ pythonnative/navigation/state.py,sha256=TN2YokWXtp02Ksv9MD-qC6r-5yDV5EicdIlmlkyNGjg,10084
51
73
  pythonnative/project/__init__.py,sha256=kAhWLONs53H1L7xVtczLmzYcQSOOkdRZTAJnIMhpBts,2064
52
- pythonnative/project/android.py,sha256=ynRQwVvv1trFB2eFaUv38-48q4m0_Z4cTbAFEOiKnhE,19797
74
+ pythonnative/project/android.py,sha256=iYEVQW7G1OxIUaSTXNBYUXB3-S14PvBY3phP_lDG30M,19997
53
75
  pythonnative/project/builder.py,sha256=TTN_ymdSyF4wVtSRRwOnSmc91LMn_g_M7_jTI0NtbVU,23946
54
76
  pythonnative/project/config.py,sha256=LkA5iHQNS_ofTMwodE5-69121O-XThaCDDwS9pA-DqQ,26479
55
77
  pythonnative/project/devices.py,sha256=zH0v95vOqWxXGdeTDljrZTA0RWBuU79v3zTQSaXkDh8,10106
56
- pythonnative/project/doctor.py,sha256=cNCUWyzu4WBJ9peARIgQ2AyoAGp81NKKM5VkIx7dpFE,8623
78
+ pythonnative/project/doctor.py,sha256=aXkmKmAzwsUK_c5S25b6nxJfIQ8byOqcrzh7HbmJh2g,8619
57
79
  pythonnative/project/icons.py,sha256=GaXpktQ9v4v2JZPM3ppueNPmwjTuFiLanHJDaZbLGuo,7945
58
- pythonnative/project/ios.py,sha256=Xrx2Jh1v4-3ftLkvKAHQuXCA5nuBdKV5nmTTXkDFaNA,13077
80
+ pythonnative/project/ios.py,sha256=7Hb-PNm8ZJwXoCQoVCb57n2Qfsjvsdgx0KnckMS9ZBA,13126
59
81
  pythonnative/project/permissions.py,sha256=WyApoqZ0mCTeAtffYbpCGV-bgLvOG7SlPcV7zSO_IV8,14227
60
- pythonnative/project/runtime_assets.py,sha256=ZDw_uYlvXDwjlGpyhmKj0KsA0sGm8pnxTWZ522nfDe4,6872
82
+ pythonnative/project/runtime_assets.py,sha256=cYqfj4LUBykUswHZPX3Kp_wRU6nQ8VBCe2oc_QT4Io0,6868
83
+ pythonnative/reconciler/__init__.py,sha256=mwlPUA5naD50N-JpTyXGy030xA-0R5x9EaunNgDKlfg,808
84
+ pythonnative/reconciler/boundaries.py,sha256=FIt0fxL-mFs6SGKwwCWMcvW8GjNyzCsK6T1egEwW2K0,15038
85
+ pythonnative/reconciler/children.py,sha256=k_eP2FmSAjtvEEGTz6eGXpMpLeQ6PgyHnZpnKzTAyhE,3340
86
+ pythonnative/reconciler/core.py,sha256=Ahooh1jNqdPTY61MLV6i7QAEvSgEXQjR_C--y_Aatz4,46962
87
+ pythonnative/reconciler/layout_pass.py,sha256=OBsqzMp6hphxPMGTRNlh8N31OHn51K2bfWRe4DM8KNw,14809
88
+ pythonnative/reconciler/vnode.py,sha256=4AMqZBFXxJeV1v2qn_FEbaLw9hSIXGJEvAx7ifImH0k,10052
61
89
  pythonnative/sdk/__init__.py,sha256=btIRfW2yy2d2LzjdpFnlc6ym-G3iJj9sVUbb2IlFMOI,3384
62
- pythonnative/sdk/_components.py,sha256=b8cvaqqNE7QGz0FTzZbpIzlewZCKJfEP_4AI9gmF8pg,14651
90
+ pythonnative/sdk/_components.py,sha256=Vk1ma0lFsQ1N5bBwR9DNm6hO7-yE8-neVQ4IU-WLnFk,14614
63
91
  pythonnative/templates/android_template/build.gradle,sha256=4gE6CRS6RuBu9kp-_e_uYYU9mBgHVZrqQg9caSxgyuc,352
64
92
  pythonnative/templates/android_template/gradle.properties,sha256=REPaKLRfQiiVfIV8wYmgwzPWvF1f3bhh_kAMV9p4HME,1358
65
93
  pythonnative/templates/android_template/gradlew,sha256=YxNShxF6Hm0SyEWA8fScYdG6AiGOzShmBgXpf5dufWU,5766
@@ -69,13 +97,13 @@ pythonnative/templates/android_template/app/build.gradle,sha256=DRXz9t1iDzjsU0ZM
69
97
  pythonnative/templates/android_template/app/proguard-rules.pro,sha256=Vv2WDPIl9spA-YKxOl27DYvD394T_3ZCKCXGBw0KGJA,750
70
98
  pythonnative/templates/android_template/app/src/androidTest/java/com/pythonnative/android_template/ExampleInstrumentedTest.kt,sha256=Am8Yla3i1eR_ac5FVgPU_RsuMrCbyT79h1BcajGE-zI,693
71
99
  pythonnative/templates/android_template/app/src/main/AndroidManifest.xml,sha256=BYJG1s2BZKY-WenCpgWYpHFUiioB9LGHAdoNV0ry9cE,1245
72
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt,sha256=GW7jFHzUB2YB_9To_eOfqr3f9R5jCKMantyKAjVS0Xw,6144
100
+ pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt,sha256=0bJ6xTh_AvAHCIP8JH0r0TR0OB4rS6c3Hsmhdmy1Pkc,9713
73
101
  pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/Navigator.kt,sha256=mDfzKTTXRP-zbSnfIPxAYyjtr9_IyyQwXM-dovcfVbg,1723
74
102
  pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNAccessibilityDelegate.kt,sha256=R2PC6BA3VCaKET0XV33tXTFf4Ht9udvvLu9TT_RfWp4,1819
75
103
  pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNBorderDrawable.kt,sha256=ejdFhpzn0-2TZa0nvG41r2m0xoejU2fXXuJSmO6tnBU,2205
76
104
  pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNFrameLayout.kt,sha256=DLozfPlPPCnv4bUgFR39Lw3Nf7CQbaEWz3TaVobJ6qo,1888
77
105
  pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNVirtualListView.java,sha256=1ve74nvOFqsduyaLOGPb8Sd9Jrwz-sELRB9y1NMN1AI,6072
78
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt,sha256=pMxpHiguY8MToTZaMKwEF88xaGh-zLqcfYy1g5DV6eI,6051
106
+ pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt,sha256=UI8J484DDlbf0egS2SbZlnPdYSTkyA8_Q0aaIeOuxug,6047
79
107
  pythonnative/templates/android_template/app/src/main/res/drawable/ic_launcher_background.xml,sha256=7UI8c6b0Ck0pCfCQHmBSezqAfNWeG1WTvKrhgIscYyE,5606
80
108
  pythonnative/templates/android_template/app/src/main/res/drawable-v24/ic_launcher_foreground.xml,sha256=AdGmpsEjTrf-Jw0JfrKD1yucla5RGIhvG2VzqtKA8fc,1702
81
109
  pythonnative/templates/android_template/app/src/main/res/layout/activity_main.xml,sha256=HIgdCNktb3YoJC8QOTIv-0qZRtMRoPdARK59nyYFO6g,461
@@ -91,9 +119,9 @@ pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launch
91
119
  pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp,sha256=MAn60Hn1dy8w7Ndn-YkkNn--D4HDAEjWctT9otXKfRI,5914
92
120
  pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp,sha256=-Y_vW8O_5bZWksQK0cuuK-xPr58bJJw5diZ0DbcbYtg,3844
93
121
  pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp,sha256=X68DN0XIyILEO7GzctTkpYkMrsY9mJKi51bJVBCLhdw,7778
94
- pythonnative/templates/android_template/app/src/main/res/navigation/nav_graph.xml,sha256=siFR0GL1QVQMPQLbA17VjwsBZHF49C7nCzj0lb7zG_o,755
122
+ pythonnative/templates/android_template/app/src/main/res/navigation/nav_graph.xml,sha256=SATxVByvfzh26RAdJVu6zfqwcgRL-wqbJDoo__CGcOM,743
95
123
  pythonnative/templates/android_template/app/src/main/res/values/colors.xml,sha256=77cNdJlUmlfOoysA55DvBjLLDJXNru_RaQPIzRLIieQ,147
96
- pythonnative/templates/android_template/app/src/main/res/values/strings.xml,sha256=y212ihQZPuuegFVU54kyOMwU50J759yi6h1CNAo-RDc,78
124
+ pythonnative/templates/android_template/app/src/main/res/values/strings.xml,sha256=JOtkOfmIW66eAGU48EP28xDwOXtCPOAOhNdzQPlDVNs,254
97
125
  pythonnative/templates/android_template/app/src/main/res/values/themes.xml,sha256=8Amp6l23WClwtnLrfXYI2UFvC1eS6JzMRivN57rQBzw,420
98
126
  pythonnative/templates/android_template/app/src/main/res/values-night/themes.xml,sha256=ajy-heM7CqnQDEP9VY7D8QarUNMSOY6uvhfuium5oIk,336
99
127
  pythonnative/templates/android_template/app/src/main/res/xml/backup_rules.xml,sha256=n35MhMj9LIFHjRWWI4P0fMb_VdYqvNQMj_pvwIqMNbc,478
@@ -101,12 +129,12 @@ pythonnative/templates/android_template/app/src/main/res/xml/data_extraction_rul
101
129
  pythonnative/templates/android_template/app/src/test/java/com/pythonnative/android_template/ExampleUnitTest.kt,sha256=Z6lsNy__lyw7Nzj4GndWK1VLZkgAyX3czP8YX9to7r4,357
102
130
  pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.jar,sha256=6ZbUUtJkXnDAHBEUPKLTdCc0oo2iv2HyXIK9wojJ5jc,59203
103
131
  pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties,sha256=nG5CWg_8OmJISSdiSxhYMPnob2ji-O7t0vYmSsW48vc,232
104
- pythonnative/templates/ios_template/ios_template/AppDelegate.swift,sha256=zF6b8VTLPlF4RhsceQHtyJyIDDMSK4A9eDf-fsPcPyU,1937
132
+ pythonnative/templates/ios_template/ios_template/AppDelegate.swift,sha256=hSZ88j8VYsKhR5BVG5FRjBT9wFbEKWQbBq723u6SH8g,3141
105
133
  pythonnative/templates/ios_template/ios_template/BridgingHeader.h,sha256=JwlbCXMf3xsSrZYhTbW-F6JJw7xrXkA1W7ctc6Qc2Nw,332
106
134
  pythonnative/templates/ios_template/ios_template/Info.plist,sha256=9EPyWeNNhKOwEMqaFFPTTmJdWA8iCe9zZvWqYC1yn6Q,902
107
- pythonnative/templates/ios_template/ios_template/PythonRuntime.swift,sha256=HGEiC0i5aM75dpBZfO7EyDq3OmJUNGP0astWWmZ7dUg,15706
135
+ pythonnative/templates/ios_template/ios_template/PythonRuntime.swift,sha256=exFadw7JevUXb2cVdO5Cb0v_PTBahkB82xSW10YdERs,15705
108
136
  pythonnative/templates/ios_template/ios_template/SceneDelegate.swift,sha256=74bnoTZ4mSRiGGM4HA8d_zM0wvpCueHjcf4Q7Uhm-ng,2083
109
- pythonnative/templates/ios_template/ios_template/ViewController.swift,sha256=pxVLREzkimfMz5yOZkHlVMuOdu0pE-Ltxp4dYFvcPS4,5578
137
+ pythonnative/templates/ios_template/ios_template/ViewController.swift,sha256=RcWoPDtquAzdvxVPCNo-PaB13FuFeJb3l7SerLqm9Wk,5759
110
138
  pythonnative/templates/ios_template/ios_template/Assets.xcassets/Contents.json,sha256=D9Sbo8NYXHCWeOAEaoIcPGBoXscGNyDTDTo0SL46IIs,63
111
139
  pythonnative/templates/ios_template/ios_template/Assets.xcassets/AccentColor.colorset/Contents.json,sha256=mvZQhvowtJJS-uGhIlcxaR3nlPd3WvdNcb7-tQfRK3w,123
112
140
  pythonnative/templates/ios_template/ios_template/Assets.xcassets/AppIcon.appiconset/Contents.json,sha256=VUwGr7K_geOvQjFh5VKB6iVXV1mi0tjGMinUmB2JvQs,177
@@ -116,9 +144,12 @@ pythonnative/templates/ios_template/ios_template.xcodeproj/project.xcworkspace/x
116
144
  pythonnative/templates/ios_template/ios_templateTests/ios_templateTests.swift,sha256=YnwzZx7yXB13xKAXEGNgz17VuhWeqkHTRTtBJ2Vu3_E,1238
117
145
  pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITests.swift,sha256=HLRr3cmouRqQeXG13WrZ2XSAmgMgCsNXfZpbwkxeFcs,1385
118
146
  pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITestsLaunchTests.swift,sha256=f5JrG0uVtLMeJQy26Yyz7Om-JUkT220osqcbeIVkj2g,815
119
- pythonnative-0.32.0.dist-info/licenses/LICENSE,sha256=A69iG7TIAe6KkGQf6xoVHkc5JSZtOr5eRSvC5iuivnI,1067
120
- pythonnative-0.32.0.dist-info/METADATA,sha256=dKunqqXKIU7nPPBf3jTndRX05TW_fdxRVYDfT3OW33A,11895
121
- pythonnative-0.32.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
122
- pythonnative-0.32.0.dist-info/entry_points.txt,sha256=iUtDawWSAJAEyWTycpZxDuYz73ol31butpzDIEAgPO0,48
123
- pythonnative-0.32.0.dist-info/top_level.txt,sha256=kT4SEATY2ywzrZ2Pgea6_zxyym44Q_PbOsUoOYjJLFE,13
124
- pythonnative-0.32.0.dist-info/RECORD,,
147
+ pythonnative/testing/__init__.py,sha256=a76InZThMU9UI1-AJURZ6rB9guZLJFl62Ep5CEOjpno,1683
148
+ pythonnative/testing/backend.py,sha256=1uePC5rWj4X2bz4w2z3esD675LEJWok8EFbixiiwk60,11299
149
+ pythonnative/testing/harness.py,sha256=nAUGPqgVhKBuSmdGXLKO7qxTRyHVLtzGVKXCZAkH35M,16418
150
+ pythonnative-0.34.0.dist-info/licenses/LICENSE,sha256=A69iG7TIAe6KkGQf6xoVHkc5JSZtOr5eRSvC5iuivnI,1067
151
+ pythonnative-0.34.0.dist-info/METADATA,sha256=kEsaYiLGxEbMgZ8hxd6snD2ga20A623u4UaO44S0dh0,11099
152
+ pythonnative-0.34.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
153
+ pythonnative-0.34.0.dist-info/entry_points.txt,sha256=iUtDawWSAJAEyWTycpZxDuYz73ol31butpzDIEAgPO0,48
154
+ pythonnative-0.34.0.dist-info/top_level.txt,sha256=kT4SEATY2ywzrZ2Pgea6_zxyym44Q_PbOsUoOYjJLFE,13
155
+ pythonnative-0.34.0.dist-info/RECORD,,