pythonnative 0.9.0__tar.gz → 0.11.0__tar.gz

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 (113) hide show
  1. {pythonnative-0.9.0/src/pythonnative.egg-info → pythonnative-0.11.0}/PKG-INFO +4 -1
  2. {pythonnative-0.9.0 → pythonnative-0.11.0}/README.md +1 -0
  3. {pythonnative-0.9.0 → pythonnative-0.11.0}/pyproject.toml +29 -3
  4. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/__init__.py +28 -3
  5. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/_ios_log.py +20 -22
  6. pythonnative-0.11.0/src/pythonnative/cli/__init__.py +7 -0
  7. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/cli/pn.py +360 -56
  8. pythonnative-0.11.0/src/pythonnative/components.py +656 -0
  9. pythonnative-0.11.0/src/pythonnative/element.py +77 -0
  10. pythonnative-0.11.0/src/pythonnative/hooks.py +706 -0
  11. pythonnative-0.11.0/src/pythonnative/hot_reload.py +329 -0
  12. pythonnative-0.11.0/src/pythonnative/layout.py +949 -0
  13. pythonnative-0.11.0/src/pythonnative/native_modules/__init__.py +25 -0
  14. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/native_modules/camera.py +32 -16
  15. pythonnative-0.11.0/src/pythonnative/native_modules/file_system.py +241 -0
  16. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/native_modules/location.py +32 -9
  17. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/native_modules/notifications.py +51 -18
  18. pythonnative-0.11.0/src/pythonnative/native_views/__init__.py +208 -0
  19. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/native_views/android.py +184 -222
  20. pythonnative-0.11.0/src/pythonnative/native_views/base.py +252 -0
  21. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/native_views/ios.py +255 -183
  22. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/navigation.py +288 -63
  23. pythonnative-0.11.0/src/pythonnative/page.py +1106 -0
  24. pythonnative-0.11.0/src/pythonnative/platform_metrics.py +139 -0
  25. pythonnative-0.11.0/src/pythonnative/reconciler.py +807 -0
  26. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/style.py +79 -18
  27. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +4 -0
  28. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PageFragment.kt +23 -0
  29. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_template/ViewController.swift +58 -4
  30. pythonnative-0.11.0/src/pythonnative/utils.py +184 -0
  31. {pythonnative-0.9.0 → pythonnative-0.11.0/src/pythonnative.egg-info}/PKG-INFO +4 -1
  32. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative.egg-info/SOURCES.txt +6 -0
  33. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative.egg-info/requires.txt +2 -0
  34. {pythonnative-0.9.0 → pythonnative-0.11.0}/tests/test_cli.py +115 -0
  35. pythonnative-0.11.0/tests/test_hot_reload.py +97 -0
  36. pythonnative-0.11.0/tests/test_layout.py +773 -0
  37. {pythonnative-0.9.0 → pythonnative-0.11.0}/tests/test_native_views.py +65 -27
  38. pythonnative-0.11.0/tests/test_page.py +189 -0
  39. pythonnative-0.11.0/tests/test_platform_metrics.py +148 -0
  40. {pythonnative-0.9.0 → pythonnative-0.11.0}/tests/test_reconciler.py +285 -1
  41. pythonnative-0.9.0/src/pythonnative/cli/__init__.py +0 -0
  42. pythonnative-0.9.0/src/pythonnative/components.py +0 -408
  43. pythonnative-0.9.0/src/pythonnative/element.py +0 -53
  44. pythonnative-0.9.0/src/pythonnative/hooks.py +0 -440
  45. pythonnative-0.9.0/src/pythonnative/hot_reload.py +0 -143
  46. pythonnative-0.9.0/src/pythonnative/native_modules/__init__.py +0 -19
  47. pythonnative-0.9.0/src/pythonnative/native_modules/file_system.py +0 -131
  48. pythonnative-0.9.0/src/pythonnative/native_views/__init__.py +0 -87
  49. pythonnative-0.9.0/src/pythonnative/native_views/base.py +0 -150
  50. pythonnative-0.9.0/src/pythonnative/page.py +0 -495
  51. pythonnative-0.9.0/src/pythonnative/reconciler.py +0 -350
  52. pythonnative-0.9.0/src/pythonnative/utils.py +0 -122
  53. {pythonnative-0.9.0 → pythonnative-0.11.0}/LICENSE +0 -0
  54. {pythonnative-0.9.0 → pythonnative-0.11.0}/setup.cfg +0 -0
  55. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/build.gradle +0 -0
  56. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/proguard-rules.pro +0 -0
  57. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/androidTest/java/com/pythonnative/android_template/ExampleInstrumentedTest.kt +0 -0
  58. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/AndroidManifest.xml +0 -0
  59. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/Navigator.kt +0 -0
  60. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/drawable/ic_launcher_background.xml +0 -0
  61. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +0 -0
  62. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/layout/activity_main.xml +0 -0
  63. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +0 -0
  64. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +0 -0
  65. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
  66. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
  67. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
  68. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
  69. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
  70. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
  71. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
  72. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
  73. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
  74. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
  75. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/navigation/nav_graph.xml +0 -0
  76. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/values/colors.xml +0 -0
  77. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/values/strings.xml +0 -0
  78. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/values/themes.xml +0 -0
  79. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/values-night/themes.xml +0 -0
  80. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/xml/backup_rules.xml +0 -0
  81. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/xml/data_extraction_rules.xml +0 -0
  82. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/test/java/com/pythonnative/android_template/ExampleUnitTest.kt +0 -0
  83. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/build.gradle +0 -0
  84. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.jar +0 -0
  85. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties +0 -0
  86. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/gradle.properties +0 -0
  87. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/gradlew +0 -0
  88. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/gradlew.bat +0 -0
  89. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/settings.gradle +0 -0
  90. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_template/AppDelegate.swift +0 -0
  91. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/AccentColor.colorset/Contents.json +0 -0
  92. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -0
  93. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/Contents.json +0 -0
  94. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_template/Base.lproj/LaunchScreen.storyboard +0 -0
  95. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_template/Base.lproj/Main.storyboard +0 -0
  96. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_template/Info.plist +0 -0
  97. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +0 -0
  98. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +0 -0
  99. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_template.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -0
  100. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_templateTests/ios_templateTests.swift +0 -0
  101. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITests.swift +0 -0
  102. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITestsLaunchTests.swift +0 -0
  103. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative.egg-info/dependency_links.txt +0 -0
  104. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative.egg-info/entry_points.txt +0 -0
  105. {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative.egg-info/top_level.txt +0 -0
  106. {pythonnative-0.9.0 → pythonnative-0.11.0}/tests/test_components.py +0 -0
  107. {pythonnative-0.9.0 → pythonnative-0.11.0}/tests/test_element.py +0 -0
  108. {pythonnative-0.9.0 → pythonnative-0.11.0}/tests/test_hooks.py +0 -0
  109. {pythonnative-0.9.0 → pythonnative-0.11.0}/tests/test_ios_log.py +0 -0
  110. {pythonnative-0.9.0 → pythonnative-0.11.0}/tests/test_navigation.py +0 -0
  111. {pythonnative-0.9.0 → pythonnative-0.11.0}/tests/test_smoke.py +0 -0
  112. {pythonnative-0.9.0 → pythonnative-0.11.0}/tests/test_style.py +0 -0
  113. {pythonnative-0.9.0 → pythonnative-0.11.0}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythonnative
3
- Version: 0.9.0
3
+ Version: 0.11.0
4
4
  Summary: Cross-platform native UI toolkit for Android and iOS
5
5
  Author: Owen Carey
6
6
  License: MIT License
@@ -48,6 +48,8 @@ Provides-Extra: docs
48
48
  Requires-Dist: mkdocs>=1.5; extra == "docs"
49
49
  Requires-Dist: mkdocs-material[imaging]>=9.5; extra == "docs"
50
50
  Requires-Dist: mkdocstrings[python]>=0.24; extra == "docs"
51
+ Requires-Dist: mkdocs-autorefs>=1.0; extra == "docs"
52
+ Requires-Dist: pymdown-extensions>=10.7; extra == "docs"
51
53
  Provides-Extra: dev
52
54
  Requires-Dist: black>=24.0; extra == "dev"
53
55
  Requires-Dist: ruff>=0.5; extra == "dev"
@@ -95,6 +97,7 @@ PythonNative is a cross-platform toolkit for building native Android and iOS app
95
97
  - **Declarative UI:** Describe *what* your UI should look like with element functions (`Text`, `Button`, `Column`, `Row`, etc.). PythonNative creates and updates native views automatically.
96
98
  - **Hooks and function components:** Manage state with `use_state`, side effects with `use_effect`, and navigation with `use_navigation`, all through one consistent pattern.
97
99
  - **`style` prop:** Pass all visual and layout properties through a single `style` dict, composable via `StyleSheet`.
100
+ - **Cross-platform flexbox engine:** A pure-Python, Yoga-style layout engine computes frames once and applies them to native views, so `flex`, `padding`, `aspect_ratio`, and `position: "absolute"` produce the same geometry on Android and iOS.
98
101
  - **Virtual view tree + reconciler:** Element trees are diffed and patched with minimal native mutations, similar to React's reconciliation.
99
102
  - **Direct native bindings:** Python calls platform APIs directly through Chaquopy and rubicon-objc, with no JavaScript bridge.
100
103
  - **CLI scaffolding:** `pn init` creates a ready-to-run project; `pn run android` and `pn run ios` build and launch your app.
@@ -33,6 +33,7 @@ PythonNative is a cross-platform toolkit for building native Android and iOS app
33
33
  - **Declarative UI:** Describe *what* your UI should look like with element functions (`Text`, `Button`, `Column`, `Row`, etc.). PythonNative creates and updates native views automatically.
34
34
  - **Hooks and function components:** Manage state with `use_state`, side effects with `use_effect`, and navigation with `use_navigation`, all through one consistent pattern.
35
35
  - **`style` prop:** Pass all visual and layout properties through a single `style` dict, composable via `StyleSheet`.
36
+ - **Cross-platform flexbox engine:** A pure-Python, Yoga-style layout engine computes frames once and applies them to native views, so `flex`, `padding`, `aspect_ratio`, and `position: "absolute"` produce the same geometry on Android and iOS.
36
37
  - **Virtual view tree + reconciler:** Element trees are diffed and patched with minimal native mutations, similar to React's reconciliation.
37
38
  - **Direct native bindings:** Python calls platform APIs directly through Chaquopy and rubicon-objc, with no JavaScript bridge.
38
39
  - **CLI scaffolding:** `pn init` creates a ready-to-run project; `pn run android` and `pn run ios` build and launch your app.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pythonnative"
7
- version = "0.9.0"
7
+ version = "0.11.0"
8
8
  description = "Cross-platform native UI toolkit for Android and iOS"
9
9
  authors = [
10
10
  { name = "Owen Carey" }
@@ -35,6 +35,8 @@ docs = [
35
35
  "mkdocs>=1.5",
36
36
  "mkdocs-material[imaging]>=9.5",
37
37
  "mkdocstrings[python]>=0.24",
38
+ "mkdocs-autorefs>=1.0",
39
+ "pymdown-extensions>=10.7",
38
40
  ]
39
41
  dev = [
40
42
  "black>=24.0",
@@ -81,8 +83,32 @@ extend-exclude = [
81
83
  ]
82
84
 
83
85
  [tool.ruff.lint]
84
- select = ["E", "F", "I"]
85
- ignore = []
86
+ # Docstring (D) rules use the Google convention; see [tool.ruff.lint.pydocstyle].
87
+ # Selectively enabled so tests/examples/templates aren't penalized.
88
+ select = ["E", "F", "I", "D"]
89
+ # D107 (missing __init__ docstring): mkdocstrings is configured to merge
90
+ # __init__ into the class docstring, so the class docstring is the source of truth.
91
+ # D105 (magic method docstring): most are self-explanatory (__repr__, __eq__, etc.).
92
+ # D203/D213: conflict with Google convention defaults; ruff auto-suppresses
93
+ # these via `convention = "google"`, but we list them defensively.
94
+ ignore = ["D107", "D105", "D203", "D213"]
95
+
96
+ [tool.ruff.lint.per-file-ignores]
97
+ "tests/**/*.py" = ["D"]
98
+ "examples/**/*.py" = ["D"]
99
+ "src/pythonnative/templates/**/*.py" = ["D"]
100
+ "setup.py" = ["D"]
101
+ "conftest.py" = ["D"]
102
+ # Platform handler subclasses implement the ViewHandler ABC defined in
103
+ # native_views/base.py, which carries the canonical docstrings for the
104
+ # protocol. The concrete classes are internal (registered by name in
105
+ # NativeViewRegistry, never imported by users), so requiring per-method
106
+ # docstrings would only add boilerplate that repeats the ABC.
107
+ "src/pythonnative/native_views/android.py" = ["D101", "D102"]
108
+ "src/pythonnative/native_views/ios.py" = ["D101", "D102"]
109
+
110
+ [tool.ruff.lint.pydocstyle]
111
+ convention = "google"
86
112
 
87
113
  [tool.black]
88
114
  line-length = 120
@@ -1,7 +1,31 @@
1
- """PythonNative declarative native UI for Android and iOS.
1
+ """PythonNative: declarative native UI for Android and iOS.
2
2
 
3
- Public API::
3
+ PythonNative is a cross-platform toolkit that turns Python ``@component``
4
+ functions into real, native Android and iOS views. The component model
5
+ is React-like (function components plus hooks), but rendering happens
6
+ through direct platform bindings: Chaquopy on Android (Java) and
7
+ rubicon-objc on iOS (Objective-C). There is no JavaScript bridge.
4
8
 
9
+ Key building blocks:
10
+
11
+ - **Element factories** ([`Text`][pythonnative.Text],
12
+ [`Button`][pythonnative.Button], [`Column`][pythonnative.Column], etc.)
13
+ return immutable [`Element`][pythonnative.Element] descriptors.
14
+ - **Hooks** ([`use_state`][pythonnative.use_state],
15
+ [`use_effect`][pythonnative.use_effect],
16
+ [`use_reducer`][pythonnative.use_reducer], etc.) manage state, side
17
+ effects, and context inside `@component` functions.
18
+ - **Navigation** is built from
19
+ [`NavigationContainer`][pythonnative.NavigationContainer] plus one of
20
+ the [`create_stack_navigator`][pythonnative.create_stack_navigator],
21
+ [`create_tab_navigator`][pythonnative.create_tab_navigator], or
22
+ [`create_drawer_navigator`][pythonnative.create_drawer_navigator]
23
+ factories.
24
+ - **Styling** uses a single ``style`` dict per element (or a list of
25
+ dicts), composable via [`StyleSheet`][pythonnative.StyleSheet].
26
+
27
+ Example:
28
+ ```python
5
29
  import pythonnative as pn
6
30
 
7
31
  @pn.component
@@ -12,9 +36,10 @@ Public API::
12
36
  pn.Button("+", on_click=lambda: set_count(count + 1)),
13
37
  style={"spacing": 12},
14
38
  )
39
+ ```
15
40
  """
16
41
 
17
- __version__ = "0.9.0"
42
+ __version__ = "0.11.0"
18
43
 
19
44
  from .components import (
20
45
  ActivityIndicator,
@@ -1,24 +1,22 @@
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
1
+ """Route Python `sys.stdout`/`sys.stderr` through fd 2 on iOS.
2
+
3
+ When an app is launched via `xcrun simctl launch --console-pty`
4
+ (what `pn run ios` does), the simulator attaches the caller's
5
+ terminal to the app's stderr, which is the same channel `NSLog` and
6
+ `os_log` write to. Python `print()` calls, however, go to
7
+ `sys.stdout` (fd 1), and for reasons specific to how CPython's
8
+ embedded framework is started on the iOS Simulator that descriptor
9
+ does not reach the attached console. As a result, users see
10
+ Swift-side `NSLog` output but never their own `print()` output.
11
+
12
+ Redirecting `sys.stdout` and `sys.stderr` at a Python level to write
15
13
  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
14
+ `simctl` (that is exactly how `NSLog` reaches the terminal), so
17
15
  Python output lands next to the Swift logs with correct ordering.
18
16
 
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.
17
+ This module is intentionally self-contained (no rubicon-objc or
18
+ platform-specific C bindings required), so it is safe to import
19
+ early during `pythonnative` package initialization.
22
20
  """
23
21
 
24
22
  from __future__ import annotations
@@ -33,8 +31,8 @@ _STDERR_FD = 2
33
31
  class _StderrStream:
34
32
  """Minimal text-mode file-like that writes UTF-8 bytes to fd 2.
35
33
 
36
- It's write-through (no buffering) so a ``print()`` call appears in
37
- the terminal immediately, which matches user expectations for an
34
+ Write-through (no buffering), so a `print()` call appears in the
35
+ terminal immediately. That matches user expectations for an
38
36
  interactive "run on simulator" log stream.
39
37
  """
40
38
 
@@ -82,9 +80,9 @@ _installed = False
82
80
 
83
81
 
84
82
  def install() -> None:
85
- """Swap ``sys.stdout`` / ``sys.stderr`` for fd-2 writers.
83
+ """Swap `sys.stdout` and `sys.stderr` for fd-2 writers.
86
84
 
87
- Safe to call multiple times; only the first call has effect.
85
+ Idempotent: only the first call has effect.
88
86
  """
89
87
  global _installed
90
88
  if _installed:
@@ -0,0 +1,7 @@
1
+ """Command-line interface package for the `pn` script.
2
+
3
+ The CLI entry point lives in
4
+ [`pythonnative.cli.pn`][pythonnative.cli.pn] and is exposed as the
5
+ `pn` console script (configured in `pyproject.toml`'s
6
+ `[project.scripts]`).
7
+ """