From 01b4228483b2f48fade9038f970b267380d8ab0f Mon Sep 17 00:00:00 2001 From: Gary Fredericks Date: Sun, 26 Apr 2015 21:03:20 -0500 Subject: [PATCH] CLJ-1716: print ex-data on throwables Also added tests for the existing exception printing behavior, and the new behavior. Signed-off-by: Stuart Halloway --- src/clj/clojure/core_print.clj | 29 ++++++++++++++++++------ test/clojure/test_clojure/errors.clj | 22 ++++++++++++++++++ test/clojure/test_clojure/printer.clj | 32 +++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 7 deletions(-) diff --git a/src/clj/clojure/core_print.clj b/src/clj/clojure/core_print.clj index f78594a8b7..21ae4d8eed 100644 --- a/src/clj/clojure/core_print.clj +++ b/src/clj/clojure/core_print.clj @@ -415,28 +415,43 @@ (defn Throwable->map [^Throwable o] (let [base (fn [^Throwable t] - {:type (class t) - :message (.getLocalizedMessage t) - :at (get (.getStackTrace t) 0)}) + (let [m {:type (class t) + :message (.getLocalizedMessage t) + :at (get (.getStackTrace t) 0)} + data (ex-data t)] + (if data + (assoc m :data data) + m))) via (loop [via [], ^Throwable t o] (if t (recur (conj via t) (.getCause t)) - via))] - {:cause (.getLocalizedMessage ^Throwable (last via)) + via)) + ^Throwable root (peek via) + m {:cause (.getLocalizedMessage root) :via (vec (map base via)) - :trace (vec (.getStackTrace (or ^Throwable (last via) o)))})) + :trace (vec (.getStackTrace ^Throwable (or root o)))} + data (ex-data root)] + (if data + (assoc m :data data) + m))) (defn- print-throwable [^Throwable o ^Writer w] (.write w "#error {\n :cause ") - (let [{:keys [cause via trace]} (Throwable->map o) + (let [{:keys [cause data via trace]} (Throwable->map o) print-via #(do (.write w "{:type ") (print-method (:type %) w) (.write w "\n :message ") (print-method (:message %) w) + (when-let [data (:data %)] + (.write w "\n :data ") + (print-method data w)) (.write w "\n :at ") (print-method (:at %) w) (.write w "}"))] (print-method cause w) + (when data + (.write w "\n :data ") + (print-method data w)) (when via (.write w "\n :via\n [") (when-let [fv (first via)] diff --git a/test/clojure/test_clojure/errors.clj b/test/clojure/test_clojure/errors.clj index 4eb9d2aa1e..ec8561a6ca 100644 --- a/test/clojure/test_clojure/errors.clj +++ b/test/clojure/test_clojure/errors.clj @@ -58,3 +58,25 @@ (catch Throwable t (is (= {:foo 1} (ex-data t))))) (is (nil? (ex-data (RuntimeException. "example non ex-data"))))) + +(deftest Throwable->map-test + (testing "base functionality" + (let [{:keys [cause via trace]} (Throwable->map + (Exception. "I am a string literal"))] + (is (= cause "I am a string literal")) + (is (= 1 (count via))) + (is (vector? via)) + (is (= ["I am a string literal"] (map :message via))))) + (testing "causes" + (let [{:keys [cause via trace]} (Throwable->map + (Exception. "I am not a number" + (Exception. "double two")))] + (is (= cause "double two")) + (is (= ["I am not a number" "double two"] + (map :message via))))) + (testing "ex-data" + (let [{[{:keys [data]}] :via + data-top-level :data} + (Throwable->map (ex-info "ex-info" + {:some "data"}))] + (is (= data data-top-level {:some "data"}))))) diff --git a/test/clojure/test_clojure/printer.clj b/test/clojure/test_clojure/printer.clj index ddaa83194b..30fd22153d 100644 --- a/test/clojure/test_clojure/printer.clj +++ b/test/clojure/test_clojure/printer.clj @@ -119,3 +119,35 @@ #'var-with-meta "#'clojure.test-clojure.printer/var-with-meta" #'var-with-type "#'clojure.test-clojure.printer/var-with-type")) +(defn ^:private ednize-stack-trace-element + [^StackTraceElement ste] + [(symbol (.getClassName ste)) + (symbol (.getMethodName ste)) + (.getFileName ste) + (.getLineNumber ste)]) + +(defn ^:private ednize-throwable-data + [throwable-data] + (-> throwable-data + (update :via (fn [vias] + (map (fn [via] + (-> via + (update :type #(symbol (.getName %))) + (update :at ednize-stack-trace-element))) + vias))) + (update :trace #(map ednize-stack-trace-element %)))) + +(deftest print-throwable + (binding [*data-readers* {'error identity}] + (are [e] (= (-> e Throwable->map ednize-throwable-data) + (-> e pr-str read-string)) + (Exception. "heyo") + (Throwable. "I can a throwable" + (Exception. "chain 1" + (Exception. "chan 2"))) + (ex-info "an ex-info" {:with "its" :data 29}) + (Exception. "outer" + (ex-info "an ex-info" {:with "data"} + (Error. "less outer" + (ex-info "the root" + {:with "even" :more 'data})))))))