From 63a6917e67abaebbd678d7f7af327e4be14fc685 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Fri, 14 Mar 2025 10:14:51 -0400 Subject: [PATCH 1/3] REPL: call display on the backend --- stdlib/REPL/src/REPL.jl | 77 ++++++++++++++++++++++++++++------------ stdlib/REPL/test/repl.jl | 2 +- 2 files changed, 56 insertions(+), 23 deletions(-) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index c621fbbb0836e..227107ad5f792 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -120,6 +120,22 @@ mutable struct REPLBackend end REPLBackend() = REPLBackend(Channel(1), Channel(1), false) +# A reference to a backend that is not mutable +struct REPLBackendRef + repl_channel::Channel{Any} + response_channel::Channel{Any} +end +REPLBackendRef(backend::REPLBackend) = REPLBackendRef(backend.repl_channel, backend.response_channel) + +function destroy(ref::REPLBackendRef, state::Task) + if istaskfailed(state) + close(ref.repl_channel, TaskFailedException(state)) + close(ref.response_channel, TaskFailedException(state)) + end + close(ref.repl_channel) + close(ref.response_channel) +end + """ softscope(ex) @@ -418,12 +434,23 @@ function repl_backend_loop(backend::REPLBackend, get_module::Function) while true tls = task_local_storage() tls[:SOURCE_PATH] = nothing - ast, show_value = take!(backend.repl_channel) + ast_or_func, show_value = take!(backend.repl_channel) if show_value == -1 # exit flag break end - eval_user_input(ast, backend, get_module()) + if ast_or_func isa Expr + ast = ast_or_func + eval_user_input(ast, backend, get_module()) + else + f = ast_or_func + try + ret = f() + put!(backend.response_channel, Pair{Any, Bool}(ret, false)) + catch err + put!(backend.response_channel, Pair{Any, Bool}(err, true)) + end + end end return nothing end @@ -526,7 +553,7 @@ function print_response(repl::AbstractREPL, response, show_value::Bool, have_col repl.waserror = response[2] with_repl_linfo(repl) do io io = IOContext(io, :module => Base.active_module(repl)::Module) - print_response(io, response, show_value, have_color, specialdisplay(repl)) + print_response(io, response, backend(repl), show_value, have_color, specialdisplay(repl)) end return nothing end @@ -543,7 +570,7 @@ function repl_display_error(errio::IO, @nospecialize errval) return nothing end -function print_response(errio::IO, response, show_value::Bool, have_color::Bool, specialdisplay::Union{AbstractDisplay,Nothing}=nothing) +function print_response(errio::IO, response, backend::Union{REPLBackendRef,Nothing}, show_value::Bool, have_color::Bool, specialdisplay::Union{AbstractDisplay,Nothing}=nothing) Base.sigatomic_begin() val, iserr = response while true @@ -557,9 +584,14 @@ function print_response(errio::IO, response, show_value::Bool, have_color::Bool, if val !== nothing && show_value try if specialdisplay === nothing - Base.invokelatest(display, val) + # display calls may require being run on the main thread + eval_with_backend(backend) do + Base.invokelatest(display, val) + end else - Base.invokelatest(display, specialdisplay, val) + eval_with_backend(backend) do + Base.invokelatest(display, specialdisplay, val) + end end catch println(errio, "Error showing value of type ", typeof(val), ":") @@ -593,21 +625,7 @@ function print_response(errio::IO, response, show_value::Bool, have_color::Bool, nothing end -# A reference to a backend that is not mutable -struct REPLBackendRef - repl_channel::Channel{Any} - response_channel::Channel{Any} -end -REPLBackendRef(backend::REPLBackend) = REPLBackendRef(backend.repl_channel, backend.response_channel) -function destroy(ref::REPLBackendRef, state::Task) - if istaskfailed(state) - close(ref.repl_channel, TaskFailedException(state)) - close(ref.response_channel, TaskFailedException(state)) - end - close(ref.repl_channel) - close(ref.response_channel) -end """ run_repl(repl::AbstractREPL) @@ -1130,10 +1148,25 @@ find_hist_file() = get(ENV, "JULIA_HISTORY", backend(r::AbstractREPL) = r.backendref -function eval_with_backend(ast, backend::REPLBackendRef) - put!(backend.repl_channel, (ast, 1)) + +function eval_with_backend(ast::Expr, backend::REPLBackendRef) + put!(backend.repl_channel, (ast, 1)) # (f, show_value) + return take!(backend.response_channel) # (val, iserr) +end +function eval_with_backend(f, backend::REPLBackendRef) + put!(backend.repl_channel, (f, false)) # (f, show_value) return take!(backend.response_channel) # (val, iserr) end +# if no backend just eval (used by tests) +function eval_with_backend(f, backend::Nothing) + try + ret = f() + return (ret, false) # (val, iserr) + catch err + return (err, true) + end +end + function respond(f, repl, main; pass_empty::Bool = false, suppress_on_semicolon::Bool = true) return function do_respond(s::MIState, buf, ok::Bool) diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index 241464ca48942..d1233ee00da1b 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -925,7 +925,7 @@ function test19864() @eval Base.showerror(io::IO, e::Error19864) = print(io, "correct19864") buf = IOBuffer() fake_response = (Base.ExceptionStack([(exception=Error19864(),backtrace=Ptr{Cvoid}[])]),true) - REPL.print_response(buf, fake_response, false, false, nothing) + REPL.print_response(buf, fake_response, nothing, false, false, nothing) return String(take!(buf)) end @test occursin("correct19864", test19864()) From 72382263c0cfedcd6c19ddb3244a4ed25d300dbf Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Fri, 14 Mar 2025 22:33:29 -0400 Subject: [PATCH 2/3] suggestions --- stdlib/REPL/src/REPL.jl | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 227107ad5f792..377b3c258d8f2 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -439,10 +439,7 @@ function repl_backend_loop(backend::REPLBackend, get_module::Function) # exit flag break end - if ast_or_func isa Expr - ast = ast_or_func - eval_user_input(ast, backend, get_module()) - else + if show_value == 2 # 2 indicates a function to be called f = ast_or_func try ret = f() @@ -450,6 +447,9 @@ function repl_backend_loop(backend::REPLBackend, get_module::Function) catch err put!(backend.response_channel, Pair{Any, Bool}(err, true)) end + else + ast = ast_or_func + eval_user_input(ast, backend, get_module()) end end return nothing @@ -582,20 +582,19 @@ function print_response(errio::IO, response, backend::Union{REPLBackendRef,Nothi repl_display_error(errio, val) else if val !== nothing && show_value - try - if specialdisplay === nothing - # display calls may require being run on the main thread - eval_with_backend(backend) do - Base.invokelatest(display, val) - end - else - eval_with_backend(backend) do - Base.invokelatest(display, specialdisplay, val) - end + val2, iserr = if specialdisplay === nothing + # display calls may require being run on the main thread + eval_with_backend(backend) do + Base.invokelatest(display, val) end - catch + else + eval_with_backend(backend) do + Base.invokelatest(display, specialdisplay, val) + end + end + if iserr println(errio, "Error showing value of type ", typeof(val), ":") - rethrow() + throw(val2) end end end @@ -1154,7 +1153,7 @@ function eval_with_backend(ast::Expr, backend::REPLBackendRef) return take!(backend.response_channel) # (val, iserr) end function eval_with_backend(f, backend::REPLBackendRef) - put!(backend.repl_channel, (f, false)) # (f, show_value) + put!(backend.repl_channel, (f, 2)) # (f, show_value) 2 indicates function (rather than ast) return take!(backend.response_channel) # (val, iserr) end # if no backend just eval (used by tests) From 424a5cedf7b24a69487af191ab44e03905eee9a9 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Sun, 16 Mar 2025 22:00:19 -0400 Subject: [PATCH 3/3] fix `backend(r::AbstractREPL)` fir REPLs with no backend --- stdlib/REPL/src/REPL.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 377b3c258d8f2..ae2fec4ee10e0 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -1145,7 +1145,7 @@ find_hist_file() = get(ENV, "JULIA_HISTORY", !isempty(DEPOT_PATH) ? joinpath(DEPOT_PATH[1], "logs", "repl_history.jl") : error("DEPOT_PATH is empty and ENV[\"JULIA_HISTORY\"] not set.")) -backend(r::AbstractREPL) = r.backendref +backend(r::AbstractREPL) = hasproperty(r, :backendref) ? r.backendref : nothing function eval_with_backend(ast::Expr, backend::REPLBackendRef)