Skip to content

Commit e658148

Browse files
orollecnuernber
authored andcommitted
Added support for anaconda python in windows (#67)
1 parent 5be0e7a commit e658148

File tree

4 files changed

+76
-18
lines changed

4 files changed

+76
-18
lines changed

docs/Usage.md

+12-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ user> (require '[libpython-clj.python
4444
python-type]])
4545
nil
4646

47-
47+
; Mac and Linux
4848
user> (initialize!)
4949
Jun 30, 2019 4:47:39 PM clojure.tools.logging$eval7369$fn__7372 invoke
5050
INFO: executing python initialize!
@@ -53,6 +53,17 @@ INFO: Library python3.6m found at [:system "python3.6m"]
5353
Jun 30, 2019 4:47:39 PM clojure.tools.logging$eval7369$fn__7372 invoke
5454
INFO: Reference thread starting
5555
:ok
56+
57+
; Windows with Anaconda
58+
(initialize! ; Python executable
59+
:python-executable "C:\\Users\\USER\\AppData\\Local\\Continuum\\anaconda3\\python.exe"
60+
; Python Library
61+
:library-path "C:\\Users\\USER\\AppData\\Local\\Continuum\\anaconda3\\python37.dll"
62+
; Anacondas PATH environment to load native dlls of modules (numpy, etc.)
63+
:windows-anaconda-activate-bat "C:\\Users\\USER\\AppData\\Local\\Continuum\\anaconda3\\Scripts\\activate.bat"
64+
)
65+
...
66+
:ok
5667
```
5768

5869
This dynamically finds the python shared library and loads it using output from

src/libpython_clj/python.clj

+7-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
[libpython-clj.python.interpreter :as pyinterp]
55
[libpython-clj.python.object :as pyobj]
66
[libpython-clj.python.bridge :as pybridge]
7+
[libpython-clj.python.windows :as win]
78
[libpython-clj.jna :as libpy]
89
;;Protocol implementations purely for nd-ness
910
[libpython-clj.python.np-array]
@@ -235,7 +236,8 @@
235236
library-path
236237
python-home
237238
no-io-redirect?
238-
python-executable]}]
239+
python-executable
240+
windows-anaconda-activate-bat]}]
239241
(when-not @pyinterp/main-interpreter*
240242
(pyinterp/initialize! :program-name program-name
241243
:library-path library-path
@@ -245,14 +247,16 @@
245247
(pyinterop/register-bridge-type!)
246248
(when-not no-io-redirect?
247249
(pyinterop/setup-std-writer #'*err* "stderr")
248-
(pyinterop/setup-std-writer #'*out* "stdout")))
250+
(pyinterop/setup-std-writer #'*out* "stdout"))
251+
(if-not (nil? windows-anaconda-activate-bat)
252+
(win/setup-windows-conda! windows-anaconda-activate-bat)))
249253
:ok)
250254

251255

252256
(defn ptr-refcnt
253257
[item]
254258
(-> (libpy/as-pyobj item)
255-
(libpython_clj.jna.PyObject. )
259+
(libpython_clj.jna.PyObject.)
256260
(.ob_refcnt)))
257261

258262

src/libpython_clj/python/interpreter.clj

+14-14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
(ns libpython-clj.python.interpreter
2-
(:require [libpython-clj.jna :as libpy ]
2+
(:require [libpython-clj.jna :as libpy]
33
[libpython-clj.jna.base :as libpy-base]
44
[libpython-clj.python.gc :as pygc]
55
[libpython-clj.python.logging
@@ -30,13 +30,13 @@
3030
(let [{:keys [out err exit]}
3131
(sh executable "-c" "import sys, json;
3232
print(json.dumps(
33-
{\"platform\": sys.platform,
34-
\"prefix\": sys.prefix,
35-
\"base_prefix\": sys.base_prefix,
36-
\"executable\": sys.executable,
37-
\"base_exec_prefix\": sys.base_exec_prefix,
38-
\"exec_prefix\": sys.exec_prefix,
39-
\"version\": list(sys.version_info)[:3]}))")]
33+
{'platform': sys.platform,
34+
'prefix': sys.prefix,
35+
'base_prefix': sys.base_prefix,
36+
'executable': sys.executable,
37+
'base_exec_prefix': sys.base_exec_prefix,
38+
'exec_prefix': sys.exec_prefix,
39+
'version': list(sys.version_info)[:3]}))")]
4040
(when (= 0 exit)
4141
(json/read-str out :key-fn keyword))))
4242

@@ -93,7 +93,7 @@ print(json.dumps(
9393
;; ..: mac and windows are for sys.platform
9494
:linux "libpython%s.%sm.so$"
9595
:mac "libpython%s.%sm.dylib$"
96-
:windows "python%s.%sm.dll$")
96+
:win32 "python%s%s.dll$")
9797
major minor))))
9898

9999
(defn python-library-paths
@@ -131,9 +131,9 @@ print(json.dumps(
131131
(let [executable "python3.7"
132132
system-info (python-system-info executable)
133133
pyregex (python-library-regex system-info)]
134-
(python-library-paths system-info pyregex))
134+
(python-library-paths system-info pyregex)))
135135
;;=> ["/usr/lib/x86_64-linux-gnu/libpython3.7m.so" "/usr/lib/python3.7/config-3.7m-x86_64-linux-gnu/libpython3.7m.so"]
136-
)
136+
137137

138138
(defn- ignore-shell-errors
139139
[& args]
@@ -179,8 +179,8 @@ print(json.dumps(
179179
;;get the type of that item if we have seen it before.
180180
(defrecord Interpreter [
181181
interpreter-state* ;;Thread state, per interpreter
182-
shared-state* ;;state shared among all interpreters
183-
])
182+
shared-state*]) ;;state shared among all interpreters
183+
184184

185185

186186
;; Main interpreter booted up during initialize!
@@ -411,7 +411,7 @@ print(json.dumps(
411411
python-executable]
412412
:as options}]
413413
(when-not (main-interpreter)
414-
(log-info (str "Executing python initialize with options:" options) )
414+
(log-info (str "Executing python initialize with options:" options))
415415
(let [{:keys [python-home libname java-library-path-addendum] :as startup-info}
416416
(detect-startup-info options)
417417
library-names (cond

src/libpython_clj/python/windows.clj

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
(ns libpython-clj.python.windows
2+
(:require [libpython-clj.python.interop :refer [run-simple-string]]
3+
[clojure.java.shell :refer [sh]]
4+
[clojure.java.io :as io]
5+
[clojure.string :as s]))
6+
7+
(defn create-echo-path-bat! []
8+
"Creates temporary file to extract condas PATH environment variable"
9+
(let [tmp (java.io.File/createTempFile "echo-path" ".bat")]
10+
(spit tmp "echo %PATH%")
11+
(.toString tmp)))
12+
13+
(defn delete-echo-path-bat! [tmp]
14+
"Deletes temporary file"
15+
(io/delete-file tmp))
16+
17+
(defn- get-windows-anaconda-env-path [activate-bat echo-bat]
18+
"Get anacondas windows PATH environment variable to load native dlls for numpy etc. like python with anaconda does."
19+
(-> (sh "cmd.exe" "/K" (str activate-bat " & " echo-bat))
20+
:out
21+
(s/split #"\r\n")
22+
reverse
23+
(nth 2)))
24+
25+
26+
(defn- generate-python-set-env-path [path]
27+
"Double quote windows path separator \\ -> \\\\"
28+
(let [quoted (s/replace path "\\" "\\\\")]
29+
(str
30+
"import os;\n"
31+
"path = '" quoted "';\n"
32+
"os.environ['PATH'] = path;\n")))
33+
34+
(defn setup-windows-conda! [windows-conda-activate-bat]
35+
"Setup python PATH environment variable like in anaconda to be able to load native dlls for numpy etc. like anaconda does."
36+
(let [echo-bat (create-echo-path-bat!)]
37+
(->> (get-windows-anaconda-env-path
38+
windows-conda-activate-bat
39+
echo-bat)
40+
generate-python-set-env-path
41+
run-simple-string)
42+
(delete-echo-path-bat! echo-bat)))
43+

0 commit comments

Comments
 (0)