I want to extend and redefine a clojure.core macro. For example, how can I redefine clojure.core/defn while using the original definition?
This is a bit tricky, but by aliasing the macro in clojure.core this is possible.
Open the repl and follow the steps below.
➜ ~ clj
Clojure 1.9.0
First alias clojure.core/defn to something else:
user=> (in-ns 'clojure.core)
#object[clojure.lang.Namespace 0x48e92c5c "clojure.core"]
clojure.core=> (defmacro defn-holder [])
#'clojure.core/defn-holder
clojure.core=> (alter-var-root #'defn-holder (constantly (var-get #'defn)))
#object[clojure.core$defn__5154 0xd3957fe "clojure.core$defn__5154#d3957fe"]
Next, create a new defn macro that uses the alias definition
clojure.core=> (in-ns 'user)
#object[clojure.lang.Namespace 0x64ba3208 "user"]
user=> (defmacro defn [& args] `(do (println "aliased version")(clojure.core/defn-holder ~#args)))
WARNING: defn already refers to: #'clojure.core/defn in namespace: user, being replaced by: #'user/defn
#'user/defn
user=> (defn foo [a])
aliased version
#'user/foo
user=> (foo 1)
nil
However it doesn't work for all namespaces yet:
user=> (ns bar)
nil
bar=> (defn foo [a])
#'bar/foo
We need to redefine defn in clojure.core with our new definition:
bar=> (in-ns 'clojure.core)
#object[clojure.lang.Namespace 0x48e92c5c "clojure.core"]
clojure.core=> (alter-var-root #'defn (constantly (var-get #'user/defn)))
#object[user$defn 0x37052337 "user$defn#37052337"]
Now it works:
clojure.core=> (in-ns 'bar)
#object[clojure.lang.Namespace 0x37efd131 "bar"]
bar=> (defn foo [a])
aliased version
#'bar/foo
Related
I'm new to Clojure so this might be obvious.
(println (nil? (resolve 'x)))
(def x 1)
(println (nil? (resolve 'x)))
This prints out
true
true
Why doesn't the second println output false?
According to the examples on ClojureDocs, shouldn't this be the case?
EDIT:
I'm not running this through REPL.
This is how I got there:
Create a project via lein new testing.
After modifications to core.clj, it looks like this:
(ns testing.core
(:gen-class))
(defn -main
[& args]
(println (nil? (resolve 'x)))
(def x 1)
(println (nil? (resolve 'x)))
)
When ran through lein run, output is:
true
true
When I evaluate those expressions in a Clojure REPL, I get true for the first one, and false for the second one. How you are you starting the Clojure REPL? What version of Clojure are you using? Can you reproduce this consistently?
(resolve 'x) try to get a Var named by (bound to) the x symbol. If this Var does not exist, it should return nil. So your assumptions are correct.
If you are coding with a live REPL, remember that code you evaluate stays in memory until you replace it by a new version or manually undefine it.
Here is a visual REPL session from my side showing the correct behavior:
There is something strange about your environment:
~/expr/demo > lein repl
demo.core=> (resolve 'x)
nil
demo.core=> (resolve 'x)
nil
demo.core=> (resolve 'x)
nil
demo.core=> (resolve 'x)
nil
demo.core=> (def x 1)
#'demo.core/x
demo.core=> (resolve 'x)
#'demo.core/x
Try running the following program instead, and read the output carefully. Note that the doc string for the function 'resolve' says that it resolves the symbol in the namespace that is the current value of *ns*, and that is a "dynamic var", which according to the output of the program shown below, is equal to the 'user' namespace by default, different than the namespace where the function is defined, which is the namespace where 'x' is 'def'd.
(ns testing.core
(:gen-class))
(defn -main
[& args]
(println)
(println "before (def x 1)")
(println "*ns*=" *ns*)
(println "(resolve 'x)=" (resolve 'x))
(println "(resolve 'user/x)=" (resolve 'user/x))
(binding [*ns* 'testing.core]
(println "*ns*=" *ns*)
(println "(resolve 'x)=" (resolve 'x))
(println "(resolve 'user/x)=" (resolve 'user/x)))
(def x 1)
(println)
(println "after (def x 1)")
(println "*ns*=" *ns*)
(println "(resolve 'x)=" (resolve 'x))
(println "(resolve 'user/x)=" (resolve 'user/x))
(binding [*ns* 'testing.core]
(println "*ns*=" *ns*)
(println "(resolve 'x)=" (resolve 'x))
(println "(resolve 'user/x)=" (resolve 'user/x))))
Output on my system:
before (def x 1)
*ns*= #object[clojure.lang.Namespace 0x524f3b3a user]
(resolve 'x)= nil
(resolve 'user/x)= nil
*ns*= testing.core
(resolve 'x)= #'testing.core/x
(resolve 'user/x)= nil
after (def x 1)
*ns*= #object[clojure.lang.Namespace 0x524f3b3a user]
(resolve 'x)= nil
(resolve 'user/x)= nil
*ns*= testing.core
(resolve 'x)= #'testing.core/x
(resolve 'user/x)= nil
In my app I'm providing some interface to users that they can provide code and app evaluates that code within sandbox(so eval fn not allowed).The thing is I need to catch if user overrides some built-in function such as =
Any ideas how to catch and prevent that thing?(The idea is they should not be able to do that)
Code:
(defn =
[]
//some code)
WARNING: = already refers to: #'clojure.core/= in namespace: user, being replaced by: #'user/=
One solution might be:
I was trying to get the warning message as String but with-out-str function did not work.
(with-out-str
(defn = []))
;=> ""
Also wrote that with-err-str(changed with-out-str little bit) did not work as well.
(defmacro with-err-str
[& body]
`(let [s# (new java.io.StringWriter)]
(binding [*err* s#]
~#body
(str s#))))
(with-err-str
(defn = []))
;=> ""
Need: "WARNING: = already refers to: #'clojure.core/= in namespace: user, being replaced by: #'user/="
It does work when you use eval:
user=> (with-err-str (eval '(defn - [] 11)))
"WARNING: - already refers to: #'clojure.core/- in namespace: user, being replaced by: #'user/-\n"
user=> (re-seq #"WARNING" (with-err-str (eval '(defn / [] 11))))
("WARNING")
Or you could redefine the defn macro in user's code, but nothing prevents them to use other clojure tools to redefine a var:
user=> (defmacro defn-safe
#_=> [nam & decls]
#_=> (if (resolve (symbol "clojure.core" (name nam)))
#_=> (print "Whoops")
#_=> (list* `defn (with-meta nam (assoc (meta nam) :private true)) decls)))
#'user/defn-safe
user=> (defn-safe foo [x] (+ x 2))
#'user/foo
user=> (foo 22)
24
user=> (defn-safe = [a b] (- a b))
Whoopsnil
user=>
Another option, and probably your best bet is using
https://github.com/clojure/tools.analyzer
clojail handles this (and many other things as well). If you're looking to sandbox Clojure, I'd recommend taking a look.
One solution might be like this:
(def before (set (vals (ns-map *ns*))))
(defn = [])
(def after (set (vals (ns-map *ns*))))
(clojure.set/difference before after)
;=> #{#'clojure.core/=}
I have a Ring handler which uses several functions to build the response. If any of these functions throw an exception, it should be caught so a custom response body can be written before returning a 500.
I'm writing the unit test for the handler, and I want to ensure that an exception thrown by any of these functions will be handled as described above. My instinct was to use with-redefs inside a doseq:
(doseq [f [ns1/fn1 ns1/fn2 ns2/fn1]]
(with-redefs [f (fn [& args] (throw (RuntimeException. "fail!"))]
(let [resp (app (request :get "/foo")))))]
(is (= (:status resp) 500))
(is (= (:body resp) "Something went wrong")))))
Of course, given that with-redefs wants to change the root bindings of vars, it is treating f as a symbol. Is there any way to get it to rebind the var referred to by f? I suspect that I'm going to need a macro to accomplish this, but I'm hoping that someone can come up with a clever solution.
with-redefs is just sugar over repeated calls to alter-var-root, so you can just write the desugared form yourself, something like:
(doseq [v [#'ns1/fn1 #'ns1/fn2 #'ns2/fn1]]
(let [old #v]
(try
(alter-var-root v (constantly (fn [& args]
(throw (RuntimeException. "fail!")))))
(let [resp (app (request :get "/foo")))
(is (= (:status resp) 500))
(is (= (:body resp) "Something went wrong")))
(finally (alter-var-root v (constantly old))))))
Following on from amalloy's great answer, here's a utility function that I wrote to use in my tests:
(defn with-mocks
"Iterates through a list of functions to-mock, rebinding each to mock-fn, and calls
test-fn with the optional test-args.
Example:
(defn foobar [a b]
(try (+ (foo a) (bar b))
(catch Exception _ 1)))
(deftest test-foobar
(testing \"Exceptions are handled\"
(with-mocks
[#'foo #'bar]
(fn [& _] (throw (RuntimeException. \"\")))
(fn [a b] (is (= 1 (foobar a b)))) 1 2)))"
[to-mock mock-fn test-fn & test-args]
(doseq [f to-mock]
(let [real-fn #f]
(try
(alter-var-root f (constantly mock-fn))
(apply test-fn test-args)
(finally
(alter-var-root f (constantly real-fn)))))))
I know how to forward declare a var for the current namespace. Instead, I want to declare a var from another namespace. How do I do this? This will help me eliminate a circular load dependency.
At the moment, this is what I've tried:
; this_ns.clj
(ns my-project.this-ns
(:require ...))
(ns my-project.other-ns)
(declare other-func)
(ns my-project.this-ns) ; return to original namespace
(defn func-1
[]
(my-project.other-ns/other-func))
It works, but I don't like it.
I think the solution you already have is the easiest one. If you wrap it into a macro it doesn't even look that bad anymore:
(defmacro declare-extern
[& syms]
(let [n (ns-name *ns*)]
`(do
~#(for [s syms]
`(do
(ns ~(symbol (namespace s)))
(declare ~(symbol (name s)))))
(in-ns '~n))))
Call it with:
(declare-extern my.extern.ns/abc) ;; => #<Namespace ...>
my.extern.ns/abc ;; => #<Unbound Unbound: #'my.extern.ns/abc>
Is there a way in clojure to get a function's code after the function has been loaded?
Ie. without doing something like [untested]
(defmacro blat [x] `(do (def code ~(quote (mexpand-all x)))
~x)))
(blat (defn func [abc] (...)))
You can get the source of a symbol using the clojure.repl/source function. However, this only works if the var for which the symbol resolves to is in a .clj file on the classpath. You can't, for example, do this:
user=> (defn foo [x] x)
#'user/foo
user=> (require 'clojure.repl)
nil
user=> (clojure.repl/source foo)
Source not found
nil