pythonnative 0.22.0__py3-none-any.whl → 0.22.1__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 (45) hide show
  1. pythonnative/__init__.py +1 -1
  2. pythonnative/animated.py +6 -6
  3. pythonnative/cli/pn.py +1 -1
  4. pythonnative/components.py +12 -12
  5. pythonnative/events.py +5 -5
  6. pythonnative/gestures.py +3 -3
  7. pythonnative/hooks.py +3 -3
  8. pythonnative/hot_reload.py +4 -4
  9. pythonnative/layout.py +3 -3
  10. pythonnative/mutations.py +1 -1
  11. pythonnative/native_modules/camera.py +1 -1
  12. pythonnative/native_modules/haptics.py +2 -2
  13. pythonnative/native_modules/location.py +1 -1
  14. pythonnative/native_modules/permissions.py +2 -2
  15. pythonnative/native_modules/secure_store.py +1 -1
  16. pythonnative/native_views/android.py +20 -20
  17. pythonnative/native_views/base.py +3 -3
  18. pythonnative/native_views/desktop.py +7 -7
  19. pythonnative/native_views/ios.py +23 -23
  20. pythonnative/navigation.py +4 -4
  21. pythonnative/net.py +3 -3
  22. pythonnative/platform.py +1 -1
  23. pythonnative/platform_metrics.py +5 -5
  24. pythonnative/preview.py +3 -3
  25. pythonnative/project/builder.py +1 -1
  26. pythonnative/project/config.py +3 -3
  27. pythonnative/project/doctor.py +2 -2
  28. pythonnative/project/icons.py +1 -1
  29. pythonnative/project/ios.py +1 -1
  30. pythonnative/project/permissions.py +2 -2
  31. pythonnative/project/runtime_assets.py +3 -3
  32. pythonnative/reconciler.py +9 -9
  33. pythonnative/runtime.py +8 -8
  34. pythonnative/screen.py +5 -5
  35. pythonnative/sdk/_components.py +1 -1
  36. pythonnative/storage.py +3 -3
  37. pythonnative/style.py +1 -1
  38. pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +2 -2
  39. pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITests.swift +1 -1
  40. {pythonnative-0.22.0.dist-info → pythonnative-0.22.1.dist-info}/METADATA +13 -13
  41. {pythonnative-0.22.0.dist-info → pythonnative-0.22.1.dist-info}/RECORD +45 -45
  42. {pythonnative-0.22.0.dist-info → pythonnative-0.22.1.dist-info}/WHEEL +0 -0
  43. {pythonnative-0.22.0.dist-info → pythonnative-0.22.1.dist-info}/entry_points.txt +0 -0
  44. {pythonnative-0.22.0.dist-info → pythonnative-0.22.1.dist-info}/licenses/LICENSE +0 -0
  45. {pythonnative-0.22.0.dist-info → pythonnative-0.22.1.dist-info}/top_level.txt +0 -0
@@ -8,7 +8,7 @@ frame application. Handlers are registered with the
8
8
 
9
9
  **Batched protocol**: the registry applies the reconciler's mutation
10
10
  ops; handlers receive callable-free props. User callbacks never reach
11
- this module every interaction (taps, text edits, scrolls, gestures)
11
+ this module; every interaction (taps, text edits, scrolls, gestures)
12
12
  is forwarded through
13
13
  [`dispatch_event`][pythonnative.events.dispatch_event] keyed by the
14
14
  view's reconciler-assigned tag.
@@ -171,7 +171,7 @@ def _has_event(view: Any, name: str) -> bool:
171
171
  # ======================================================================
172
172
  #
173
173
  # rubicon-objc's ``@objc_method`` FFI bridge is unreliable on iOS arm64
174
- # for some delegate callback shapes in particular when UIKit passes
174
+ # for some delegate callback shapes, in particular when UIKit passes
175
175
  # tagged pointers (e.g. NSIndexPath) or invokes selectors that return
176
176
  # objects, the FFI closure ends up in CPython's ``_ctypes.O_get`` and
177
177
  # crashes on bogus PyObject* dereferences.
@@ -688,7 +688,7 @@ def _register_control_action(control: Any, events_mask: int, handler: Any) -> No
688
688
 
689
689
  The UIControl counterpart of ``_register_action``: control events
690
690
  (TouchUpInside, ValueChanged, ...) must not be delivered through
691
- rubicon's ``@objc_method`` bridge either the trampoline's ``sender``
691
+ rubicon's ``@objc_method`` bridge either; the trampoline's ``sender``
692
692
  marshaling is what crashed UISwitch toggles on iOS 18.x (the action
693
693
  fired, but touching the marshaled ``sender`` segfaulted). The raw IMP
694
694
  receives only the sender *pointer*; ``handler`` closures read any
@@ -763,7 +763,7 @@ def _make_gesture_handler(
763
763
  pass
764
764
  elif kind == "swipe":
765
765
  # Discrete: UIKit only calls us on recognition, and only the
766
- # recognizer whose direction matched fires so the bound
766
+ # recognizer whose direction matched fires, so the bound
767
767
  # per-recognizer direction is the actual swipe direction.
768
768
  payload["state"] = "ended"
769
769
  payload["direction"] = direction
@@ -1154,7 +1154,7 @@ class IOSViewHandler(ViewHandler):
1154
1154
 
1155
1155
  ``decay`` (and any unknown kind) returns ``False`` so the Python
1156
1156
  ticker integrates the exact physics. Off-main-thread starts also
1157
- fall back UIKit animation APIs are main-thread-only.
1157
+ fall back; UIKit animation APIs are main-thread-only.
1158
1158
  """
1159
1159
  if native_view is None or not isinstance(spec, dict):
1160
1160
  return False
@@ -1190,7 +1190,7 @@ class IOSViewHandler(ViewHandler):
1190
1190
 
1191
1191
 
1192
1192
  class FlexContainerHandler(IOSViewHandler):
1193
- """Container for flex layout a bare `UIView`.
1193
+ """Container for flex layout, a bare `UIView`.
1194
1194
 
1195
1195
  All flex semantics (direction, alignment, distribution, padding)
1196
1196
  are computed by the layout engine and applied via
@@ -1416,7 +1416,7 @@ class ButtonHandler(IOSViewHandler):
1416
1416
 
1417
1417
  # ``scrollViewDidScroll:`` hands the delegate a ``UIScrollView*``. rubicon's
1418
1418
  # ``@objc_method`` FFI bridge is unreliable for delegate callbacks that take
1419
- # ObjC object arguments on arm64 (see the module header note) on the arm64
1419
+ # ObjC object arguments on arm64 (see the module header note); on the arm64
1420
1420
  # simulator the callback simply never reaches Python, so ``on_scroll`` would
1421
1421
  # silently never fire. Exactly like the UITabBar delegate, we therefore build
1422
1422
  # the delegate class with raw libobjc and dispatch through a CFUNCTYPE IMP.
@@ -1462,7 +1462,7 @@ if _PN_SCROLL_DELEGATE_CLS:
1462
1462
 
1463
1463
 
1464
1464
  class ScrollViewHandler(IOSViewHandler):
1465
- """Scroll container wraps a single child whose height is unbounded.
1465
+ """Scroll container: wraps a single child whose height is unbounded.
1466
1466
 
1467
1467
  The child is positioned by the layout engine using its natural
1468
1468
  content height. The shared frame applier expands the parent
@@ -1713,7 +1713,7 @@ class ImageHandler(IOSViewHandler):
1713
1713
 
1714
1714
 
1715
1715
  # ----------------------------------------------------------------------
1716
- # TextInput raw libobjc target/delegate
1716
+ # TextInput: raw libobjc target/delegate
1717
1717
  # ----------------------------------------------------------------------
1718
1718
  #
1719
1719
  # UITextField control events and UITextField/UITextView delegate
@@ -1786,7 +1786,7 @@ def _tf_on_submit_imp(self_ptr: int, _cmd: int, sender_ptr: int) -> None:
1786
1786
 
1787
1787
 
1788
1788
  def _tf_should_return_imp(self_ptr: int, _cmd: int, tf_ptr: int) -> bool:
1789
- """``textFieldShouldReturn:`` — dismiss the keyboard on Return.
1789
+ """Dismiss the keyboard on Return (``textFieldShouldReturn:``).
1790
1790
 
1791
1791
  iOS doesn't dismiss the keyboard on Return by default; the standard
1792
1792
  pattern is for the delegate to resign first responder and return
@@ -2444,7 +2444,7 @@ class ProgressBarHandler(IOSViewHandler):
2444
2444
 
2445
2445
 
2446
2446
  # ======================================================================
2447
- # WebView WKWebView with navigation + script-message delegates
2447
+ # WebView: WKWebView with navigation + script-message delegates
2448
2448
  # ======================================================================
2449
2449
 
2450
2450
  # WKWebView.scrollView isn't auto-detected as a property by rubicon, so it
@@ -2466,7 +2466,7 @@ def _webview_url(webview: Any) -> str:
2466
2466
  # WKNavigationDelegate + WKScriptMessageHandler bridge. WebKit passes
2467
2467
  # object arguments (``WKNavigation*`` / ``WKScriptMessage*``) to these
2468
2468
  # delegate callbacks, which rubicon's ``@objc_method`` FFI bridge
2469
- # mismarshals on iOS 18.x the app dies with EXC_BAD_ACCESS inside
2469
+ # mismarshals on iOS 18.x; the app dies with EXC_BAD_ACCESS inside
2470
2470
  # ``objc_msgSend`` (see the module header note). Like the scroll and
2471
2471
  # tab-bar delegates we therefore build the class with raw libobjc and
2472
2472
  # CFUNCTYPE IMPs, keep per-delegate state keyed by the delegate
@@ -2702,7 +2702,7 @@ class SafeAreaViewHandler(IOSViewHandler):
2702
2702
 
2703
2703
 
2704
2704
  # ======================================================================
2705
- # Modal actually presents a UIViewController
2705
+ # Modal: actually presents a UIViewController
2706
2706
  # ======================================================================
2707
2707
 
2708
2708
 
@@ -2767,7 +2767,7 @@ class ModalHandler(IOSViewHandler):
2767
2767
  buf.remove(child)
2768
2768
 
2769
2769
  def set_frame(self, native_view: Any, x: float, y: float, width: float, height: float) -> None:
2770
- # Modal is a virtual placeholder not rendered inline.
2770
+ # Modal is a virtual placeholder, not rendered inline.
2771
2771
  return
2772
2772
 
2773
2773
  def measure_intrinsic(
@@ -2858,7 +2858,7 @@ class ModalHandler(IOSViewHandler):
2858
2858
 
2859
2859
 
2860
2860
  # ======================================================================
2861
- # StatusBar global side effect, no view in the tree
2861
+ # StatusBar: global side effect, no view in the tree
2862
2862
  # ======================================================================
2863
2863
 
2864
2864
 
@@ -2903,7 +2903,7 @@ class StatusBarHandler(IOSViewHandler):
2903
2903
 
2904
2904
 
2905
2905
  # ======================================================================
2906
- # KeyboardAvoidingView publishes the keyboard height to Python
2906
+ # KeyboardAvoidingView: publishes the keyboard height to Python
2907
2907
  # ======================================================================
2908
2908
 
2909
2909
 
@@ -2976,7 +2976,7 @@ class KeyboardAvoidingViewHandler(IOSViewHandler):
2976
2976
 
2977
2977
 
2978
2978
  # ======================================================================
2979
- # TabBar UITabBar with a raw ctypes delegate
2979
+ # TabBar: UITabBar with a raw ctypes delegate
2980
2980
  # ======================================================================
2981
2981
  #
2982
2982
  # ``tabBar:didSelectItem:`` passes the UITabBarItem as an ObjC object;
@@ -3193,7 +3193,7 @@ def _present_alert(
3193
3193
  ) -> None:
3194
3194
  """Present a UIAlertController of the given style.
3195
3195
 
3196
- Safe to call from any thread the UIKit work is automatically
3196
+ Safe to call from any thread; the UIKit work is automatically
3197
3197
  marshalled to the main thread via
3198
3198
  [`pythonnative.runtime.call_on_main_thread`][pythonnative.runtime.call_on_main_thread].
3199
3199
  Returns immediately; the alert appears on the next main-loop tick.
@@ -3261,7 +3261,7 @@ def _present_alert(
3261
3261
 
3262
3262
 
3263
3263
  # ======================================================================
3264
- # Picker action-sheet dropdown
3264
+ # Picker: action-sheet dropdown
3265
3265
  # ======================================================================
3266
3266
  #
3267
3267
  # The PythonNative `Picker` renders as a `UIButton` whose tap presents
@@ -3304,7 +3304,7 @@ def _present_picker_sheet(btn: Any) -> None:
3304
3304
 
3305
3305
 
3306
3306
  class PickerHandler(IOSViewHandler):
3307
- """``Picker`` element handler native action-sheet dropdown."""
3307
+ """``Picker`` element handler, native action-sheet dropdown."""
3308
3308
 
3309
3309
  def _build(self, props: Dict[str, Any]) -> Any:
3310
3310
  btn = ObjCClass("UIButton").buttonWithType_(1) # UIButtonTypeSystem
@@ -3333,7 +3333,7 @@ class PickerHandler(IOSViewHandler):
3333
3333
 
3334
3334
 
3335
3335
  # ======================================================================
3336
- # Checkbox SF Symbol UIButton toggling checked / unchecked
3336
+ # Checkbox: SF Symbol UIButton toggling checked / unchecked
3337
3337
  # ======================================================================
3338
3338
 
3339
3339
 
@@ -3438,7 +3438,7 @@ class CheckboxHandler(IOSViewHandler):
3438
3438
 
3439
3439
 
3440
3440
  # ======================================================================
3441
- # SegmentedControl native UISegmentedControl
3441
+ # SegmentedControl: native UISegmentedControl
3442
3442
  # ======================================================================
3443
3443
 
3444
3444
 
@@ -3526,7 +3526,7 @@ class SegmentedControlHandler(IOSViewHandler):
3526
3526
 
3527
3527
 
3528
3528
  # ======================================================================
3529
- # DatePicker native UIDatePicker (compact style on iOS 13.4+)
3529
+ # DatePicker: native UIDatePicker (compact style on iOS 13.4+)
3530
3530
  # ======================================================================
3531
3531
 
3532
3532
 
@@ -25,7 +25,7 @@ Stack navigators rendered as the root of an app host (i.e. the parent
25
25
  own handle) talk to the platform via that host's ``_push`` / ``_pop``
26
26
  methods, so the back stack matches what UIKit / AndroidX maintain.
27
27
  Nested stacks (e.g. a stack inside a tab) fall back to in-Python
28
- state there is no second native navigation controller to push
28
+ state; there's no second native navigation controller to push
29
29
  onto in that case.
30
30
 
31
31
  Example:
@@ -66,7 +66,7 @@ from .hooks import (
66
66
 
67
67
  # Defaults to True: components rendered outside any declarative
68
68
  # navigator (e.g. the root component of a screen pushed via the host's
69
- # native nav stack) are by definition focused the host's own
69
+ # native nav stack) are by definition focused; the host's own
70
70
  # ``on_resume`` / ``on_pause`` lifecycle drives the focus state for
71
71
  # those. Declarative navigators override this provider on the active
72
72
  # subtree (always True today; reserved for future inactive-screen
@@ -152,7 +152,7 @@ class _DeclarativeNavHandle:
152
152
 
153
153
  When ``parent`` is the host's own ``NavigationHandle`` (root
154
154
  Stack), ``navigate`` / ``go_back`` / ``reset`` drive the native
155
- navigation controller and the in-Python stack is bypassed the
155
+ navigation controller and the in-Python stack is bypassed; the
156
156
  OS owns the back-stack source of truth.
157
157
 
158
158
  When ``parent`` is another declarative handle (nested navigator),
@@ -212,7 +212,7 @@ class _DeclarativeNavHandle:
212
212
  untouched because the native controller is the source of
213
213
  truth.
214
214
  - **Nested stack / tab / drawer**: ``set_stack`` is called with
215
- the new route the parent reconciler re-renders the active
215
+ the new route; the parent reconciler re-renders the active
216
216
  screen subtree in place.
217
217
  - **Unknown route**: forwarded to ``parent`` if one exists,
218
218
  otherwise raises ``ValueError``.
pythonnative/net.py CHANGED
@@ -4,7 +4,7 @@ A small, dependency-free coroutine wrapper around
4
4
  :mod:`urllib.request`. Operates on bytes internally and exposes a
5
5
  :class:`Response` with `text()`, `json()`, and `bytes` accessors.
6
6
 
7
- The implementation is deliberately minimal it covers the
7
+ The implementation is deliberately minimal; it covers the
8
8
  "call a JSON API" path that's overwhelmingly the use case for mobile
9
9
  apps. For streaming, multipart uploads, or HTTP/2, integrate
10
10
  ``httpx`` / ``aiohttp`` directly; this module won't try to compete.
@@ -161,7 +161,7 @@ async def fetch(
161
161
  TimeoutError: If the request doesn't complete within
162
162
  ``timeout`` seconds.
163
163
  OSError: For network errors (DNS failure, connection refused,
164
- etc.) re-raised from ``urllib``.
164
+ etc.), re-raised from ``urllib``.
165
165
 
166
166
  Example:
167
167
  ```python
@@ -223,7 +223,7 @@ def _dispatch_request(request: urllib.request.Request, timeout: float) -> Respon
223
223
  content=content,
224
224
  )
225
225
  except urllib.error.HTTPError as exc:
226
- # HTTPError is itself a response object propagate the body
226
+ # HTTPError is itself a response object; propagate the body
227
227
  # so callers can inspect it before deciding to raise.
228
228
  body = exc.read() if hasattr(exc, "read") else b""
229
229
  return Response(
pythonnative/platform.py CHANGED
@@ -102,7 +102,7 @@ class Platform:
102
102
  """Pick the value matching the current platform.
103
103
 
104
104
  Looks up ``spec[Platform.OS]``, then falls back to
105
- ``spec["native"]`` (matches iOS and Android *not* desktop,
105
+ ``spec["native"]`` (matches iOS and Android, *not* desktop,
106
106
  which is a development surface), then to ``spec["default"]``,
107
107
  then to the explicit ``default`` argument.
108
108
 
@@ -16,7 +16,7 @@ that state to size themselves correctly:
16
16
  Rather than threading those values through every
17
17
  [`measure_intrinsic`][pythonnative.native_views.base.ViewHandler.measure_intrinsic]
18
18
  call signature, the screen host writes them here and handlers read
19
- them on demand. Values are in **dp on Android** and **pt on iOS** —
19
+ them on demand. Values are in **dp on Android** and **pt on iOS**,
20
20
  i.e., the same "layout units" the layout engine uses on each
21
21
  platform, so handlers can add them to other layout-unit values
22
22
  without further conversion. On iOS the screen host consumes the top
@@ -88,7 +88,7 @@ def subscribe(callback: Callable[[], None]) -> Callable[[], None]:
88
88
 
89
89
  Returns an unsubscribe function. Hooks pass a state setter so a
90
90
  component re-renders whenever the platform reports a new value.
91
- Threadsafe multiple subscribers may register/unregister
91
+ Threadsafe: multiple subscribers may register/unregister
92
92
  concurrently.
93
93
  """
94
94
  with _subscribers_lock:
@@ -138,7 +138,7 @@ def set_safe_area_insets(top: float, left: float, bottom: float, right: float) -
138
138
  def get_safe_area_insets() -> SafeAreaInsets:
139
139
  """Return the current safe-area insets.
140
140
 
141
- The default value is ``(0, 0, 0, 0)`` handlers should still
141
+ The default value is ``(0, 0, 0, 0)``; handlers should still
142
142
  function correctly on a desktop / unit-test environment where no
143
143
  screen host has published insets.
144
144
  """
@@ -217,7 +217,7 @@ def reset_keyboard_height() -> None:
217
217
  # trust ``UITabBar.sizeThatFits_`` (it has historically returned 0 in
218
218
  # some configurations) and the screen host deliberately extends the
219
219
  # root view past the bottom safe area so the bar reaches the home
220
- # indicator both pieces conspire to require a single source of
220
+ # indicator; both pieces conspire to require a single source of
221
221
  # truth for the height formula.
222
222
  #
223
223
  # Android intentionally has no equivalent: ``BottomNavigationView``
@@ -240,7 +240,7 @@ def ios_tab_bar_height() -> float:
240
240
  Equal to ``IOS_TAB_BAR_BASE_HEIGHT_PT + safe_area_insets.bottom``
241
241
  so the bar reaches the home indicator. The iOS screen host
242
242
  deliberately extends the root view past the bottom safe area for
243
- this very reason the tab bar absorbs the inset and UIKit
243
+ this very reason; the tab bar absorbs the inset and UIKit
244
244
  renders the pill with internal padding for the home indicator.
245
245
  Used by ``pythonnative.native_views.ios.TabBarHandler``; exposed
246
246
  here so the formula is testable without importing the iOS
pythonnative/preview.py CHANGED
@@ -1,4 +1,4 @@
1
- """Desktop preview runtime the engine behind ``pn preview``.
1
+ """Desktop preview runtime, the engine behind ``pn preview``.
2
2
 
3
3
  ``pn preview`` renders a PythonNative app in a real OS window using the
4
4
  Tkinter backend ([`pythonnative.native_views.desktop`][pythonnative.native_views.desktop]),
@@ -50,7 +50,7 @@ class DesktopApp:
50
50
  [`screen`][pythonnative.screen] host as the ``native_instance`` so
51
51
  hosts can drive navigation (``push_screen`` / ``pop_screen`` /
52
52
  ``reset_to_root``), report the viewport size, and set the window
53
- title mirroring the role a ``UIViewController`` / ``Activity``
53
+ title, mirroring the role a ``UIViewController`` / ``Activity``
54
54
  plays on device.
55
55
  """
56
56
 
@@ -424,7 +424,7 @@ def run_preview(
424
424
 
425
425
  root.protocol("WM_DELETE_WINDOW", _on_close)
426
426
  root.after(_POLL_INTERVAL_MS, _poll)
427
- print(f"[pn preview] {component_path} {int(width)}x{int(height)}", file=sys.stderr)
427
+ print(f"[pn preview] {component_path} ({int(width)}x{int(height)})", file=sys.stderr)
428
428
  try:
429
429
  root.mainloop()
430
430
  except KeyboardInterrupt:
@@ -3,7 +3,7 @@
3
3
  The [`Builder`][pythonnative.project.builder.Builder] ties the pieces
4
4
  together: it stages the bundled native template, runs the platform
5
5
  [`configurators`][pythonnative.project.android], invokes the native
6
- toolchains (Gradle / Xcode), andon iOSembeds the CPython runtime into
6
+ toolchains (Gradle / Xcode), and, on iOS, embeds the CPython runtime into
7
7
  the built app.
8
8
 
9
9
  All shell-outs go through a small
@@ -113,7 +113,7 @@ class IOSSigning:
113
113
  """iOS code-signing / export configuration (``[ios.signing]``).
114
114
 
115
115
  Attributes:
116
- export_method: How the archive is exported one of
116
+ export_method: How the archive is exported, one of
117
117
  ``development``, ``ad-hoc``, ``app-store``, ``enterprise``.
118
118
  provisioning_profile: Optional provisioning profile name or UUID
119
119
  for manual signing.
@@ -590,7 +590,7 @@ def render_default_toml(*, name: str, app_id: str, python_version: str = "3.11")
590
590
  """
591
591
  display = name.replace("_", " ").replace("-", " ").strip().title() or name
592
592
  return f"""# PythonNative project configuration.
593
- # Docs: https://docs.pythonnative.com/guide/configuration/
593
+ # Docs: https://pythonnative.com/guides/configuration/
594
594
 
595
595
  [app]
596
596
  id = "{app_id}"
@@ -604,7 +604,7 @@ entry_point = "app/main.py"
604
604
 
605
605
  # Declare the device capabilities your app needs. A string becomes the
606
606
  # iOS permission prompt text; `true` uses a sensible default.
607
- # See: https://docs.pythonnative.com/guide/permissions/
607
+ # See: https://pythonnative.com/guides/permissions/
608
608
  [permissions]
609
609
  # camera = "Scan receipts with your camera."
610
610
  # location_when_in_use = "Show nearby stores."
@@ -1,7 +1,7 @@
1
1
  """Environment diagnostics for ``pn doctor``.
2
2
 
3
3
  Inspects the local toolchain and the project's ``pythonnative.toml`` and
4
- reports what's ready and what's missing for building on each platform
4
+ reports what's ready and what's missing for building on each platform,
5
5
  analogous to ``flutter doctor`` / ``npx react-native doctor``. The checks
6
6
  are deliberately read-only and fast; they shell out only to ask tools for
7
7
  their versions.
@@ -44,7 +44,7 @@ class CheckResult:
44
44
  def format(self) -> str:
45
45
  """Return a single aligned line for terminal output."""
46
46
  symbol = _SYMBOLS.get(self.level, "[?]")
47
- suffix = f" {self.detail}" if self.detail else ""
47
+ suffix = f": {self.detail}" if self.detail else ""
48
48
  return f" {symbol} {self.name}{suffix}"
49
49
 
50
50
 
@@ -77,7 +77,7 @@ def _circular(img: "object") -> "object":
77
77
  def generate_ios_icons(source: Path, appiconset_dir: Path) -> bool:
78
78
  """Generate a single-size iOS ``AppIcon.appiconset``.
79
79
 
80
- Writes ``icon-1024.png`` (a flattened, opaque 1024x1024 image the
80
+ Writes ``icon-1024.png`` (a flattened, opaque 1024x1024 image; the
81
81
  App Store rejects icons with alpha) and a ``Contents.json`` that
82
82
  declares it as the universal iOS app icon. Xcode derives every other
83
83
  size at build time.
@@ -3,7 +3,7 @@
3
3
  Adapts the bundled ``ios_template`` Xcode project to a specific
4
4
  [`AppConfig`][pythonnative.project.config.AppConfig]. Unlike Android, the
5
5
  iOS bundle identifier, version, team, and deployment target are *not*
6
- baked into files they're passed to ``xcodebuild`` as build-setting
6
+ baked into files; they're passed to ``xcodebuild`` as build-setting
7
7
  overrides (see [`build_settings`][pythonnative.project.ios.build_settings]),
8
8
  which avoids fragile ``project.pbxproj`` edits. This module owns the
9
9
  parts that must live on disk:
@@ -23,7 +23,7 @@ artifacts it requires:
23
23
  A capability's value may be either a string (used verbatim as the iOS
24
24
  usage description) or ``true`` (use the capability's
25
25
  [`default_reason`][pythonnative.project.permissions.Capability]). A value
26
- of ``false`` disables the capability useful for switching one off
26
+ of ``false`` disables the capability, useful for switching one off
27
27
  without deleting the line.
28
28
 
29
29
  The catalog is the single source of truth shared by the iOS and Android
@@ -284,7 +284,7 @@ def resolve_permissions(
284
284
  """Resolve a declared capability map into native permission artifacts.
285
285
 
286
286
  Args:
287
- permissions: The ``[permissions]`` table capability key to a
287
+ permissions: The ``[permissions]`` table, capability key to a
288
288
  reason string or boolean. ``false``/``None`` values are
289
289
  skipped (capability disabled).
290
290
  extra_android_permissions: Additional raw Android permission
@@ -9,8 +9,8 @@ paths the [`ios`][pythonnative.project.ios] configurator needs:
9
9
  ``Python.xcframework``, the simulator ``Python.framework``, the standard
10
10
  library, and the simulator headers/static lib.
11
11
 
12
- Android doesn't need any of this Chaquopy ships its own CPython via
13
- Gradle so there's no Android equivalent here.
12
+ Android doesn't need any of this: Chaquopy ships its own CPython via
13
+ Gradle, so there's no Android equivalent here.
14
14
  """
15
15
 
16
16
  from __future__ import annotations
@@ -238,7 +238,7 @@ def prepare_ios_runtime(
238
238
  try:
239
239
  return _locate_runtime(extract_root, python_version)
240
240
  except RuntimeError:
241
- # Stale/partial extraction re-extract below.
241
+ # Stale/partial extraction: re-extract below.
242
242
  pass
243
243
 
244
244
  url = resolve_asset_url(python_version, preferred_name=preferred_name)
@@ -9,7 +9,7 @@ The diff phase is *pure*: it never touches the native layer. Each pass
9
9
  appends ops (`pythonnative.mutations`) to a transaction list, and the
10
10
  commit applies them through a single
11
11
  ``backend.apply_mutations(ops)`` call. Event callbacks never cross into
12
- the native layer at all they live in the Python-side
12
+ the native layer at all; they live in the Python-side
13
13
  [`EventRegistry`][pythonnative.events.EventRegistry], keyed by tag, so
14
14
  re-renders that only produce fresh closures cost nothing natively.
15
15
 
@@ -66,7 +66,7 @@ def _shallow_equal_props(old: dict, new: dict) -> bool:
66
66
 
67
67
  Used by [`memo`][pythonnative.memo] to skip re-rendering when none
68
68
  of a component's props changed identity. Callables only count as
69
- equal if they're the *same object* fresh closures always invalidate
69
+ equal if they're the *same object*; fresh closures always invalidate
70
70
  the memo (matching React's behavior; pair with
71
71
  [`use_callback`][pythonnative.use_callback] when stability matters).
72
72
  """
@@ -173,7 +173,7 @@ class VNode:
173
173
  # node's props change, so unchanged leaves skip native
174
174
  # ``measure_intrinsic`` calls entirely.
175
175
  self._measure_cache: Optional[Tuple[float, float, float, float]] = None
176
- # Last frame sent to the native side frames that don't change
176
+ # Last frame sent to the native side; frames that don't change
177
177
  # are skipped (frame diffing).
178
178
  self._last_frame: Optional[Tuple[float, float, float, float]] = None
179
179
  # Cached LayoutNode reused across passes while the subtree is
@@ -444,8 +444,8 @@ class Reconciler:
444
444
  Unlike a full reconcile from the root, a local update starts in
445
445
  the *middle* of the tree, so the context stack of every
446
446
  ``__Provider__`` ancestor must be re-established before the body
447
- runs (otherwise [`use_context`][pythonnative.use_context] and
448
- therefore [`use_navigation`][pythonnative.use_navigation] would
447
+ runs (otherwise [`use_context`][pythonnative.use_context], and
448
+ therefore [`use_navigation`][pythonnative.use_navigation], would
449
449
  read the context default instead of the provided value). Nested
450
450
  providers *inside* this subtree are pushed/popped normally by the
451
451
  recursive reconcile beneath us.
@@ -939,7 +939,7 @@ class Reconciler:
939
939
  node.hook_state.cleanup_all_effects()
940
940
  # Break the back-references so the unmounted component's hook
941
941
  # state (and the closures it captured) can be freed by plain
942
- # refcounting important on iOS, where the cyclic GC is
942
+ # refcounting, important on iOS, where the cyclic GC is
943
943
  # disabled.
944
944
  node.hook_state._vnode = None
945
945
  node.hook_state._reconciler = None
@@ -983,7 +983,7 @@ class Reconciler:
983
983
 
984
984
  Event callables never appear here (they live in the event
985
985
  registry), so listener identity churn produces no native
986
- traffic only the ``_pn_events`` name set is compared.
986
+ traffic; only the ``_pn_events`` name set is compared.
987
987
  """
988
988
  changed = {}
989
989
  for key, new_val in new.items():
@@ -1056,7 +1056,7 @@ class Reconciler:
1056
1056
  """Whether ``changed`` props can alter the node's layout.
1057
1057
 
1058
1058
  Content-sized leaves re-measure on *any* prop change (text,
1059
- font, image source almost everything affects their intrinsic
1059
+ font, image source: almost everything affects their intrinsic
1060
1060
  size). Containers only care about the layout style keys.
1061
1061
  """
1062
1062
  if type_name in cls._INTRINSIC_TYPES:
@@ -1110,7 +1110,7 @@ class Reconciler:
1110
1110
  )
1111
1111
  viewport.dirty = True
1112
1112
  calculate_layout(viewport, viewport_w, viewport_h)
1113
- # Skip set_frame for the root itself descendants are
1113
+ # Skip set_frame for the root itself; descendants are
1114
1114
  # positioned relative to the root's local origin, which is
1115
1115
  # what they want regardless of where the host placed the
1116
1116
  # root in the screen.
pythonnative/runtime.py CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  PythonNative runs a single, framework-wide ``asyncio`` event loop on
4
4
  a dedicated daemon thread. Every awaitable surface in the framework
5
- the async hooks
5
+ schedules its work on this loop via
6
+ [`run_async`][pythonnative.runtime.run_async]: the async hooks
6
7
  ([`use_async_effect`][pythonnative.hooks.use_async_effect],
7
8
  [`use_query`][pythonnative.hooks.use_query],
8
9
  [`use_mutation`][pythonnative.hooks.use_mutation]), the
@@ -12,8 +13,7 @@ native modules
12
13
  ([`Camera`][pythonnative.native_modules.camera.Camera] /
13
14
  [`Location`][pythonnative.native_modules.location.Location] /
14
15
  [`Notifications`][pythonnative.native_modules.notifications.Notifications]),
15
- and awaited animations — schedules its work on this loop via
16
- [`run_async`][pythonnative.runtime.run_async].
16
+ and awaited animations.
17
17
 
18
18
  The reconciler is **not** asyncio-aware; it still runs synchronously on
19
19
  the platform main thread. Coroutines that want to mutate component
@@ -66,7 +66,7 @@ _lock = threading.Lock()
66
66
  # ======================================================================
67
67
  #
68
68
  # By default ``threading.Thread`` lands at a low QoS class on Apple
69
- # platforms, which iOS subjects to wake-up coalescing the asyncio
69
+ # platforms, which iOS subjects to wake-up coalescing; the asyncio
70
70
  # loop only gets ~2 timeslices per second (~500ms granularity). Bumping
71
71
  # the thread to ``QOS_CLASS_USER_INTERACTIVE`` is *necessary* for it to
72
72
  # be treated as foreground work, but on the simulator it's not
@@ -85,7 +85,7 @@ def _apply_apple_thread_qos() -> None:
85
85
  No-op on other platforms or if the symbol can't be loaded. Must be
86
86
  called from inside the target thread (the underlying syscall is
87
87
  ``pthread_set_qos_class_self_np``). Empirically ``USER_INTERACTIVE``
88
- is required on iOS anything lower triggers wake-up coalescing on
88
+ is required on iOS; anything lower triggers wake-up coalescing on
89
89
  the background asyncio thread, which adds ~500ms latency to every
90
90
  cross-thread dispatch.
91
91
  """
@@ -151,7 +151,7 @@ def _shutdown_for_tests() -> None:
151
151
  Cancels every pending task, stops the loop, joins the thread, and
152
152
  clears the module-level state so the next call to
153
153
  [`get_loop`][pythonnative.runtime.get_loop] starts a fresh loop.
154
- Production code should not call this the loop is a daemon and
154
+ Production code should not call this; the loop is a daemon and
155
155
  will be torn down with the process.
156
156
  """
157
157
  global _loop, _thread
@@ -316,7 +316,7 @@ def call_on_main_thread(fn: Callable[[], None]) -> None:
316
316
 
317
317
  - **iOS**: dispatches ``fn`` onto the main dispatch queue via
318
318
  ``libdispatch.dispatch_async_f`` (called through
319
- :class:`ctypes.PyDLL` to keep the GIL held see the
319
+ :class:`ctypes.PyDLL` to keep the GIL held; see the
320
320
  ``_ios_call_on_main`` comment block for why this matters).
321
321
  - **Android**: posts a ``Runnable`` to
322
322
  ``Handler(Looper.getMainLooper())``.
@@ -469,7 +469,7 @@ def _ios_call_on_main(fn: Callable[[], None]) -> None:
469
469
  _main_next_id += 1
470
470
  key = _main_next_id
471
471
  _main_pending[key] = fn
472
- # dispatch_async_f(queue, context, work) non-blocking; just
472
+ # dispatch_async_f(queue, context, work): non-blocking; just
473
473
  # enqueues the work onto the main queue and returns.
474
474
  assert _dispatch_async_f_c is not None
475
475
  _dispatch_async_f_c(_dispatch_main_q_ptr, key, _main_trampoline_c)
pythonnative/screen.py CHANGED
@@ -186,7 +186,7 @@ def _init_host_common(host: Any, component_path: str, component_func: Any) -> No
186
186
  host._hot_reload_manifest_path = None
187
187
  host._hot_reload_last_version = None
188
188
  host._layout_listener = None # retained on Android to prevent GC
189
- # Focus state drives ``use_focus_effect``. Starts focused because
189
+ # Focus state: drives ``use_focus_effect``. Starts focused because
190
190
  # a host is only created when the screen is being presented; the
191
191
  # platform lifecycle hooks (``on_resume`` / ``on_pause``) flip this
192
192
  # when the user navigates to / from another screen.
@@ -276,14 +276,14 @@ def _on_create(host: Any) -> None:
276
276
  # Android the FragmentManager destroys and recreates a screen's
277
277
  # view every time the user pops back to it, and the platform
278
278
  # template calls ``screen.on_create()`` again from
279
- # ``onViewCreated`` but the Python screen object (and therefore
279
+ # ``onViewCreated``, but the Python screen object (and therefore
280
280
  # the reconciler, hook state, focus subscribers, etc.) persists
281
281
  # across that. Re-running the full mount path here would reset
282
282
  # use_state, clobber use_focus_effect subscriptions, and break
283
283
  # navigation handles held by existing components, which is why
284
284
  # the focus counter never advanced past ``1`` before this guard.
285
285
  # If we're already mounted, just re-attach the existing root view
286
- # to the (newly created) native container ``on_resume`` will
286
+ # to the (newly created) native container; ``on_resume`` will
287
287
  # fire the focus subscribers separately.
288
288
  if host._reconciler is not None and host._root_native_view is not None:
289
289
  host._attach_root(host._root_native_view)
@@ -728,7 +728,7 @@ if IS_ANDROID:
728
728
  # Publish insets first so the very first layout pass sees
729
729
  # them. Otherwise handlers reading insets at first paint
730
730
  # would get ``(0, 0, 0, 0)`` and re-measure once the
731
- # ``OnLayoutChangeListener`` fires moments later a
731
+ # ``OnLayoutChangeListener`` fires moments later, a
732
732
  # measurable flicker (~50–200 ms on a stock Pixel
733
733
  # emulator).
734
734
  _android_publish_window_insets(view)
@@ -1089,7 +1089,7 @@ else:
1089
1089
  the concrete type varies between releases:
1090
1090
 
1091
1091
  - On rubicon-objc 0.5.x ``ptr`` is ``bytes`` (the raw 8-byte,
1092
- little-endian address) ``int(ptr)`` raises ``ValueError``
1092
+ little-endian address); ``int(ptr)`` raises ``ValueError``
1093
1093
  because Python tries to parse the bytes as a decimal string.
1094
1094
  - Older releases return a ``c_void_p`` for which ``int(ptr)``
1095
1095
  works.
@@ -361,7 +361,7 @@ def element_factory(name: str) -> Callable[..., Element]:
361
361
  arguments matching the registered props dataclass.
362
362
 
363
363
  If no ``props`` dataclass was registered for ``name``, kwargs flow
364
- through unmodified useful when iterating before locking down a
364
+ through unmodified, useful when iterating before locking down a
365
365
  prop schema.
366
366
 
367
367
  Args: