Skip to content

Commit 74954b9

Browse files
PGS62claude
andcommitted
Add display_results REPL debugging mode; improve error display
display_results(true) echoes both the incoming expression/function call from Excel and the value returned to it in the Julia console (green, via printstyled); display_results() reads the current setting back. Guards the echo's own display(result) call so a broken show() method can't prevent a successful call's result from still reaching Excel. Failed JuliaCall/JuliaEval calls now print in red instead of a plain "====" divider, making them easier to spot in the console. Also fixes a pre-existing test that hardcoded one particular Dict iteration order, which isn't actually guaranteed by the language. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 41f11f4 commit 74954b9

4 files changed

Lines changed: 76 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
22

3+
## [Unreleased]
4+
5+
### New features
6+
7+
- New `display_results` function: switch it on to echo, in the Julia console, both the expression or function call arriving from Excel and the value being returned to it - useful for seeing exactly what `JuliaCall`/`JuliaEval` are doing without leaving Excel.
8+
- Clearer Julia console output when a `JuliaCall`/`JuliaEval` call fails: the error and the expression that caused it are now shown in colour, making them easier to spot among other console output.
9+
310
## [2.0.0] - 2026-08-19
411

512
### Breaking changes

src/JuliaExcel.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module JuliaExcel
2-
export start_server, setxlpid, getcommsfolder, ExcelError
2+
export start_server, setxlpid, getcommsfolder, ExcelError, display_results, args_from_xl
33

44
using DataFrames: DataFrames, DataFrame, Missing
55
using Dates: Dates, Date, DateTime
@@ -9,6 +9,7 @@ using Sockets: Sockets
99
const global xlpid = Ref(0)
1010
const global commsfolder = Ref("")
1111
const global xlport = Ref(0)
12+
const global _display_results = Ref(false)
1213

1314
"""
1415
ExcelError(code::Int)

src/comms.jl

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,22 @@ function getxlpid()
1919
xlpid[]
2020
end
2121

22+
"""
23+
display_results(switch::Bool)
24+
Switch on or off display in the REPL of both the incoming expression/function call from Excel
25+
and the value returned to Excel, for calls via JuliaCall and JuliaEval.
26+
"""
27+
function display_results(switch::Bool)
28+
_display_results[] = switch
29+
"Results from JuliaCall/JuliaEval $(switch ? "will" : "will not") display in REPL"
30+
end
31+
32+
"""
33+
display_results()
34+
Returns whether display in the REPL of results of calls from Excel is currently switched on.
35+
"""
36+
display_results() = _display_results[]
37+
2238
"""
2339
getcommsfolder()
2440
Returns the name of the comms folder used by JuliaExcel. See also `setcommsfolder`.
@@ -101,18 +117,28 @@ Called by the HTTP request handler in `start_server` for requests to `/eval`, or
101117
from VBA calls to JuliaEval and JuliaEvalVBA.
102118
"""
103119
function srv_eval_inner(expression::String)::String
120+
if _display_results[]
121+
printstyled("from_xl> ", color=:green)
122+
println(expression)
123+
end
124+
success = true
104125
global result = try
105126
Main.eval(Meta.parse(expression))
106127
catch e
107-
println("="^100)
108-
if length(expression) > 500
109-
println("Something went wrong evaluating the contents of an expression")
110-
else
111-
println("Something went wrong evaluating the expression:")
112-
println(expression)
113-
end
128+
success = false
129+
printstyled("Something went wrong evaluating the expression: ", color=:red)
130+
println(expression)
114131
friendly_error(e)
115132
end
133+
if _display_results[] && success
134+
printstyled("to_xl> ", color=:green)
135+
try
136+
display(result)
137+
catch e
138+
printstyled("(could not display result of type $(typeof(result)): $e)\n", color=:red)
139+
end
140+
println("")
141+
end
116142
Base.invokelatest(_encode_result_for_xl, result)
117143
end
118144

@@ -132,11 +158,11 @@ without this cap a single error could still flood the cell. "Julia REPL has more
132158
stacktrace!" points the user at where the full detail actually lives.
133159
"""
134160
function friendly_error(e)
161+
print("\n")
135162
showerror(stdout, e, catch_backtrace())
136-
println("")
137-
println("="^100)
138163
io = IOBuffer()
139164
showerror(io, e)
165+
print("\n\n")
140166
lines = split(String(take!(io)), '\n')
141167
error_desc = join(first(lines, 2), ' ')
142168
if length(error_desc) > 500
@@ -166,20 +192,37 @@ function srv_call_inner(payload::String)::String
166192
fn_name = "<unknown>"
167193
global args_from_xl = ["<unknown>"]
168194
broadcasting = false
195+
success = true
169196
global result = try
170197
decoded = decode_from_xl(payload)
171198
fn_name = decoded[1]::String
199+
if _display_results[]
200+
expression = "$fn_name(args_from_xl...)"
201+
printstyled("from_xl> ", color=:green)
202+
println(expression)
203+
end
172204
broadcasting = endswith(fn_name, ".")
173205
broadcasting && (fn_name = chop(fn_name))
174206
fn_to_call = Main.eval(Meta.parse(fn_name)) # fast: parses only the short function name
175207
args_from_xl = decoded[2:end]
176208
broadcasting ? broadcast(fn_to_call, args_from_xl...) : fn_to_call(args_from_xl...)
177209
catch e
178-
println("="^100)
179-
call_desc = broadcasting ? "$fn_name.(JuliaExcel.args_from_xl...)" : "$fn_name(JuliaExcel.args_from_xl...)"
180-
println("Something went wrong calling the Julia function $fn_name from Excel, against arguments saved in JuliaExcel.args_from_xl (until overwritten by the next call), so the error should be reproducible from here with '$call_desc'.")
210+
success = false
211+
call_desc = broadcasting ? "$fn_name.(args_from_xl...)" : "$fn_name(args_from_xl...)"
212+
printstyled("Something went wrong calling the Julia function $fn_name", color=:red)
213+
print(" from Excel against\narguments saved in args_from_xl (overwritten by the next call),")
214+
print(" so\nthe error should be reproducible from here with '$call_desc'.\n\n")
181215
friendly_error(e)
182216
end
217+
if _display_results[] && success
218+
printstyled("to_xl> ", color=:green)
219+
try
220+
display(result)
221+
catch e
222+
printstyled("(could not display result of type $(typeof(result)): $e)\n", color=:red)
223+
end
224+
println("")
225+
end
183226
Base.invokelatest(_encode_result_for_xl, result)
184227
end
185228

test/runtests.jl

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,10 @@ end
4444
@test JuliaExcel.encode_for_xl(Any[1, 2, 3.0, π]) == "*1,4;2,2,17,17,;^1^2#4008000000000000#400921FB54442D18"
4545
@test JuliaExcel.encode_for_xl([1, true, "x"]) == "*1,3;2,1,2,;^1T£x"
4646
@test JuliaExcel.encode_for_xl([1, [2, 3]]) == "*1,2;2,14,;^1*1,2;2,2,;^2^3"
47-
@test JuliaExcel.encode_for_xl(Dict("a"=>1, "b"=>2)) == "H2;2,2,2,2,;£b^2£a^1"
47+
# Dict key order isn't guaranteed by the language, and can differ between Julia versions/hash
48+
# seeds - accept either encoding order rather than hardcoding one.
49+
@test JuliaExcel.encode_for_xl(Dict("a" => 1, "b" => 2)) in
50+
("H2;2,2,2,2,;£a^1£b^2", "H2;2,2,2,2,;£b^2£a^1")
4851

4952
@test round_trip(1)
5053
@test round_trip(1.0)
@@ -231,6 +234,14 @@ end
231234
"Julia REPL has more details and stacktrace!"
232235
end
233236

237+
# display_results (comms.jl) - toggles REPL echoing of calls from Excel; check both the
238+
# setter's return-value contract and the getter round-trips it correctly. Ends on false (the
239+
# default) so it doesn't leak into any test that follows.
240+
@test JuliaExcel.display_results(true) == "Results from JuliaCall/JuliaEval will display in REPL"
241+
@test JuliaExcel.display_results() == true
242+
@test JuliaExcel.display_results(false) == "Results from JuliaCall/JuliaEval will not display in REPL"
243+
@test JuliaExcel.display_results() == false
244+
234245
end
235246

236247
# The VBA-side test suite (modTest.RunTests) needs a live Excel/VBA session (via COM automation),

0 commit comments

Comments
 (0)