@@ -263,7 +263,18 @@ func (g *pyGen) genFuncBody(sym *symbol, fsym *Func) {
263263
264264 // release GIL
265265 g .gofile .Printf ("_saved_thread := C.PyEval_SaveThread()\n " )
266- if ! rvIsErr && nres != 2 {
266+ // Use defer only when there is no go2py return conversion that calls
267+ // Python C API (which requires the GIL to already be reacquired).
268+ // For go2py returns (excluding handle types) and error/multi-return cases,
269+ // we restore explicitly. Handle types use handleFromPtr which is pure Go
270+ // and doesn't need the GIL, so they can keep using defer.
271+ nresForDefer := nres
272+ // hasRetCvt fires when go2py != "" and NOT (hasHandle && !isPtrOrIface).
273+ // Suppress defer in exactly those cases so the explicit restore below is the only one.
274+ if nres > 0 && res [0 ].sym .go2py != "" && ! (res [0 ].sym .hasHandle () && ! res [0 ].sym .isPtrOrIface ()) {
275+ nresForDefer = 2 // treat like nres==2: no defer, explicit restore
276+ }
277+ if ! rvIsErr && nresForDefer != 2 {
267278 // reacquire GIL after return
268279 g .gofile .Printf ("defer C.PyEval_RestoreThread(_saved_thread)\n " )
269280 }
@@ -357,8 +368,8 @@ if __err != nil {
357368 g .pywrap .Printf ("_%s.%s(" , pkgname , mnm )
358369 }
359370
360- hasRetCvt := false
361371 hasAddrOfTmp := false
372+ hasRetCvt := false
362373 if nres > 0 {
363374 ret := res [0 ]
364375 switch {
@@ -370,8 +381,10 @@ if __err != nil {
370381 hasAddrOfTmp = true
371382 g .gofile .Printf ("cret := " )
372383 case ret .sym .go2py != "" :
384+ // Assign to cret first; we'll restore the GIL before calling go2py
385+ // so that Python C API functions inside go2py run with the GIL held.
373386 hasRetCvt = true
374- g .gofile .Printf ("return %s(" , ret . sym . go2py )
387+ g .gofile .Printf ("cret := " )
375388 default :
376389 g .gofile .Printf ("return " )
377390 }
@@ -394,11 +407,6 @@ if __err != nil {
394407 } else {
395408 funCall = fmt .Sprintf ("%s(%s)" , fsym .GoFmt (), strings .Join (callArgs , ", " ))
396409 }
397- if hasRetCvt {
398- ret := res [0 ]
399- funCall += fmt .Sprintf (")%s" , ret .sym .go2pyParenEx )
400- }
401-
402410 if nres == 0 {
403411 g .gofile .Printf ("if boolPyToGo(goRun) {\n " )
404412 g .gofile .Indent ()
@@ -413,6 +421,13 @@ if __err != nil {
413421 g .gofile .Printf ("%s\n " , funCall )
414422 }
415423
424+ if hasRetCvt {
425+ // Restore GIL before calling go2py, which may call Python C API.
426+ ret := res [0 ]
427+ g .gofile .Printf ("C.PyEval_RestoreThread(_saved_thread)\n " )
428+ g .gofile .Printf ("return %s(cret)%s\n " , ret .sym .go2py , ret .sym .go2pyParenEx )
429+ }
430+
416431 if rvIsErr || nres == 2 {
417432 g .gofile .Printf ("\n " )
418433 // reacquire GIL
0 commit comments