From 235430dcfb55bca5d8f21f039f95c9cfaf4b6a84 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Wed, 27 Aug 2025 16:15:09 -0400 Subject: [PATCH 1/6] Fix startup when history file is bad --- 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 78cd049905825..c3554582b6b85 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -1183,7 +1183,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) = hasproperty(r, :backendref) ? r.backendref : nothing +backend(r::AbstractREPL) = hasproperty(r, :backendref) && isdefined(r, :backendref) ? r.backendref : nothing function eval_on_backend(ast, backend::REPLBackendRef) From fec6bf826e140a51226dc8d379f71d4504c38cb7 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 28 Aug 2025 14:35:58 -0400 Subject: [PATCH 2/6] add test --- stdlib/REPL/test/bad_history_startup.jl | 67 +++++++++++++++++++++++++ stdlib/REPL/test/runtests.jl | 3 ++ 2 files changed, 70 insertions(+) create mode 100644 stdlib/REPL/test/bad_history_startup.jl diff --git a/stdlib/REPL/test/bad_history_startup.jl b/stdlib/REPL/test/bad_history_startup.jl new file mode 100644 index 0000000000000..162cfd9608d32 --- /dev/null +++ b/stdlib/REPL/test/bad_history_startup.jl @@ -0,0 +1,67 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Test that interactive mode starts up without error when history file is bad + +using Test + +const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") +isdefined(Main, :FakePTYs) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "FakePTYs.jl")) +import .Main.FakePTYs: with_fake_pty + +@testset "Bad history file startup" begin + mktempdir() do tmpdir + # Create a bad history file + hist_file = joinpath(tmpdir, "repl_history.jl") + write(hist_file, "{ invalid json content\nmore bad content\n") + + julia_exe = Base.julia_cmd()[1] + + # Test interactive Julia startup with bad history file + with_fake_pty() do pts, ptm + # Set up environment with our bad history file + nENV = copy(ENV) + nENV["JULIA_HISTORY"] = hist_file + + # Start Julia in interactive mode + p = run(detach(setenv(`$julia_exe --startup-file=no --color=no -q`, nENV)), pts, pts, pts, wait=false) + Base.close_stdio(pts) + + # Read output until we get the prompt, which indicates successful startup + output = readuntil(ptm, "julia> ", keep=true) + + # Test conditions: + # 1. We should see the invalid history file error + has_history_error = occursin("Invalid history file", output) || + occursin("Invalid character", output) + @test has_history_error + + # 2. We should NOT see UndefRefError (the bug being fixed) + has_undef_error = occursin("UndefRefError", output) + @test !has_undef_error + + # 3. We should see the "Disabling history file" message if the fix works + has_disable_message = occursin("Disabling history file for this session", output) + @test has_disable_message + + # Send exit command to clean shutdown + write(ptm, "exit()\n") + + # Read any remaining output until the process exits + try + read(ptm, String) + catch ex + # Handle platform-specific EOF behavior + if ex isa Base.IOError && ex.code == Base.UV_EIO + # This is expected on some platforms (e.g., Linux) + else + rethrow() + end + end + + # Wait for process to finish + wait(p) + + @test p.exitcode == 0 + end + end +end diff --git a/stdlib/REPL/test/runtests.jl b/stdlib/REPL/test/runtests.jl index d3eb6b9964981..9f3727485f81a 100644 --- a/stdlib/REPL/test/runtests.jl +++ b/stdlib/REPL/test/runtests.jl @@ -22,6 +22,9 @@ end module TerminalMenusTest include("TerminalMenus/runtests.jl") end +module BadHistoryStartupTest + include("bad_history_startup.jl") +end # Restore the original environment for k in keys(ENV) From 3dbc3522d9e550d5e4e66718a8c7820a478b9ea9 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Sun, 31 Aug 2025 16:55:47 -0400 Subject: [PATCH 3/6] debug windows failure --- stdlib/REPL/test/bad_history_startup.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/stdlib/REPL/test/bad_history_startup.jl b/stdlib/REPL/test/bad_history_startup.jl index 162cfd9608d32..2642a6733b0f7 100644 --- a/stdlib/REPL/test/bad_history_startup.jl +++ b/stdlib/REPL/test/bad_history_startup.jl @@ -44,7 +44,11 @@ import .Main.FakePTYs: with_fake_pty @test has_disable_message # Send exit command to clean shutdown - write(ptm, "exit()\n") + if isopen(ptm) + write(ptm, "exit()\n") + else + @warn "PTY master is already closed before sending exit command" + end # Read any remaining output until the process exits try From a2fc5b09d94b1fef6a7edf7afc3cb59f5149208f Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 2 Sep 2025 12:31:49 -0400 Subject: [PATCH 4/6] more debug --- stdlib/REPL/test/bad_history_startup.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stdlib/REPL/test/bad_history_startup.jl b/stdlib/REPL/test/bad_history_startup.jl index 2642a6733b0f7..7f08c8fbf9c83 100644 --- a/stdlib/REPL/test/bad_history_startup.jl +++ b/stdlib/REPL/test/bad_history_startup.jl @@ -28,6 +28,9 @@ import .Main.FakePTYs: with_fake_pty # Read output until we get the prompt, which indicates successful startup output = readuntil(ptm, "julia> ", keep=true) + println("====== subprocess output ======") + println(output) + println("====== end subprocess output ======") # Test conditions: # 1. We should see the invalid history file error From 9a606a37e01a4f5154e1ea5904ed0499e1abafc2 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 2 Sep 2025 23:10:25 -0400 Subject: [PATCH 5/6] windows tryfix --- stdlib/REPL/src/Terminals.jl | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/stdlib/REPL/src/Terminals.jl b/stdlib/REPL/src/Terminals.jl index 14ea6dd3dff77..71a287573ffbf 100644 --- a/stdlib/REPL/src/Terminals.jl +++ b/stdlib/REPL/src/Terminals.jl @@ -123,9 +123,18 @@ cmove_col(t::UnixTerminal, n) = (write(t.out_stream, '\r'); n > 1 && cmove_right if Sys.iswindows() function raw!(t::TTYTerminal,raw::Bool) if Base.ispty(t.in_stream) - run((raw ? `stty raw -echo onlcr -ocrnl opost` : `stty sane`), - t.in_stream, t.out_stream, t.err_stream) - true + try + run((raw ? `stty raw -echo onlcr -ocrnl opost` : `stty sane`), + t.in_stream, t.out_stream, t.err_stream) + true + catch ex + # Fall back to ccall if stty fails (e.g., in some CI environments) + if ex isa ProcessFailedException + ccall(:jl_tty_set_mode, Int32, (Ptr{Cvoid},Int32), t.in_stream.handle::Ptr{Cvoid}, raw) == 0 + else + rethrow() + end + end else ccall(:jl_tty_set_mode, Int32, (Ptr{Cvoid},Int32), t.in_stream.handle::Ptr{Cvoid}, raw) == 0 end From fc0a9b1796b77456854ed58b05502242e1e867b8 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Wed, 3 Sep 2025 07:07:19 -0400 Subject: [PATCH 6/6] remove debug --- stdlib/REPL/test/bad_history_startup.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/REPL/test/bad_history_startup.jl b/stdlib/REPL/test/bad_history_startup.jl index 7f08c8fbf9c83..09f7a00e1c31a 100644 --- a/stdlib/REPL/test/bad_history_startup.jl +++ b/stdlib/REPL/test/bad_history_startup.jl @@ -28,9 +28,9 @@ import .Main.FakePTYs: with_fake_pty # Read output until we get the prompt, which indicates successful startup output = readuntil(ptm, "julia> ", keep=true) - println("====== subprocess output ======") - println(output) - println("====== end subprocess output ======") + # println("====== subprocess output ======") + # println(output) + # println("====== end subprocess output ======") # Test conditions: # 1. We should see the invalid history file error