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
154 lines (134 loc) · 4.67 KB
/
Copy pathcore.clj
File metadata and controls
154 lines (134 loc) · 4.67 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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
(ns clojure-python.core
(:use (clojure.core.*))
(:import (org.python.util PythonInterpreter)
(org.python.core.*)
(java.lang System String)
(java.util Properties )))
(defn init
"this may later take keywords and initialize other things
for now it is just used to specify python library paths"
([py libpath]
(doto py
(.exec "import sys")
(.exec (str "sys.path.append('" libpath "')"))))
([py libpath & more]
(init py libpath)
(apply init more)))
(defmacro py-import
"define a library using the same name it has in python
if multiple arguments are given, the first is assumed
to be a library that has been imported,
and the others are objects to import from this library"
([] nil)
([py] nil)
([py lib] ; import a library
`(do
(.exec ~py (str "import " ~(name lib)))
(def ~lib (. (. (. ~py getLocals)
__getitem__ ~(name lib))
__dict__))
(print ~lib)))
([py lib object] ; import object from a library
`(do (def ~object (.__finditem__
~lib
~(name object)))
(print ~object)))
([py lib object & more-objects] ; import multiple objects
`(do (py-import ~py ~lib ~object)
(py-import ~py ~lib ~@more-objects))))
(defn java2py
"to wrap java objects for input as jython, and unwrap
Jython output as java (thanks to Marc Downie on Clojure
list for suggesting this)"
[args]
(into-array
org.python.core.PyObject
(map
(fn [n] (. org.python.core.Py java2py n))
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))
(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]
`(def ~fun (py-fn ~lib ~fun)))
([lib fun & more-funs]
`(do (import-fn ~lib ~fun)
(import-fn ~lib ~@more-funs))))
(defn python-mod [py module & {:keys [funcs objs]}]
(do
(py-import py module)
(if (seq objs)
(dorun (map #(py-import py module %) objs)))
(if (seq funcs)
(dorun (map #(import-fn module %) funcs)))))
(defn properties [h]
{:pre (map? h)}
(let [p (Properties.)]
(dorun (map #(.setProperty p (str (first %)) (str (second %))) h))
p))
(defn python
[name & {:keys [post-properties argv sys-path modules]
:or {post-properties {} argv []}}]
(let [pre (System/getProperties)
post (properties post-properties)
args (into-array String (map str argv))]
;; Initialization has to occur before any interpreter instances are
;; created.
(PythonInterpreter/initialize pre post args)
(let [py (PythonInterpreter.)]
(if (seq sys-path)
(init py sys-path))
(loop [mods modules]
(println (count mods))
(if (seq mods)
(let [[m args] (first mods) k (:funcs args) o (:objs args)]
(python-mod py m :funcs k :objs o)
(recur (rest mods)))))
py)))
(defmacro __
"access attribute of class or attribute of attribute of (and so on) class"
([class attr]
`(.__findattr__ ~class ~(name attr)))
([class attr & more]
`(__ (__ ~class ~attr) ~@more)))
(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)))