Skip to content

Commit 83c04f1

Browse files
[inspect] Add dedicated inspector overload for exceptions
1 parent 7586c3c commit 83c04f1

File tree

3 files changed

+82
-5
lines changed

3 files changed

+82
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* [#318](https://github.com/clojure-emacs/orchard/pull/318): **BREAKING:** Remove no longer used functions: `orchard.misc/lazy-seq?`, `orchard.misc/safe-count`, `orchard.misc/normalize-subclass`, `orchard.misc/remove-type-param`.
1010
* [#320](https://github.com/clojure-emacs/orchard/pull/320): Info: recognize printed Java classes/methods and munged Clojure functions in stacktrace outputs.
1111
* [#322](https://github.com/clojure-emacs/orchard/pull/322): Stacktrace: bring back `orchard.stacktrace` for stacktrace analysis (copied from `haystack.analyzer` and improved).
12+
* [#324](https://github.com/clojure-emacs/orchard/pull/324): Add dedicated renderers for exceptions to `orchard.print` and `orchard.inspect`.
1213

1314
## 0.30.1 (2025-02-24)
1415

src/orchard/inspect.clj

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@
6262
(instance? Class obj) :class
6363
(instance? clojure.lang.Namespace obj) :namespace
6464
(instance? clojure.lang.ARef obj) :aref
65+
(instance? Throwable obj) :throwable
66+
(instance? StackTraceElement obj) :stack-trace-element
6567
(.isArray (class obj)) :array
6668
:else (or (:inspector-tag (meta obj))
6769
(type obj))))
@@ -704,6 +706,50 @@
704706
(.toGenericString ^Method %)))
705707
(render-datafy))))
706708

709+
(defn- render-stacktrace-cause [inspector ^Throwable cause]
710+
(as-> inspector ins
711+
(render-indent ins (.getMessage cause))
712+
(render-ln ins)
713+
(render-indent ins)
714+
(render-value ins (class cause))
715+
(if-let [first-frame (first (.getStackTrace cause))]
716+
(-> ins
717+
(render " at ")
718+
(render-value first-frame))
719+
ins)
720+
(render-ln ins)
721+
(if-let [data (not-empty (ex-data cause))]
722+
(-> ins
723+
(indent)
724+
(render-value-maybe-expand data)
725+
(unindent))
726+
ins)))
727+
728+
(defmethod inspect :throwable [inspector ^Throwable obj]
729+
(let [causes (vec (take-while some? (iterate #(.getCause ^Throwable %) obj)))
730+
root-cause ^Throwable (peek causes)]
731+
(as-> inspector ins
732+
(render-labeled-value ins "Class" (class obj))
733+
(render-indent ins "Message: " (.getMessage obj))
734+
(render-ln ins)
735+
(render-section-header ins "Causes")
736+
(indent ins)
737+
(render-stacktrace-cause ins (first causes))
738+
(reduce #(render-stacktrace-cause (render-ln %1) %2) ins (rest causes))
739+
(unindent ins)
740+
(render-section-header ins "Trace")
741+
(indent ins)
742+
(render-items ins (.getStackTrace root-cause) false 0 false)
743+
(unindent ins)
744+
(render-datafy ins))))
745+
746+
(defmethod inspect :stack-trace-element [inspector ^StackTraceElement obj]
747+
(-> inspector
748+
(render-labeled-value "Class" (class obj))
749+
(render-section-header "Contents")
750+
(indent)
751+
(render-items (StackTraceElement->vec obj) false 0 false)))
752+
707753
(defmethod inspect :aref [inspector ^clojure.lang.ARef obj]
708754
(let [val (deref obj)]
709755
(-> (render-class-name inspector obj)

test/orchard/inspect_test.clj

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,13 +1277,17 @@
12771277
(is (match? '("Class: "
12781278
(:value "clojure.lang.ExceptionInfo" 0)
12791279
(:newline)
1280-
"Value: "
1281-
(:value
1282-
"#error {\n :cause \"BOOM\"\n :data {}\n :via\n [{:type clojure.lang.ExceptionInfo\n :message \"BOOM\"\n :data {}}]\n :trace\n []}"
1283-
1)
1280+
"Message: " "BOOM"
12841281
(:newline)
12851282
(:newline))
12861283
(header rendered))))
1284+
(testing "renders a causes section"
1285+
(is (match? '("--- Causes:"
1286+
(:newline)
1287+
" " "BOOM" (:newline)
1288+
" " (:value "clojure.lang.ExceptionInfo" 1) (:newline)
1289+
(:newline))
1290+
(section "Causes" rendered))))
12871291
(testing "renders the datafy section"
12881292
(is (match? (resolve-syms
12891293
(if (> java-api-version 8)
@@ -1321,7 +1325,33 @@
13211325
(:newline)
13221326
" " (:value ":data" number?) " = " (:value "{}" number?)
13231327
(:newline))))
1324-
(datafy-section rendered)))))))
1328+
(datafy-section rendered))))))
1329+
1330+
(testing "exception with multiple causes"
1331+
(let [rendered (-> (ex-info "Outer" {} (RuntimeException. "Inner"))
1332+
inspect render)]
1333+
(is (match? (resolve-syms
1334+
'("--- Causes:"
1335+
(:newline)
1336+
" " "Outer" (:newline)
1337+
" " (:value "clojure.lang.ExceptionInfo" number?) " at "
1338+
(:value #"orchard.inspect_test\$fn" number?) (:newline)
1339+
(:newline)
1340+
" " "Inner" (:newline) " " (:value "java.lang.RuntimeException" number?) " at "
1341+
(:value #"orchard.inspect_test\$fn" number?) (:newline)
1342+
(:newline)))
1343+
(section "Causes" rendered)))
1344+
(testing "trace is rendered"
1345+
(is (match? (matchers/prefix
1346+
(resolve-syms
1347+
'("--- Trace:"
1348+
(:newline)
1349+
" " " 0" ". " (:value #"orchard.inspect_test\$fn" number?) (:newline)
1350+
" " " 1" ". " (:value #"orchard.inspect_test\$fn" number?) (:newline)
1351+
" " " 2" ". " (:value string? number?) (:newline)
1352+
" " " 3" ". " (:value string? number?) (:newline)
1353+
" " " 4" ". " (:value string? number?) (:newline))))
1354+
(section "Trace" rendered)))))))
13251355

13261356
(deftest inspect-eduction-test
13271357
(testing "inspecting eduction shows its object fields"

0 commit comments

Comments
 (0)