aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/org/ob-python.el
diff options
context:
space:
mode:
authorKyle Meyer2024-06-09 13:06:28 -0400
committerKyle Meyer2024-06-09 16:54:38 -0400
commit5a125fb5a9736bd3c67cf6ff9acc185d8e2260e2 (patch)
treedcadcaa7be32c0edb4087e0d4139368f9409083e /lisp/org/ob-python.el
parente1cc2d1f61836e1da08817524999878b639e6761 (diff)
downloademacs-5a125fb5a9736bd3c67cf6ff9acc185d8e2260e2.tar.gz
emacs-5a125fb5a9736bd3c67cf6ff9acc185d8e2260e2.zip
Update to Org 9.7.3
Diffstat (limited to 'lisp/org/ob-python.el')
-rw-r--r--lisp/org/ob-python.el450
1 files changed, 281 insertions, 169 deletions
diff --git a/lisp/org/ob-python.el b/lisp/org/ob-python.el
index 1a442a5a08f..89cdf4c4795 100644
--- a/lisp/org/ob-python.el
+++ b/lisp/org/ob-python.el
@@ -36,53 +36,70 @@
36(require 'org-macs) 36(require 'org-macs)
37(require 'python) 37(require 'python)
38 38
39(declare-function py-shell "ext:python-mode" (&rest args))
40(declare-function py-choose-shell "ext:python-mode" (&optional shell))
41(declare-function py-shell-send-string "ext:python-mode" (strg &optional process))
42
43(defvar org-babel-tangle-lang-exts) 39(defvar org-babel-tangle-lang-exts)
44(add-to-list 'org-babel-tangle-lang-exts '("python" . "py")) 40(add-to-list 'org-babel-tangle-lang-exts '("python" . "py"))
45 41
46(defvar org-babel-default-header-args:python '()) 42(defvar org-babel-default-header-args:python '())
47 43
48(defcustom org-babel-python-command "python" 44(defconst org-babel-header-args:python
49 "Name of the command for executing Python code." 45 '((return . :any)
50 :version "24.4" 46 (python . :any)
51 :package-version '(Org . "8.0") 47 (async . ((yes no))))
48 "Python-specific header arguments.")
49
50(defcustom org-babel-python-command 'auto
51 "Command (including arguments) for interactive and non-interactive Python code.
52When not `auto', it overrides `org-babel-python-command-session'
53and `org-babel-python-command-nonsession'."
54 :package-version '(Org . "9.7")
52 :group 'org-babel 55 :group 'org-babel
53 :type 'string) 56 :type '(choice string (const auto)))
57
58(defcustom org-babel-python-command-session 'auto
59 "Command (including arguments) for starting interactive Python sessions.
60If `auto' (the default), uses the values from
61`python-shell-interpreter' and `python-shell-interpreter-args'.
62If `org-babel-python-command' is set, then it overrides this
63option."
64 :package-version '(Org . "9.7")
65 :group 'org-babel
66 :type '(choice string (const auto)))
54 67
55(defcustom org-babel-python-mode 68(defcustom org-babel-python-command-nonsession "python"
56 (if (featurep 'python-mode) 'python-mode 'python) 69 "Command (including arguments) for executing non-interactive Python code.
57 "Preferred python mode for use in running python interactively. 70If `org-babel-python-command' is set, then it overrides this option."
58This will typically be either `python' or `python-mode'." 71 :package-version '(Org . "9.7")
59 :group 'org-babel 72 :group 'org-babel
60 :version "24.4" 73 :type 'string)
61 :package-version '(Org . "8.0")
62 :type 'symbol)
63 74
64(defcustom org-babel-python-hline-to "None" 75(defcustom org-babel-python-hline-to "None"
65 "Replace hlines in incoming tables with this when translating to python." 76 "Replace hlines in incoming tables with this when translating to python."
66 :group 'org-babel 77 :group 'org-babel
67 :version "24.4"
68 :package-version '(Org . "8.0") 78 :package-version '(Org . "8.0")
69 :type 'string) 79 :type 'string)
70 80
71(defcustom org-babel-python-None-to 'hline 81(defcustom org-babel-python-None-to 'hline
72 "Replace `None' in python tables with this before returning." 82 "Replace `None' in python tables with this before returning."
73 :group 'org-babel 83 :group 'org-babel
74 :version "24.4"
75 :package-version '(Org . "8.0") 84 :package-version '(Org . "8.0")
76 :type 'symbol) 85 :type 'symbol)
77 86
87(defun org-babel-python-associate-session (session)
88 "Associate Python code buffer with an Python session.
89Make SESSION without earmuffs be the Python buffer name."
90 (setq-local python-shell-buffer-name
91 (org-babel-python-without-earmuffs session)))
92
78(defun org-babel-execute:python (body params) 93(defun org-babel-execute:python (body params)
79 "Execute a block of Python code with Babel. 94 "Execute Python BODY according to PARAMS.
80This function is called by `org-babel-execute-src-block'." 95This function is called by `org-babel-execute-src-block'."
81 (let* ((org-babel-python-command 96 (let* ((org-babel-python-command
82 (or (cdr (assq :python params)) 97 (or (cdr (assq :python params))
83 org-babel-python-command)) 98 org-babel-python-command))
84 (session (org-babel-python-initiate-session 99 (session (org-babel-python-initiate-session
85 (cdr (assq :session params)))) 100 (cdr (assq :session params))))
101 (graphics-file (and (member "graphics" (assq :result-params params))
102 (org-babel-graphical-output-file params)))
86 (result-params (cdr (assq :result-params params))) 103 (result-params (cdr (assq :result-params params)))
87 (result-type (cdr (assq :result-type params))) 104 (result-type (cdr (assq :result-type params)))
88 (return-val (when (eq result-type 'value) 105 (return-val (when (eq result-type 'value)
@@ -98,7 +115,7 @@ This function is called by `org-babel-execute-src-block'."
98 (format (if session "\n%s" "\nreturn %s") return-val)))) 115 (format (if session "\n%s" "\nreturn %s") return-val))))
99 (result (org-babel-python-evaluate 116 (result (org-babel-python-evaluate
100 session full-body result-type 117 session full-body result-type
101 result-params preamble async))) 118 result-params preamble async graphics-file)))
102 (org-babel-reassemble-table 119 (org-babel-reassemble-table
103 result 120 result
104 (org-babel-pick-name (cdr (assq :colname-names params)) 121 (org-babel-pick-name (cdr (assq :colname-names params))
@@ -130,8 +147,63 @@ VARS contains resolved variable references."
130 147
131;; helper functions 148;; helper functions
132 149
150(defconst org-babel-python--output-graphics-wrapper "\
151import matplotlib.pyplot
152matplotlib.pyplot.gcf().clear()
153%s
154matplotlib.pyplot.savefig('%s')"
155 "Format string for saving Python graphical output.
156Has two %s escapes, for the Python code to be evaluated, and the
157file to save the graphics to.")
158
159(defconst org-babel-python--def-format-value "\
160def __org_babel_python_format_value(result, result_file, result_params):
161 with open(result_file, 'w') as f:
162 if 'graphics' in result_params:
163 result.savefig(result_file)
164 elif 'pp' in result_params:
165 import pprint
166 f.write(pprint.pformat(result))
167 elif 'list' in result_params and isinstance(result, dict):
168 f.write(str(['{} :: {}'.format(k, v) for k, v in result.items()]))
169 else:
170 if not set(result_params).intersection(\
171['scalar', 'verbatim', 'raw']):
172 def dict2table(res):
173 if isinstance(res, dict):
174 return [(k, dict2table(v)) for k, v in res.items()]
175 elif isinstance(res, list) or isinstance(res, tuple):
176 return [dict2table(x) for x in res]
177 else:
178 return res
179 if 'table' in result_params:
180 result = dict2table(result)
181 try:
182 import pandas
183 except ImportError:
184 pass
185 else:
186 if isinstance(result, pandas.DataFrame) and 'table' in result_params:
187 result = [[result.index.name or ''] + list(result.columns)] + \
188[None] + [[i] + list(row) for i, row in result.iterrows()]
189 elif isinstance(result, pandas.Series) and 'table' in result_params:
190 result = list(result.items())
191 try:
192 import numpy
193 except ImportError:
194 pass
195 else:
196 if isinstance(result, numpy.ndarray):
197 if 'table' in result_params:
198 result = result.tolist()
199 else:
200 result = repr(result)
201 f.write(str(result))"
202 "Python function to format value result and save it to file.")
203
133(defun org-babel-variable-assignments:python (params) 204(defun org-babel-variable-assignments:python (params)
134 "Return a list of Python statements assigning the block's variables." 205 "Return a list of Python statements assigning the block's variables.
206The assignments are defined in PARAMS."
135 (mapcar 207 (mapcar
136 (lambda (pair) 208 (lambda (pair)
137 (format "%s=%s" 209 (format "%s=%s"
@@ -153,9 +225,13 @@ specifying a variable of the same value."
153 225
154(defun org-babel-python-table-or-string (results) 226(defun org-babel-python-table-or-string (results)
155 "Convert RESULTS into an appropriate elisp value. 227 "Convert RESULTS into an appropriate elisp value.
156If the results look like a list or tuple, then convert them into an 228If the results look like a list or tuple (but not a dict), then
157Emacs-lisp table, otherwise return the results as a string." 229convert them into an Emacs-lisp table. Otherwise return the
158 (let ((res (org-babel-script-escape results))) 230results as a string."
231 (let ((res (if (and (> (length results) 0)
232 (string-equal "{" (substring results 0 1)))
233 results ;don't covert dicts to elisp
234 (org-babel-script-escape results))))
159 (if (listp res) 235 (if (listp res)
160 (mapcar (lambda (el) (if (eq el 'None) 236 (mapcar (lambda (el) (if (eq el 'None)
161 org-babel-python-None-to el)) 237 org-babel-python-None-to el))
@@ -169,6 +245,7 @@ Emacs-lisp table, otherwise return the results as a string."
169 (cdr (assoc session org-babel-python-buffers))) 245 (cdr (assoc session org-babel-python-buffers)))
170 246
171(defun org-babel-python-with-earmuffs (session) 247(defun org-babel-python-with-earmuffs (session)
248 "Return SESSION name as string, ensuring *...* around."
172 (let ((name (if (stringp session) session (format "%s" session)))) 249 (let ((name (if (stringp session) session (format "%s" session))))
173 (if (and (string= "*" (substring name 0 1)) 250 (if (and (string= "*" (substring name 0 1))
174 (string= "*" (substring name (- (length name) 1)))) 251 (string= "*" (substring name (- (length name) 1))))
@@ -176,74 +253,113 @@ Emacs-lisp table, otherwise return the results as a string."
176 (format "*%s*" name)))) 253 (format "*%s*" name))))
177 254
178(defun org-babel-python-without-earmuffs (session) 255(defun org-babel-python-without-earmuffs (session)
256 "Return SESSION name as string, without *...* around."
179 (let ((name (if (stringp session) session (format "%s" session)))) 257 (let ((name (if (stringp session) session (format "%s" session))))
180 (if (and (string= "*" (substring name 0 1)) 258 (if (and (string= "*" (substring name 0 1))
181 (string= "*" (substring name (- (length name) 1)))) 259 (string= "*" (substring name (- (length name) 1))))
182 (substring name 1 (- (length name) 1)) 260 (substring name 1 (- (length name) 1))
183 name))) 261 name)))
184 262
185(defvar py-which-bufname) 263(defun org-babel-session-buffer:python (session &optional _)
186(defvar python-shell-buffer-name) 264 "Return session buffer name for SESSION."
265 (or (org-babel-python-session-buffer session)
266 (org-babel-python-with-earmuffs session)))
267
268(defun org-babel-python--python-util-comint-end-of-output-p ()
269 "Return non-nil if the last prompt matches input prompt.
270Backport of `python-util-comint-end-of-output-p' to emacs28. To
271be removed after minimum supported version reaches emacs29."
272 (when-let ((prompt (python-util-comint-last-prompt)))
273 (python-shell-comint-end-of-output-p
274 (buffer-substring-no-properties
275 (car prompt) (cdr prompt)))))
276
277(defun org-babel-python--command (is-session)
278 "Helper function to return the Python command.
279This checks `org-babel-python-command', and then
280`org-babel-python-command-session' (if IS-SESSION) or
281`org-babel-python-command-nonsession' (if not IS-SESSION). If
282IS-SESSION, this might return `nil', which means to use
283`python-shell-calculate-command'."
284 (or (unless (eq org-babel-python-command 'auto)
285 org-babel-python-command)
286 (if is-session
287 (unless (eq org-babel-python-command-session 'auto)
288 org-babel-python-command-session)
289 org-babel-python-command-nonsession)))
290
187(defvar-local org-babel-python--initialized nil 291(defvar-local org-babel-python--initialized nil
188 "Flag used to mark that python session has been initialized.") 292 "Flag used to mark that python session has been initialized.")
293(defun org-babel-python--setup-session ()
294 "Babel Python session setup code, to be run once per session.
295Function should be run from within the Python session buffer.
296This is often run as a part of `python-shell-first-prompt-hook',
297unless the Python session was created outside Org."
298 (python-shell-send-string-no-output org-babel-python--def-format-value)
299 (setq-local org-babel-python--initialized t))
189(defun org-babel-python-initiate-session-by-key (&optional session) 300(defun org-babel-python-initiate-session-by-key (&optional session)
190 "Initiate a python session. 301 "Initiate a python session.
191If there is not a current inferior-process-buffer in SESSION 302If there is not a current inferior-process-buffer matching
192then create. Return the initialized session." 303SESSION then create it. If inferior process already
304exists (e.g. if it was manually started with `run-python'), make
305sure it's configured to work with ob-python. If session has
306already been configured as such, do nothing. Return the
307initialized session."
193 (save-window-excursion 308 (save-window-excursion
194 (let* ((session (if session (intern session) :default)) 309 (let* ((session (if session (intern session) :default))
195 (py-buffer (org-babel-python-session-buffer session)) 310 (py-buffer (org-babel-session-buffer:python session))
196 (cmd (if (member system-type '(cygwin windows-nt ms-dos)) 311 (python-shell-buffer-name
197 (concat org-babel-python-command " -i") 312 (org-babel-python-without-earmuffs py-buffer))
198 org-babel-python-command))) 313 (existing-session-p (comint-check-proc py-buffer))
199 (cond 314 (cmd (org-babel-python--command t)))
200 ((eq 'python org-babel-python-mode) ; python.el 315 (if cmd
201 (unless py-buffer 316 (let* ((cmd-split (split-string-and-unquote cmd))
202 (setq py-buffer (org-babel-python-with-earmuffs session))) 317 (python-shell-interpreter (car cmd-split))
203 (let ((python-shell-buffer-name 318 (python-shell-interpreter-args
204 (org-babel-python-without-earmuffs py-buffer))) 319 (combine-and-quote-strings
205 (run-python cmd) 320 (append (cdr cmd-split)
206 (with-current-buffer py-buffer 321 (when (member system-type
207 (add-hook 322 '(cygwin windows-nt ms-dos))
208 'python-shell-first-prompt-hook 323 (list "-i"))))))
209 (lambda () 324 (run-python))
210 (setq-local org-babel-python--initialized t) 325 (run-python))
211 (message "I am running!!!")) 326 (with-current-buffer py-buffer
212 nil 'local)))) 327 (if existing-session-p
213 ((and (eq 'python-mode org-babel-python-mode) 328 ;; Session was created outside Org. Assume first prompt
214 (fboundp 'py-shell)) ; python-mode.el 329 ;; already happened; run session setup code directly
215 (require 'python-mode) 330 (unless org-babel-python--initialized
216 ;; Make sure that py-which-bufname is initialized, as otherwise 331 ;; Ensure first prompt. Based on python-tests.el
217 ;; it will be overwritten the first time a Python buffer is 332 ;; (`python-tests-shell-wait-for-prompt')
218 ;; created. 333 (while (not (org-babel-python--python-util-comint-end-of-output-p))
219 (py-choose-shell) 334 (sit-for 0.1))
220 ;; `py-shell' creates a buffer whose name is the value of 335 (org-babel-python--setup-session))
221 ;; `py-which-bufname' with '*'s at the beginning and end 336 ;; Adding to `python-shell-first-prompt-hook' immediately
222 (let* ((bufname (if (and py-buffer (buffer-live-p py-buffer)) 337 ;; after `run-python' should be safe from race conditions,
223 (replace-regexp-in-string ;; zap surrounding * 338 ;; because subprocess output only arrives when Emacs is
224 "^\\*\\([^*]+\\)\\*$" "\\1" py-buffer) 339 ;; waiting (see elisp manual, "Output from Processes")
225 (concat "Python-" (symbol-name session)))) 340 (add-hook
226 (py-which-bufname bufname)) 341 'python-shell-first-prompt-hook
227 (setq py-buffer (org-babel-python-with-earmuffs bufname)) 342 #'org-babel-python--setup-session
228 (py-shell nil nil t org-babel-python-command py-buffer nil nil t nil))) 343 nil 'local)))
229 (t 344 ;; Wait until Python initializes
230 (error "No function available for running an inferior Python"))) 345 ;; This is more reliable compared to
231 ;; Wait until Python initializes. 346 ;; `org-babel-comint-wait-for-output' as python may emit
232 (if (eq 'python org-babel-python-mode) ; python.el 347 ;; multiple prompts during initialization.
233 ;; This is more reliable compared to 348 (with-current-buffer py-buffer
234 ;; `org-babel-comint-wait-for-output' as python may emit 349 (while (not org-babel-python--initialized)
235 ;; multiple prompts during initialization. 350 (sleep-for 0.010)))
236 (with-current-buffer py-buffer
237 (while (not org-babel-python--initialized)
238 (sleep-for 0.01)))
239 (org-babel-comint-wait-for-output py-buffer))
240 (setq org-babel-python-buffers 351 (setq org-babel-python-buffers
241 (cons (cons session py-buffer) 352 (cons (cons session py-buffer)
242 (assq-delete-all session org-babel-python-buffers))) 353 (assq-delete-all session org-babel-python-buffers)))
243 session))) 354 session)))
244 355
245(defun org-babel-python-initiate-session (&optional session _params) 356(defun org-babel-python-initiate-session (&optional session _params)
246 "Create a session named SESSION according to PARAMS." 357 "Initiate Python session named SESSION according to PARAMS.
358If there is not a current inferior-process-buffer matching
359SESSION then create it. If inferior process already
360exists (e.g. if it was manually started with `run-python'), make
361sure it's configured to work with ob-python. If session has
362already been configured as such, do nothing."
247 (unless (string= session "none") 363 (unless (string= session "none")
248 (org-babel-python-session-buffer 364 (org-babel-python-session-buffer
249 (org-babel-python-initiate-session-by-key session)))) 365 (org-babel-python-initiate-session-by-key session))))
@@ -251,31 +367,10 @@ then create. Return the initialized session."
251(defvar org-babel-python-eoe-indicator "org_babel_python_eoe" 367(defvar org-babel-python-eoe-indicator "org_babel_python_eoe"
252 "A string to indicate that evaluation has completed.") 368 "A string to indicate that evaluation has completed.")
253 369
254(defconst org-babel-python-wrapper-method
255 "
256def main():
257%s
258
259open('%s', 'w').write( str(main()) )")
260(defconst org-babel-python-pp-wrapper-method
261 "
262import pprint
263def main():
264%s
265
266open('%s', 'w').write( pprint.pformat(main()) )")
267
268(defconst org-babel-python--exec-tmpfile "\
269with open('%s') as __org_babel_python_tmpfile:
270 exec(compile(__org_babel_python_tmpfile.read(), __org_babel_python_tmpfile.name, 'exec'))"
271 "Template for Python session command with output results.
272
273Has a single %s escape, the tempfile containing the source code
274to evaluate.")
275
276(defun org-babel-python-format-session-value 370(defun org-babel-python-format-session-value
277 (src-file result-file result-params) 371 (src-file result-file result-params)
278 "Return Python code to evaluate SRC-FILE and write result to RESULT-FILE." 372 "Return Python code to evaluate SRC-FILE and write result to RESULT-FILE.
373RESULT-PARAMS defines the result type."
279 (format "\ 374 (format "\
280import ast 375import ast
281with open('%s') as __org_babel_python_tmpfile: 376with open('%s') as __org_babel_python_tmpfile:
@@ -286,30 +381,25 @@ if isinstance(__org_babel_python_final, ast.Expr):
286 exec(compile(__org_babel_python_ast, '<string>', 'exec')) 381 exec(compile(__org_babel_python_ast, '<string>', 'exec'))
287 __org_babel_python_final = eval(compile(ast.Expression( 382 __org_babel_python_final = eval(compile(ast.Expression(
288 __org_babel_python_final.value), '<string>', 'eval')) 383 __org_babel_python_final.value), '<string>', 'eval'))
289 with open('%s', 'w') as __org_babel_python_tmpfile:
290 if %s:
291 import pprint
292 __org_babel_python_tmpfile.write(pprint.pformat(__org_babel_python_final))
293 else:
294 __org_babel_python_tmpfile.write(str(__org_babel_python_final))
295else: 384else:
296 exec(compile(__org_babel_python_ast, '<string>', 'exec')) 385 exec(compile(__org_babel_python_ast, '<string>', 'exec'))
297 __org_babel_python_final = None" 386 __org_babel_python_final = None
387__org_babel_python_format_value(__org_babel_python_final, '%s', %s)"
298 (org-babel-process-file-name src-file 'noquote) 388 (org-babel-process-file-name src-file 'noquote)
299 (org-babel-process-file-name result-file 'noquote) 389 (org-babel-process-file-name result-file 'noquote)
300 (if (member "pp" result-params) "True" "False"))) 390 (org-babel-python-var-to-python result-params)))
301 391
302(defun org-babel-python-evaluate 392(defun org-babel-python-evaluate
303 (session body &optional result-type result-params preamble async) 393 (session body &optional result-type result-params preamble async graphics-file)
304 "Evaluate BODY as Python code." 394 "Evaluate BODY as Python code."
305 (if session 395 (if session
306 (if async 396 (if async
307 (org-babel-python-async-evaluate-session 397 (org-babel-python-async-evaluate-session
308 session body result-type result-params) 398 session body result-type result-params graphics-file)
309 (org-babel-python-evaluate-session 399 (org-babel-python-evaluate-session
310 session body result-type result-params)) 400 session body result-type result-params graphics-file))
311 (org-babel-python-evaluate-external-process 401 (org-babel-python-evaluate-external-process
312 body result-type result-params preamble))) 402 body result-type result-params preamble graphics-file)))
313 403
314(defun org-babel-python--shift-right (body &optional count) 404(defun org-babel-python--shift-right (body &optional count)
315 (with-temp-buffer 405 (with-temp-buffer
@@ -325,33 +415,40 @@ else:
325 (buffer-string))) 415 (buffer-string)))
326 416
327(defun org-babel-python-evaluate-external-process 417(defun org-babel-python-evaluate-external-process
328 (body &optional result-type result-params preamble) 418 (body &optional result-type result-params preamble graphics-file)
329 "Evaluate BODY in external python process. 419 "Evaluate BODY in external python process.
330If RESULT-TYPE equals `output' then return standard output as a 420If RESULT-TYPE equals `output' then return standard output as a
331string. If RESULT-TYPE equals `value' then return the value of the 421string. If RESULT-TYPE equals `value' then return the value of
332last statement in BODY, as elisp." 422the last statement in BODY, as elisp. If GRAPHICS-FILE is
423non-nil, then save graphical results to that file instead."
333 (let ((raw 424 (let ((raw
334 (pcase result-type 425 (pcase result-type
335 (`output (org-babel-eval org-babel-python-command 426 (`output (org-babel-eval (org-babel-python--command nil)
336 (concat preamble (and preamble "\n") 427 (concat preamble (and preamble "\n")
337 body))) 428 (if graphics-file
338 (`value (let ((tmp-file (org-babel-temp-file "python-"))) 429 (format org-babel-python--output-graphics-wrapper
339 (org-babel-eval 430 body graphics-file)
340 org-babel-python-command 431 body))))
432 (`value (let ((results-file (or graphics-file
433 (org-babel-temp-file "python-"))))
434 (org-babel-eval (org-babel-python--command nil)
341 (concat 435 (concat
342 preamble (and preamble "\n") 436 preamble (and preamble "\n")
343 (format 437 (format
344 (if (member "pp" result-params) 438 (concat org-babel-python--def-format-value "
345 org-babel-python-pp-wrapper-method 439def main():
346 org-babel-python-wrapper-method) 440%s
347 (org-babel-python--shift-right body) 441
348 (org-babel-process-file-name tmp-file 'noquote)))) 442__org_babel_python_format_value(main(), '%s', %s)")
349 (org-babel-eval-read-file tmp-file)))))) 443 (org-babel-python--shift-right body)
444 (org-babel-process-file-name results-file 'noquote)
445 (org-babel-python-var-to-python result-params))))
446 (org-babel-eval-read-file results-file))))))
350 (org-babel-result-cond result-params 447 (org-babel-result-cond result-params
351 raw 448 raw
352 (org-babel-python-table-or-string (org-trim raw))))) 449 (org-babel-python-table-or-string raw))))
353 450
354(defun org-babel-python--send-string (session body) 451(defun org-babel-python-send-string (session body)
355 "Pass BODY to the Python process in SESSION. 452 "Pass BODY to the Python process in SESSION.
356Return output." 453Return output."
357 (with-current-buffer session 454 (with-current-buffer session
@@ -369,48 +466,54 @@ finally:
369 print('%s')" 466 print('%s')"
370 (org-babel-python--shift-right body 4) 467 (org-babel-python--shift-right body 4)
371 org-babel-python-eoe-indicator))) 468 org-babel-python-eoe-indicator)))
372 (if (not (eq 'python-mode org-babel-python-mode)) 469 (let ((python-shell-buffer-name
373 (let ((python-shell-buffer-name 470 (org-babel-python-without-earmuffs session)))
374 (org-babel-python-without-earmuffs session))) 471 (python-shell-send-string body))
375 (python-shell-send-string body))
376 (require 'python-mode)
377 (py-shell-send-string body (get-buffer-process session)))
378 ;; same as `python-shell-comint-end-of-output-p' in emacs-25.1+ 472 ;; same as `python-shell-comint-end-of-output-p' in emacs-25.1+
379 (while (not (string-match 473 (while (not (and (python-shell-comint-end-of-output-p string-buffer)
380 org-babel-python-eoe-indicator 474 (string-match
381 string-buffer)) 475 org-babel-python-eoe-indicator
476 string-buffer)))
382 (accept-process-output (get-buffer-process (current-buffer)))) 477 (accept-process-output (get-buffer-process (current-buffer))))
383 (org-babel-chomp (substring string-buffer 0 (match-beginning 0)))))) 478 (org-babel-chomp (substring string-buffer 0 (match-beginning 0))))))
384 479
385(defun org-babel-python-evaluate-session 480(defun org-babel-python-evaluate-session
386 (session body &optional result-type result-params) 481 (session body &optional result-type result-params graphics-file)
387 "Pass BODY to the Python process in SESSION. 482 "Pass BODY to the Python process in SESSION.
388If RESULT-TYPE equals `output' then return standard output as a 483If RESULT-TYPE equals `output' then return standard output as a
389string. If RESULT-TYPE equals `value' then return the value of the 484string. If RESULT-TYPE equals `value' then return the value of
390last statement in BODY, as elisp." 485the last statement in BODY, as elisp. If GRAPHICS-FILE is
486non-nil, then save graphical results to that file instead."
391 (let* ((tmp-src-file (org-babel-temp-file "python-")) 487 (let* ((tmp-src-file (org-babel-temp-file "python-"))
392 (results 488 (results
393 (progn 489 (progn
394 (with-temp-file tmp-src-file (insert body)) 490 (with-temp-file tmp-src-file
491 (insert (if (and graphics-file (eq result-type 'output))
492 (format org-babel-python--output-graphics-wrapper
493 body graphics-file)
494 body)))
395 (pcase result-type 495 (pcase result-type
396 (`output 496 (`output
397 (let ((body (format org-babel-python--exec-tmpfile 497 (let ((body (format "\
498with open('%s') as f:
499 exec(compile(f.read(), f.name, 'exec'))"
398 (org-babel-process-file-name 500 (org-babel-process-file-name
399 tmp-src-file 'noquote)))) 501 tmp-src-file 'noquote))))
400 (org-babel-python--send-string session body))) 502 (org-babel-python-send-string session body)))
401 (`value 503 (`value
402 (let* ((tmp-results-file (org-babel-temp-file "python-")) 504 (let* ((results-file (or graphics-file
505 (org-babel-temp-file "python-")))
403 (body (org-babel-python-format-session-value 506 (body (org-babel-python-format-session-value
404 tmp-src-file tmp-results-file result-params))) 507 tmp-src-file results-file result-params)))
405 (org-babel-python--send-string session body) 508 (org-babel-python-send-string session body)
406 (sleep-for 0.01) 509 (sleep-for 0.010)
407 (org-babel-eval-read-file tmp-results-file))))))) 510 (org-babel-eval-read-file results-file)))))))
408 (org-babel-result-cond result-params 511 (org-babel-result-cond result-params
409 results 512 results
410 (org-babel-python-table-or-string results)))) 513 (org-babel-python-table-or-string results))))
411 514
412(defun org-babel-python-read-string (string) 515(defun org-babel-python-read-string (string)
413 "Strip \\='s from around Python string." 516 "Strip \\='s from around Python STRING."
414 (if (and (string-prefix-p "'" string) 517 (if (and (string-prefix-p "'" string)
415 (string-suffix-p "'" string)) 518 (string-suffix-p "'" string))
416 (substring string 1 -1) 519 (substring string 1 -1)
@@ -428,7 +531,7 @@ last statement in BODY, as elisp."
428 (org-babel-python-table-or-string results)))) 531 (org-babel-python-table-or-string results))))
429 532
430(defun org-babel-python-async-evaluate-session 533(defun org-babel-python-async-evaluate-session
431 (session body &optional result-type result-params) 534 (session body &optional result-type result-params graphics-file)
432 "Asynchronously evaluate BODY in SESSION. 535 "Asynchronously evaluate BODY in SESSION.
433Returns a placeholder string for insertion, to later be replaced 536Returns a placeholder string for insertion, to later be replaced
434by `org-babel-comint-async-filter'." 537by `org-babel-comint-async-filter'."
@@ -436,28 +539,37 @@ by `org-babel-comint-async-filter'."
436 session (current-buffer) 539 session (current-buffer)
437 "ob_comint_async_python_\\(.+\\)_\\(.+\\)" 540 "ob_comint_async_python_\\(.+\\)_\\(.+\\)"
438 'org-babel-chomp 'org-babel-python-async-value-callback) 541 'org-babel-chomp 'org-babel-python-async-value-callback)
439 (let ((python-shell-buffer-name (org-babel-python-without-earmuffs session))) 542 (pcase result-type
440 (pcase result-type 543 (`output
441 (`output 544 (let ((uuid (org-id-uuid)))
442 (let ((uuid (md5 (number-to-string (random 100000000))))) 545 (with-temp-buffer
443 (with-temp-buffer 546 (insert (format org-babel-python-async-indicator "start" uuid))
444 (insert (format org-babel-python-async-indicator "start" uuid)) 547 (insert "\n")
445 (insert "\n") 548 (insert (if graphics-file
446 (insert body) 549 (format org-babel-python--output-graphics-wrapper
447 (insert "\n") 550 body graphics-file)
448 (insert (format org-babel-python-async-indicator "end" uuid)) 551 body))
449 (python-shell-send-buffer)) 552 (insert "\n")
450 uuid)) 553 (insert (format org-babel-python-async-indicator "end" uuid))
451 (`value 554 (let ((python-shell-buffer-name
452 (let ((tmp-results-file (org-babel-temp-file "python-")) 555 (org-babel-python-without-earmuffs session)))
453 (tmp-src-file (org-babel-temp-file "python-"))) 556 (python-shell-send-buffer)))
454 (with-temp-file tmp-src-file (insert body)) 557 uuid))
455 (with-temp-buffer 558 (`value
456 (insert (org-babel-python-format-session-value tmp-src-file tmp-results-file result-params)) 559 (let ((results-file (or graphics-file
457 (insert "\n") 560 (org-babel-temp-file "python-")))
458 (insert (format org-babel-python-async-indicator "file" tmp-results-file)) 561 (tmp-src-file (org-babel-temp-file "python-")))
459 (python-shell-send-buffer)) 562 (with-temp-file tmp-src-file (insert body))
460 tmp-results-file))))) 563 (with-temp-buffer
564 (insert (org-babel-python-format-session-value
565 tmp-src-file results-file result-params))
566 (insert "\n")
567 (unless graphics-file
568 (insert (format org-babel-python-async-indicator "file" results-file)))
569 (let ((python-shell-buffer-name
570 (org-babel-python-without-earmuffs session)))
571 (python-shell-send-buffer)))
572 results-file))))
461 573
462(provide 'ob-python) 574(provide 'ob-python)
463 575