diff --git a/.gitignore b/.gitignore index b29b640..9b1c5e8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /target .lein-failures /pom.xml +.lein-repl-history diff --git a/README.md b/README.md index 12cc809..a84ffa9 100644 --- a/README.md +++ b/README.md @@ -276,6 +276,17 @@ these messages to `*out*` and `*err*`. Stderr from the pod is redirected to "err" "debug"} ``` +#### readers + +If `format` is `edn` then the pod may describe reader functions: + +``` clojure +{"readers" {"my/tag" "clojure.core/identity"}} +``` + +so payloads containing tagged values like `#my/tag[1 2 3]` are read correctly as +`[1 2 3]`. + #### Error handling Responses may contain an `ex-message` string and `ex-data` payload string (JSON diff --git a/src/babashka/pods/impl.clj b/src/babashka/pods/impl.clj index cb41ec8..6beee87 100644 --- a/src/babashka/pods/impl.clj +++ b/src/babashka/pods/impl.clj @@ -36,8 +36,9 @@ chans (:chans pod) out-stream (:out pod) err-stream (:err pod) + readers (:readers pod) read-fn (case format - :edn edn/read-string + :edn #(edn/read-string {:readers readers} %) :json #(cheshire/parse-string-strict % true))] (try (loop [] @@ -100,7 +101,7 @@ (println err)))) (recur)))) (catch Exception e - (binding [*out* err-stream] + (binding [*out* *err* #_err-stream] (prn e)))))) (defn next-id [] @@ -151,9 +152,18 @@ (let [[o _] (swap-vals! counter inc)] o)))) +(def bytes->symbol + (comp symbol bytes->string)) + +(defn read-readers [reply resolve-fn] + (when-let [dict (get reply "readers")] + (let [dict-keys (map symbol (keys dict)) + dict-vals (map (comp resolve-fn bytes->symbol) (vals dict))] + (zipmap dict-keys dict-vals)))) + (defn load-pod ([pod-spec] (load-pod pod-spec nil)) - ([pod-spec {:keys [:remove-ns]}] + ([pod-spec {:keys [:remove-ns :resolve]}] (let [pod-spec (if (string? pod-spec) [pod-spec] pod-spec) pb (ProcessBuilder. ^java.util.List pod-spec) _ (.redirectError pb java.lang.ProcessBuilder$Redirect/INHERIT) @@ -168,7 +178,8 @@ reply (read stdout) format (-> (get reply "format") bytes->string keyword) ops (some->> (get reply "ops") keys (map keyword) set) - ;; pod-id (get-maybe-string reply "pod/id") + readers (when (identical? :edn format) + (read-readers reply resolve)) pod {:process p :pod-spec pod-spec :stdin stdin @@ -178,7 +189,8 @@ :ops ops :out *out* :err *err* - :remove-ns remove-ns} + :remove-ns remove-ns + :readers readers} _ (add-shutdown-hook! #(destroy pod)) pod-namespaces (get reply "namespaces") pod-id (or (when-let [ns (first pod-namespaces)] diff --git a/src/babashka/pods/jvm.clj b/src/babashka/pods/jvm.clj index 03a06b6..404516d 100644 --- a/src/babashka/pods/jvm.clj +++ b/src/babashka/pods/jvm.clj @@ -4,7 +4,14 @@ (defn load-pod ([pod-spec] (load-pod pod-spec nil)) ([pod-spec _opts] - (let [pod (impl/load-pod pod-spec {:remove-ns remove-ns}) + (let [pod (impl/load-pod + pod-spec + {:remove-ns remove-ns + :resolve (fn [sym] + (or (resolve sym) + (intern + (create-ns (symbol (namespace sym))) + (symbol (name sym)))))}) namespaces (:namespaces pod)] (doseq [[ns-sym v] namespaces] (binding [*ns* (load-string (format "(ns %s) *ns*" ns-sym))] diff --git a/src/babashka/pods/sci.clj b/src/babashka/pods/sci.clj index dcd902f..5defed5 100644 --- a/src/babashka/pods/sci.clj +++ b/src/babashka/pods/sci.clj @@ -10,17 +10,31 @@ (let [env (:env ctx) pod (binding [*out* @sci/out *err* @sci/err] - (impl/load-pod pod-spec - {:remove-ns - (fn [sym] - (swap! env update :namespaces dissoc sym))})) + (impl/load-pod + pod-spec + {:remove-ns + (fn [sym] + (swap! env update :namespaces dissoc sym)) + :resolve + (fn [sym] + (let [sym-ns (or (some-> (namespace sym) + symbol) + 'clojure.core) + sym-name (symbol (name sym))] + (or (get-in @env [:namespaces sym-ns sym-name]) + (let [v (sci/new-var sym {:predefined true})] + (swap! env assoc-in [:namespaces sym-ns sym-name] + v) + v))))})) namespaces (:namespaces pod)] (doseq [[ns-name vars] namespaces :let [sci-ns (sci/create-ns ns-name)]] (sci/binding [sci/ns sci-ns] (doseq [[var-name var-value] vars] (cond (ifn? var-value) - (swap! env assoc-in [:namespaces ns-name var-name] var-value) + (swap! env assoc-in [:namespaces ns-name var-name] + (sci/new-var + (symbol (str ns-name) (str var-name)) var-value)) (string? var-value) (sci/eval-string* ctx var-value))))) (sci/future (impl/processor pod)) diff --git a/test-pod/pod/test_pod.clj b/test-pod/pod/test_pod.clj index d1a0472..d4f4caf 100644 --- a/test-pod/pod/test_pod.clj +++ b/test-pod/pod/test_pod.clj @@ -58,6 +58,10 @@ (do (write {"format" (if (= format :json) "json" "edn") + "readers" {"my/tag" "identity" + ;; NOTE: this function is defined later, + ;; which should be supported + "my/other-tag" "pod.test-pod/read-other-tag"} "namespaces" [{"name" "pod.test-pod" "vars" (into [{"name" "add-sync"} @@ -69,7 +73,13 @@ {"name" "print-err"} {"name" "return-nil"} {"name" "do-twice" - "code" "(defmacro do-twice [x] `(do ~x ~x))"}] + "code" "(defmacro do-twice [x] `(do ~x ~x))"} + {"name" "reader-tag"} + ;; returns thing with other tag + {"name" "other-tag"} + ;; reads thing with other tag + {"name" "read-other-tag" + "code" "(defn read-other-tag [x] [x x])"}] dependents)}] "ops" {"shutdown" {}}}) (recur)) @@ -134,7 +144,17 @@ (write {"status" ["done"] "id" id - "value" "nil"})) + "value" "nil"}) + pod.test-pod/reader-tag + (write + {"status" ["done"] + "id" id + "value" "#my/tag[1 2 3]"}) + pod.test-pod/other-tag + (write + {"status" ["done"] + "id" id + "value" "#my/other-tag[1]"})) (recur)) :shutdown (System/exit 0)))))) (catch Exception e diff --git a/test-resources/test_program.clj b/test-resources/test_program.clj index 0bb6ee2..64293c5 100644 --- a/test-resources/test_program.clj +++ b/test-resources/test_program.clj @@ -43,6 +43,9 @@ (def x9 pod.test-pod/x9) +(def tagged (pod/reader-tag)) +(def other-tagged (pod/other-tag)) + (pods/unload-pod pod-id) (def successfully-removed (nil? (find-ns 'pod.test-pod))) @@ -58,4 +61,6 @@ (:ex-message @error-result) (:ex-data @error-result) successfully-removed - x9] + x9 + tagged + other-tagged] diff --git a/test/babashka/pods/test_common.clj b/test/babashka/pods/test_common.clj index bb2a6b1..861bc12 100644 --- a/test/babashka/pods/test_common.clj +++ b/test/babashka/pods/test_common.clj @@ -1,6 +1,5 @@ (ns babashka.pods.test-common (:require [clojure.java.io :as io] - [clojure.string :as str] [clojure.test :refer [is]])) (def test-program (slurp (io/file "test-resources" "test_program.clj"))) @@ -16,12 +15,15 @@ "Illegal arguments / {:args (1 2 3)}" nil 3 - "java.lang.String cannot be cast to java.lang.Number" + #"cast" {:args ["1" 2]} true - 9] ret)] - (if (string? expected) - (str/includes? actual expected) - (= expected actual))) + 9 + [1 2 3] + [[1] [1]]] + (concat ret (repeat ::nil)))] + (if (instance? java.util.regex.Pattern expected) + (is (re-find expected actual)) + (is (= expected actual)))) (is (= "(\"hello\" \"print\" \"this\" \"debugging\" \"message\")\n:foo\n:foo\n" (str out))) (is (= "(\"hello\" \"print\" \"this\" \"error\")\n" (str err))))