pythonnative 0.8.0__py3-none-any.whl → 0.9.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 CHANGED
@@ -14,7 +14,7 @@ Public API::
14
14
  )
15
15
  """
16
16
 
17
- __version__ = "0.8.0"
17
+ __version__ = "0.9.0"
18
18
 
19
19
  from .components import (
20
20
  ActivityIndicator,
@@ -0,0 +1,94 @@
1
+ """Route Python ``sys.stdout``/``sys.stderr`` through fd 2 on iOS.
2
+
3
+ Why
4
+ ---
5
+ When an app is launched via ``xcrun simctl launch --console-pty`` (what
6
+ ``pn run ios`` does), the simulator attaches the caller's terminal to
7
+ the app's stderr, which is the same channel ``NSLog`` / ``os_log``
8
+ writes to. Python ``print()`` calls, however, go to ``sys.stdout``
9
+ (fd 1), and for reasons specific to how CPython's embedded framework
10
+ is started on the iOS Simulator that descriptor does not reach the
11
+ attached console. As a result users see Swift-side ``NSLog`` output
12
+ but never their own ``print()`` output.
13
+
14
+ Redirecting ``sys.stdout`` / ``sys.stderr`` at a Python level to write
15
+ straight to fd 2 is a small, reliable fix: fd 2 *is* visible to
16
+ ``simctl`` (that's exactly how ``NSLog`` reaches the terminal), so
17
+ Python output lands next to the Swift logs with correct ordering.
18
+
19
+ This module is intentionally self-contained: no rubicon-objc or
20
+ platform-specific C bindings required, so it's safe to import early
21
+ during ``pythonnative`` package initialization.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import os
27
+ import sys
28
+ from typing import Iterable
29
+
30
+ _STDERR_FD = 2
31
+
32
+
33
+ class _StderrStream:
34
+ """Minimal text-mode file-like that writes UTF-8 bytes to fd 2.
35
+
36
+ It's write-through (no buffering) so a ``print()`` call appears in
37
+ the terminal immediately, which matches user expectations for an
38
+ interactive "run on simulator" log stream.
39
+ """
40
+
41
+ encoding = "utf-8"
42
+ errors = "replace"
43
+ mode = "w"
44
+ name = "<stderr>"
45
+
46
+ def write(self, s: str) -> int:
47
+ if not s:
48
+ return 0
49
+ data = s.encode(self.encoding, self.errors)
50
+ try:
51
+ return os.write(_STDERR_FD, data)
52
+ except OSError:
53
+ return 0
54
+
55
+ def writelines(self, lines: Iterable[str]) -> None:
56
+ for line in lines:
57
+ self.write(line)
58
+
59
+ def flush(self) -> None:
60
+ # os.write is unbuffered; nothing to flush.
61
+ return None
62
+
63
+ def isatty(self) -> bool:
64
+ try:
65
+ return os.isatty(_STDERR_FD)
66
+ except OSError:
67
+ return False
68
+
69
+ def fileno(self) -> int:
70
+ return _STDERR_FD
71
+
72
+ def close(self) -> None:
73
+ # Don't actually close fd 2.
74
+ return None
75
+
76
+ @property
77
+ def closed(self) -> bool:
78
+ return False
79
+
80
+
81
+ _installed = False
82
+
83
+
84
+ def install() -> None:
85
+ """Swap ``sys.stdout`` / ``sys.stderr`` for fd-2 writers.
86
+
87
+ Safe to call multiple times; only the first call has effect.
88
+ """
89
+ global _installed
90
+ if _installed:
91
+ return
92
+ sys.stdout = _StderrStream()
93
+ sys.stderr = _StderrStream()
94
+ _installed = True
pythonnative/cli/pn.py CHANGED
@@ -246,6 +246,55 @@ def _read_requirements(requirements_path: str) -> list[str]:
246
246
  return result
247
247
 
248
248
 
249
+ ANDROID_LOGCAT_FILTERS: list[str] = [
250
+ "python.stdout:V",
251
+ "python.stderr:V",
252
+ "MainActivity:V",
253
+ "PageFragment:V",
254
+ "Navigator:V",
255
+ "PythonNative:V",
256
+ "AndroidRuntime:E",
257
+ "System.err:W",
258
+ "*:S",
259
+ ]
260
+
261
+ IOS_BUNDLE_ID: str = "com.pythonnative.ios-template"
262
+
263
+
264
+ def _start_android_log_stream() -> Optional[subprocess.Popen]:
265
+ """Clear logcat and stream Python-relevant log tags to the terminal.
266
+
267
+ Returns the ``adb logcat`` subprocess, or ``None`` when ``adb`` is
268
+ unavailable. Python's ``print()`` output reaches logcat via Chaquopy,
269
+ which redirects ``sys.stdout``/``sys.stderr`` to the ``python.stdout``
270
+ and ``python.stderr`` tags.
271
+ """
272
+ try:
273
+ subprocess.run(["adb", "logcat", "-c"], check=False, capture_output=True)
274
+ except FileNotFoundError:
275
+ print("Note: 'adb' not found on PATH; skipping log streaming.")
276
+ return None
277
+ try:
278
+ proc = subprocess.Popen(["adb", "logcat", *ANDROID_LOGCAT_FILTERS])
279
+ except FileNotFoundError:
280
+ return None
281
+ print("Streaming Python logs from device (Ctrl+C to stop)...")
282
+ return proc
283
+
284
+
285
+ def _terminate_subprocess(proc: Optional[subprocess.Popen]) -> None:
286
+ """Politely stop a subprocess, escalating to SIGKILL if needed."""
287
+ if proc is None:
288
+ return
289
+ if proc.poll() is not None:
290
+ return
291
+ proc.terminate()
292
+ try:
293
+ proc.wait(timeout=3)
294
+ except subprocess.TimeoutExpired:
295
+ proc.kill()
296
+
297
+
249
298
  def run_project(args: argparse.Namespace) -> None:
250
299
  """
251
300
  Run the specified project.
@@ -254,6 +303,7 @@ def run_project(args: argparse.Namespace) -> None:
254
303
  platform: str = args.platform
255
304
  prepare_only: bool = getattr(args, "prepare_only", False)
256
305
  hot_reload: bool = getattr(args, "hot_reload", False)
306
+ show_logs: bool = not getattr(args, "no_logs", False)
257
307
 
258
308
  # Read project configuration and save project root before any chdir
259
309
  project_dir: str = os.getcwd()
@@ -371,6 +421,18 @@ def run_project(args: argparse.Namespace) -> None:
371
421
  ],
372
422
  check=True,
373
423
  )
424
+
425
+ # Stream Python logs from logcat unless the user opted out or requested
426
+ # hot-reload (hot-reload handles its own log tailing below).
427
+ if show_logs and not hot_reload:
428
+ logcat_proc = _start_android_log_stream()
429
+ if logcat_proc is not None:
430
+ try:
431
+ logcat_proc.wait()
432
+ except KeyboardInterrupt:
433
+ print()
434
+ _terminate_subprocess(logcat_proc)
435
+ print("Stopped log streaming.")
374
436
  elif platform == "ios":
375
437
  # Attempt to build and run on iOS Simulator (best-effort)
376
438
  ios_project_dir: str = os.path.join(build_dir, "ios_template")
@@ -640,22 +702,64 @@ def run_project(args: argparse.Namespace) -> None:
640
702
  return
641
703
 
642
704
  udid = preferred.get("udid")
643
- # Boot (no-op if already booted)
644
- subprocess.run(["xcrun", "simctl", "boot", udid], check=False)
645
- # Install and launch
705
+ # Boot (no-op if already booted). simctl returns non-zero and
706
+ # prints to stderr when the device is already Booted; we
707
+ # don't care about that case, so swallow its output.
708
+ subprocess.run(["xcrun", "simctl", "boot", udid], check=False, capture_output=True)
709
+ # Install
646
710
  subprocess.run(["xcrun", "simctl", "install", udid, app_path], check=False)
647
- subprocess.run(["xcrun", "simctl", "launch", udid, "com.pythonnative.ios-template"], check=False)
648
- print("Launched iOS app on Simulator (best-effort).")
711
+ if show_logs and not hot_reload:
712
+ # Attach the app's stdout/stderr to this terminal so Python
713
+ # print() calls and exceptions are visible. SIMCTL_CHILD_*
714
+ # env vars are forwarded to the launched process.
715
+ sim_env = os.environ.copy()
716
+ sim_env["SIMCTL_CHILD_PYTHONUNBUFFERED"] = "1"
717
+ print("Launched iOS app on Simulator. Streaming logs (Ctrl+C to stop)...")
718
+ try:
719
+ subprocess.run(
720
+ [
721
+ "xcrun",
722
+ "simctl",
723
+ "launch",
724
+ "--console-pty",
725
+ "--terminate-running-process",
726
+ udid,
727
+ IOS_BUNDLE_ID,
728
+ ],
729
+ env=sim_env,
730
+ check=False,
731
+ )
732
+ except KeyboardInterrupt:
733
+ print()
734
+ subprocess.run(
735
+ ["xcrun", "simctl", "terminate", udid, IOS_BUNDLE_ID],
736
+ check=False,
737
+ capture_output=True,
738
+ )
739
+ print("Stopped log streaming.")
740
+ else:
741
+ subprocess.run(["xcrun", "simctl", "launch", udid, IOS_BUNDLE_ID], check=False)
742
+ print("Launched iOS app on Simulator (best-effort).")
743
+ if show_logs and hot_reload:
744
+ print(
745
+ "Note: live Python log streaming on iOS is disabled while --hot-reload is active; "
746
+ "use Console.app or Xcode to view logs."
747
+ )
649
748
  except Exception:
650
749
  print("Failed to auto-run on Simulator; open the project in Xcode to run.")
651
750
 
652
751
  # Hot-reload file watcher
653
752
  if hot_reload and not prepare_only:
654
- _run_hot_reload(platform, project_dir, build_dir)
753
+ _run_hot_reload(platform, project_dir, build_dir, show_logs=show_logs)
754
+
655
755
 
756
+ def _run_hot_reload(platform: str, project_dir: str, build_dir: str, show_logs: bool = True) -> None:
757
+ """Watch ``app/`` for changes and push updated files to the device.
656
758
 
657
- def _run_hot_reload(platform: str, project_dir: str, build_dir: str) -> None:
658
- """Watch ``app/`` for changes and push updated files to the device."""
759
+ When ``show_logs`` is true and targeting Android, ``adb logcat`` is
760
+ streamed in parallel so Python print/exception output stays visible
761
+ alongside hot-reload notifications.
762
+ """
659
763
  from .hot_reload import FileWatcher
660
764
 
661
765
  app_dir = os.path.join(project_dir, "app")
@@ -673,12 +777,23 @@ def _run_hot_reload(platform: str, project_dir: str, build_dir: str) -> None:
673
777
  print("[hot-reload] Watching app/ for changes. Press Ctrl+C to stop.")
674
778
  watcher = FileWatcher(app_dir, on_change, interval=1.0)
675
779
  watcher.start()
780
+
781
+ logcat_proc: Optional[subprocess.Popen] = None
782
+ if show_logs and platform == "android":
783
+ logcat_proc = _start_android_log_stream()
784
+
676
785
  try:
677
- import time
786
+ if logcat_proc is not None:
787
+ logcat_proc.wait()
788
+ else:
789
+ import time
678
790
 
679
- while True:
680
- time.sleep(1)
791
+ while True:
792
+ time.sleep(1)
681
793
  except KeyboardInterrupt:
794
+ pass
795
+ finally:
796
+ _terminate_subprocess(logcat_proc)
682
797
  watcher.stop()
683
798
  print("\n[hot-reload] Stopped.")
684
799
 
@@ -721,6 +836,11 @@ def main() -> None:
721
836
  action="store_true",
722
837
  help="Watch app/ for changes and push updates to the running app",
723
838
  )
839
+ parser_run.add_argument(
840
+ "--no-logs",
841
+ action="store_true",
842
+ help="Don't attach to the app's stdout/stderr after launching (default: stream logs)",
843
+ )
724
844
  parser_run.set_defaults(func=run_project)
725
845
 
726
846
  # Create a new command 'clean' that calls clean_project
pythonnative/page.py CHANGED
@@ -27,7 +27,7 @@ import importlib
27
27
  import json
28
28
  from typing import Any, Dict, Optional
29
29
 
30
- from .utils import IS_ANDROID, set_android_context
30
+ from .utils import IS_ANDROID, IS_IOS, set_android_context
31
31
 
32
32
  _MAX_RENDER_PASSES = 25
33
33
 
@@ -250,6 +250,21 @@ else:
250
250
  except ImportError:
251
251
  pass
252
252
 
253
+ # Redirect Python's stdout/stderr through fd 2 so ``print()`` output is
254
+ # visible via ``xcrun simctl launch --console-pty``. This runs at
255
+ # ``pythonnative.page`` import time, i.e. before any user page module
256
+ # (e.g. ``app.main_page``) is imported, so their top-level ``print()``
257
+ # calls are captured too. Gated on ``IS_IOS`` rather than rubicon-objc
258
+ # being importable, so installing the ``[ios]`` extra on macOS does
259
+ # not silently swap ``sys.stdout`` on a dev machine.
260
+ if IS_IOS:
261
+ try:
262
+ from . import _ios_log
263
+
264
+ _ios_log.install()
265
+ except Exception:
266
+ pass
267
+
253
268
  _IOS_PAGE_REGISTRY: _Dict[int, Any] = {}
254
269
 
255
270
  def _ios_register_page(vc_instance: Any, host_obj: Any) -> None:
@@ -27,13 +27,18 @@ class ViewController: UIViewController {
27
27
  super.viewDidLoad()
28
28
  // Ensure a visible background when created programmatically (storyboards set this automatically)
29
29
  view.backgroundColor = .systemBackground
30
- NSLog("[PN][ViewController] viewDidLoad")
31
- if let bundleId = Bundle.main.bundleIdentifier {
32
- NSLog("[PN] Bundle Identifier: \(bundleId)")
33
- }
34
- NSLog("[PN] Bundle Path: \(Bundle.main.bundlePath)")
35
- NSLog("[PN] Resource Path: \(Bundle.main.resourcePath ?? "nil")")
36
- // Configure embedded Python if available in bundle
30
+
31
+ let firstInit = !ViewController.hasInitializedPython
32
+
33
+ // Signal to pythonnative that we're running on iOS. Read on the
34
+ // Python side (pythonnative.utils.IS_IOS) to gate iOS-only setup
35
+ // like sys.stdout redirection. Set before Python starts so it's
36
+ // visible to the very first import.
37
+ setenv("PN_PLATFORM", "ios", 1)
38
+
39
+ // Configure embedded Python if available in bundle. PYTHONHOME /
40
+ // PYTHONPATH only need to be set once per process, but setting them
41
+ // again is harmless and keeps the flow simple.
37
42
  if let resourcePath = Bundle.main.resourcePath {
38
43
  let pyStd = "\(resourcePath)/python-stdlib"
39
44
  let pyDyn = "\(resourcePath)/python-stdlib/lib-dynload"
@@ -44,8 +49,6 @@ class ViewController: UIViewController {
44
49
  }
45
50
  setenv("PYTHONHOME", pyStd, 1)
46
51
  setenv("PYTHONPATH", pyPath, 1)
47
- NSLog("[PN] Set PYTHONHOME=\(pyStd)")
48
- NSLog("[PN] Set PYTHONPATH=\(pyPath)")
49
52
  }
50
53
  #if canImport(PythonKit)
51
54
  // Ensure PythonKit knows where to load the Python library from when using an embedded framework.
@@ -53,34 +56,25 @@ class ViewController: UIViewController {
53
56
  let frameworkLib = "\(bundlePath)/Frameworks/Python.framework/Python"
54
57
  setenv("PYTHON_LIBRARY", frameworkLib, 1)
55
58
  if FileManager.default.fileExists(atPath: frameworkLib) {
56
- if !ViewController.hasInitializedPython {
57
- NSLog("[PN] Using embedded Python lib at: \(frameworkLib)")
59
+ if firstInit {
58
60
  PythonLibrary.useLibrary(at: frameworkLib)
59
61
  ViewController.hasInitializedPython = true
60
- } else {
61
- NSLog("[PN] Python library already initialized; skipping useLibrary")
62
62
  }
63
63
  pythonReady = true
64
64
  } else {
65
65
  NSLog("[PN] Embedded Python library not found at: \(frameworkLib)")
66
66
  }
67
67
  }
68
- NSLog("[PN] PythonKit available; attempting Python bootstrap")
69
68
  let sys = Python.import("sys")
70
- NSLog("[PN] Python version: \(sys.version)")
71
- NSLog("[PN] Initial sys.path: \(sys.path)")
69
+ if firstInit {
70
+ // One concise bootstrap line per process; per-page detail is left
71
+ // to Python-side print() statements streamed via pn run ios.
72
+ let shortVersion = "\(sys.version)".split(separator: "\n").first.map(String.init) ?? "\(sys.version)"
73
+ NSLog("[PN] Python \(shortVersion) initialized")
74
+ }
72
75
  if let resourcePath = Bundle.main.resourcePath {
73
76
  sys.path.append(resourcePath)
74
77
  sys.path.append("\(resourcePath)/app")
75
- NSLog("[PN] Updated sys.path: \(sys.path)")
76
- // List bundled resources to verify Python files are present
77
- let fm = FileManager.default
78
- let appDir = "\(resourcePath)/app"
79
- if let entries = try? fm.contentsOfDirectory(atPath: appDir) {
80
- NSLog("[PN] Contents of /app in bundle: \(entries)")
81
- } else {
82
- NSLog("[PN] Could not list contents of \(appDir).")
83
- }
84
78
  }
85
79
  // Determine which Python page to load
86
80
  let pagePath: String = requestedPagePath ?? "app.main_page.MainPage"
pythonnative/utils.py CHANGED
@@ -5,6 +5,7 @@ importing platform-specific packages at module level.
5
5
  """
6
6
 
7
7
  import os
8
+ import sys
8
9
  from typing import Any, Optional
9
10
 
10
11
  # ======================================================================
@@ -12,6 +13,7 @@ from typing import Any, Optional
12
13
  # ======================================================================
13
14
 
14
15
  _is_android: Optional[bool] = None
16
+ _is_ios: Optional[bool] = None
15
17
 
16
18
 
17
19
  def _detect_android() -> bool:
@@ -27,10 +29,40 @@ def _detect_android() -> bool:
27
29
  return False
28
30
 
29
31
 
32
+ def _detect_ios() -> bool:
33
+ """Detect whether we're running inside an iOS app bundle.
34
+
35
+ Signals, in priority order:
36
+
37
+ - Explicit ``PN_PLATFORM=ios`` env var (set by the iOS template's
38
+ ``ViewController.swift`` before Python starts). This is the
39
+ canonical signal and survives even on hosts where ``sys.platform``
40
+ is generic ``darwin``.
41
+ - ``sys.platform == "ios"`` (CPython 3.13+ native iOS builds).
42
+ - ``/CoreSimulator/Devices/`` in ``$HOME`` (iOS Simulator fallback
43
+ if the template signal is missing for some reason).
44
+
45
+ Crucially, having ``rubicon-objc`` importable is *not* enough:
46
+ developers frequently install it on macOS via the ``[ios]`` extra,
47
+ and treating that as iOS would cause subtle side effects
48
+ (e.g. stdout redirection) on desktop machines.
49
+ """
50
+ if os.environ.get("PN_PLATFORM") == "ios":
51
+ return True
52
+ if sys.platform == "ios":
53
+ return True
54
+ home = os.environ.get("HOME", "")
55
+ if "/CoreSimulator/Devices/" in home:
56
+ return True
57
+ return False
58
+
59
+
30
60
  def _ensure_platform_detection() -> None:
31
- global _is_android
61
+ global _is_android, _is_ios
32
62
  if _is_android is None:
33
63
  _is_android = _detect_android()
64
+ if _is_ios is None:
65
+ _is_ios = (not _is_android) and _detect_ios()
34
66
 
35
67
 
36
68
  def _get_is_android() -> bool:
@@ -39,7 +71,14 @@ def _get_is_android() -> bool:
39
71
  return _is_android
40
72
 
41
73
 
74
+ def _get_is_ios() -> bool:
75
+ _ensure_platform_detection()
76
+ assert _is_ios is not None
77
+ return _is_ios
78
+
79
+
42
80
  IS_ANDROID: bool = _get_is_android()
81
+ IS_IOS: bool = _get_is_ios()
43
82
 
44
83
  # ======================================================================
45
84
  # Android context management
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythonnative
3
- Version: 0.8.0
3
+ Version: 0.9.0
4
4
  Summary: Cross-platform native UI toolkit for Android and iOS
5
5
  Author: Owen Carey
6
6
  License: MIT License
@@ -1,15 +1,16 @@
1
- pythonnative/__init__.py,sha256=PJ5VzzASR3xUdgP1iPlGtwXaxzNmC-c_UlJeKu0mCBQ,2038
1
+ pythonnative/__init__.py,sha256=zKplWL38v3kFMeQ8Q2VqnjF-nAM5vx3EW8UIKqJddDo,2038
2
+ pythonnative/_ios_log.py,sha256=dkTSUeOF9xRQjgxMYdH6EohjzjmeInwOfr-_0tkBmpE,2639
2
3
  pythonnative/components.py,sha256=zF24vXM6halq-fweLtLHxZrmBrUKCa9c44hf9Bych-Y,12767
3
4
  pythonnative/element.py,sha256=RBUsXzzzM7KdK-NqMD-InVPKdAb8XJ0h0VpI2rwsfHs,1795
4
5
  pythonnative/hooks.py,sha256=bqnJvEpbYSlbwkBKphT5lLEXQ1mO6AlO4r9SCTBlu7w,13768
5
6
  pythonnative/hot_reload.py,sha256=dtppJaQI6Rl7muCTMgLjFNsD4_F4yYnkEpliCLaaWm8,4508
6
7
  pythonnative/navigation.py,sha256=dDY9qBPLJKr2NnvdIDs8Xy-6kFesQNCEaLjUDT-0_l8,19042
7
- pythonnative/page.py,sha256=ya-UUk_0_lO2YLx3UmBjtPL0j1YXItzP3VhPIloRhXQ,16168
8
+ pythonnative/page.py,sha256=J1mC6jVAu6IjFSwDGPU3cMfiXLPHoW2RbyZ1kj3OSfk,16814
8
9
  pythonnative/reconciler.py,sha256=EuB8InxqlbZpZ0qI8hSjwNQsS-lsvh0vdqGzTnmY1M8,14133
9
10
  pythonnative/style.py,sha256=NG58FSJCBTBBWRrDOyUiHZsTxQDA3jg2PSOcCsU2F5g,3882
10
- pythonnative/utils.py,sha256=IqR_GYknveM_NfAblcaizg9S66hCZfrfiH08HzpOc-4,2537
11
+ pythonnative/utils.py,sha256=dp-cOYSflWvsY9Qh851OeBq-x6S8W1cx4nyjDxlsd0M,3859
11
12
  pythonnative/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- pythonnative/cli/pn.py,sha256=Yi-RHjl3wUWHgGdwtA4UyEDy-tnjpmBB-PGupyDTae0,32823
13
+ pythonnative/cli/pn.py,sha256=KGDbxRYtZHUS4rId_Br6oI7kEGl0pcRAo5UGztUvTN4,37478
13
14
  pythonnative/native_modules/__init__.py,sha256=M_4SW-wCZ8azLuB0JLwUc8PESw00xMCM887-HuN9YNo,647
14
15
  pythonnative/native_modules/camera.py,sha256=b3UErkABhBm2nQ-e72lEeFOgXoVDY6245cV6TN9iPBk,3509
15
16
  pythonnative/native_modules/file_system.py,sha256=2fvYsIboCtjEyxmVeV72glOjtc9fJN3jTM6uaccru6E,4478
@@ -59,7 +60,7 @@ pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties
59
60
  pythonnative/templates/ios_template/ios_template/AppDelegate.swift,sha256=_6G8GNcw4idXd75qKgQKTDCr45Ez73QB8WTvhBqqcMw,1349
60
61
  pythonnative/templates/ios_template/ios_template/Info.plist,sha256=ZQIJGpo8Y2qP0j29xqOsIEGvPpEVICLTAw2NehC5CSo,704
61
62
  pythonnative/templates/ios_template/ios_template/SceneDelegate.swift,sha256=lqtre92dc6d6s-f4ieh_M_4xmc_zMGW79j46tDu9cOY,2177
62
- pythonnative/templates/ios_template/ios_template/ViewController.swift,sha256=eRyxIIVYAYWY9Lt9IH498C2c0FDERUcq-L2TalkEoe0,8315
63
+ pythonnative/templates/ios_template/ios_template/ViewController.swift,sha256=5bVNJ4TT6GIaICzVxl8hgcFmBgaPqjiCivlJzFy0p7c,7920
63
64
  pythonnative/templates/ios_template/ios_template/Assets.xcassets/Contents.json,sha256=D9Sbo8NYXHCWeOAEaoIcPGBoXscGNyDTDTo0SL46IIs,63
64
65
  pythonnative/templates/ios_template/ios_template/Assets.xcassets/AccentColor.colorset/Contents.json,sha256=mvZQhvowtJJS-uGhIlcxaR3nlPd3WvdNcb7-tQfRK3w,123
65
66
  pythonnative/templates/ios_template/ios_template/Assets.xcassets/AppIcon.appiconset/Contents.json,sha256=VUwGr7K_geOvQjFh5VKB6iVXV1mi0tjGMinUmB2JvQs,177
@@ -70,9 +71,9 @@ pythonnative/templates/ios_template/ios_template.xcodeproj/project.xcworkspace/x
70
71
  pythonnative/templates/ios_template/ios_templateTests/ios_templateTests.swift,sha256=YnwzZx7yXB13xKAXEGNgz17VuhWeqkHTRTtBJ2Vu3_E,1238
71
72
  pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITests.swift,sha256=l2Pwa50F_rv-qPu2go6e4bQernM6PTQJeNPFl_c4ivY,1387
72
73
  pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITestsLaunchTests.swift,sha256=f5JrG0uVtLMeJQy26Yyz7Om-JUkT220osqcbeIVkj2g,815
73
- pythonnative-0.8.0.dist-info/licenses/LICENSE,sha256=A69iG7TIAe6KkGQf6xoVHkc5JSZtOr5eRSvC5iuivnI,1067
74
- pythonnative-0.8.0.dist-info/METADATA,sha256=FZB-auwBCmhEBCt3nKpY7ZSNN3mF6_kbDLjVNWwfBMw,6692
75
- pythonnative-0.8.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
76
- pythonnative-0.8.0.dist-info/entry_points.txt,sha256=iUtDawWSAJAEyWTycpZxDuYz73ol31butpzDIEAgPO0,48
77
- pythonnative-0.8.0.dist-info/top_level.txt,sha256=kT4SEATY2ywzrZ2Pgea6_zxyym44Q_PbOsUoOYjJLFE,13
78
- pythonnative-0.8.0.dist-info/RECORD,,
74
+ pythonnative-0.9.0.dist-info/licenses/LICENSE,sha256=A69iG7TIAe6KkGQf6xoVHkc5JSZtOr5eRSvC5iuivnI,1067
75
+ pythonnative-0.9.0.dist-info/METADATA,sha256=YOu6Me-TqLtHWoWkL8A8UWp6WY9f-OzzkSfXZp8X86s,6692
76
+ pythonnative-0.9.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
77
+ pythonnative-0.9.0.dist-info/entry_points.txt,sha256=iUtDawWSAJAEyWTycpZxDuYz73ol31butpzDIEAgPO0,48
78
+ pythonnative-0.9.0.dist-info/top_level.txt,sha256=kT4SEATY2ywzrZ2Pgea6_zxyym44Q_PbOsUoOYjJLFE,13
79
+ pythonnative-0.9.0.dist-info/RECORD,,