+1 for the assertion macro suggestions (custom messages).
On the topic of stack traces: it's high time Clojure stopped
generating shit like
java.lang.RuntimeException: java.lang.RuntimeException:
java.lang.IllegalArgumentException: Don't know how to create ISeq
from: java.lang.Integer (NO_SOURCE_FILE:3545)
at clojure.lang.Compiler.eval(Compiler.java:5440)
at clojure.lang.Compiler.eval(Compiler.java:5415)
at clojure.lang.Compiler.eval(Compiler.java:5391)
at clojure.core$eval.invoke(core.clj:2382)
at com.example.yourns$eval39664.invoke(NO_SOURCE_FILE:354)
blah, blah, blah
at java.lang.Thread.run (Thread.java:616)
Caused by: java.lang.RuntimeException:
java.lang.IllegalArgumentException: Don't know how to create ISeq
from: java.lang.Integer
at clojure.core$foo
at clojure.core$bar
at some.package.SomeJavaClass.quux(SomeJavaClass.java:666)
... 17 more
Caused by: java.lang.IllegalArgumentException: Don't know how to
create ISeq from: java.lang.Integer
at clojure.lang.RT.seqFrom(RT.java:471)
at clojure.lang.RT.seq(RT.java:452)
at clojure.lang.RT.cons(RT.java:534)
at clojure.core$cons.invoke(core.clj:28)
... 14 more
without any of the visible stack trace lines containing any reference
to your own project except for
at com.example.yourns$eval39664.invoke(NO_SOURCE_FILE:354)
which just refers to the REPL expression you tried to evaluate which
test exposed the bug, not the line of source containing the bug
itself.
The crucial lines always seems to start one past the end of what the
last stack trace shows, in this case the very next line quite likely
would have been a line of the project trying to cons onto an integer
(which was probably a bad argument it got from its caller, mind).
I suggest altering the default behavior of the REPL in printing stack
traces to compress all but the LAST in a chain of "Caused by:"
exceptions. Usually the last one is the critical one, not the first.
In the meantime I offer this tool:
(defn get-ultimate-cause [e]
(if-let [c (.getCause e)]
(recur c)
e))
(defn p-relevant [e]
(println (.toString e))
(doseq [elt (.getStackTrace e)]
(let [cn (.getClassName elt)]
(if-not
(or
(.startsWith cn "clojure.")
(.startsWith cn "java."))
(println " " (.toString elt))))))
(defmacro ttry [& body]
`(try
(eval (quote (do ~@body)))
(catch Exception e#
(p-relevant (get-ultimate-cause e#)))))
The eval is so it catches both compiler exceptions (e.g. undefined
symbol) and runtime ones (e.g. ClassCastException passing arg of wrong
type somewhere).
It prints just the parts of the stack trace of the final cause
exception you're probably interested in: the ones outside the various
clojure.foo and java.foo namespaces (so, the ones in your own project,
and maybe Swing methods and such, and third-party library entries).
That gets rid of much of the clutter and shows the various lines
within your own code that were involved in the error.
Incidentally, I uncovered a bug in Clojure 1.2(!) while testing this:
(defmacro ttry [& body]
`(try
(eval (quote (do ~@body)))
(catch Exception e#
(let [c# (loop [e# e#]
(if-let [c# (.getCause e#)]
(recur c#)
; First bug site
e#))]
(println (.toString c#))
(doseq [elt# (.getStackTrace c#)]
(let [cn# (.getClassName elt#)]
(if-not
(or
(.startsWith cn# "clojure.")
(.startsWith cn# "java."))
(println " " (.toString elt#)))))))))
; Second bug site
Both compiler exceptions are
java.lang.UnsupportedOperationException: Cannot recur from catch/finally
But in neither location is there an attempt to "recur FROM
catch/finally"; in the first instance, the recur should go back to a
loop entirely contained within the catch, so not out of the catch, and
in the second instance, I can only presume that the doseq expands into
a loop that would also be entirely contained within the catch.
Things like
(loop [x 256 out []]
(if (> x 0)
(try
(let [y (do-something-dangerous-only-when-x-is-100 x)]
(recur (dec x) (conj out y)))
(catch Foo e (recur 42 out)))
out))
are clearly what're supposed to be verboten here -- and even then I'm
not sure why, as this equivalent Java with a continue in a catch is
legal:
List<Bar> out = new ArrayList<Bar>();
for (x = 256; x > 0; x--) {
try {
Bar y = doSomethingDangerousOnlyWhenXIs100(x);
} catch (Foo e) {
x = 42;
continue;
}
out.add(y);
}
return out;
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en