clojure test that assertion throws - clojure

I have a function defined as:
(defn strict-get
[m key]
{:pre [(is (contains? m key))]}
(get m key))
And then I have a test for it:
(is (thrown? java.lang.AssertionError (strict-get {} :abc)))
However this test fails:
;; FAIL in () (myfile.clj:189)
;; throws exception when key is not present
;; expected: (contains? m key)
;; actual: (not (contains? {} :abc))
What is needed to check that the assertion would throw an error?

The reason your assertion fails because you are nesting two is. The inner is already catches the exception so the outer is test then fails because nothing is thrown.
(defn strict-get
[m key]
{:pre [(contains? m key)]} ;; <-- fix
(get m key))
(is (thrown? java.lang.AssertionError (strict-get {} nil)))
;; does not throw, but returns exception object for reasons idk
(deftest strict-get-test
(is (thrown? java.lang.AssertionError (strict-get {} nil))))
(strict-get-test) ;; passes

Related

Catch multiple exceptions in Clojure and handle them the same

This is a bit similar to this question, but I want to catch multiple exceptions and handle them all the same. In Ruby, I can write
begin
rand(2) == 0 ? ([] + '') : (foo)
rescue TypeError, NameError => e
puts "oops: #{e.message}"
end
Can I do the same in Clojure? For now I let a function and just call it in each catch body.
(ns mastering.stackoverflow
(:use
[slingshot.slingshot :only [try+]]))
(try+
; ...
(catch (comp #{TypeError NameError} class) _ "caught"))
The slingshot library is available on github.
You could also delegate to a local function, although it gets a little verbose:
(let [handle #(println %)]
(try
(throwing-op)
(catch TypeError e (handle e))
(catch NameError e (handle e))))
There is no simple built-in solution for that yet, however, there is an open ticket.
You can write a dispatch on type in the catch block by hand.
(try
(do-dangerous-operation-here)
(catch Exception e
(condp (fn [cs t] (some #(instance? % t) cs)) e
[IllegalStateException IllegalArgumentException]
(println "Either illegal state or illegal argument!")
[java.sql.SQLException]
(println "Sql error!")
;; whe pass through the exception when not handled
(throw e))))
You can also use this macro:
(defmacro try*
"Macro to catch multiple exceptions with one catch body.
Usage:
(try*
(println :a)
(println :b)
(catch* [A B] e (println (class e)))
(catch C e (println :C))
(finally (println :finally-clause)))
Will be expanded to:
(try
(println :a)
(println :b)
(catch A e (println (class e)))
(catch B e (println (class e)))
(catch C e (println :C))
(finally (println :finally-clause)))
"
[& body]
(letfn [(catch*? [form]
(and (seq form)
(= (first form) 'catch*)))
(expand [[_catch* classes & catch-tail]]
(map #(list* 'catch % catch-tail) classes))
(transform [form]
(if (catch*? form)
(expand form)
[form]))]
(cons 'try (mapcat transform body))))
credits https://gist.github.com/Gonzih/5814945

Why call map in macro will fail in my Clojure code?

I want to transfer code from
("AAA" ("BB" 11 #"XXX"))
to
("AAA" ("BB" 11 "YYY"))
I just want to change #"XXX" to "YYY".
I write a function it works.
(defn tt [clause]
(cond (not (sequential? clause)) clause
(and (sequential? clause)
(= 2 (count clause))
(= `deref (first clause))
(string? (second clause)))
"YYY"
:else (map tt clause)))
there is my result:
(tt '("AAA" ("BB" 11 #"XXX"))) --> ("AAA" ("BB" 11 "YYY"))
But when I change the function to macro , it raise exception.
(defmacro test [& clause]
(let [f (fn tt [clause]
(cond (not (sequential? clause)) clause
(and (sequential? clause)
(= 2 (count clause))
(= `deref (first clause))
(string? (second clause)))
"YYY"
:else (map tt clause)))]
(f clause)))
and it raise exception like this
(test "AAA" ("BB" 11 #"XXX")) --> ClassCastException java.lang.String cannot be cast to clojure.lang.IFn
I have test map for prewalk function. Both of them raise exception.
I have no idea what is wrong with it and how to fix this error in macro??

'get' replacement that throws exception on not found?

I'd like to access values in maps and records, throwing an exception when the key isn't present. Here's what I've tried. Is there a better strategy?
This doesn't work because throw is evaluated every time:
(defn get-or-throw-1 [rec key]
(get rec key (throw (Exception. "No value."))))
Maybe there's a simple method using a macro? Well, this isn't it; it has same problem as the first definition, even if the evaluation of throw happens later:
(defmacro get-or-throw-2 [rec key]
`(get ~rec ~key (throw (Exception. "No value."))))
This one works by letting get return a value that (in theory) would never be generated any other way:
(defn get-or-throw-3 [rec key]
(let [not-found-thing :i_WoUlD_nEvEr_NaMe_SoMe_ThInG_tHiS_021138465079313
value (get rec key not-found-thing)]
(if (= value not-found-thing)
(throw (Exception. "No value."))
value)))
I don't like having to guess what keywords or symbols would never occur through other processes. (I could use gensym to generate the special value of not-found-thing, but I don't see why that would be better. I don't have to worry about someone intentionally trying to defeat the purpose of the function by using the value of not-found-thing in a map or record.)
Any suggestions?
This is the sort of thing that preconditions were meant for. They are built in to the language and should be used for input validation (though you can alternately use an assertion if preconditions are not flexible for a specific case).
user> (defn strict-get
[place key]
{:pre [(contains? place key)]}
(get place key))
#'user/strict-get
user> (strict-get {:a 0 :b 1} :a)
0
user> (strict-get {:a 0 :b 1} :c)
AssertionError Assert failed: (contains? place key) user/eval6998/fn--6999/strict-get--7000 (form-init7226451188544039940.clj:1)
This is what the find function is for: (find m k) returns nil if nothing was found, or [k v] if a mapping from k to v was found. You can always distinguish these two, and don't need to guess at what might already be in the map. So you can write:
(defn strict-get [m k]
(if-let [[k v] (find m k)]
v
(throw (Exception. "Just leave me alone!"))))
You can use a namespace-qualified keyword, which reduces the chance of an accidental use of your keyword:
(defn get-or-throw [coll key]
(let [result (get coll key ::not-found)]
(if (= result ::not-found)
(throw (Exception. "No value."))
result)))
Alternatively, you can just use contains?:
(defn get-or-throw [coll key]
(if (contains? coll key)
(get coll key)
(throw (Exception. "No value."))))
This should be safe as your map/record should be immutable.
This function is also implemented in the tupelo library under the name grab. Note that the argument order is reversed here, seemingly intentionally: (grab :my-key my-map).
I prefer the name and implementation from simulant: getx and getx-in
(defn getx
"Like two-argument get, but throws an exception if the key is
not found."
[m k]
(let [e (get m k ::sentinel)]
(if-not (= e ::sentinel)
e
(throw (ex-info "Missing required key" {:map m :key k})))))
(defn getx-in
"Like two-argument get-in, but throws an exception if the key is
not found."
[m ks]
(reduce getx m ks))
https://github.com/Datomic/simulant/blob/d681b2375c3e0ea13a0df3caffeb7b3d8a20c6a3/src/simulant/util.clj#L24-L37

how to raise a exception in clojure pre-expr?

clojure's pre-expr seems cool, but does it possible if I want to raise an Exception when the :pre is false?
thanks.
You can use Dire instead
(ns mytask
(:require [dire.core :refer [with-precondition! with-handler!]]))
(defn add-one [n]
(inc n))
(with-precondition! #'add-one
"An optional docstring."
;;; Name of the precondition
:not-two
(fn [n & args]
(not= n 2)))
(with-handler! #'add-one
{:precondition :not-two}
(fn [e & args] (apply str "Precondition failure for argument list: " (vector args))))
(add-one 2) ; => "Precondition failure for argument list: (2)"
Preconditions are conditions that must be true or else an exception is thrown. If you have a condition where you want an exception to be thrown when false, just complement the conditional or not the result.
user=> (defn magic? [n] (= 0 (rem n 42)))
#'user/magic?
user=> (defn foo [n] {:pre [(magic? n)]} n)
#'user/foo
user=> (foo 42)
42
user=> (defn bar [n] {:pre [(not (magic? n))]} n)
#'user/bar
user=> (bar 42)
AssertionError Assert failed: (not (magic? n)) user/bar
user=> (defn baz [n] {:pre [((complement magic?) n)]} n)
#'user/baz
user=> (baz 42)
AssertionError Assert failed: ((complement magic?) n) user/baz

Clojure: How to to recur upon exception?

I am trying to execute a func several times before giving up upon exceptions.
But it is not valid in Clojure to recur from catch block.
How can this be achieved ?
(loop [tries 10]
(try
(might-throw-exception)
(catch Exception e
(when (pos? tries) (recur (dec tries))))))
java.lang.UnsupportedOperationException: Cannot recur from catch/finally
The best I could find is the following clumsy solution (wrapping in func and calling it)
(defn do-it []
(try
(might-throw-exception)
(catch Exception e nil)))
(loop [times 10]
(when (and (nil? (do-it)) (pos? times))
(recur (dec times))))
Macros are calling...
How about this:
(defn try-times*
"Executes thunk. If an exception is thrown, will retry. At most n retries
are done. If still some exception is thrown it is bubbled upwards in
the call chain."
[n thunk]
(loop [n n]
(if-let [result (try
[(thunk)]
(catch Exception e
(when (zero? n)
(throw e))))]
(result 0)
(recur (dec n)))))
(defmacro try-times
"Executes body. If an exception is thrown, will retry. At most n retries
are done. If still some exception is thrown it is bubbled upwards in
the call chain."
[n & body]
`(try-times* ~n (fn [] ~#body)))
kotarak's idea is the way to go, but this question tickled my fancy so I'd like to provide a riff on the same theme that I prefer because it doesn't use loop/recur:
(defn try-times* [thunk times]
(let [res (first (drop-while #{::fail}
(repeatedly times
#(try (thunk)
(catch Throwable _ ::fail)))))]
(when-not (= ::fail res)
res)))
And leave the try-times macro as it is.
If you want to allow the thunk to return nil, you can drop the let/when pair, and let ::fail represent "the function failed n times", while nil means "the function returned nil". This behavior would be more flexible but less convenient (the caller has to check for ::fail to see if it worked rather than just nil), so perhaps it would be best implemented as an optional second parameter:
(defn try-times* [thunk n & fail-value]
(first (drop-while #{fail-value} ...)))
A try-times macro is elegant, but for a one-off, just pull your when out of the try block:
(loop [tries 10]
(when (try
(might-throw-exception)
false ; so 'when' is false, whatever 'might-throw-exception' returned
(catch Exception e
(pos? tries)))
(recur (dec tries))))
My proposal:
(defmacro try-times
"Retries expr for times times,
then throws exception or returns evaluated value of expr"
[times & expr]
`(loop [err# (dec ~times)]
(let [[result# no-retry#] (try [(do ~#expr) true]
(catch Exception e#
(when (zero? err#)
(throw e#))
[nil false]))]
(if no-retry#
result#
(recur (dec err#))))))
Will print "no errors here" once:
(try-times 3 (println "no errors here") 42)
Will print "trying" 3 times, then throw Divide by zero:
(try-times 3 (println "trying") (/ 1 0))
One more solution, without macro
(defn retry [& {:keys [fun waits ex-handler]
:or {ex-handler #(log/error (.getMessage %))}}]
(fn [ctx]
(loop [[time & rem] waits]
(let [{:keys [res ex]} (try
{:res (fun ctx)}
(catch Exception e
(when ex-handler
(ex-handler e))
{:ex e}))]
(if-not ex
res
(do
(Thread/sleep time)
(if (seq rem)
(recur rem)
(throw ex))))))))
This allows catching multiple more then one exception and provides some feedback about the causes for the retries.
(defmacro try-n-times
"Try running the body `n` times, catching listed exceptions."
{:style/indent [2 :form :form [1]]}
[n exceptions & body]
`(loop [n# ~n
causes# []]
(if (> n# 0)
(let [result#
(try
~#body
~#(map (partial apply list 'catch) exceptions (repeat `(e# e#))))]
(if (some #(instance? % result#) ~exceptions)
(recur (dec n#) (conj causes# result#))
result#))
(throw (ex-info "Maximum retries exceeded!"
{:retries ~n
:causes causes#})))))
If you add a result arg to your loop, you can nest the (try) block inside of the (recur). I solved it like this:
(loop [result nil tries 10]
(cond (some? result) result
(neg? tries) nil
:else (recur (try (might-throw-exception)
(catch Exception e nil))
(dec tries))))
Here's yet another approach:
(loop [tries 10]
(let [res (try
(might-throw-exception)
(catch Exception e
(if (pos? tries)
::retry
(throw e))))]
(if (#{::retry} res)
(recur (dec tries))
res)))
But may I also recommend a cool little trick, instead of having a number of retries, provide a seq of times to sleep for:
(loop [tries [10 10 100 1000]]
(let [res (try
(might-throw-exception)
(catch Exception e
(if tries
::retry
(throw e))))]
(if (#{::retry} res)
(do
(Thread/sleep (first tries))
(recur (next tries)))
res)))
And finally put it all into a macro if you want it to be less verbose:
(defmacro with-retries
[retries & body]
`(loop [retries# ~retries]
(let [res# (try ~#body
(catch Exception e#
(if retries#
'retry#
(throw e#))))]
(if (= 'retry# res#)
(do (Thread/sleep (first retries#))
(recur (next retries#)))
res#))))
(with-retries [10 10 100 1000]
(might-throw-exception))