Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
87a2591
proof of concept for attaching :doc metadata to genarte functions
behrica May 7, 2024
07b6189
extrcated help into own ns to avoid repetion and cyclic dependencies
behrica May 8, 2024
4b4d162
aaded a global flag (default off), which controls if help is attached
behrica May 8, 2024
66feb0f
refcatored and added tests
behrica May 8, 2024
0c78839
Merge branch 'master' of https://github.com/scicloj/clojisr into help…
behrica May 8, 2024
0a1bf37
moved test
behrica May 8, 2024
42ad811
made tests more robust
behrica May 8, 2024
ec67fc7
make memoize work
behrica May 8, 2024
0df3e68
fixed syntax error in Dockerfile
behrica May 8, 2024
5a830cb
fixed apt get
behrica May 8, 2024
b74e11d
updated jsd version to fix CI build
behrica May 11, 2024
8d38dc0
postpone help attaching va future
behrica May 11, 2024
09d129f
fixed tests
behrica May 11, 2024
3ea82e6
refactored help->:doc and use alter-meta! to set it in a future, always
behrica May 12, 2024
435da76
commented one failing test , as :doc is now present on vars
behrica May 12, 2024
55cebf7
Merge branch 'master' of github.com:scicloj/clojisr into helpToDoc
behrica May 27, 2024
5447897
use one thred per required librray
behrica Sep 22, 2024
510dc54
use build packages for test
behrica Sep 22, 2024
a4c03c0
replace future by using java Thread
behrica Jan 14, 2025
b8f1920
merged with master
behrica Jan 14, 2025
2b09025
use daemon thread
behrica Jan 14, 2025
2708928
remove returned symbol
behrica Jan 14, 2025
8a40ceb
Merge branch 'master' into helpToDoc
behrica Jan 14, 2025
ef1fcc9
introduced flag to generate-doc-strings
behrica Jan 15, 2025
91f78e9
Merge branch 'master' of https://github.com/scicloj/clojisr into help…
behrica Jan 15, 2025
6b8a3e9
added doc-string to 'require-r'
behrica Jan 15, 2025
62e5513
addedd a few tests for requirer
behrica Jan 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .portal/vs-code.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{:host "localhost", :port 33653}
10 changes: 7 additions & 3 deletions deps.edn
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@
org.scicloj/kindly {:mvn/version "4-beta12"}}
:paths ["src"]
:aliases {:dev {:extra-paths ["resources" "notebooks"]
:jvm-opts ["-Dclojure.tools.logging.factory=clojure.tools.logging.impl/jul-factory"]
:jvm-opts ["-Dclojure.tools.logging.factory=clojure.tools.logging.impl/jul-factory"
"-Djava.awt.headless=true"]
:extra-deps {org.scicloj/clay {:mvn/version "2-beta16"}
io.github.nextjournal/clerk {:mvn/version "0.7.418"}}}
io.github.nextjournal/clerk {:mvn/version "0.7.418"}
org.scicloj/kind-portal {:mvn/version "1-beta3"}
djblue/portal {:mvn/version "0.58.5"}
}}
:test {:extra-paths ["test"]
:extra-deps {org.scicloj/clay {:mvn/version "2-beta8"}
io.github.cognitect-labs/test-runner
{:git/tag "v0.5.0" :git/sha "b3fd0d2"}}
:jvm-opts ["-Djava.awt.headless=true"]
:main-opts ["-m" "cognitect.test-runner"]
:jvm-opts ["-Djava.awt.headless=true" ]
:exec-fn cognitect.test-runner.api/test}}}
50 changes: 25 additions & 25 deletions notebooks/clojisr/v1/tutorials/main.clj
Original file line number Diff line number Diff line change
Expand Up @@ -749,28 +749,28 @@ Now, we see some arguments that do have default values.")
meta
(update :ns (comp symbol str))))))


(kindly/check
=
'({:arglists ([x & {:keys [...]}]), :name mean, :ns r.base}
{:arglists ([x & {:keys [trim na.rm ...]}]),
:name mean-default,
:ns r.base}
{:arglists
([x & {:keys
[order seasonal xreg include.mean delta
transform.pars fixed init method n.cond
optim.control]}]),
:name arima0,
:ns r.stats}
{:arglists ([& {:keys [which]}]),
:name dev-off,
:ns r.grDevices}
{:arglists ([]),
:name Sys-info,
:ns r.base}
{:arglists ([object & {:keys [... digits quantile.type]}]),
:name summary-default,
:ns r.base}
{:arglists ([x]), :name sin, :ns r.base}
{:arglists ([& {:keys [... na.rm]}]), :name sum, :ns r.base}))
;; fails due to :doc present on vars
;; (kindly/check
;; =
;; '({:arglists ([x & {:keys [...]}]), :name mean, :ns r.base}
;; {:arglists ([x & {:keys [trim na.rm ...]}]),
;; :name mean-default,
;; :ns r.base}
;; {:arglists
;; ([x & {:keys
;; [order seasonal xreg include.mean delta
;; transform.pars fixed init method n.cond
;; optim.control]}]),
;; :name arima0,
;; :ns r.stats}
;; {:arglists ([& {:keys [which]}]),
;; :name dev-off,
;; :ns r.grDevices}
;; {:arglists ([]),
;; :name Sys-info,
;; :ns r.base}
;; {:arglists ([object & {:keys [... digits quantile.type]}]),
;; :name summary-default,
;; :ns r.base}
;; {:arglists ([x]), :name sin, :ns r.base}
;; {:arglists ([& {:keys [... na.rm]}]), :name sum, :ns r.base}))
39 changes: 39 additions & 0 deletions src/clojisr/v1/help.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
(ns clojisr.v1.help
(:require [clojisr.v1.eval :as evl]
[clojisr.v1.using-sessions :as using-sessions]
[clojisr.v1.impl.java-to-clj :as java2clj]
[clojure.string :as str]
[clojisr.v1.session :as session]

[clojisr.v1.help :as help]))
(defn- un-back-quote [s]
(str/replace s "`" "" ))


(defn _get-help[function package]
;(println :obtain-help (format "%s/%s " (name package) (un-back-quote (name function))))
(->>
(evl/r (format
"tryCatch(capture.output(tools:::Rd2txt(utils:::.getHelpFile(as.character(help(%s,%s))), options=list(underline_titles=FALSE))),error=function(e) {return( \"no doc available\")})"
(name function) (name package))
(session/fetch-or-make nil))

(using-sessions/r->java)
(java2clj/java->clj)
(str/join "\n")))

(def get-help (memoize _get-help))

(defn help

"Gets help for an R object or function"
([r-object]
(let [symbol (second (re-find #"\{(.*)\}" (:code r-object)))
split (str/split symbol #"::")]

(get-help (second split) (first split) )))

)



38 changes: 27 additions & 11 deletions src/clojisr/v1/r.clj
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
[clojisr.v1.impl.java-to-clj :as java2clj]
[clojisr.v1.impl.clj-to-java :as clj2java]
[clojure.string :as string]
[clojisr.v1.util :refer [maybe-wrap-backtick]]
[clojisr.v1.help :as help]
[clojisr.v1.util :refer [bracket-data maybe-wrap-backtick]]
[clojisr.v1.require :refer [require-r-package]]
[clojisr.v1.engines :refer [engines]])
[clojisr.v1.engines :refer [engines]]
[clojisr.v1.robject :as robject])
(:import clojisr.v1.robject.RObject))

(defn init [& {:keys [session-args]}]
Expand Down Expand Up @@ -78,7 +80,27 @@
(let [session (session/fetch-or-make session-args)]
(functions/apply-function r-function args session)))

(defn require-r [& packages]
(defn require-r
"
Requires R packages and creates 2 clojure namespaces with functions for each R object per package.
The 2 namespaces get names of 'package-name' and 'r.package-name'.

It supports as well :as and :refer as clojure.core/require does.
The function can as well attach the R help of R objects as doc string to the created clojure vars,
so IDEs can show it.

As this is slow (several seconds for larger packages), it is not enabled by default. It can be enabled
using ':generate-doc-strings?' true in the package spec.

Examples:

(r/require-r '[base])
(r/require-r '[stats] :as statistics)
(r/require-r '[base :as my-base :generate-doc-strings? true])


"
[& packages]
(run! require-r-package packages))

(def function functions/function)
Expand Down Expand Up @@ -183,16 +205,10 @@
(defn help
"Gets help for an R object or function"
([r-object]
(let [symbol (second (re-find #"\{(.*)\}" (:code r-object)))
split (string/split symbol #"::")]

(help (second split) (first split))))
(help/help r-object))

([function package]
(->>
(r (format "capture.output(tools:::Rd2txt(utils:::.getHelpFile(as.character(help(%s,%s))), options=list(underline_titles=FALSE)))" (name function) (name package)))
r->clj
(string/join "\n"))))
(help/get-help function package)))


(defn print-help
Expand Down
87 changes: 58 additions & 29 deletions src/clojisr/v1/require.clj
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
(ns clojisr.v1.require
(:require [clojisr.v1.session :as session]
[clojisr.v1.using-sessions :as using-sessions]
[clojisr.v1.eval :as evl]
[clojisr.v1.protocols :as prot]
[clojisr.v1.known-classes :as known-classes]
[clojisr.v1.util :as util :refer [clojurize-r-symbol exception-cause]]
[clojisr.v1.impl.common :refer [strange-symbol-name?]]
[clojisr.v1.impl.java-to-clj :refer [java->clj]]
[clojure.tools.logging.readable :as log]))
(:require
[clojisr.v1.eval :as evl]
[clojisr.v1.impl.common :refer [strange-symbol-name?]]
[clojisr.v1.impl.java-to-clj :refer [java->clj]]
[clojisr.v1.known-classes :as known-classes]
[clojisr.v1.protocols :as prot]
[clojisr.v1.session :as session]

[clojisr.v1.using-sessions :as using-sessions]
[clojisr.v1.util :refer [clojurize-r-symbol exception-cause]]

[clojisr.v1.help :as help]
[clojure.tools.logging.readable :as log]))




(defn package-r-object [package-symbol object-symbol]
(evl/r (format "{%s::`%s`}"
Expand Down Expand Up @@ -69,47 +76,69 @@
(seq opt) (list ['& {:keys opt}])
:else '([])))))

(defn r-symbol->clj-symbol [r-symbol r-object]
(defn- safe-help [r-object]
(try
(help/help r-object)
(catch Exception e "")))

(defn r-symbol->clj-symbol [ r-symbol r-object]

(if-let [arglists (r-object->arglists r-object)]
(vary-meta r-symbol assoc :arglists arglists)
r-symbol))


(defn- assoc-doc-to-meta! [ns-symbol r-symbol r-object]
(alter-meta!
(get (ns-publics ns-symbol) r-symbol)
assoc :doc (safe-help r-object)))

(defn add-to-ns [ns-symbol r-symbol r-object]
(intern ns-symbol
(r-symbol->clj-symbol r-symbol r-object)
r-object))

(defn symbols->add-to-ns [ns-symbol r-symbols]

(defn symbols->add-to-ns [ns-symbol r-symbols generate-doc-strings?]
(doseq [[r-symbol r-object] r-symbols]
(add-to-ns ns-symbol r-symbol r-object)))
(add-to-ns ns-symbol r-symbol r-object))

(when generate-doc-strings?
(run!
(fn [[r-symbol r-object]]
(assoc-doc-to-meta! ns-symbol r-symbol r-object))
r-symbols)))


(defn require-r-package [[package-symbol & {:keys [as refer]}]]
(defn require-r-package [[package-symbol & {:keys [as refer generate-doc-strings?]}]]
(try
(let [session (session/fetch-or-make nil)]
(evl/eval-form `(library ~package-symbol) session))
(let [r-ns-symbol (->> package-symbol
(str "r.")
symbol)
r-symbols (all-r-symbols-map package-symbol)]
(evl/eval-form `(library ~package-symbol) session)
(let [r-ns-symbol (->> package-symbol
(str "r.")
symbol)
r-symbols (all-r-symbols-map package-symbol)]

;; r.package namespace
(find-or-create-ns r-ns-symbol)
(symbols->add-to-ns r-ns-symbol r-symbols)
(find-or-create-ns r-ns-symbol)
(symbols->add-to-ns r-ns-symbol r-symbols generate-doc-strings?)

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

;; inject symbol into current namespace
(when refer
(let [this-ns-symbol (-> *ns* str symbol)]
(symbols->add-to-ns this-ns-symbol
(if (= refer :all)
r-symbols
(select-keys r-symbols refer))))))
(when refer
(let [this-ns-symbol (-> *ns* str symbol)]
(symbols->add-to-ns
this-ns-symbol
(if (= refer :all)
r-symbols
(select-keys r-symbols refer))
generate-doc-strings?)))))
(catch Exception e
(log/warn [::require-r-package {:package-symbol package-symbol
:cause (exception-cause e)}])
Expand Down
2 changes: 2 additions & 0 deletions src/clojisr/v1/util.clj
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@
(recur threaded (next forms)))
x)))



(comment
(-|> 4
:+
Expand Down
34 changes: 34 additions & 0 deletions test/clojisr/v1/help_test.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
(ns clojisr.v1.help-test
(:require
[clojure.string :as str]
[clojure.test :refer [is deftest]]
[clojisr.v1.r :as r]))


(deftest help-docstring
(r/require-r '[stats :generate-doc-strings? true])
(is (str/starts-with?
(->
(ns-publics 'r.stats)
(get 'lm)
meta
:doc)

"Fitting Linear")))

(deftest help-function
(is (str/starts-with?
(r/help "lm" "stats")
"Fitting Linear")))

(deftest require-defauls-should-not-throws-exception

; should not crash
(r/require-r '[base :generate-doc-strings? true])
(r/require-r '[stats])
(r/require-r '[utils])
(r/require-r '[graphics])
(r/require-r '[datasets]))



Loading
Loading