[#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
|
||||
.lein-failures
|
||||
/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"}
|
||||
```
|
||||
|
||||
#### 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
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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))]
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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))))
|
||||
|
|
|
|||
Loading…
Reference in a new issue