@@ -13,7 +13,6 @@ export 'gen.dart';
1313CPython ? _cpython;
1414String ? _logcatForwardingError;
1515Future <void > _pythonRunQueue = Future <void >.value ();
16- var _pythonRunSeq = 0 ;
1716
1817Future <T > _enqueuePythonRun <T >(Future <T > Function () action) {
1918 final completer = Completer <T >();
@@ -66,50 +65,32 @@ CPython getCPython(String dynamicLibPath) {
6665Future <String > runPythonProgramFFI (bool sync , String dynamicLibPath,
6766 String pythonProgramPath, String script) async {
6867 return _enqueuePythonRun (() async {
69- final runId = ++ _pythonRunSeq;
7068 spDebug (
71- "Python run#$ runId start (sync=$sync , script=${script .isNotEmpty }, program=$pythonProgramPath )" );
69+ "Python run start (sync=$sync , script=${script .isNotEmpty }, program=$pythonProgramPath )" );
7270 if (sync ) {
7371 // Sync run: do not involve ports (avoids GC/close races).
7472 final result =
7573 _runPythonProgram (dynamicLibPath, pythonProgramPath, script);
76- spDebug ("Python run#$ runId done (resultLength=${result .length })" );
74+ spDebug ("Python run done (resultLength=${result .length })" );
7775 return result;
7876 } else {
7977 // Async run: use Isolate.run() to avoid manual port lifecycle issues.
8078 try {
79+ spDebug (
80+ "Python async run start (sync=$sync , script=${script .isNotEmpty }, program=$pythonProgramPath )" );
8181 final result = await Isolate .run (
8282 () => _runPythonProgram (dynamicLibPath, pythonProgramPath, script));
83- spDebug ("Python run#$ runId done (resultLength=${result .length })" );
83+ spDebug ("Python run done (resultLength=${result .length })" );
8484 return result;
8585 } catch (e, st) {
8686 final message = "Dart error running Python: $e \n $st " ;
8787 spDebug (message);
88- spDebug ("Python run#$runId failed" );
8988 return message;
9089 }
9190 }
9291 });
9392}
9493
95- Future <String > runPythonProgramInIsolate (List <Object > arguments) async {
96- final sendPort = arguments[0 ] as SendPort ;
97- final dynamicLibPath = arguments[1 ] as String ;
98- final pythonProgramPath = arguments[2 ] as String ;
99- final script = arguments[3 ] as String ;
100-
101- try {
102- final result = _runPythonProgram (dynamicLibPath, pythonProgramPath, script);
103- sendPort.send (result);
104- return result;
105- } catch (e, st) {
106- final message = "Dart error running Python: $e \n $st " ;
107- spDebug (message);
108- sendPort.send (message);
109- return message;
110- }
111- }
112-
11394String _runPythonProgram (
11495 String dynamicLibPath, String pythonProgramPath, String script) {
11596 var programDirPath = p.dirname (pythonProgramPath);
@@ -122,7 +103,8 @@ String _runPythonProgram(
122103 final cpython = getCPython (dynamicLibPath);
123104 spDebug ("CPython loaded" );
124105 if (cpython.Py_IsInitialized () != 0 ) {
125- spDebug ("Python already initialized, skipping execution." );
106+ spDebug (
107+ "Python already initialized and another program is running, skipping execution." );
126108 return "" ;
127109 }
128110
@@ -163,119 +145,84 @@ String _runPythonProgram(
163145}
164146
165147String getPythonError (CPython cpython) {
166- // get error object
167148 final exPtr = cpython.PyErr_GetRaisedException ();
168- if (exPtr == nullptr) {
169- return "Unknown Python error (no exception set)." ;
149+ if (exPtr == nullptr) return "Unknown Python error (no exception set)." ;
150+
151+ try {
152+ final formatted = _formatPythonException (cpython, exPtr);
153+ if (formatted != null && formatted.isNotEmpty) return formatted;
154+
155+ final fallback = _pyObjectToDartString (cpython, exPtr);
156+ return fallback ?? "Unknown Python error (failed to stringify exception)." ;
157+ } finally {
158+ cpython.Py_DecRef (exPtr);
159+ // Defensive: formatting can set a new Python error.
160+ cpython.PyErr_Clear ();
170161 }
162+ }
171163
172- // use 'traceback' module to format exception
164+ String ? _formatPythonException (
165+ CPython cpython, Pointer <PyObject > exceptionPtr) {
166+ // Uses `traceback.format_exception(exc)` (Python 3.10+ signature).
173167 final tracebackModuleNamePtr = "traceback" .toNativeUtf8 ();
174- var tracebackModulePtr =
168+ final tracebackModulePtr =
175169 cpython.PyImport_ImportModule (tracebackModuleNamePtr.cast <Char >());
176170 malloc.free (tracebackModuleNamePtr);
171+ if (tracebackModulePtr == nullptr) return null ;
177172
178- if (tracebackModulePtr != nullptr) {
179- //spDebug("Traceback module loaded");
180-
181- final formatFuncName = "format_exception" .toNativeUtf8 ();
182- final pFormatFunc = cpython.PyObject_GetAttrString (
183- tracebackModulePtr, formatFuncName.cast ());
184- malloc.free (formatFuncName);
185-
186- if (pFormatFunc != nullptr && cpython.PyCallable_Check (pFormatFunc) != 0 ) {
187- // call `traceback.format_exception()` method
188- final pArgs = cpython.PyTuple_New (1 );
189- if (pArgs == nullptr) {
190- final fallback = cpython.PyObject_Str (exPtr);
191- if (fallback == nullptr) {
192- cpython.Py_DecRef (pFormatFunc);
193- cpython.Py_DecRef (tracebackModulePtr);
194- cpython.Py_DecRef (exPtr);
195- return "Failed to allocate args to format Python exception." ;
196- }
197- final s = cpython
198- .PyUnicode_AsUTF8 (fallback)
199- .cast <Utf8 >()
200- .toDartString ();
201- cpython.Py_DecRef (fallback);
202- cpython.Py_DecRef (pFormatFunc);
203- cpython.Py_DecRef (tracebackModulePtr);
204- cpython.Py_DecRef (exPtr);
205- return s;
206- }
207- // Keep a reference for fallback error formatting.
208- cpython.Py_IncRef (exPtr);
209- cpython.PyTuple_SetItem (pArgs, 0 , exPtr);
210-
211- // result is a list
212- var listPtr = cpython.PyObject_CallObject (pFormatFunc, pArgs);
213- cpython.Py_DecRef (pArgs);
214- cpython.Py_DecRef (pFormatFunc);
215- cpython.Py_DecRef (tracebackModulePtr);
216-
217- // get and combine list items
218- var exLines = [];
219- if (listPtr == nullptr) {
220- final fallback = cpython.PyObject_Str (exPtr);
221- if (fallback == nullptr) {
222- cpython.Py_DecRef (exPtr);
223- return "Failed to format Python exception." ;
224- }
225- final s = cpython
226- .PyUnicode_AsUTF8 (fallback)
227- .cast <Utf8 >()
228- .toDartString ();
229- cpython.Py_DecRef (fallback);
230- cpython.Py_DecRef (exPtr);
231- return s;
232- }
173+ try {
174+ final formatFuncNamePtr = "format_exception" .toNativeUtf8 ();
175+ final formatFuncPtr = cpython.PyObject_GetAttrString (
176+ tracebackModulePtr, formatFuncNamePtr.cast ());
177+ malloc.free (formatFuncNamePtr);
178+ if (formatFuncPtr == nullptr) return null ;
233179
234- var listSize = cpython.PyList_Size (listPtr);
235- if (listSize < 0 ) {
236- cpython.Py_DecRef (listPtr);
237- final fallback = cpython.PyObject_Str (exPtr);
238- if (fallback == nullptr) {
239- cpython.Py_DecRef (exPtr);
240- return "Failed to format Python exception." ;
241- }
242- final s = cpython
243- .PyUnicode_AsUTF8 (fallback)
244- .cast <Utf8 >()
245- .toDartString ();
246- cpython.Py_DecRef (fallback);
247- cpython.Py_DecRef (exPtr);
248- return s;
249- }
250- for (var i = 0 ; i < listSize; i++ ) {
251- var itemObj = cpython.PyList_GetItem (listPtr, i);
252- var itemObjStr = cpython.PyObject_Str (itemObj);
253- if (itemObjStr == nullptr) {
254- continue ;
255- }
256- final cStr = cpython.PyUnicode_AsUTF8 (itemObjStr);
257- if (cStr == nullptr) {
258- cpython.Py_DecRef (itemObjStr);
259- continue ;
180+ try {
181+ if (cpython.PyCallable_Check (formatFuncPtr) == 0 ) return null ;
182+
183+ final listPtr = cpython.PyObject_CallOneArg (formatFuncPtr, exceptionPtr);
184+ if (listPtr == nullptr) return null ;
185+
186+ try {
187+ final listSize = cpython.PyList_Size (listPtr);
188+ if (listSize < 0 ) return null ;
189+
190+ final buffer = StringBuffer ();
191+ for (var i = 0 ; i < listSize; i++ ) {
192+ final itemObj = cpython.PyList_GetItem (listPtr, i); // borrowed ref
193+ if (itemObj == nullptr) continue ;
194+
195+ final line = _pyUnicodeToDartString (cpython, itemObj) ??
196+ _pyObjectToDartString (cpython, itemObj);
197+ if (line == null ) continue ;
198+ buffer.write (line);
260199 }
261- var s = cStr.cast <Utf8 >().toDartString ();
262- cpython.Py_DecRef (itemObjStr);
263- exLines.add (s);
264- }
265- cpython.Py_DecRef (listPtr);
266- cpython.Py_DecRef (exPtr);
267- return exLines.join ("" );
268- } else {
269- if (pFormatFunc != nullptr) {
270- cpython.Py_DecRef (pFormatFunc);
200+ return buffer.toString ();
201+ } finally {
202+ cpython.Py_DecRef (listPtr);
271203 }
272- cpython.Py_DecRef (tracebackModulePtr);
273- cpython.Py_DecRef (exPtr);
274- return "traceback.format_exception() method not found." ;
204+ } finally {
205+ cpython.Py_DecRef (formatFuncPtr);
275206 }
276- } else {
277- cpython.Py_DecRef (exPtr);
278- return "Error loading traceback module." ;
207+ } finally {
208+ cpython.Py_DecRef (tracebackModulePtr);
209+ }
210+ }
211+
212+ String ? _pyUnicodeToDartString (
213+ CPython cpython, Pointer <PyObject > unicodeObjPtr) {
214+ final cStr = cpython.PyUnicode_AsUTF8 (unicodeObjPtr);
215+ if (cStr == nullptr) return null ;
216+ return cStr.cast <Utf8 >().toDartString ();
217+ }
218+
219+ String ? _pyObjectToDartString (CPython cpython, Pointer <PyObject > objPtr) {
220+ final strObj = cpython.PyObject_Str (objPtr);
221+ if (strObj == nullptr) return null ;
222+ try {
223+ return _pyUnicodeToDartString (cpython, strObj);
224+ } finally {
225+ cpython.Py_DecRef (strObj);
279226 }
280227}
281228
0 commit comments