Fork me on GitHub
#babashka
<
2020-06-15
>
borkdude14:06:35

@nate Talking about binary formats... cheshire also supports JSON smile, which is a compact binary encoding of JSON

$ bb '(json/generate-smile {:foo "bar" :baz 5})'
#object["[B" 0x2cc3e974 "[[email protected]"]

nate15:06:37

Very cool!

grzm16:06:36

Is there a pattern people are using that provides behavior similar to python’s __name__ == __main__ ? I’d like to provide a main entry point in the script but still be able to source entire files in my editor without firing main.

borkdude16:06:38

@grzm yeah, there is a System property you can read to determine if bb was called with a --main arg: (System/getProperty "babashka.main"). If that's not set, you can do (apply -main *command-line-args*)

grzm16:06:41

Nice 🙂

grzm16:06:50

Thanks, @borkdude 😄

nate16:06:01

for small scripts without a main, I've been doing this:

(defn say-hello
  [n]
  (println "Hello" n))

(when-not (System/getenv "DEV")
  (say-hello (first *command-line-args*)))
and then I start bb's nrepl server like this: DEV=1 bb --nrepl-server and I can eval the entire namespace without worry

grzm18:06:18

@nate That sounds like it might be more along the lines I’m looking for. I was hoping to use this in conjunction with a #!, so as to not call it with bb directly, but it looks like babashka.main isn’t set in that case (e.g., calling ./my-wonderful-bb-script rather than bb '(my-wonderful-bb-script/-main)'

borkdude18:06:37

@grzm I'm not entirely sure now what you're trying to do. But *file* might also be of help: it contains the name of the file that's currently being evaluated

grzm18:06:00

cat some-script
#!/usr/bin/env bb
(ns com.grzm.some-script
  (:require [clojure.pprint :as pprint]))

(defn -main [& args]
  (prn "lookee!"))

(when *file*
  (apply -main *command-line-args*))

grzm18:06:01

@borkdude^ Thanks! That works. When I run load-file in Cider, *file* isn’t set, so -main doesn’t run. When called from the command line, it is.

grzm18:06:14

./some-script 
"lookee!"

nate18:06:16

the (when *file* ,,,) trick doesn't work for me when using bb --nrepl-server, still evals

nate18:06:41

but that works well when developing scripts with clojure and a connected editor

borkdude18:06:21

I'm not sure if this defined or undefined behavior though. Maybe babashka should also have a babashka.file property which contains the filename of --file

grzm18:06:29

Thinking out loud, would it make sense for --nrepl-server to set a babashka.nrepl-server property?

grzm18:06:12

(for the record, I’m on macOS Catalina, babashka v0.1.2)

nate18:06:27

interesting, or babashka.repl (socket and nrepl)

borkdude18:06:01

@grzm Maybe all the parsed arguments should just be visible somewhere

grzm18:06:30

Ah, that might make sense, too.

grzm18:06:20

@borkdude Is it clear what I’m trying to accomplish? I’m not sure if I’ve explained it well enough. Often I find I’m tackling something from the wrong direction, which is fine, but my explanations leave people wondering “what on earth is he trying to do?”

borkdude18:06:15

@grzm Well, for the script you are writing there I would expect you to invoke it with -cp + -main or --file/shebang. The property babashka.main is used to distinguish between those two cases

borkdude18:06:02

from the perspective of babashka there is no difference between a shebang or --file

borkdude18:06:19

it actually can't see that, because the shell calls the binary with the file argument for you

borkdude18:06:54

afk for a bit

grzm18:06:15

I’d rather not have people worry about which interpreter they’re using: ruby, or perl, or bash, or whatever, so I’d rather not have to call bb some-script, if I’m understanding correctly. I could wrap everything in some separate bash script that calls bb some-script, but that’s just extra wrapping.

grzm18:06:26

@nate’s System/getenv is workable and robust, so I’ll lean on that for now. Having all the args potentially available would work, too, and I wouldn’t have to remember to set the right environment variable, 🙂

borkdude19:06:34

@grzm I think I might have a solution, but I first want to have your problem straight. You want to execute using a shebang. In that case, the -main function should be executed. Else it should not?

grzm19:06:33

Yup. In the some-script example above, I want (apply -main *command-line-args*) to execute when ./some-script is called from the command line. When I load-file file from the repl, I don’t want (apply -main *command-line-args*) to be called.

borkdude19:06:45

and you use load-file only in development?

borkdude19:06:28

in normal clojure I would just solve that with multiple entrypoint script. script1 contains some functions. script2 load-files script1 and calls the function. you tell the end user to call script2.

borkdude19:06:39

and you load-file script1 from elsewhere

grzm19:06:53

Right now, yes. I haven’t had a need to dynamically call load-file. (To be really explicit, I’m calling cider-load-buffer, which I believe calls load-file)

grzm19:06:44

Yeah, that’s how I’d do it in normal clojure, too. But that’s not how I’d do it when I’m writing scripts, which I wouldn’t use Clojure for, generally.

borkdude19:06:44

ok, env var it is then

borkdude19:06:42

the use case you are describing doesn't correspond to the __name__ == __main__ python example I think?

borkdude19:06:59

or maybe it does, in the case you load the file from some other file, name__ would not be equal to main__, but if you invoke it directly, it would?

grzm19:06:20

My understanding is that people use it as a guard to be able to use the file as a library and as a main entry point, using if __name__ == __main__ for the main entry point. I agree it’s not exactly the same.

borkdude19:06:27

in that case (System/getProperty "babashka.file") might make sense, so you can do: (= (System/getProperty "babashka.file") *file*)

borkdude19:06:58

*file* contains the canonical path of the currently executing file. we could make babashka.file also have the canonical file path of the -f argument (or shebang)

borkdude19:06:32

so when you load-file from some other file, babashka.file surely isn't the same as *file*

borkdude19:06:41

make sense?

grzm19:06:59

I think that sounds reasonable, and as far as I’ve seen, *file* is nil when I’m running cider-load-buffer. However, @nate reported that didn’t work for him.

borkdude19:06:27

*file* should contain the canonical file path when using load-file. you can very easily try this out yourself

borkdude19:06:52

this is the implementation:

(let [f (io/file f)
            s (slurp f)]
        (sci/with-bindings {sci/ns @sci/ns
                            sci/file (.getCanonicalPath f)}
          (sci/eval-string* sci-ctx s)))

borkdude19:06:02

I think cider-load-buffer just sends the buffer to the nrepl server

borkdude19:06:11

but not sure of course

grzm19:06:38

okay, let me be clear: I’m calling cider-load-buffer. I assumed (it looks like wrongly) that it’s calling load-file.

borkdude19:06:32

you can check what the babashka nrepl server receives when starting it with BABASHKA_DEV=true bb --nrepl-server

borkdude19:06:51

you can then see if it contains a giant eval op with the entire buffer or a load-string with a filename

jeroenvandijk19:06:30

Is there a way to read the version of the babashka binary? I can think of shelling out (--version), but it would be nicer without starting a new process

jeroenvandijk19:06:39

The reason I want to use it is checking if the user has a babashka installation that supports the features being used. With all the additions this might save some time in debugging users issues

jeroenvandijk19:06:34

E.g. typical usecase (in my mind): at some point in time I build a script with the latest babashka. I would set the minimal babashka version to that babashka version. I’m assuming it will work with all newer versions, but not necessarily with older ones. I can see how this will save myself some headaches

borkdude19:06:51

not yet, so I'll add babashka.main-file (or some other name?) which contains the -f argument and babashka.version which contains the version as system properties?

jeroenvandijk19:06:51

Clojure uses *file* . I don’t have a strong opinion about the name

jeroenvandijk19:06:02

And clojure has *clojure-version* I guess it makes sense to not copy that, but make that *babashka-version* . A use case for clojure I have seen is this one https://github.com/tonsky/clojure-future-spec/blob/master/src/clojure/future.clj#L3

borkdude19:06:32

babashka already has *file*. this has a different meaning (see the above discussion)

borkdude19:06:09

I won't introduce any non-clojure public vars into the core namespace for compatibility. I was proposing system properties

jeroenvandijk19:06:19

Ah ok. I don’t need *file* yet so I haven’t thought about that yet

jeroenvandijk19:06:33

Yeah system properties sound good

jeroenvandijk19:06:03

Ah ok now I see what you said

jeroenvandijk20:06:02

I didn’t read the above discussion about *file* 🙈 It was a coincidence I guess 🙂

borkdude20:06:33

it's in the air. sometimes I don't hear something about clj-kondo for a while and then one day there are like 5 issues or questions within a few hours

borkdude20:06:26

or some library or new babashka feature was just introduced and a new user asks the next day if that is supported.. 🙂

jeroenvandijk20:06:17

Maybe everyone is leveling up their usage of Babashka around the same time 🙂

jeroenvandijk20:06:29

I don’t want to create Graalvm projects anymore for tooling. I just want to use Babashka now and expect of people that they have a babashka binary installed

borkdude20:06:52

So to summarize: name__ = "main__" pattern: https://github.com/borkdude/babashka/issues/478 And one property for the version: https://github.com/borkdude/babashka/issues/479