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.
- pythonnative/__init__.py +27 -15
- pythonnative/animated.py +7 -13
- pythonnative/cli/pn.py +1 -2
- pythonnative/component.py +255 -0
- pythonnative/components/__init__.py +106 -0
- pythonnative/components/_base.py +132 -0
- pythonnative/components/controls.py +515 -0
- pythonnative/components/layout.py +459 -0
- pythonnative/components/lists.py +809 -0
- pythonnative/components/media.py +204 -0
- pythonnative/components/overlays.py +107 -0
- pythonnative/components/pressable.py +252 -0
- pythonnative/components/structural.py +167 -0
- pythonnative/components/text.py +291 -0
- pythonnative/diagnostics.py +1 -1
- pythonnative/element.py +108 -29
- pythonnative/gestures.py +1 -1
- pythonnative/hooks.py +366 -671
- pythonnative/hosts/__init__.py +85 -0
- pythonnative/hosts/android.py +269 -0
- pythonnative/hosts/base.py +665 -0
- pythonnative/hosts/desktop.py +107 -0
- pythonnative/hosts/ios.py +404 -0
- pythonnative/hot_reload.py +16 -24
- pythonnative/layout.py +372 -66
- pythonnative/native_modules/__init__.py +23 -0
- pythonnative/native_modules/net_info.py +5 -1
- pythonnative/native_views/__init__.py +1 -1
- pythonnative/native_views/android.py +50 -6
- pythonnative/native_views/base.py +64 -1
- pythonnative/native_views/desktop.py +11 -3
- pythonnative/native_views/ios.py +72 -12
- pythonnative/navigation/__init__.py +102 -0
- pythonnative/navigation/container.py +154 -0
- pythonnative/navigation/handle.py +573 -0
- pythonnative/navigation/hooks.py +94 -0
- pythonnative/navigation/host.py +58 -0
- pythonnative/navigation/linking.py +200 -0
- pythonnative/navigation/navigators.py +637 -0
- pythonnative/navigation/screen.py +149 -0
- pythonnative/navigation/state.py +248 -0
- pythonnative/net.py +4 -0
- pythonnative/platform_metrics.py +1 -1
- pythonnative/preview.py +21 -19
- pythonnative/project/android.py +7 -1
- pythonnative/project/doctor.py +1 -1
- pythonnative/project/ios.py +1 -0
- pythonnative/project/runtime_assets.py +1 -1
- pythonnative/reconciler/__init__.py +29 -0
- pythonnative/reconciler/boundaries.py +365 -0
- pythonnative/reconciler/children.py +88 -0
- pythonnative/reconciler/core.py +1153 -0
- pythonnative/reconciler/layout_pass.py +359 -0
- pythonnative/reconciler/vnode.py +266 -0
- pythonnative/scheduler.py +159 -0
- pythonnative/sdk/_components.py +2 -4
- pythonnative/style.py +95 -13
- pythonnative/suspense.py +8 -12
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +85 -1
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt +6 -7
- pythonnative/templates/android_template/app/src/main/res/navigation/nav_graph.xml +1 -1
- pythonnative/templates/android_template/app/src/main/res/values/strings.xml +3 -1
- pythonnative/templates/ios_template/ios_template/AppDelegate.swift +33 -0
- pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +1 -1
- pythonnative/templates/ios_template/ios_template/ViewController.swift +12 -10
- pythonnative/testing/__init__.py +53 -0
- pythonnative/testing/backend.py +276 -0
- pythonnative/testing/harness.py +391 -0
- {pythonnative-0.32.0.dist-info → pythonnative-0.34.0.dist-info}/METADATA +2 -21
- {pythonnative-0.32.0.dist-info → pythonnative-0.34.0.dist-info}/RECORD +74 -43
- pythonnative/components.py +0 -2856
- pythonnative/navigation.py +0 -1031
- pythonnative/reconciler.py +0 -2298
- pythonnative/screen.py +0 -2068
- {pythonnative-0.32.0.dist-info → pythonnative-0.34.0.dist-info}/WHEEL +0 -0
- {pythonnative-0.32.0.dist-info → pythonnative-0.34.0.dist-info}/entry_points.txt +0 -0
- {pythonnative-0.32.0.dist-info → pythonnative-0.34.0.dist-info}/licenses/LICENSE +0 -0
- {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.
|
|
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=
|
|
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=
|
|
4
|
+
pythonnative/animated.py,sha256=T8xVB7uqbHLzOJAfSqjYmiaA30OikdYOcE1GX8LbqaY,60065
|
|
5
5
|
pythonnative/appearance.py,sha256=vMkDRIz9ptaprMmlPmiYJLVwrDslfnc1bhwyE27M2ZI,3891
|
|
6
|
-
pythonnative/
|
|
7
|
-
pythonnative/diagnostics.py,sha256=
|
|
8
|
-
pythonnative/element.py,sha256=
|
|
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=
|
|
11
|
-
pythonnative/hooks.py,sha256=
|
|
12
|
-
pythonnative/hot_reload.py,sha256=
|
|
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=
|
|
14
|
+
pythonnative/layout.py,sha256=ZWaItiNh-i7rofchnNaHK8L_YPSz487ejP4dmygZNQQ,61647
|
|
15
15
|
pythonnative/mutations.py,sha256=whmxO6ENWjm-yYH0QteoCV1Gdw_v-fJrhicMQJ5A0rk,3827
|
|
16
|
-
pythonnative/
|
|
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=
|
|
20
|
-
pythonnative/preview.py,sha256=
|
|
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/
|
|
21
|
+
pythonnative/scheduler.py,sha256=PAj3ybfs6Gn1RF2PHLN7Oqbe8R7ps83RXP8lwy-0vYQ,5457
|
|
24
22
|
pythonnative/storage.py,sha256=DNhRJWkT_iwb8lb-8YgTx2P_BHnYkOePLeOPPxcvREc,11983
|
|
25
|
-
pythonnative/style.py,sha256=
|
|
26
|
-
pythonnative/suspense.py,sha256=
|
|
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=
|
|
31
|
-
pythonnative/
|
|
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=
|
|
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=
|
|
47
|
-
pythonnative/native_views/android.py,sha256=
|
|
48
|
-
pythonnative/native_views/base.py,sha256=
|
|
49
|
-
pythonnative/native_views/desktop.py,sha256=
|
|
50
|
-
pythonnative/native_views/ios.py,sha256=
|
|
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=
|
|
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=
|
|
78
|
+
pythonnative/project/doctor.py,sha256=aXkmKmAzwsUK_c5S25b6nxJfIQ8byOqcrzh7HbmJh2g,8619
|
|
57
79
|
pythonnative/project/icons.py,sha256=GaXpktQ9v4v2JZPM3ppueNPmwjTuFiLanHJDaZbLGuo,7945
|
|
58
|
-
pythonnative/project/ios.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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
|
|
120
|
-
pythonnative
|
|
121
|
-
pythonnative
|
|
122
|
-
pythonnative-0.
|
|
123
|
-
pythonnative-0.
|
|
124
|
-
pythonnative-0.
|
|
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,,
|