[#11] reader functions

This commit is contained in:
Michiel Borkent 2020-05-22 12:45:01 -03:00
parent 05257ada1c
commit 62e0a1b074
8 changed files with 92 additions and 20 deletions

1
.gitignore vendored
View file

@ -2,3 +2,4 @@
/target
.lein-failures
/pom.xml
.lein-repl-history

View file

@ -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

View file

@ -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)]

View file

@ -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))]

View file

@ -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))

View file

@ -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

View file

@ -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]

View file

@ -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))))