Fork me on GitHub
#babashka
<
2023-09-22
>
Otto Nascarella13:09:50

hiya community! I got a question for BB experts: I wanna be able to run an external command in the background… and: 1. check if it’s doing OK 2. exit my BB program, but keep that one running in the background how would you approach this?

Otto Nascarella13:09:19

like… you start here dear program… please don’t print to stdout, save stuff to this temp file… I am exiting now, but you carry on doing your stuff!

Daniel Gerson13:09:19

Not sure, but you can always use unix* commands to do this https://www.howtogeek.com/440848/how-to-run-and-control-background-processes-on-linux/ and write to a file. Then you'd just be launching the babashka script as the main starting point.

mmer13:09:29

I tend to use linux screen.

Daniel Gerson13:09:50

Got a feeling that you're asking for a unix style fork, but don't think JVM supports this https://stackoverflow.com/questions/287633/java-c-like-fork . Then again I don't do enough of this stuff to know.

Otto Nascarella13:09:42

Ok… this actually keeps the process running after program has exited

(let [{:keys [proc]} (process "python" "daemon.py")] 
  (println (format "PID %s" (.pid proc))))

isak14:09:17

Yea I think that is usually the easy case, doing the opposite is what requires a little work usually

Otto Nascarella14:09:50

also… weirdly enough, if I do a

(future 
  (while true
    (Thread/sleep 1000)
    (println "YAY!")))
That does not keep the BB thing running think_beret

borkdude14:09:29

@U5B8QSSC9 Indeed, threads are killed on exit. If you want to keep bb running in the background, start it with $ bb ... & on the command line, I'd say?

borkdude14:09:12

you can also use "exec" in babashka process, to do a unix exec call (let another process take over exection of the process), not sure if that is relevant here though

danm15:09:31

How does babashka get Clojure proper if it doesn't already exist on the system/in the expected path?

borkdude15:09:18

Can you elaborate? Not sure what you mean

dpritchett18:09:32

@U6SUWNB9N the readme suggests that Babashka ships its own tiny graal jvm and supports a reduced subset of the full Clojure language: https://github.com/babashka/babashka#setting-expectations If you have a hard requirement to connect your scripts to a full Clojure install I imagine babashka wouldn’t be the best fit.

dpritchett18:09:03

Scratch that, I’ve just read the next thread and it looks like you got what you needed.

danm15:09:07

CLJ_CONFIG=bb-test/.clojure  DEPS_CLJ_TOOLS_DIR=bb-test/.deps.clj bb --config bb-test/bb.edn uberjar deps.jar
Clojure tools not yet in expected location: bb-test/.deps.clj/clojure-tools-1.11.1.1403.jar
Downloading  to bb-test/.deps.clj/clojure-tools.zip
Unzipping bb-test/.deps.clj/clojure-tools.zip ...
Successfully installed clojure tools!
Error: Could not find or load main class clojure.main
Caused by: java.lang.ClassNotFoundException: clojure.main
Exception in thread "main" clojure.lang.ExceptionInfo:  babashka.process.Process@e72f7c71

borkdude15:09:44

Please follow up in thread

danm15:09:34

Sorry. I don't have access to ~/.clojure, so I need to point it somewhere else which I thought CLJ_CONFIG should do, but then I don't know what I need to populate into that dir and what bb should auto-fetch

danm15:09:42

So I assume I'm getting the "Could not find clojure.main" error because I'm missing some step in populating that CLJ_CONFIG directory. If I have a look after the command fails it contains a deps.edn and tools/tools.edn, but no .cpcache, which I guess is what it's looking for

borkdude15:09:40

Try -Sforce and also upgrade bb to latest. At a conf right now so I have to be short

1
borkdude15:09:49

Also delete .cpcache

danm15:09:09

Already @ latest, so that's good 🙂 Same with -Sforce and deleting the existing cache in my homedir

borkdude15:09:03

Not sure what happens. Perhaps try a non-relative path for the env vars!

borkdude15:09:27

Question mark, sorry my phone

danm16:09:46

I have been banging my head on this for so long. Never thought to try an absolute path

danm16:09:49

That worked!

danm16:09:58

THANK YOU! 🎉

borkdude16:09:09

Ok please file an issue with deps.clj :)

Patrick Brown15:09:27

I'm trying to make a deftype or defrecord that is over IAtom and a couple other related protocols Babashka compatible. I'm running into issues with the interfaces IObj and IRef. I'm using these two to implement the add-watch method and the with-meta method. The error is about defrecord/type only supporting protocol implementations. I believe that Babashka supports atom watchers. But I don't know. Can anyone point me in the right direction? Cheers!

borkdude15:09:41

This might need some work in SCI, check out the IAtom workarounds in there

borkdude15:09:57

Can check in more detail later, at a conference right now

borkdude13:09:08

Did you get any insights on this?

Patrick Brown13:09:50

Thanks for checking in. As it sits atm I've just got the bb version of the code without methods for IRef, IObj, IMeta, IReference, and Closeable.

Patrick Brown13:09:14

Looking at how you implemented protocols with multi-methods was a very cool idea.

Patrick Brown13:09:25

If you have the time to answer a few bb development related question a little nudge would help me help myself.

Patrick Brown13:09:22

I'd like to run a server bb so I can get fast starts on edge, When I've got a library like kixi.stats that isn't bb compatible, how do I go about finding out which namespaces contain that non-portable code so I can assess if I'm actually using anything that's a problem and formulate a plan?

borkdude13:09:21

I usually just try to run the library's unit tests. See test-resources/lib-tests, bb tests a plethora of libs on CI

Patrick Brown13:09:53

As for the IRef and IReference stuff I'm a bit lost on what I would do, we talked long ago about porting durable atom to bb, I've got everything working quite well, outside of the watch stuff, but feel a bit lost on exactly what to do when I get into a spot where I want to use a protocol that isn't ready out of the box

borkdude13:09:30

The issue is usually with deftypes which implement Java interfaces:

$ bb -cp src:test -e "(require '[kixi.stats.core])"
----- Error --------------------------------------------------------------------
Type:     clojure.lang.ExceptionInfo
Message:  defrecord/deftype currently only support protocol implementations, found: clojure.lang.Seqable

Patrick Brown13:09:47

So, does that mean that I've touched a hard line or is there some finesse I can employ?

borkdude13:09:21

The issue with bb is that graalvm needs to know all types beforehand. So if you create a deftype in userland with a number of protocols, it needs to exactly match with a type that already exists in bb. I've done this with reify for a number of often used combinations, but this doesn't work in general

borkdude13:09:31

If you have any ideas on this, I'd be more than open to listen

borkdude13:09:57

I've tried the approach to create a type which just implements all possible interfaces, but this will not work with instance? checks obviously

Patrick Brown13:09:12

Shucks bork, I seem to have great ideas and a complete lack of if my ideas touch reality/my abilities

Patrick Brown13:09:22

I do know if I could put watches in a protocol implementation it would open up a lot of my functionality on the JVM/CLJS front, and I wouldn't miss not calling instance?

borkdude13:09:37

you could try to rewrite kixi so that it doesn't use seq but the specific function sampleable->seq instead and then avoid the overloaded interface clojure.lang.Seq for bb

Patrick Brown13:09:54

So does that mean I can make a generic type explicit with those methods and then implement watches on them?

Patrick Brown13:09:53

So instead of implementing IReference, I just do the same thing on a protocol MYIReference?

borkdude13:09:30

I commented out all clojure.lang.Seqable overloads in kixi.stats.distribution. The next thing I hit was com.tdunning.math.stats.TDigest is not included in bb, so it relies on a Java lib it seems

borkdude13:09:40

yeah you could write your own protocol indeed

borkdude13:09:36

What bb could also do is allow all types on the "type that implements all user types" and patch bb's instance? to check if a type dynamically implements an interface. The only thing is when passing such a type to an external library, you could get false positives

borkdude13:09:02

but maybe the chance of that isn't so large and those external libraries could be patched in bb (since there is a fixed number of them anyway), but it's a bit risky maybe

borkdude13:09:13

this is the nr 1 incompatibility with JVM Clojure

borkdude13:09:28

aside from missing JVM classes

Patrick Brown13:09:01

In that type of situation I typically put metadata on a var or I call (-> % class str) and predicate the string.

Patrick Brown13:09:33

But I don't know if that's viable. I'm really just guessing seemingly all the time:smiley:

borkdude13:09:55

not sure if I'm following regarding metadata on a var

Patrick Brown13:09:16

I'll def with a metadata key like, atom-watch? true and check if somethings and atom-watch by passing a ref. I hope that isn't too ugly and hacky to admit.

Patrick Brown13:09:08

I should have said var reference

Patrick Brown13:09:48

IRef
          (setValidator [_ validator]
            (.setValidator ^IRef atom-ref validator))
          (getValidator [_]
            (.getValidator ^IRef atom-ref))
          (addWatch [this watch-key watch-fn]
            (.addWatch ^IRef atom-ref watch-key watch-fn)
            this)
          (removeWatch [this watch-key]
            (.removeWatch ^IRef atom-ref watch-key)
            this)
          (getWatches [_]
            (.getWatches ^IRef atom-ref))
See if I got this right? I'm worried it's too simple for me to be right. So I'd need to define my own protocol MyIWatchable, with a addWatch method, then dig into core and see what .removeWatch is doing, and basically do a less JVM dependent version of that.

borkdude13:09:24

Yes, but the trouble is that add-watch (in Clojure) calls the JVM interface, not your protocol, so you can't re-use add-watch for your custom atom then, but maybe that's not so bad

borkdude13:09:42

you'll just have to make your own my-lib/add-watch function

Patrick Brown13:09:08

I can manage that part.

Patrick Brown13:09:29

One more thing that's confusing me. If I can't implement the methods in IRef in bb, how come I can require clojure.lang IRef in bb? What exactly is the point where I cross the JVM/BB line and break?

borkdude13:09:18

The clojure.lang.IRef interface exists in bb as a regular JVM interface and is there when people e.g. write (instance? clojure.lang.IRef ..) - this happens in some libraries

borkdude13:09:46

whereas IAtom was "faked" as a protocol in bb

borkdude13:09:02

specifically to allow something when somebody asked for it

borkdude13:09:07

this was portal

borkdude13:09:11

but it's also been useful for other libs

borkdude13:09:26

we could do the same for clojure.lang.IRef perhaps

Patrick Brown13:09:53

I think I get enough of what's going on... Just as a guage, is the http-kit integration you've built into bb being used to good effect in production? Or was that more intended for dev tooling?

borkdude14:09:14

it works fine in production I think but I don't have numbers on how many people use it in production

borkdude14:09:37

you could also just build your own graalvm binary for the edge of course

Patrick Brown14:09:13

Currently I'm doing the lazy thing where I call bb task-name in a dockfile. But the binary approach is proper modern and sexy.

borkdude14:09:15

or fork babashka and add some libraries you're missing and build your own version for the edge with your libs

Patrick Brown14:09:30

Now we're talking!

Patrick Brown14:09:51

Surely, you've got some docs on forking and adding libs I've missed.

Patrick Brown14:09:23

"https://github.com/babashka/babashka/blob/master/doc/dev.md#adding-classes Add necessary classes to babashka/impl/classes.clj. For every addition, write a unit test, so it's clear why it is added and removing it will break the tests. Try to reduce the size of the binary by only adding the necessary parts of a class in :instance-check, :constructors, :methods, :fields or :custom. The reflection.json file that is needed for GraalVM compilation is generated as part of script/uberjar."

borkdude14:09:58

There are a number of feature flags for specific libs: https://github.com/babashka/babashka/blob/master/doc/build.md That should give you an idea where to add stuff

borkdude14:09:34

and also the classes file, yes

Patrick Brown14:09:00

Cursory scan tells me that's within my current capabilities, a stretch but I'm capable.

borkdude14:09:30

yeah, you just need to set GRAALVM_HOME to a download of graalvm and then run script/uberjar && script/compile - should just work ™️

borkdude14:09:45

and also read up on the SCI README.md

borkdude14:09:54

most of the library integration has to do with how SCI works

borkdude14:09:34

sometimes it's just a matter of (sci/copy-ns kixi.stats.core (sci/create-ns 'kixi.stats.core)) if you're lucky

borkdude14:09:39

and add that to the SCI context

Patrick Brown14:09:49

I'm familiar with the sci context. I'm not sure how much I'm following, but I know my mental picture is fuller.

Patrick Brown14:09:18

I need to spend more time with the code instead of treating it like another shell utility.

Patrick Brown14:09:13

I won't take up any more of your time. Thanks Borkent! As usual, you've been helpful and didn't make me feel stupid. What a treat for me. lololol CHEERS!

😃 1
borkdude14:09:54

ok, hope to see a custom bb on the edge near me soon :)

Patrick Brown14:09:33

There's a blazingly fast bun, hono, and nbb in an edge in Dallas. So I'm already close to a BB in Amsterdam.

vlad_poh17:09:40

Having a senior moment. I know how to load pods and use them in a bb script but how do i use it in a bb task? Script i’m trying to convert to a bb task below

#!/usr/bin/env bb
(require
 '[babashka.pods :as pods]
 '[clojure.pprint :refer [print-table] :rename {print-table pt}])
(pods/load-pod 'org.babashka/mssql "0.1.2")
(require '[pod.babashka.mssql :as ms])
(def db {:dbtype "mssql",
         :dbname "***",
         :host "localhost",
         :port "1434",
         :user "sa",
         :password "***",
         :trustServerCertificate "true"})
(defn q [s] (ms/execute! db [s]))

(pt
 (q "update dbo.users set email = '***'"))

p4v4n17:09:37

AFAIK there is no difference in running tasks whether pods or used or not.

p4v4n17:09:27

Adding something like this to bb.edn and running bb run <task-name> should work.

{:paths ["."]
 :tasks
 {task-name {:doc "Task doc"
             :requires ([abc :as abc])
             :task (abc/update-user-email)}
  }}

vlad_poh17:09:31

@U64FXM56W i want to keep it all in a single bb.edn

p4v4n18:09:51

It should still work the same way only the namespace will change. The namespace should match with where the file is located w.r.t bb.edn.

vlad_poh18:09:56

nope doesn’t work.

Could not resolve symbol: pod.babashka.mssql/execute!

borkdude18:09:55

You can load the pod in bb.edn, search the book for :pods

👍 2
vlad_poh18:09:51

that’s it! :pods Thanks

p4v4n18:09:34

Did it fix the issue @U06GMV0B0? I am more curious now.:thinking_face: Scripts with pods work fine for me without using :pods in bb.edn. Also if loading the pod failed earlier the error from babashka would have been at the line (pod/load-pod ..) itself.

vlad_poh18:09:43

Yep it did. without it doesn’t know any of the pod namespaces

👍 1