Skip to content

Commit 5f6c029

Browse files
committed
Add logcat forwarding for Python stdout/stderr
Introduces a Python initialization script that redirects stdout and stderr to Android logcat using a custom writer and logging handler. Adds a Dart function to inject this script into the Python interpreter, ensuring Python logs are visible in Android logcat. Also adds debug messages to trace script execution.
1 parent 15be21d commit 5f6c029

File tree

1 file changed

+50
-0
lines changed

1 file changed

+50
-0
lines changed

src/serious_python_android/lib/src/cpython.dart

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,32 @@ import 'gen.dart';
1111
export 'gen.dart';
1212

1313
CPython? _cpython;
14+
const _logcatInitScript = r'''
15+
import sys, logging
16+
from ctypes import cdll
17+
liblog = cdll.LoadLibrary("liblog.so")
18+
ANDROID_LOG_INFO = 4
19+
20+
def _log_to_logcat(msg, level=ANDROID_LOG_INFO):
21+
if not msg:
22+
return
23+
if isinstance(msg, bytes):
24+
msg = msg.decode("utf-8", errors="replace")
25+
liblog.__android_log_write(level, b"serious_python", msg.encode("utf-8"))
26+
27+
class _LogcatWriter:
28+
def write(self, msg):
29+
_log_to_logcat(msg.strip())
30+
def flush(self):
31+
pass
32+
33+
sys.stdout = sys.stderr = _LogcatWriter()
34+
handler = logging.StreamHandler(sys.stderr)
35+
handler.setFormatter(logging.Formatter("%(levelname)s %(message)s"))
36+
root = logging.getLogger()
37+
root.handlers[:] = [handler]
38+
root.setLevel(logging.DEBUG)
39+
''';
1440

1541
CPython getCPython(String dynamicLibPath) {
1642
return _cpython ??= _cpython = CPython(DynamicLibrary.open(dynamicLibPath));
@@ -61,10 +87,17 @@ Future<String> runPythonProgramInIsolate(List<Object> arguments) async {
6187
_debug("after Py_Initialize()");
6288
}
6389

90+
final logcatSetupError = _setupLogcatForwarding(cpython);
91+
if (logcatSetupError != null) {
92+
sendPort.send(logcatSetupError);
93+
return logcatSetupError;
94+
}
95+
6496
var result = "";
6597

6698
if (script != "") {
6799
// run script
100+
_debug("Running script: $script");
68101
final scriptPtr = script.toNativeUtf8();
69102
int sr = cpython.PyRun_SimpleString(scriptPtr.cast<Char>());
70103
_debug("PyRun_SimpleString for script result: $sr");
@@ -74,6 +107,7 @@ Future<String> runPythonProgramInIsolate(List<Object> arguments) async {
74107
}
75108
} else {
76109
// run program
110+
_debug("Running program module: $programModuleName");
77111
final moduleNamePtr = programModuleName.toNativeUtf8();
78112
var modulePtr = cpython.PyImport_ImportModule(moduleNamePtr.cast<Char>());
79113
if (modulePtr == nullptr) {
@@ -82,6 +116,8 @@ Future<String> runPythonProgramInIsolate(List<Object> arguments) async {
82116
malloc.free(moduleNamePtr);
83117
}
84118

119+
_debug("Python program finished");
120+
85121
sendPort.send(result);
86122

87123
return result;
@@ -131,3 +167,17 @@ String getPythonError(CPython cpython) {
131167
return "Error loading traceback module.";
132168
}
133169
}
170+
171+
String? _setupLogcatForwarding(CPython cpython) {
172+
_debug("Setting up logcat forwarding");
173+
final setupPtr = _logcatInitScript.toNativeUtf8();
174+
final result = cpython.PyRun_SimpleString(setupPtr.cast<Char>());
175+
malloc.free(setupPtr);
176+
177+
if (result != 0) {
178+
return getPythonError(cpython);
179+
}
180+
181+
_debug("logcat forwarding configured");
182+
return null;
183+
}

0 commit comments

Comments
 (0)