|
3 | 3 | [libpython-clj.python.interop :as pyinterop] |
4 | 4 | [libpython-clj.python.interpreter :as pyinterp |
5 | 5 | :refer [with-gil with-interpreter]] |
6 | | - [libpython-clj.python.bridge]) |
| 6 | + [libpython-clj.python.object :as pyobj] |
| 7 | + [libpython-clj.python.bridge] |
| 8 | + [libpython-clj.jna :as pyjna] |
| 9 | + [tech.jna :as jna] |
| 10 | + [libpython-clj.jna.concrete.err :as py-err]) |
7 | 11 | (:import [com.sun.jna Pointer] |
| 12 | + [com.sun.jna.ptr PointerByReference] |
8 | 13 | [java.io Writer] |
9 | 14 | [libpython_clj.jna PyObject])) |
10 | 15 |
|
|
146 | 151 | global interpreter and reinitialization is unsupported cpython." |
147 | 152 | [] |
148 | 153 | (pyinterp/finalize!)) |
| 154 | + |
| 155 | + |
| 156 | +(defmacro with |
| 157 | + "Support for the 'with' statement in python." |
| 158 | + [bind-vec & body] |
| 159 | + (when-not (= 2 (count bind-vec)) |
| 160 | + (throw (Exception. "Bind vector must have 2 items"))) |
| 161 | + (let [varname (first bind-vec)] |
| 162 | + `(with-gil |
| 163 | + (let [~@bind-vec] |
| 164 | + (try |
| 165 | + (with-bindings |
| 166 | + {#'libpython-clj.python.interpreter/*python-error-handler* |
| 167 | + (fn [] |
| 168 | + (let [ptype# (PointerByReference.) |
| 169 | + pvalue# (PointerByReference.) |
| 170 | + ptraceback# (PointerByReference.) |
| 171 | + _# (pyjna/PyErr_Fetch ptype# pvalue# ptraceback#) |
| 172 | + ptype# (-> (jna/->ptr-backing-store ptype#) |
| 173 | + (pyobj/wrap-pyobject true)) |
| 174 | + pvalue# (-> (jna/->ptr-backing-store pvalue#) |
| 175 | + (pyobj/wrap-pyobject true)) |
| 176 | + ptraceback# (-> (jna/->ptr-backing-store ptraceback#) |
| 177 | + (pyobj/wrap-pyobject true))] |
| 178 | + ;;We own the references so they have to be released. |
| 179 | + (throw (ex-info "python error in flight" |
| 180 | + {:ptype ptype# |
| 181 | + :pvalue pvalue# |
| 182 | + :ptraceback ptraceback#}))))} |
| 183 | + (call-attr ~varname "__enter__") |
| 184 | + (let [retval# |
| 185 | + (do |
| 186 | + ~@body)] |
| 187 | + (call-attr ~varname "__exit__" nil nil nil) |
| 188 | + retval#)) |
| 189 | + (catch Throwable e# |
| 190 | + (let [einfo# (ex-data e#)] |
| 191 | + (if (= #{:ptype :pvalue :ptraceback} (set (keys einfo#))) |
| 192 | + (let [{ptype# :ptype |
| 193 | + pvalue# :pvalue |
| 194 | + ptraceback# :ptraceback} einfo# |
| 195 | + suppress-error?# (call-attr ~varname "__exit__" |
| 196 | + ptype# |
| 197 | + pvalue# |
| 198 | + ptraceback#)] |
| 199 | + (when (and ptype# pvalue# ptraceback# |
| 200 | + (not suppress-error?#)) |
| 201 | + (do |
| 202 | + ;;MAnuall incref here because we cannot detach the object |
| 203 | + ;;from our gc decref hook added above. |
| 204 | + (pyjna/Py_IncRef ptype#) |
| 205 | + (pyjna/Py_IncRef pvalue#) |
| 206 | + (pyjna/Py_IncRef ptraceback#) |
| 207 | + (pyjna/PyErr_Restore ptype# pvalue# ptraceback#) |
| 208 | + (pyinterp/check-error-throw)))) |
| 209 | + (throw e#))))))))) |
0 commit comments