forked from rplevy/clojure-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcore.clj
More file actions
136 lines (119 loc) · 4.09 KB
/
core.clj
File metadata and controls
136 lines (119 loc) · 4.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
(ns clojure-python.core
(:require [clojure [string :as str]])
(:import [org.python.core Py PyObject]
[org.python.util PythonInterpreter]))
(declare ^:dynamic *interp*)
(defn append-paths
"Appends a vector of paths to the python system path."
[libpaths]
(.exec *interp* "import sys")
(doseq [p libpaths]
(.exec *interp* (str "sys.path.append('" p "')")))
*interp*)
(defn set-encoding
"Sets encoding for reading libs"
[encoding]
(.exec *interp* "import sys")
(.exec *interp* "reload(sys)")
(.exec *interp* (str "sys.setdefaultencoding('" encoding "')"))
*interp*)
(defn init
"Establish a global python interpreter. The init function is only usefully
called once. Alternatively, only use with-interpreter."
[{:keys [libpaths] :as options}]
(defonce ^:dynamic
^{:doc "root binding serves as global python interpreter"}
*interp*
(PythonInterpreter.))
(append-paths libpaths))
(defmacro with-interpreter
"Dynamically bind a new python interpreter for the calling context."
[{:keys [libpaths encoding] :as options} & body]
`(binding [*interp* (PythonInterpreter.)]
(append-paths ~libpaths)
(set-encoding ~encoding)
~@body))
(defmacro py-import-lib
"Import lib. Defaults to use same name it has in python. If it is something
like foo.bar, the name is bar."
[lib & libs]
(let [lib-sym (or (last libs) lib)
lib-strs (map name (cons lib libs))
py-name (str/join "." lib-strs)]
`(do (.exec *interp* (str "import " ~py-name))
(def ~lib-sym
(-> *interp*
.getLocals
(.__getitem__ ~(first lib-strs))
~@(map (fn [lib#] `(.__getattr__ ~lib#))
(next lib-strs))
.__dict__)))))
(defmacro py-import-obj
"Import objects from lib."
[lib obj & objs]
(cons 'do
(map
(fn [o#]
`(def ~o# (.__finditem__ ~lib ~(name o#))))
(cons obj objs))))
(defmacro py-fn
"Create a native clojure function applying the python wrapper calls on a python
function at the top level of the library use this where lambda is preferred
over named function."
[lib fun]
`(let [f# (.__finditem__
~lib
~(name fun))]
(fn [& args#]
(call f# args#))))
(defmacro import-fn
"This is like import but it defines the imported item as a native function that
applies the python wrapper calls."
[lib fun & funs]
(cons 'do
(map
(fn [fun]
`(def ~fun (py-fn ~lib ~fun)))
(cons fun funs))))
(defmacro __
"Access attribute of class or attribute of attribute of (and so on) class."
([class attr]
`(.__findattr__ ~class ~(name attr)))
([class attr & attrs]
`(__ (__ ~class ~attr) ~@attrs)))
(defmacro _>
"Call attribute as a method.
Basic usage: (_> [class attrs ...] args ...)
Usage with keyword args: (_> [class attrs ...] args ... :key arg :key arg)
Keyword args must come after any non-keyword args"
([[class & attrs] & args]
(let [keywords (map name (filter keyword? args))
non-keywords (filter (fn [a] (not (keyword? a))) args)]
`(call (__ ~class ~@attrs) [~@non-keywords] ~@keywords))))
(defn dir
"It's slightly nicer to call the dir method in this way."
[x] (seq (.__dir__ x)))
(defn pyobj-nth
"Nth item in a 'PyObjectDerived'."
[o i] (.__getitem__ o i))
(defn pyobj-range
"Access 'PyObjectDerived' items as non-lazy range."
[o start end] (for [i (range start end)] (pyobj-nth o i)))
(defn pyobj-iterate
"Access 'PyObjectDerived' items as Lazy Seq."
[pyobj] (lazy-seq (.__iter__ pyobj)))
(defn java2py
"To wrap java objects for input as jython, and unwrap Jython output as java."
[args]
(into-array
PyObject
(map #(. Py java2py %) args)))
(defn call
"The first len(args)-len(keywords) members of args[] are plain arguments. The
last len(keywords) arguments are the values of the keyword arguments."
[fun args & key-args]
(.__tojava__
(if key-args
(.__call__ fun (java2py args) (into-array java.lang.String key-args))
(.__call__ fun (java2py args)))
Object))