|
6 | 6 | [libpython-clj2.metadata :as pymeta]
|
7 | 7 | [clojure.datafy :refer [datafy nav]]
|
8 | 8 | [clojure.tools.logging :as log]
|
9 |
| - [clojure.core.protocols :as clj-proto])) |
| 9 | + [clojure.core.protocols :as clj-proto]) |
| 10 | + (:import (java.io File))) |
10 | 11 |
|
11 | 12 |
|
12 | 13 | (defn- parse-flags
|
|
19 | 20 | ;; First attempt is to filter keywords and make sure any keywords are
|
20 | 21 | ;; in supported-flags
|
21 | 22 | (let [total-flags (set (concat supported-flags [:as :refer :exclude
|
22 |
| - :* :all :bind-ns]))] |
| 23 | + :* :all :bind-ns :path]))] |
23 | 24 | (when-let [missing-flags (->> reqs
|
24 | 25 | (filter #(and (not (total-flags %))
|
25 | 26 | (keyword? %)))
|
|
100 | 101 | no-arglists? (:no-arglists flags)
|
101 | 102 | bind-ns? (:bind-ns flags)
|
102 | 103 | alias-name (:as etc)
|
| 104 | + path (:path etc) |
103 | 105 | exclude (into #{} (:exclude etc))
|
104 |
| - |
105 | 106 | refer-data (cond
|
106 | 107 | (= :all (:refer etc)) #{:all}
|
107 | 108 | (= :* (:refer etc)) #{:*}
|
108 | 109 | :else (into #{} (:refer etc)))
|
109 |
| - pyobj (py/path->py-obj (str module-name) :reload? reload?) |
| 110 | + pyobj (if path |
| 111 | + (let [cwd (pymeta/py-getcwd)] |
| 112 | + (try |
| 113 | + (pymeta/py-chdir path) |
| 114 | + (py/path->py-obj (str module-name) :reload? reload?) |
| 115 | + (finally |
| 116 | + (pymeta/py-chdir cwd)))) |
| 117 | + (py/path->py-obj (str module-name) :reload? reload?)) |
110 | 118 | existing-py-ns? (find-ns module-name)]
|
111 | 119 | (create-ns module-name)
|
112 | 120 |
|
|
170 | 178 | (into [(req-transform prefix base)] reqs))))))
|
171 | 179 |
|
172 | 180 |
|
| 181 | + |
173 | 182 | (defn require-python
|
174 | 183 | "## Basic usage ##
|
175 | 184 |
|
|
219 | 228 | ## Setting up classpath for custom modules ##
|
220 | 229 |
|
221 | 230 | Note: you may need to setup your PYTHONPATH correctly.
|
222 |
| - One technique to do this is, if your foo.py lives at |
223 |
| - /path/to/foodir/foo.py: |
224 |
| -
|
| 231 | + **WARNING**: This is very handy for local REPL development, |
| 232 | + ..: if you are going to AOT classes, |
| 233 | + ..: refer to the documentation on codegen |
| 234 | + ..: or your AOT compilation will fail. |
| 235 | + If your foo.py lives at /path/to/foodir/foo.py, the easiest |
| 236 | + way to do it is: |
| 237 | +
|
| 238 | + (require-python :from \"/path/to/foodir\" |
| 239 | + 'foo) ;; or |
| 240 | + (require-python \"/path/to/foodir\" |
| 241 | + 'foo) ;; or |
| 242 | + (require-python {:from \"/path/to/foodir\"} |
| 243 | + 'foo) |
| 244 | +
|
| 245 | + as you prefer. |
| 246 | +
|
| 247 | + Additionally, if you want to keep the namespacing as you have |
| 248 | + it in Python, you may prefer to use a relative import |
| 249 | + starting from a location of your choosing. If your |
| 250 | + os.getcwd() => /some/path/foo, |
| 251 | + and your directory structure looks something like: |
| 252 | +
|
| 253 | + /some $ tree |
| 254 | + . |
| 255 | + └── path |
| 256 | + ├── baz |
| 257 | + │ └── quux.py |
| 258 | + └── foo |
| 259 | + └── bar.py |
| 260 | +
|
| 261 | + |
| 262 | + (require-python :from \"path\" |
| 263 | + '[baz.quux :as quux] |
| 264 | + :from \"path/foo\" |
| 265 | + 'bar) |
| 266 | + |
| 267 | + is perfectly acceptable. It probably makes the most |
| 268 | + sense to keep you style consistent, but you can mix |
| 269 | + and match as you see fit between <path>, :from <path>, |
| 270 | + and {:from <path>}. <path> can either be a file or a |
| 271 | + directory. If it is a file, the Python path will be |
| 272 | + set to the directory containing that file. |
| 273 | +
|
| 274 | + You may also stack several require-pythons under one path: |
| 275 | +
|
| 276 | + (require-python {:from \"dir-a\"} |
| 277 | + 'a |
| 278 | + 'b |
| 279 | + 'c |
| 280 | + {:from \"dir-b\"} |
| 281 | + 'e.f |
| 282 | + 'g |
| 283 | + {:from \"dir-c} |
| 284 | + 'hi.there) |
| 285 | +
|
| 286 | + Other options more in keeping with traditional PYTHONPATH |
| 287 | + management include: |
| 288 | + |
225 | 289 | (require-python 'sys)
|
226 | 290 | (py/call-attr (py/get-attr sys \"path\")
|
227 | 291 | \"append\"
|
|
275 | 339 | (throw (Exception. "Invalid argument: %s" req))))
|
276 | 340 | :ok)
|
277 | 341 | ([req & reqs]
|
278 |
| - (require-python req) |
279 |
| - (when (not-empty reqs) |
280 |
| - (apply require-python reqs)) |
| 342 | + (cond (and (map? req) |
| 343 | + (contains? req :from) |
| 344 | + (seq reqs)) |
| 345 | + (apply require-python (:from req) reqs) |
| 346 | + (and (keyword? req) |
| 347 | + (= :from req) |
| 348 | + (string? (first reqs))) |
| 349 | + (apply require-python (first reqs) (rest reqs)) |
| 350 | + (and (string? req) |
| 351 | + (.isFile (File. req))) |
| 352 | + (let [file (File. req) |
| 353 | + cwd (pymeta/py-getcwd)] |
| 354 | + (apply require-python (str (.getParent file)) reqs)) |
| 355 | + (and (string? req) |
| 356 | + (.isDirectory (File. req))) |
| 357 | + (let [cwd (pymeta/py-getcwd)] |
| 358 | + (try |
| 359 | + (pymeta/py-chdir req) |
| 360 | + (apply require-python reqs) |
| 361 | + (finally |
| 362 | + (pymeta/py-chdir cwd)))) |
| 363 | + :else |
| 364 | + (do |
| 365 | + (require-python req) |
| 366 | + (when (not-empty reqs) |
| 367 | + (apply require-python reqs)))) |
281 | 368 | :ok))
|
282 | 369 |
|
283 | 370 |
|
|
0 commit comments