Assertions in Clojure applications enabled by default when running uberjar built with leiningen
Clash Royale CLAN TAG#URR8PPP
Assertions in Clojure applications enabled by default when running uberjar built with leiningen
Should assertions be enabled by default when running a Clojure application as a jar?
We use leiningen to build an uberjar and then java -jar ...
to run it and I've just found out that *assert*
is true.
java -jar ...
*assert*
I couldn't find what default value should this dynamic variable hold but I thought it would be false (and only set to true in REPL development environment).
This lead to a nasty issue in combination with future
where a precondition assertion error caused the future thread to die and we didn't know what happened (because we caught only Exception
not Throwable
).
future
Exception
Throwable
I went through the source code of clojure.lang.RT
but I couldn't figure out where is the *assert*
var set to true.
clojure.lang.RT
*assert*
I've also found the option :global-vars
in the leinigen sample project (this is mentioned in How to disable Clojure assertions, including preconditions?).
That looks reasonable but I thought that for "production" build I would get *assert* false
automatically.
:global-vars
*assert* false
EDIT: I missed that little T
thing (boolean true) used to initialize *assert*
variable in RT.java
. However, still curious why that's the default and what is the recommended way to turn off assertions (if it's even recommended).
T
*assert*
RT.java
T
*assert*
RT.java
1 Answer
1
From the source, clojure.core/assert
is a macro that evaluates *assert*
at compile time:
clojure.core/assert
*assert*
(defmacro assert
"Evaluates expr and throws an exception if it does not evaluate to
logical true."
:added "1.0"
([x]
(when *assert*
`(when-not ~x
(throw (new AssertionError (str "Assert failed: " (pr-str '~x)))))))
([x message]
(when *assert*
`(when-not ~x
(throw (new AssertionError (str "Assert failed: " ~message "n" (pr-str '~x))))))))
This means that both alter-var-root
and binding
cannot change the behavior of assert
, since they operate at runtime, after assert
has already evaluated *assert*
. Therefore, the :global-vars
option in project.clj
:
alter-var-root
binding
assert
assert
*assert*
:global-vars
project.clj
:global-vars *warn-on-reflection* false
*assert* false
is the only solution that will work, since it is evaluated before macro expansion. With this test code:
(dotest
(spyxx *assert*)
(assert false "Don't do that!")
(println "past the assertion"))
we get result:
----------------------------------
Clojure 1.9.0 Java 10.0.1
----------------------------------
lein test tst.demo.core
*assert* => <#java.lang.Boolean false>
past the assertion
Ran 2 tests containing 0 assertions.
0 failures, 0 errors.
lein test 32.28s user 0.56s system 349% cpu 9.387 total
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Ok, I missed that little
T
thing (boolean true) used to initialize*assert*
variable inRT.java
. However, still curious why that's the default and what is the recommended way to turn off assertions (if it's even recommended).– Juraj Martinka
Aug 10 at 17:58