PythonCall.jl icon indicating copy to clipboard operation
PythonCall.jl copied to clipboard

Stack trace order is inconsistent

Open LilithHafner opened this issue 2 years ago • 2 comments

Is your feature request related to a problem? Please describe. Python stack traces place the most recent call last, Julia's place the most recent call first. When I the call stack has a bunch of Python function then a bunch of Julia functions, the most recent call is in the middle. I don't like that.

Describe the solution you'd like It'd be nice if PythonCall printed Python stack traces in julian order and JuliaCall printed julia stack traces in pythonic order.

Julia's Base could also flip the order all stacktraces are printed in :P

LilithHafner avatar Oct 23 '23 21:10 LilithHafner

In Julia, PythonCall already has custom stacktrace printing so that the Python stack is in the same order as Julia:

julia> @pyexec """
       global pyinner, pyouter
       def pyinner():
           raise ValueError()
       def pyouter():
           pyinner()
       """

julia> jlinner() = @pyeval "pyouter()"
jlinner (generic function with 1 method)

julia> jlouter() = jlinner()
jlouter (generic function with 1 method)

julia> jlouter()
ERROR: Python: ValueError:
Python stacktrace:
 [1] pyinner
   @ REPL[5]:1:3
 [2] pyouter
   @ REPL[5]:1:5
 [3] <module>
   @ REPL[6]:1:1
Stacktrace:
  [1] pythrow()
    @ PythonCall C:\Users\chris\.julia\dev\PythonCall\src\err.jl:94
  [2] errcheck
    @ PythonCall C:\Users\chris\.julia\dev\PythonCall\src\err.jl:10 [inlined]
  [3] pycallargs(f::Py, args::Py)
    @ PythonCall C:\Users\chris\.julia\dev\PythonCall\src\abstract\object.jl:210
  [4] pycall(::Py, ::Py, ::Vararg{Py}; kwargs::@Kwargs{})
    @ PythonCall C:\Users\chris\.julia\dev\PythonCall\src\abstract\object.jl:228
  [5] pycall
    @ PythonCall C:\Users\chris\.julia\dev\PythonCall\src\abstract\object.jl:218 [inlined]
  [6] Py
    @ PythonCall C:\Users\chris\.julia\dev\PythonCall\src\Py.jl:341 [inlined]
  [7] pyeval(::Type{Py}, code::Py, globals::Module, locals::Tuple{})
    @ PythonCall C:\Users\chris\.julia\dev\PythonCall\src\concrete\code.jl:50
  [8] jlinner
    @ Main .\REPL[6]:1 [inlined]
  [9] jlouter()
    @ Main .\REPL[7]:1
 [10] top-level scope
    @ REPL[8]:1

cjdoris avatar Oct 24 '23 08:10 cjdoris

But yes, you're right that from Python, the Julia stack is printed in reverse. This is because custom stack printing has not been implemented on that side, so just uses Base.showerror(io, error, backtrace).

>>> jl.seval("""begin
...     jlinner() = error()
...     jlouter() = jlinner()
... end""")
Julia: jlouter (generic function with 1 method)
>>> def pyinner():
...     jl.seval("jlouter()")
...
>>> def pyouter():
...     pyinner()
...
>>> pyouter()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in pyouter
  File "<stdin>", line 2, in pyinner
  File "C:\Users\chris\.julia\packages\PythonCall\qTEA1\src\jlwrap\module.jl", line 25, in seval
    return self._jl_callmethod($(pyjl_methodnum(pyjlmodule_seval)), expr)
juliacall.JuliaError:
Stacktrace:
 [1] error()
   @ Base .\error.jl:44
 [2] jlinner()
   @ Main .\none:2
 [3] jlouter()
   @ Main .\none:3
 [4] top-level scope
   @ none:1
 [5] eval
   @ .\boot.jl:370 [inlined]
 [6] eval
   @ .\Base.jl:68 [inlined]
 [7] pyjlmodule_seval(self::Module, expr::Py)
   @ PythonCall C:\Users\chris\.julia\packages\PythonCall\qTEA1\src\jlwrap\module.jl:13
 [8] _pyjl_callmethod(f::Any, self_::Ptr{PythonCall.C.PyObject}, args_::Ptr{PythonCall.C.PyObject}, nargs::Int64)
   @ PythonCall C:\Users\chris\.julia\packages\PythonCall\qTEA1\src\jlwrap\base.jl:62
 [9] _pyjl_callmethod(o::Ptr{PythonCall.C.PyObject}, args::Ptr{PythonCall.C.PyObject})
   @ PythonCall.C C:\Users\chris\.julia\packages\PythonCall\qTEA1\src\cpython\jlwrap.jl:47

It would indeed be nice to improve these stack traces.

cjdoris avatar Oct 24 '23 08:10 cjdoris