[#11] reader functions
This commit is contained in:
parent
05257ada1c
commit
62e0a1b074
8 changed files with 92 additions and 20 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,3 +2,4 @@
|
||||||
/target
|
/target
|
||||||
.lein-failures
|
.lein-failures
|
||||||
/pom.xml
|
/pom.xml
|
||||||
|
.lein-repl-history
|
||||||
|
|
|
||||||
11
README.md
11
README.md
|
|
@ -276,6 +276,17 @@ these messages to `*out*` and `*err*`. Stderr from the pod is redirected to
|
||||||
"err" "debug"}
|
"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
|
#### Error handling
|
||||||
|
|
||||||
Responses may contain an `ex-message` string and `ex-data` payload string (JSON
|
Responses may contain an `ex-message` string and `ex-data` payload string (JSON
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,9 @@
|
||||||
chans (:chans pod)
|
chans (:chans pod)
|
||||||
out-stream (:out pod)
|
out-stream (:out pod)
|
||||||
err-stream (:err pod)
|
err-stream (:err pod)
|
||||||
|
readers (:readers pod)
|
||||||
read-fn (case format
|
read-fn (case format
|
||||||
:edn edn/read-string
|
:edn #(edn/read-string {:readers readers} %)
|
||||||
:json #(cheshire/parse-string-strict % true))]
|
:json #(cheshire/parse-string-strict % true))]
|
||||||
(try
|
(try
|
||||||
(loop []
|
(loop []
|
||||||
|
|
@ -100,7 +101,7 @@
|
||||||
(println err))))
|
(println err))))
|
||||||
(recur))))
|
(recur))))
|
||||||
(catch Exception e
|
(catch Exception e
|
||||||
(binding [*out* err-stream]
|
(binding [*out* *err* #_err-stream]
|
||||||
(prn e))))))
|
(prn e))))))
|
||||||
|
|
||||||
(defn next-id []
|
(defn next-id []
|
||||||
|
|
@ -151,9 +152,18 @@
|
||||||
(let [[o _] (swap-vals! counter inc)]
|
(let [[o _] (swap-vals! counter inc)]
|
||||||
o))))
|
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
|
(defn load-pod
|
||||||
([pod-spec] (load-pod pod-spec nil))
|
([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)
|
(let [pod-spec (if (string? pod-spec) [pod-spec] pod-spec)
|
||||||
pb (ProcessBuilder. ^java.util.List pod-spec)
|
pb (ProcessBuilder. ^java.util.List pod-spec)
|
||||||
_ (.redirectError pb java.lang.ProcessBuilder$Redirect/INHERIT)
|
_ (.redirectError pb java.lang.ProcessBuilder$Redirect/INHERIT)
|
||||||
|
|
@ -168,7 +178,8 @@
|
||||||
reply (read stdout)
|
reply (read stdout)
|
||||||
format (-> (get reply "format") bytes->string keyword)
|
format (-> (get reply "format") bytes->string keyword)
|
||||||
ops (some->> (get reply "ops") keys (map keyword) set)
|
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 {:process p
|
||||||
:pod-spec pod-spec
|
:pod-spec pod-spec
|
||||||
:stdin stdin
|
:stdin stdin
|
||||||
|
|
@ -178,7 +189,8 @@
|
||||||
:ops ops
|
:ops ops
|
||||||
:out *out*
|
:out *out*
|
||||||
:err *err*
|
:err *err*
|
||||||
:remove-ns remove-ns}
|
:remove-ns remove-ns
|
||||||
|
:readers readers}
|
||||||
_ (add-shutdown-hook! #(destroy pod))
|
_ (add-shutdown-hook! #(destroy pod))
|
||||||
pod-namespaces (get reply "namespaces")
|
pod-namespaces (get reply "namespaces")
|
||||||
pod-id (or (when-let [ns (first pod-namespaces)]
|
pod-id (or (when-let [ns (first pod-namespaces)]
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,14 @@
|
||||||
(defn load-pod
|
(defn load-pod
|
||||||
([pod-spec] (load-pod pod-spec nil))
|
([pod-spec] (load-pod pod-spec nil))
|
||||||
([pod-spec _opts]
|
([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)]
|
namespaces (:namespaces pod)]
|
||||||
(doseq [[ns-sym v] namespaces]
|
(doseq [[ns-sym v] namespaces]
|
||||||
(binding [*ns* (load-string (format "(ns %s) *ns*" ns-sym))]
|
(binding [*ns* (load-string (format "(ns %s) *ns*" ns-sym))]
|
||||||
|
|
|
||||||
|
|
@ -10,17 +10,31 @@
|
||||||
(let [env (:env ctx)
|
(let [env (:env ctx)
|
||||||
pod (binding [*out* @sci/out
|
pod (binding [*out* @sci/out
|
||||||
*err* @sci/err]
|
*err* @sci/err]
|
||||||
(impl/load-pod pod-spec
|
(impl/load-pod
|
||||||
{:remove-ns
|
pod-spec
|
||||||
(fn [sym]
|
{:remove-ns
|
||||||
(swap! env update :namespaces dissoc sym))}))
|
(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)]
|
namespaces (:namespaces pod)]
|
||||||
(doseq [[ns-name vars] namespaces
|
(doseq [[ns-name vars] namespaces
|
||||||
:let [sci-ns (sci/create-ns ns-name)]]
|
:let [sci-ns (sci/create-ns ns-name)]]
|
||||||
(sci/binding [sci/ns sci-ns]
|
(sci/binding [sci/ns sci-ns]
|
||||||
(doseq [[var-name var-value] vars]
|
(doseq [[var-name var-value] vars]
|
||||||
(cond (ifn? var-value)
|
(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)
|
(string? var-value)
|
||||||
(sci/eval-string* ctx var-value)))))
|
(sci/eval-string* ctx var-value)))))
|
||||||
(sci/future (impl/processor pod))
|
(sci/future (impl/processor pod))
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,10 @@
|
||||||
(do (write {"format" (if (= format :json)
|
(do (write {"format" (if (= format :json)
|
||||||
"json"
|
"json"
|
||||||
"edn")
|
"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"
|
"namespaces"
|
||||||
[{"name" "pod.test-pod"
|
[{"name" "pod.test-pod"
|
||||||
"vars" (into [{"name" "add-sync"}
|
"vars" (into [{"name" "add-sync"}
|
||||||
|
|
@ -69,7 +73,13 @@
|
||||||
{"name" "print-err"}
|
{"name" "print-err"}
|
||||||
{"name" "return-nil"}
|
{"name" "return-nil"}
|
||||||
{"name" "do-twice"
|
{"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)}]
|
dependents)}]
|
||||||
"ops" {"shutdown" {}}})
|
"ops" {"shutdown" {}}})
|
||||||
(recur))
|
(recur))
|
||||||
|
|
@ -134,7 +144,17 @@
|
||||||
(write
|
(write
|
||||||
{"status" ["done"]
|
{"status" ["done"]
|
||||||
"id" id
|
"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))
|
(recur))
|
||||||
:shutdown (System/exit 0))))))
|
:shutdown (System/exit 0))))))
|
||||||
(catch Exception e
|
(catch Exception e
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,9 @@
|
||||||
|
|
||||||
(def x9 pod.test-pod/x9)
|
(def x9 pod.test-pod/x9)
|
||||||
|
|
||||||
|
(def tagged (pod/reader-tag))
|
||||||
|
(def other-tagged (pod/other-tag))
|
||||||
|
|
||||||
(pods/unload-pod pod-id)
|
(pods/unload-pod pod-id)
|
||||||
(def successfully-removed (nil? (find-ns 'pod.test-pod)))
|
(def successfully-removed (nil? (find-ns 'pod.test-pod)))
|
||||||
|
|
||||||
|
|
@ -58,4 +61,6 @@
|
||||||
(:ex-message @error-result)
|
(:ex-message @error-result)
|
||||||
(:ex-data @error-result)
|
(:ex-data @error-result)
|
||||||
successfully-removed
|
successfully-removed
|
||||||
x9]
|
x9
|
||||||
|
tagged
|
||||||
|
other-tagged]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
(ns babashka.pods.test-common
|
(ns babashka.pods.test-common
|
||||||
(:require [clojure.java.io :as io]
|
(:require [clojure.java.io :as io]
|
||||||
[clojure.string :as str]
|
|
||||||
[clojure.test :refer [is]]))
|
[clojure.test :refer [is]]))
|
||||||
|
|
||||||
(def test-program (slurp (io/file "test-resources" "test_program.clj")))
|
(def test-program (slurp (io/file "test-resources" "test_program.clj")))
|
||||||
|
|
@ -16,12 +15,15 @@
|
||||||
"Illegal arguments / {:args (1 2 3)}"
|
"Illegal arguments / {:args (1 2 3)}"
|
||||||
nil
|
nil
|
||||||
3
|
3
|
||||||
"java.lang.String cannot be cast to java.lang.Number"
|
#"cast"
|
||||||
{:args ["1" 2]}
|
{:args ["1" 2]}
|
||||||
true
|
true
|
||||||
9] ret)]
|
9
|
||||||
(if (string? expected)
|
[1 2 3]
|
||||||
(str/includes? actual expected)
|
[[1] [1]]]
|
||||||
(= expected actual)))
|
(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\" \"debugging\" \"message\")\n:foo\n:foo\n" (str out)))
|
||||||
(is (= "(\"hello\" \"print\" \"this\" \"error\")\n" (str err))))
|
(is (= "(\"hello\" \"print\" \"this\" \"error\")\n" (str err))))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue