hmm, I am trying to use expound with instrument
in the browser, by setting cljs.spec.alpha/*explain-out*
, and the printer never seems to be called
I see uncaught exceptinos in the console, eg:
i added this:
(set! s/*explain-out* (fn [& args]
(prn :expound-printer! args)
(apply expound/printer args)))
and that fn is never calledis there some other setup needed?
I have this in my init function:
(set! s/*explain-out* expound/printer)
so, something like this should work?:
(set! s/*explain-out* expound/printer)
(defn add [x y] (+ x y))
(s/fdef add :args (s/coll-of number?))
(st/instrument `add)
(add "hello" "there")
is fdef
supposed to call explain when it throws an exception?
or does one catch exceptions and handle them manually somehow
I think that should work yes.
are you calling this from your REPL or from app code?
it’s in app code
i’ve never actually got this working so i am not sure what the expected behaviour is
is a nice string supposed to print to the console, or be contained somewhere in the exception that is thrown?
printed to console
I think…
let me try in my own app
hah, mine also doesn’t print anymore
what cljs version and expound version?
clj 1.10.0
, cljs 1.10.520
, expound 0.7.2
same here
related to this? https://github.com/clojure/clojurescript/commit/5f0fabc65ae7ba201b32cc513a1e5931a80a2bf7#diff-c62a39b0b4b1d81ebd2ed6b5d265e8cf
well, according to this matrix it should in theory work: https://github.com/bhb/expound/blob/master/doc/compatibility.md
I’ll be following here to see if Ben has a solution. I didn’t even know my own printer was broken 🙂
it does seem to be configured correctly when I type cljs.spec.alpha._STAR_explain_out_STAR_
in the browser console
i wonder exactly where/when the message is supposed to be produced
ie. where the call to explain-out
was moved
The call to explain out is now in the code that handles errors in the REPL
This was a change that occurred in the recent changes to how Clojure(script) handles errors
If you throw a spec error in the REPL, the error handler will call explain-out. But outside of the REPL there is no such handler.
so the tooling that handles hot reloading should do this ..?
not sure…
but this is quite useless to me, since I don’t even use a REPL for my browser dev
do you know the reason why this was done?
As to why it was done, I won’t be able to give an accurate summary of https://dev.clojure.org/jira/browse/CLJ-2373 , but AIUI, the gist was that they wanted errors to contain data, leave printing to the edges
edges of the system
that means that in a CLJS app, I would expect some layer (the app, or the reloader, etc) to catch errors and print them with a configured printer … but that’s quite vague I realize 🙂 . I don’t have psuedocode handy
ok. I think it’s possible to install some global error handler which does this. I mean, sentry is also able to do this, so it must be possible 🙂
Yeah, in principle, it should be doable, but I guess global error handlers are tricky in JS from my underestanding
I wonder how ghostwheel does it?
The other option is essentially to patch the way errors are thrown to put the string back in the error message itself. I don’t know if orchestra has an option for this
but basically you could change instrument to say “no, don’t include the raw data, include the formatted string”
window.onerror probably 🙂
I don’t like patching vanilla spec
FWIW, here is the code in the REPL that calls explain-out
https://github.com/clojure/clojurescript/blob/4dfe78719bee33d44a058d9549ab98cde9e0b8fb/src/main/cljs/cljs/repl.cljs#L223
Oh, haha, I missed this. Yes, exactly this 🙂
cool. I don’t have time right now, but it’s worth finding out what can be done in a user app and document this some time
Totally agree. I should add this to the expound documentation because it’s not obvious. Global handler examples for both Clojure and Clojurescript would be useful, but in the meantime, at least explaining why the behavior seems odd would be good
@mhuebert Would it be possible to wrap this code in a try/catch?
I realize that’s not always viable, depending on where this code is called
(set! s/*explain-out* expound/printer)
(defn error-handler [message url line column e]
(js/console.error e)
(print (repl/error->str e))
true)
(j/assoc! js/window :onerror error-handler)
^this appears to work for uncaught errors
ie. set the global window.onerror
callback to something that wraps the repl utility
that link to the repl code was useful
This already does something:
(js/window.addEventListener "error" (fn [ev] (println "message" (.-message ev))))
but I’m getting "Uncaught #error {…}"
as a string which isn’t very helpful yet 🙂back to work now… thanks for now
If you want to create an issue in expound to update the docs at least and include whatever snippet works for you (and which browser you use), I can include some ideas in the FAQ
only issue i have is that shadow’s reload has its own try/catch, separate issue to figure out
Yeah, I imagine setting a global handler is a bit tricky to do reliably across browsers so I might just include some snippets instead of trying to include handler in expound. We’ll see
k i will make an issue
i wonder if cljs-devtools already prints errors using those utils
one could also
(extend-type ExceptionInfo
IPrintWithWriter
(-pr-writer [o writer opts]
(-write writer (repl/error->str o))))
then if you are catching errors yourself, printing them would always use the repl utils. though maybe sometimes you want the default representation.
a custom formatter seems like the way to go
i didn’t realize how easy they are to set up, i thought they required using a chrome extension
(ns app.core
(:require [cljs.repl :as repl]))
(def devtools-error-formatter
"Uses cljs.repl utilities to format ExceptionInfo objects in Chrome devtools console."
#js{:header
(fn [object config]
(when (instance? ExceptionInfo object)
(let [message (some->> (repl/error->str object)
(re-find #"[^\n]+"))]
#js["span" message])))
:hasBody (constantly true)
:body (fn [object config]
#js["div" (repl/error->str object)])})
(defonce _
(some-> js/window.devtoolsFormatters
(.unshift devtools-error-formatter)))
Awesome, thanks for looking into this!!