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.
- {pythonnative-0.9.0/src/pythonnative.egg-info → pythonnative-0.11.0}/PKG-INFO +4 -1
- {pythonnative-0.9.0 → pythonnative-0.11.0}/README.md +1 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/pyproject.toml +29 -3
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/__init__.py +28 -3
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/_ios_log.py +20 -22
- pythonnative-0.11.0/src/pythonnative/cli/__init__.py +7 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/cli/pn.py +360 -56
- pythonnative-0.11.0/src/pythonnative/components.py +656 -0
- pythonnative-0.11.0/src/pythonnative/element.py +77 -0
- pythonnative-0.11.0/src/pythonnative/hooks.py +706 -0
- pythonnative-0.11.0/src/pythonnative/hot_reload.py +329 -0
- pythonnative-0.11.0/src/pythonnative/layout.py +949 -0
- pythonnative-0.11.0/src/pythonnative/native_modules/__init__.py +25 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/native_modules/camera.py +32 -16
- pythonnative-0.11.0/src/pythonnative/native_modules/file_system.py +241 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/native_modules/location.py +32 -9
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/native_modules/notifications.py +51 -18
- pythonnative-0.11.0/src/pythonnative/native_views/__init__.py +208 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/native_views/android.py +184 -222
- pythonnative-0.11.0/src/pythonnative/native_views/base.py +252 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/native_views/ios.py +255 -183
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/navigation.py +288 -63
- pythonnative-0.11.0/src/pythonnative/page.py +1106 -0
- pythonnative-0.11.0/src/pythonnative/platform_metrics.py +139 -0
- pythonnative-0.11.0/src/pythonnative/reconciler.py +807 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/style.py +79 -18
- {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
- {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
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_template/ViewController.swift +58 -4
- pythonnative-0.11.0/src/pythonnative/utils.py +184 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0/src/pythonnative.egg-info}/PKG-INFO +4 -1
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative.egg-info/SOURCES.txt +6 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative.egg-info/requires.txt +2 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/tests/test_cli.py +115 -0
- pythonnative-0.11.0/tests/test_hot_reload.py +97 -0
- pythonnative-0.11.0/tests/test_layout.py +773 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/tests/test_native_views.py +65 -27
- pythonnative-0.11.0/tests/test_page.py +189 -0
- pythonnative-0.11.0/tests/test_platform_metrics.py +148 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/tests/test_reconciler.py +285 -1
- pythonnative-0.9.0/src/pythonnative/cli/__init__.py +0 -0
- pythonnative-0.9.0/src/pythonnative/components.py +0 -408
- pythonnative-0.9.0/src/pythonnative/element.py +0 -53
- pythonnative-0.9.0/src/pythonnative/hooks.py +0 -440
- pythonnative-0.9.0/src/pythonnative/hot_reload.py +0 -143
- pythonnative-0.9.0/src/pythonnative/native_modules/__init__.py +0 -19
- pythonnative-0.9.0/src/pythonnative/native_modules/file_system.py +0 -131
- pythonnative-0.9.0/src/pythonnative/native_views/__init__.py +0 -87
- pythonnative-0.9.0/src/pythonnative/native_views/base.py +0 -150
- pythonnative-0.9.0/src/pythonnative/page.py +0 -495
- pythonnative-0.9.0/src/pythonnative/reconciler.py +0 -350
- pythonnative-0.9.0/src/pythonnative/utils.py +0 -122
- {pythonnative-0.9.0 → pythonnative-0.11.0}/LICENSE +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/setup.cfg +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/build.gradle +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/proguard-rules.pro +0 -0
- {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
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/AndroidManifest.xml +0 -0
- {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
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/drawable/ic_launcher_background.xml +0 -0
- {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
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/layout/activity_main.xml +0 -0
- {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
- {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
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
- {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
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
- {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
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
- {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
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
- {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
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
- {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
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/navigation/nav_graph.xml +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/values/colors.xml +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/values/strings.xml +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/values/themes.xml +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/values-night/themes.xml +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/xml/backup_rules.xml +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/app/src/main/res/xml/data_extraction_rules.xml +0 -0
- {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
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/build.gradle +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.jar +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/gradle.properties +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/gradlew +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/gradlew.bat +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/android_template/settings.gradle +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_template/AppDelegate.swift +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/AccentColor.colorset/Contents.json +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/Contents.json +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_template/Base.lproj/LaunchScreen.storyboard +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_template/Base.lproj/Main.storyboard +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_template/Info.plist +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_template.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_templateTests/ios_templateTests.swift +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITests.swift +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITestsLaunchTests.swift +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative.egg-info/dependency_links.txt +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative.egg-info/entry_points.txt +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/src/pythonnative.egg-info/top_level.txt +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/tests/test_components.py +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/tests/test_element.py +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/tests/test_hooks.py +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/tests/test_ios_log.py +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/tests/test_navigation.py +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/tests/test_smoke.py +0 -0
- {pythonnative-0.9.0 → pythonnative-0.11.0}/tests/test_style.py +0 -0
- {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.
|
|
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.
|
|
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
|
-
|
|
85
|
-
|
|
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
|
|
1
|
+
"""PythonNative: declarative native UI for Android and iOS.
|
|
2
2
|
|
|
3
|
-
|
|
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.
|
|
42
|
+
__version__ = "0.11.0"
|
|
18
43
|
|
|
19
44
|
from .components import (
|
|
20
45
|
ActivityIndicator,
|
|
@@ -1,24 +1,22 @@
|
|
|
1
|
-
"""Route Python
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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
|
|
20
|
-
platform-specific C bindings required, so it
|
|
21
|
-
during
|
|
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
|
-
|
|
37
|
-
|
|
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
|
|
83
|
+
"""Swap `sys.stdout` and `sys.stderr` for fd-2 writers.
|
|
86
84
|
|
|
87
|
-
|
|
85
|
+
Idempotent: only the first call has effect.
|
|
88
86
|
"""
|
|
89
87
|
global _installed
|
|
90
88
|
if _installed:
|