Skip to content

Commit e547f21

Browse files
committed
clojisr 1.1.0
1 parent 4eaaac5 commit e547f21

9 files changed

Lines changed: 88 additions & 103 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. This change
66
- use devcontainer setup following template
77
- generate docs for r-required symbols
88
- TMD bump to 8.x
9+
- :err and :out messages are redirected to `*err*` and `*out*` (was `*out*` always)
910

1011
## [1.0.0]
1112
- `require-r` creates namespace as `r.namespace`, also `namespace` as an alias

deps.edn

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,21 @@
11
{:deps {org.clojure/clojure {:mvn/version "1.12.4"},
2-
org.clojure/tools.logging {:mvn/version "1.3.0"},
2+
org.clojure/tools.logging {:mvn/version "1.3.1"},
33
scicloj/tablecloth {:mvn/version "8.016"}
44
org.scicloj/clojisr-rengine {:mvn/version "0.1.2"}
5-
;; org.rosuda.REngine/Rserve {:mvn/version "1.8.1"}
6-
hiccup/hiccup {:mvn/version "2.0.0-RC3"}
7-
org.clojure/core.async {:mvn/version "1.6.681"}
8-
org.scicloj/kindly {:mvn/version "4-beta12"}}
5+
hiccup/hiccup {:mvn/version "2.0.0"}
6+
org.scicloj/kindly {:mvn/version "4-beta23"}}
97
:paths ["src"]
108
:aliases {:dev {:extra-paths ["resources" "notebooks"]
119
:jvm-opts ["-Dclojure.tools.logging.factory=clojure.tools.logging.impl/jul-factory"
1210
"-Djava.awt.headless=true"]
1311
:extra-deps {org.scicloj/clay {:mvn/version "2-beta16"}
1412
io.github.nextjournal/clerk {:mvn/version "0.7.418"}
1513
org.scicloj/kind-portal {:mvn/version "1-beta3"}
16-
djblue/portal {:mvn/version "0.58.5"}
17-
}}
14+
djblue/portal {:mvn/version "0.63.1"}}}
1815
:test {:extra-paths ["test"]
1916
:extra-deps {org.scicloj/clay {:mvn/version "2-beta8"}
2017
io.github.cognitect-labs/test-runner
21-
{:git/tag "v0.5.0" :git/sha "b3fd0d2"}}
18+
{:git/tag "v0.5.1" :git/sha "dfb30dd"}}
2219
:jvm-opts ["-Djava.awt.headless=true"]
2320
:main-opts ["-m" "cognitect.test-runner"]
2421
:exec-fn cognitect.test-runner.api/test}}}

src/clojisr/v1/applications/plotting.clj

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,33 @@
11
(ns ^:no-doc clojisr.v1.applications.plotting
2-
(:require [clojisr.v1.r :refer [r r->clj rsymbol]]
2+
(:require [clojisr.v1.r :as r :refer [r r->clj rsymbol require-r]]
33
[clojisr.v1.util :refer [exception-cause]]
4-
[clojisr.v1.require :refer [require-r]]
54
[clojure.tools.logging.readable :as log]
6-
[clojure.java.io :refer [make-parents]]
5+
[clojure.java.io :as io]
76
[scicloj.kindly.v4.kind :as kind])
87
(:import [java.io File]
98
[clojisr.v1.robject RObject]
109
[java.awt Graphics2D Image]
1110
[java.awt.image BufferedImage]
1211
[javax.swing ImageIcon]))
1312

14-
15-
13+
(defn- svglite-installed?
14+
"Check if svglite is available."
15+
[]
16+
(-> '(%in% "svglite" (rownames (installed.packages)))
17+
(r)
18+
(r->clj)
19+
(first)))
1620

1721
(def files->fns (delay
18-
(atom (let [_ (require-r '[grDevices])
19-
devices (select-keys (ns-publics 'r.grDevices) '[pdf png svg jpeg tiff bmp])]
20-
(if-let [jpg (get devices 'jpeg)]
21-
(let [devices (assoc devices 'jpg jpg)]
22-
(if (-> '(%in% "svglite" (rownames (installed.packages))) ;; check if svglite is available
23-
(r)
24-
(r->clj)
25-
(first))
26-
(assoc devices 'svg (rsymbol "svglite" "svglite"))
27-
(do (log/warn [::plotting {:message "We highly recommend installing of `svglite` package."}])
28-
devices)))
29-
devices)))))
30-
22+
(atom (let [_ (require-r '[grDevices])
23+
devices (select-keys (ns-publics 'r.grDevices) '[pdf png svg jpeg tiff bmp xfig postscript])
24+
svglite? (svglite-installed?)]
25+
(when-not svglite?
26+
(log/warn [::plotting {:message "We highly recommend installing of `svglite` package."}]))
27+
(cond-> devices
28+
(devices 'postscript) (assoc 'ps (devices 'postscript))
29+
(devices 'jpeg) (assoc 'jpg (devices 'jpeg))
30+
svglite? (assoc 'svg (rsymbol "svglite" "svglite")))))))
3131

3232
(defn use-svg!
3333
"Use from now on build-in svg device for plotting svg."
@@ -40,9 +40,6 @@
4040
[]
4141
(swap! @files->fns assoc 'svg (rsymbol "svglite" "svglite")))
4242

43-
44-
45-
4643
(defn plot->file
4744
[^String filename plotting-function-or-object & device-params]
4845
(let [r-print (delay (r "print"))
@@ -52,7 +49,7 @@
5249
(if-not (contains? @@files->fns extension)
5350
(log/warn [::plot->file {:message (format "%s filetype is not supported!" (name extension))}])
5451
(try
55-
(make-parents filename)
52+
(io/make-parents filename)
5653
(apply device :filename apath device-params)
5754
(let [the-plot-robject (try
5855
(if (instance? RObject plotting-function-or-object)

src/clojisr/v1/impl/rserve/proc.clj

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
(ns clojisr.v1.impl.rserve.proc
55
(:require [clojure.java.io :refer [reader writer]]
6+
[clojure.string :as str]
67
[clojure.java.shell :refer [sh]]
78
[clojisr.v1.util :refer [file-exists?]]
89
[clojure.tools.logging.readable :as log])
@@ -31,10 +32,9 @@
3132
"Find path to R executable"
3233
[]
3334
{:post [(not= % "")]}
34-
(apply str
35-
(-> (sh "which" "R")
36-
(get :out)
37-
(butlast)))) ; avoid trailing newline
35+
(-> (sh "which" "R")
36+
(get :out)
37+
(str/trim)))
3838

3939
(defn alive? [rserve]
4040
(when rserve
@@ -60,8 +60,8 @@
6060
rstr-temp)]
6161

6262
(spawn (r-path)
63-
"--no-save" ; don't save workspace when quitting
63+
"--no-save" ; don't save workspace when quitting
6464
"--no-restore-data"
6565
"--slave"
66-
"-e" ; evaluate (boot server)
66+
"-e" ; evaluate (boot server)
6767
rstr)))

src/clojisr/v1/impl/rserve/session.clj

Lines changed: 37 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,30 @@
66
[clojisr.v1.impl.rserve.call :as call]
77
[clojisr.v1.impl.rserve.packages :as packages]
88
[clojisr.v1.impl.rserve.printing :as printing]
9-
[clojure.core.async :as async]
109
[clojure.tools.logging.readable :as log]
1110
[clojisr.v1.util :refer [exception-cause get-free-port]])
1211
(:import [org.rosuda.REngine.Rserve RConnection]
1312
[java.io BufferedReader]))
1413

15-
(def defaults
16-
(atom
17-
{:host "localhost"
18-
:spawn-rserve? true}))
14+
(def defaults (atom {:host "localhost"
15+
:spawn-rserve? true}))
1916

2017
(defn close! [{:keys [^RConnection r-connection rserve]}]
2118
(when r-connection
2219
(.close r-connection))
2320
(when rserve
24-
(proc/close rserve))
25-
nil)
21+
(proc/close rserve)))
2622

2723
;; Session is valid when there is connection, also when we have rserve, process should be active
2824
;; if something is not true, ensure cleaning the rest and close the session
2925
(defn active?-or-close! [{:keys [^RConnection r-connection rserve]
30-
:as sess}]
26+
:as session}]
3127
(let [state (and r-connection
3228
(.isConnected r-connection)
3329
(if-not rserve
3430
true
3531
(proc/alive? rserve)))]
36-
(or state (close! sess))))
32+
(or state (close! session))))
3733

3834
(defrecord RserveSession [id
3935
session-args
@@ -44,12 +40,9 @@
4440
(close! session))
4541
(closed? [session]
4642
(not (active?-or-close! session)))
47-
(id [_session]
48-
id)
49-
(session-args [_session]
50-
session-args)
51-
(desc [_session]
52-
session-args)
43+
(id [_session] id)
44+
(session-args [_session] session-args)
45+
(desc [_session] session-args)
5346
(eval-r->java [session code]
5447
(log/debug [::eval-r->java {:code code
5548
:session-args (:session-args session)}])
@@ -58,16 +51,11 @@
5851
;; Unlike (.assign r-connection ...), the following approach
5952
;; allows for a varname like "abc$xyz".
6053
(locking r-connection
61-
(.eval
62-
r-connection
63-
(call/assignment varname java-obj)
64-
nil
65-
true)))
54+
(.eval r-connection (call/assignment varname java-obj) nil true)))
6655
(print-to-string [session r-obj]
6756
(printing/print-to-string session r-obj))
6857
(package-symbol->r-symbol-names [session package-symbol]
69-
(packages/package-symbol->r-symbol-names
70-
session package-symbol))
58+
(packages/package-symbol->r-symbol-names session package-symbol))
7159

7260
iprot/Engine
7361
(->nil [_] (rexp/->rexp-nil))
@@ -82,33 +70,37 @@
8270
(->named-list [_ ks vs] (rexp/->rexp-named-list ks vs))
8371
(native? [_ x] (rexp/rexp? x)))
8472

85-
(defn rserve-print-loop [{:keys [rserve]
86-
:as session}]
87-
(log/info [::rserve-print-loop {:action :started
88-
:session-args (:session-args session)}])
89-
(async/go-loop []
90-
(doseq [^BufferedReader reader
91-
(-> rserve
92-
((juxt :out :err)))]
93-
(loop []
94-
(when (.ready reader)
95-
(let [line (.readLine reader)]
96-
(when-not
97-
(re-find
98-
;; Just avoidingg this confusing message.
99-
#"(This session will block until Rserve is shut down)" line)
100-
(println line)))
101-
(recur))))
73+
(defn print-loop-task
74+
[{:keys [rserve] :as session}]
75+
(fn [] (doseq [[k output-stream] [[:out *out*] [:err *err*]]]
76+
(let [^BufferedReader reader (-> rserve k)]
77+
(binding [*out* output-stream]
78+
(loop []
79+
(when (.ready reader)
80+
(let [line (.readLine reader)]
81+
(when-not
82+
(re-find
83+
;; Just avoidingg this confusing message.
84+
#"(This session will block until Rserve is shut down)" line)
85+
(println line)))
86+
(recur))))))
10287
(Thread/sleep 100)
10388
(if (not (prot/closed? session))
10489
(recur)
10590
(log/info [::rserve-print-loop {:action :stopped
10691
:session-args (:session-args session)}]))))
10792

93+
(defn rserve-print-loop [session]
94+
(log/info [::rserve-print-loop {:action :started
95+
:session-args (:session-args session)}])
96+
(.start (Thread. (print-loop-task session))))
97+
98+
(defn make
99+
"Creates RServe session.
108100
109-
(defn make [id session-args]
110-
(let [{:keys [host port spawn-rserve? init-r]} (merge @defaults
111-
session-args)
101+
Process is spawned (optionally), then connection is established."
102+
[id session-args]
103+
(let [{:keys [host port spawn-rserve? init-r] :as args} (merge @defaults session-args)
112104
port (or port (get-free-port))
113105
rserve (when spawn-rserve?
114106
(proc/start-rserve port init-r))]
@@ -119,14 +111,14 @@
119111
(if (or (zero? attempts)
120112
(proc/alive? rserve))
121113
(when-not (proc/alive? rserve)
122-
(throw (Exception. "Can't create RServe process.")))
114+
(throw (ex-info "Can't create RServe process." args)))
123115
(do
124116
(log/warn [::rserve-spawn {:message "Rserve is not alive yet, waiting 0.5s"}])
125117
(recur (dec attempts))))))
126118

127119
(let [conn (loop [attempts (int 1)] ;; try 5 times to connect
128120
(when (> attempts 5) ;; throw an Exception when can't connect
129-
(throw (Exception. "Can't connect to RServe, please check host/port settings.")))
121+
(throw (ex-info "Can't connect to RServe, please check host/port settings." args)))
130122
(Thread/sleep (* attempts 200))
131123
(let [conn (try
132124
(RConnection. host port)
@@ -142,6 +134,6 @@
142134
session (->RserveSession id
143135
session-args
144136
conn
145-
rserve)]
137+
rserve)]
146138
(when rserve (rserve-print-loop session))
147139
session)))

src/clojisr/v1/impl/types.clj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646

4747
(defonce ^:private ^java.text.SimpleDateFormat date-format (java.text.SimpleDateFormat. "yyyy-MM-dd HH:mm:ss"))
4848
(defonce ^:private ^DateTimeFormatter java-time-date-format (DateTimeFormatter/ofPattern "yyyy-MM-dd HH:mm:ss"))
49+
4950
(def ^:private java-time-formatters
5051
{ZonedDateTime java-time-date-format
5152
LocalDateTime java-time-date-format

src/clojisr/v1/require.clj

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -99,18 +99,18 @@
9999
r-object))
100100

101101

102-
(defn symbols->add-to-ns [ns-symbol r-symbols generate-doc-strings?]
102+
(defn symbols->add-to-ns [ns-symbol r-symbols docstrings?]
103103
(doseq [[r-symbol r-object] r-symbols]
104104
(add-to-ns ns-symbol r-symbol r-object))
105105

106-
(when generate-doc-strings?
106+
(when docstrings?
107107
(run!
108108
(fn [[r-symbol r-object]]
109109
(assoc-doc-to-meta! ns-symbol r-symbol r-object))
110110
r-symbols)))
111111

112112

113-
(defn require-r-package [[package-symbol & {:keys [as refer generate-doc-strings?]}]]
113+
(defn require-r-package [[package-symbol & {:keys [as refer docstrings?]}]]
114114
(try
115115
(let [session (session/fetch-or-make nil)]
116116
(evl/eval-form `(library ~package-symbol) session)
@@ -119,26 +119,25 @@
119119
symbol)
120120
r-symbols (all-r-symbols-map package-symbol)]
121121

122-
;; r.package namespace
122+
;; r.package namespace
123123
(find-or-create-ns r-ns-symbol)
124-
(symbols->add-to-ns r-ns-symbol r-symbols generate-doc-strings?)
124+
(symbols->add-to-ns r-ns-symbol r-symbols docstrings?)
125125

126-
;; alias namespaces
127-
;; https://clojurians.zulipchat.com/#narrow/stream/224816-clojisr-dev/topic/require-r.20vs.20-require-python
128-
;; https://clojurians.zulipchat.com/#narrow/stream/224816-clojisr-dev/topic/clojisr.201.2E1.2E0/near/441026754
126+
;; alias namespaces
127+
;; https://clojurians.zulipchat.com/#narrow/stream/224816-clojisr-dev/topic/require-r.20vs.20-require-python
128+
;; https://clojurians.zulipchat.com/#narrow/stream/224816-clojisr-dev/topic/clojisr.201.2E1.2E0/near/441026754
129129
(if as
130130
(alias as r-ns-symbol)
131131
(alias package-symbol r-ns-symbol))
132132

133-
;; inject symbol into current namespace
133+
;; inject symbol into current namespace
134134
(when refer
135135
(let [this-ns-symbol (-> *ns* str symbol)]
136-
(symbols->add-to-ns
137-
this-ns-symbol
138-
(if (= refer :all)
139-
r-symbols
140-
(select-keys r-symbols refer))
141-
generate-doc-strings?)))))
136+
(symbols->add-to-ns this-ns-symbol
137+
(if (= refer :all)
138+
r-symbols
139+
(select-keys r-symbols refer))
140+
docstrings?)))))
142141
(catch Exception e
143142
(log/warn [::require-r-package {:package-symbol package-symbol
144143
:cause (exception-cause e)}])

test/clojisr/v1/help_test.clj

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77

88
(deftest help-docstring
9-
(r/require-r '[stats :generate-doc-strings? true])
9+
(r/require-r '[stats :docstrings? true])
1010
(is (str/starts-with?
1111
(->
1212
(ns-publics 'r.stats)
@@ -22,9 +22,7 @@
2222
"Fitting Linear")))
2323

2424
(deftest require-defauls-should-not-throws-exception
25-
26-
; should not crash
27-
(r/require-r '[base :generate-doc-strings? true])
25+
(r/require-r '[base :docstrings? true])
2826
(r/require-r '[stats])
2927
(r/require-r '[utils])
3028
(r/require-r '[graphics])

0 commit comments

Comments
 (0)