Skip to content

Write Python stdout/stderr to the console when BRIEFCASE_CONSOLE_OUTPUT=1 - #94

Open
kattni wants to merge 1 commit into
beeware:mainfrom
kattni:ios-console-output
Open

kattni wants to merge 1 commit into
beeware:mainfrom
kattni:ios-console-output

Conversation

@kattni

@kattni kattni commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

Adds a "console output" mode to the iOS launcher (main.m) so Briefcase can capture Python output when it runs an app on a physical device.

When Briefcase runs an app on a physical device (beeware/briefcase#860), it launches it with xcrun devicectl device process launch --console and reads the process's console to detect test results and errors. Python's sys.stdout and sys.stderr are written to the Apple system log, though, so print() output never shows up there; only the launcher's NSLog lines do.

The contract

If the environment variable BRIEFCASE_CONSOLE_OUTPUT is set to 1 when the app launches, Python's sys.stdout and sys.stderr write to the process's real stdout and stderr (file descriptors 1 and 2) instead of the system log. Briefcase passes this in the --environment-variables argument to devicectl. Any other value, or no value, keeps the current behavior, so simulator logging and normal launches are unaffected. In console mode the launcher logs one extra line: Python stdout and stderr will be written to the console. All existing NSLog messages stay as they are.

Implementation

  • Python 3.14+: config.use_system_logger is now set to !console_output instead of always 1.
  • Python 3.11–3.13: there is no config field for this. On iOS, _apple_support.init_streams() always replaces sys.stdout and sys.stderr with SystemLog wrappers during startup (all three support packages ship _apple_support.py). In console mode, the launcher runs a short PyRun_SimpleString after Py_InitializeFromConfig that rebinds sys.stdout and sys.stderr to sys.__stdout__ and sys.__stderr__. Those are the original UTF-8 streams on fds 1 and 2, unbuffered because of config.buffered_stdio = 0. If that call fails, the crash dialog is shown and the app exits.

Verification

I generated a probe app from this branch with Briefcase 0.4.7 for Python 3.13 (support b18) and 3.14, built it for the iPhone 16 simulator, and launched it with xcrun simctl launch --console-pty. The probe prints repr(sys.stdout) to stdout and repr(sys.stderr) to stderr, then raises SystemExit(0).

Python BRIEFCASE_CONSOLE_OUTPUT Console System log (log show)
3.13 unset NSLog lines only <SystemLog (level 0)> / <SystemLog (level 16)>
3.13 1 <_io.TextIOWrapper name='<stdout>' ... encoding='utf-8'> and <stderr> no probe lines
3.14 unset NSLog lines only <SystemLog (level 0)> / <SystemLog (level 16)>
3.14 1 <_io.TextIOWrapper name='<stdout>' ... encoding='utf-8'> and <stderr> no probe lines

On the simulator the variable was passed as SIMCTL_CHILD_BRIEFCASE_CONSOLE_OUTPUT=1. I did not test Python 3.11, 3.12, or 3.15 directly: 3.11 and 3.12 use the same _apple_support path as 3.13, and 3.15 uses the same use_system_logger path as 3.14. I also haven't run this on a physical device yet; that will happen through the Briefcase side of #860.

Refs beeware/briefcase#860

PR Checklist:

  • I will abide by the BeeWare Code of Conduct
  • I have read and have followed the CONTRIBUTING.md file
  • This PR was generated or assisted using an AI tool

Assisted-by: Claude Code (Claude Opus 5.5)

…UT=1.

When Briefcase launches an app on a physical iOS device, it streams the
process console with `devicectl --console`. Python's stdout and stderr go
to the Apple system log, so print() output never reached that console.

If BRIEFCASE_CONSOLE_OUTPUT=1 is set in the environment, the launcher now
leaves config.use_system_logger disabled on Python 3.14+, and on Python
3.13 and earlier restores sys.stdout/sys.stderr to the original streams
on file descriptors 1 and 2 after initialization. Without the variable,
behavior is unchanged.

Assisted-by: Claude Code (claude-opus-5-5)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant