aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--etc/ORG-NEWS9
-rw-r--r--lisp/ob-python.el68
-rw-r--r--testing/lisp/test-ob-python.el35
3 files changed, 85 insertions, 27 deletions
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 6518c318d05..2068b3aabae 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -11,6 +11,15 @@ See the end of the file for license conditions.
11Please send Org bug reports to mailto:emacs-orgmode@gnu.org. 11Please send Org bug reports to mailto:emacs-orgmode@gnu.org.
12 12
13* Version 9.4 (not yet released) 13* Version 9.4 (not yet released)
14** Incompatible changes
15*** Python session return values must be top-level expression statements
16
17Python blocks with ~:session :results value~ header arguments now only
18return a value if the last line is a top-level expression statement,
19otherwise the result is None. Also, None will now show up under
20"#+RESULTS:", as it already did with ~:results value~ for non-session
21blocks.
22
14** New features 23** New features
15 24
16*** Numeric priorities are now allowed (up to 65) 25*** Numeric priorities are now allowed (up to 65)
diff --git a/lisp/ob-python.el b/lisp/ob-python.el
index 823f6e63d57..5f71577cb67 100644
--- a/lisp/ob-python.el
+++ b/lisp/ob-python.el
@@ -247,6 +247,25 @@ open('%s', 'w').write( pprint.pformat(main()) )")
247 ")); " 247 ")); "
248 "__org_babel_python_fh.close()")) 248 "__org_babel_python_fh.close()"))
249 249
250(defconst org-babel-python--eval-ast "\
251import ast
252try:
253 with open('%s') as f:
254 __org_babel_python_ast = ast.parse(f.read())
255 __org_babel_python_final = __org_babel_python_ast.body[-1]
256 if isinstance(__org_babel_python_final, ast.Expr):
257 __org_babel_python_ast.body = __org_babel_python_ast.body[:-1]
258 exec(compile(__org_babel_python_ast, '<string>', 'exec'))
259 __org_babel_python_final = eval(compile(ast.Expression(
260 __org_babel_python_final.value), '<string>', 'eval'))
261 else:
262 exec(compile(__org_babel_python_ast, '<string>', 'exec'))
263 __org_babel_python_final = None
264except Exception:
265 from traceback import format_exc
266 __org_babel_python_final = format_exc()
267 raise")
268
250(defun org-babel-python-evaluate 269(defun org-babel-python-evaluate
251 (session body &optional result-type result-params preamble) 270 (session body &optional result-type result-params preamble)
252 "Evaluate BODY as Python code." 271 "Evaluate BODY as Python code."
@@ -294,32 +313,9 @@ If RESULT-TYPE equals `output' then return standard output as a
294string. If RESULT-TYPE equals `value' then return the value of the 313string. If RESULT-TYPE equals `value' then return the value of the
295last statement in BODY, as elisp." 314last statement in BODY, as elisp."
296 (let* ((send-wait (lambda () (comint-send-input nil t) (sleep-for 0 5))) 315 (let* ((send-wait (lambda () (comint-send-input nil t) (sleep-for 0 5)))
297 (dump-last-value
298 (lambda
299 (tmp-file pp)
300 (mapc
301 (lambda (statement) (insert statement) (funcall send-wait))
302 (if pp
303 (list
304 "import pprint"
305 (format "open('%s', 'w').write(pprint.pformat(_))"
306 (org-babel-process-file-name tmp-file 'noquote)))
307 (list (format "open('%s', 'w').write(str(_))"
308 (org-babel-process-file-name tmp-file
309 'noquote)))))))
310 (last-indent 0) 316 (last-indent 0)
311 (input-body (lambda (body) 317 (input-body (lambda (body)
312 (dolist (line (split-string body "[\r\n]")) 318 (dolist (line (split-string body "[\r\n]"))
313 ;; Insert a blank line to end an indent
314 ;; block.
315 (let ((curr-indent (string-match "\\S-" line)))
316 (if curr-indent
317 (progn
318 (when (< curr-indent last-indent)
319 (insert "")
320 (funcall send-wait))
321 (setq last-indent curr-indent))
322 (setq last-indent 0)))
323 (insert line) 319 (insert line)
324 (funcall send-wait)) 320 (funcall send-wait))
325 (funcall send-wait))) 321 (funcall send-wait)))
@@ -344,17 +340,35 @@ last statement in BODY, as elisp."
344 (funcall send-wait)) 340 (funcall send-wait))
345 2) "\n"))) 341 2) "\n")))
346 (`value 342 (`value
347 (let ((tmp-file (org-babel-temp-file "python-"))) 343 (let ((tmp-results-file (org-babel-temp-file "python-"))
344 (body (let ((tmp-src-file (org-babel-temp-file
345 "python-")))
346 (with-temp-file tmp-src-file (insert body))
347 (format org-babel-python--eval-ast
348 tmp-src-file))))
348 (org-babel-comint-with-output 349 (org-babel-comint-with-output
349 (session org-babel-python-eoe-indicator nil body) 350 (session org-babel-python-eoe-indicator nil body)
350 (let ((comint-process-echoes nil)) 351 (let ((comint-process-echoes nil))
351 (funcall input-body body) 352 (funcall input-body body)
352 (funcall dump-last-value tmp-file 353 (dolist
353 (member "pp" result-params)) 354 (statement
355 (if (member "pp" result-params)
356 (list
357 "import pprint"
358 (format "open('%s', 'w').write(pprint.pformat(\
359__org_babel_python_final))"
360 (org-babel-process-file-name
361 tmp-results-file 'noquote)))
362 (list (format "open('%s', 'w').write(str(\
363__org_babel_python_final))"
364 (org-babel-process-file-name
365 tmp-results-file 'noquote)))))
366 (insert statement)
367 (funcall send-wait))
354 (funcall send-wait) (funcall send-wait) 368 (funcall send-wait) (funcall send-wait)
355 (insert org-babel-python-eoe-indicator) 369 (insert org-babel-python-eoe-indicator)
356 (funcall send-wait))) 370 (funcall send-wait)))
357 (org-babel-eval-read-file tmp-file)))))) 371 (org-babel-eval-read-file tmp-results-file))))))
358 (unless (string= (substring org-babel-python-eoe-indicator 1 -1) results) 372 (unless (string= (substring org-babel-python-eoe-indicator 1 -1) results)
359 (org-babel-result-cond result-params 373 (org-babel-result-cond result-params
360 results 374 results
diff --git a/testing/lisp/test-ob-python.el b/testing/lisp/test-ob-python.el
index 48ca3d64088..7e2826404f2 100644
--- a/testing/lisp/test-ob-python.el
+++ b/testing/lisp/test-ob-python.el
@@ -138,6 +138,41 @@ if True:
138 (org-babel-execute-maybe) 138 (org-babel-execute-maybe)
139 (org-babel-execute-src-block))))) 139 (org-babel-execute-src-block)))))
140 140
141(ert-deftest test-ob-python/if-else-block ()
142 (should
143 (equal "success" (org-test-with-temp-text "#+begin_src python :session :results value
144value = 'failure'
145if False:
146 pass
147else:
148 value = 'success'
149value
150#+end_src"
151 (org-babel-execute-src-block)))))
152
153(ert-deftest test-ob-python/indent-block-with-blank-lines ()
154 (should
155 (equal 20
156 (org-test-with-temp-text "#+begin_src python :session :results value
157 foo = 0
158 for i in range(10):
159 foo += 1
160
161 foo += 1
162
163 foo
164#+end_src"
165 (org-babel-execute-src-block)))))
166
167(ert-deftest test-ob-python/assign-underscore ()
168 (should
169 (equal "success"
170 (org-test-with-temp-text "#+begin_src python :session :results value
171_ = 'failure'
172'success'
173#+end_src"
174 (org-babel-execute-src-block)))))
175
141(provide 'test-ob-python) 176(provide 'test-ob-python)
142 177
143;;; test-ob-python.el ends here 178;;; test-ob-python.el ends here