Skip to content

Commit 992d673

Browse files
IanButterworthDilumAluthge
authored andcommitted
Fix startup when history file is bad (#59418)
(cherry picked from commit 6c02a21)
1 parent 8702c18 commit 992d673

File tree

4 files changed

+90
-4
lines changed

4 files changed

+90
-4
lines changed

stdlib/REPL/src/REPL.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -994,7 +994,7 @@ find_hist_file() = get(ENV, "JULIA_HISTORY",
994994
!isempty(DEPOT_PATH) ? joinpath(DEPOT_PATH[1], "logs", "repl_history.jl") :
995995
error("DEPOT_PATH is empty and and ENV[\"JULIA_HISTORY\"] not set."))
996996

997-
backend(r::AbstractREPL) = hasproperty(r, :backendref) ? r.backendref : nothing
997+
backend(r::AbstractREPL) = hasproperty(r, :backendref) && isdefined(r, :backendref) ? r.backendref : nothing
998998

999999

10001000
function eval_on_backend(ast, backend::REPLBackendRef)

stdlib/REPL/src/Terminals.jl

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,18 @@ if Sys.iswindows()
126126
is_precompiling[] && return true
127127
check_open(t.in_stream)
128128
if Base.ispty(t.in_stream)
129-
run((raw ? `stty raw -echo onlcr -ocrnl opost` : `stty sane`),
130-
t.in_stream, t.out_stream, t.err_stream)
131-
true
129+
try
130+
run((raw ? `stty raw -echo onlcr -ocrnl opost` : `stty sane`),
131+
t.in_stream, t.out_stream, t.err_stream)
132+
true
133+
catch ex
134+
# Fall back to ccall if stty fails (e.g., in some CI environments)
135+
if ex isa ProcessFailedException
136+
ccall(:jl_tty_set_mode, Int32, (Ptr{Cvoid},Int32), t.in_stream.handle::Ptr{Cvoid}, raw) == 0
137+
else
138+
rethrow()
139+
end
140+
end
132141
else
133142
ccall(:jl_tty_set_mode, Int32, (Ptr{Cvoid},Int32), t.in_stream.handle::Ptr{Cvoid}, raw) != -1
134143
end
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# This file is a part of Julia. License is MIT: https://julialang.org/license
2+
3+
# Test that interactive mode starts up without error when history file is bad
4+
5+
using Test
6+
7+
const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test")
8+
isdefined(Main, :FakePTYs) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "FakePTYs.jl"))
9+
import .Main.FakePTYs: with_fake_pty
10+
11+
@testset "Bad history file startup" begin
12+
mktempdir() do tmpdir
13+
# Create a bad history file
14+
hist_file = joinpath(tmpdir, "repl_history.jl")
15+
write(hist_file, "{ invalid json content\nmore bad content\n")
16+
17+
julia_exe = Base.julia_cmd()[1]
18+
19+
# Test interactive Julia startup with bad history file
20+
with_fake_pty() do pts, ptm
21+
# Set up environment with our bad history file
22+
nENV = copy(ENV)
23+
nENV["JULIA_HISTORY"] = hist_file
24+
25+
# Start Julia in interactive mode
26+
p = run(detach(setenv(`$julia_exe --startup-file=no --color=no -q`, nENV)), pts, pts, pts, wait=false)
27+
Base.close_stdio(pts)
28+
29+
# Read output until we get the prompt, which indicates successful startup
30+
output = readuntil(ptm, "julia> ", keep=true)
31+
# println("====== subprocess output ======")
32+
# println(output)
33+
# println("====== end subprocess output ======")
34+
35+
# Test conditions:
36+
# 1. We should see the invalid history file error
37+
has_history_error = occursin("Invalid history file", output) ||
38+
occursin("Invalid character", output)
39+
@test has_history_error
40+
41+
# 2. We should NOT see UndefRefError (the bug being fixed)
42+
has_undef_error = occursin("UndefRefError", output)
43+
@test !has_undef_error
44+
45+
# 3. We should see the "Disabling history file" message if the fix works
46+
has_disable_message = occursin("Disabling history file for this session", output)
47+
@test has_disable_message
48+
49+
# Send exit command to clean shutdown
50+
if isopen(ptm)
51+
write(ptm, "exit()\n")
52+
else
53+
@warn "PTY master is already closed before sending exit command"
54+
end
55+
56+
# Read any remaining output until the process exits
57+
try
58+
read(ptm, String)
59+
catch ex
60+
# Handle platform-specific EOF behavior
61+
if ex isa Base.IOError && ex.code == Base.UV_EIO
62+
# This is expected on some platforms (e.g., Linux)
63+
else
64+
rethrow()
65+
end
66+
end
67+
68+
# Wait for process to finish
69+
wait(p)
70+
71+
@test p.exitcode == 0
72+
end
73+
end
74+
end

stdlib/REPL/test/runtests.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ end
2222
module TerminalMenusTest
2323
include("TerminalMenus/runtests.jl")
2424
end
25+
module BadHistoryStartupTest
26+
include("bad_history_startup.jl")
27+
end
2528

2629
# Restore the original environment
2730
for k in keys(ENV)

0 commit comments

Comments
 (0)