babashka 2025-08-29

Is there a way to replace/patch/alter a single dependency namespace? I'm doing a http-kit server that's meant to run in both bb and clj, but keep running into errors when loading ring.util.response , which is a transitive dep of a lot of ring middleware.

----- Error --------------------------------------------------------------------
Type:     clojure.lang.ExceptionInfo
Message:  Unable to resolve classname: java.net.JarURLConnection
Data:     {:type :sci/error, :line 277, :column 1, :file "ring/util/response.clj", :phase "analysis"}
Location: ring/util/response.clj:278:20
Phase:    analysis

----- Context ------------------------------------------------------------------
274:     path
275:     (str path "/")))
276:
277: (defn- jar-directory? [^java.net.JarURLConnection conn]
278:   (let [jar-file   (.getJarFile conn)
                        ^--- Unable to resolve classname: java.net.JarURLConnection
279:         entry-name (.getEntryName conn)
280:         dir-entry  (.getEntry jar-file (add-ending-slash entry-name))]
281:     (and dir-entry (.isDirectory dir-entry))))
282:
283: (defn- connection-content-length [^java.net.URLConnection conn]

----- Stack trace --------------------------------------------------------------
ring.util.response/let   - ring/util/response.clj:278:20
ring.util.response       - ring/util/response.clj:278:3
clojure.core/let         - <built-in>
ring.util.response/fn    - ring/util/response.clj:278:3
clojure.core/fn          - <built-in>
... (run with --debug to see elided elements)
ring.util.response       - ring/util/response.clj:277:1
ring.util.response       - ring/util/response.clj:277:1
ring.middleware.resource - ring/middleware/resource.clj:3:3
invoker                  - /Users/filipesilva/repos/personal/invoker/src/invoker.clj:3:3
user                     - <expr>:1:10
This is similar to https://clojurians.slack.com/archives/CLX41ASCS/p1682919417008599

will that replace my global bb install via homebrew or?

ah no I see that it got installed to /tmp/bb, nice

I can confirm that wrap-resource works now in my original usage (serving files on bb using http-kit)!

filipesilva@m4 ~/r/p/invoker (master)> curl localhost/file.txt
foo

thank you for taking the time to fix this 🙏

👍 1

heya, sorry to bother you again, but about clojure.lang.Ref... there's a couple of places in clojure-plus that try to use it, like this in clojure+.test

(defn- inc-report-counter [name]
  (condp instance? test/*report-counters*
    clojure.lang.Ref
    (dosync (commute test/*report-counters* update name (fnil inc 0)))
as far as I can tell it's kinda there in bb, but can't be resolved?
user=> (require '[clojure.test :as test])
nil
user=> (test/deftest foo (println (type test/*report-counters*)))
#'user/foo
user=> (test/run-tests)

Testing user
clojure.lang.Ref

Ran 1 tests containing 0 assertions.
0 failures, 0 errors.
{:test 1, :pass 0, :fail 0, :error 0, :type :summary}
user=> clojure.lang.Ref
Could not resolve symbol: clojure.lang.Ref [at <repl>:7:-16]
do you have any suggestions on what to do in this case? I could check the str instead of using instance?
user=> (test/deftest foo (println (= "class clojure.lang.Ref" (str (type test/*report-counters*)))))
#'user/foo
user=> (test/run-tests)

Testing user
true

Ran 1 tests containing 0 assertions.
0 failures, 0 errors.
{:test 1, :pass 0, :fail 0, :error 0, :type :summary}
user=>

yes, I can add it. shall we use a different thread so we don't bother niki with all the details? let's take it one by one. please make a github issue

sgtm, thank you

let me just add clojure.lang.Ref right now, no need for a gh issue

🔥 1

@tonsky tried adding babashka support to clj-reload here https://github.com/tonsky/clj-reload/pull/25, gonna look at all the clojure+ stuff now

@filipematossilva I already have some WIP here for clj-reload as well: https://github.com/borkdude/clj-reload but I'm still working on SCI improvements to retain types defined with deftype and defclass even after you remove the namespaces. You will see that not working like JVM Clojure when you run the tests in babashka

oh shoot, I think you linked me that already but I completely forgot

well I needed a working version of clj-reload anyway to run the clojure+ tests so it wasn't wasted time

with that minimal change, at least the code runs. but it just doesn't work fully for the defclass/deftype stuff. so maybe a good start

Removed stuff from clojure+ until I could get the remaining tests passing in a bb env, it looks a bit like this: • hashp and error install need .doReset , which looks like it could be replaced with an atom anyway • print install needed .addMethod on MultiFn • print has lots of java imports that don't work, and error tests also rely on print readers • hashp, print, and error tests are all down • then it's a bunch of smaller stuff that looks like it can be worked around • there's a couple of macros around clojure version support that look like they could be used for babashka support/not support If you're curious what it looks like, https://github.com/filipesilva/clojure-plus/commit/82d5b69bb8607e1741067fdd39ac25b71cbec00f

> hashp and error install need .doReset , which looks like it could be replaced with an atom anyway this is about vars. you can use alter-var-root instead.

I never quite understood why some codebases prefer to store state in vars and alter them, is it just preference or are some cases where the atom won't work?

I don't know but alter-var-root does what you want if you want to stay close to the original

👍 1

@filipematossilva I decided to just add everything to bb that's needed to run clojure+.hashp unmodified. The only thing that could be improved now is the line number stuff. But you can already run it with the newest bb I believe

awesome!

let me try

filipesilva@m4 ~>  bb --version
babashka v1.12.208
filipesilva@m4 ~> rlwrap bb
Babashka v1.12.208 REPL.
Use :repl/quit or :repl/exit to quit the REPL.
Clojure rocks, Bash reaches.

user=> (deps/add-deps '{:deps {io.github.tonsky/clojure-plus {:mvn/version "1.6.3"}}})
nil
user=> (require 'clojure+.hashp)
nil
user=> (clojure+.hashp/install!)
java.lang.IllegalArgumentException: No matching method doReset found taking 1 args for class sci.lang.Var [at clojure+/hashp.clj:123:4]
user=>
hm I think there's still something wrong... I guess it's a different type of problem though, because it's on the install fn

oh yes, you're right.... I forgot about .doReset. This can be changed in alter-var-root + constantly

(I sound like an LLM now...)

you're absolutely right! XD

Guys, if at any point you need anything changed in clojure+ itself, just open a PR. Will be happy to help

❤️ 2

@tonsky fyi this is the thread related to https://github.com/babashka/babashka/issues/1853, I tagged you on the issue with the results of trying to install the rest of the clojure+ ns's

I'm already talking with @tonsky on this. I'll send a PR based on the code in the issue. But one thing that wasn't working that well is showing the location of the #p in the output, was trying to improve on that still

I could also send a PR without the improvements first so at least it's working on bb

ah sorry, didn't mean to get in the way, it's just that I was trying to install all the others and thought I should put down what happens in case the whole thing was a bit of a fools errand

thanks! it's appreciated

I added Compiler/demunge to bb. I'll make a PR to remove TaggedLiteral interop in favor of tagged-literal in core. These changes should at least make things work

I'm in no rush myself really... I just happened to be trying it out, and since there were no issue reports on it I thought I'd make some, then eventually if other people tried there was some help and context

thanks :) I might have done a PR on weavejester's hashp at some point maybe

(no I didn't)

you had https://github.com/weavejester/hashp/issues/21, but I think it still suffers from the location issue

it shows

filipesilva@m4 ~/r/p/invoker (master) [SIGINT]> invoker http
#p[clojure.core/apply:667] req => {:args [],
 :async-channel #<org.httpkit.server.AsyncChannel@51c7a210 /[0:0:0:0:0:0:0:1]:80<->/[0:0:0:0:0:0:0:1]:54325>,
 :body #<org.httpkit.BytesInputStream@4d90c592 BytesInputStream[len=3]>,
 :character-encoding "utf8",
for
(defn http-invoke [{:as req, :keys [var args opts]}]
  (try
    #p req
...

should I close that issue?

Your issue is about how the location isn't accurate, which is still true, so I'd say no. https://github.com/weavejester/hashp/pull/25 aimed to fix how it didn't work at all in clojure 1.12, but that's been fixed already.

I don't really know if the middleware I'm trying to use right now (`ring.middleware.resource`) would work at all if I managed to get past this particular error... but a couple of weeks ago I hit the same issue with another middleware, and at any rate ring.util.responses has lots of useful helpers that are ubiquitous in ring apps, like

(defn not-found
  "Returns a 404 'not found' response."
  {:added "1.1"}
  [body]
  {:status  404
   :headers {}
   :body    body})

(defn response
  "Returns a skeletal Ring response with the given body, status of 200, and no
  headers."
  [body]
  {:status  200
   :headers {}
   :body    body})

btw java.net.JarURLConnection was added in 1.12.206 (2025-07-16)

if you can upgrade, we can see if there's other things to add/fix

borkdude you are a gem ❤️

Sorry for the delay confirming it works, it seems to have a problem and I was debugging it. Well I can confirm the require goes through, but ring.middleware.resource doesn't work. It seems to be related to https://github.com/ring-clojure/ring/blob/8fcd3b6594fc66c156e7c4e5f66a55c6acf952f6/ring-core/src/ring/util/response.clj#L309-L312, that gets called in the fns below it. Specifically this piece of code behaves differently between bb and clj:

(-> (.getContextClassLoader (Thread/currentThread))
      (.getResources "public")
      (enumeration-seq))
here's a repro:
mkdir repro
cd repro
echo '{:paths ["src" "resources"]}' > bb.edn
cp bb.edn deps.edn
mkdir -p resources/public
touch resources/public/file.txt
then in clj we see the folder
filipesilva@m4 ~/r/s/bb-resources> clj -M -e '(-> (.getContextClassLoader (Thread/currentThread)) (.getResources "public") (enumeration-seq))'
(#object[java.net.URL 0x772485dd "file:/Users/filipesilva/repos/sandbox/bb-resources/resources/public"])
but in bb we do not
filipesilva@m4 ~/r/s/bb-resources> bb -e '(-> (.getContextClassLoader (Thread/currentThread)) (.getResources "public") (enumeration-seq))'

maybe it's just a matter of using the right class loader in the middleware opts

ok, nice repro. can you file an issue for this, then I'll have a look

do you actually have the resources folder on your classpath in bb?

oh yes, you have sorry

uhm I assume echo '{:paths ["src" "resources"]}' > bb.edn did that, maybe I am wrong

I'll print the file in the repo to demonstrate that

yeap there

filipesilva@m4 ~/r/s/bb-resources> clj -M -e '( "public/file.txt")'
#object[java.net.URL 0x44e3760b "file:/Users/filipesilva/repos/sandbox/bb-resources/resources/public/file.txt"]
filipesilva@m4 ~/r/s/bb-resources> bb -e '( "public/file.txt")'
#object[java.net.URL 0x72531f93 "file:/Users/filipesilva/repos/sandbox/bb-resources/resources/public/file.txt"]

nice, they are still active to do :)

feel free to bump those issues with a thumbs up

👍 1

clj-reload is the first thing I want to fix. it basically already works except not with deftype/defrecord stuff yet

out of curiosity have you tried https://github.com/tonsky/clojure-plus?tab=readme-ov-file#clojurehashp ? I think atm it doesn't work

filipesilva@m4 ~/r/s/bb-resources [SIGINT]> rlwrap bb
Babashka v1.12.207 REPL.
Use :repl/quit or :repl/exit to quit the REPL.
Clojure rocks, Bash reaches.

user=> (require '[babashka.deps :as deps])
nil
user=> (deps/add-deps '{:deps {io.github.tonsky/clojure-plus {:mvn/version "1.6.3"}}})
Downloading: io/github/tonsky/clojure-plus/1.6.3/clojure-plus-1.6.3.pom from clojars
Downloading: io/github/tonsky/clojure-plus/1.6.3/clojure-plus-1.6.3.jar from clojars
nil
user=> (require 'clojure+.hashp)
java.lang.Exception: Unable to resolve classname: clojure.lang.TaggedLiteral [at clojure+/hashp.clj:7:3]

the original hashp seems fine though

user=> (deps/add-deps '{:deps {dev.weavejester/hashp {:mvn/version "0.4.0"}}})
nil
user=> (require 'hashp.preload)
nil
user=> #p "hello"
#p[babashka.impl.repl/repl:84] "hello"
"hello"

the original works fine. I haven't tried clojure+

add an issue for it, I can take a look

👍 1

👍 I'll see what I can do

🙏 1

@filipematossilva This issue is fixed on master. https://github.com/babashka/babashka/issues/1852 You can try it out with:

bash <(curl )  --dev-build --dir /tmp
when CI is finished. Should be in 10 minutes or so

There's going to be a bb-and-clj-kondo-related talk at the #clojure-conj this year!

🎉 15