diff options
| -rw-r--r-- | etc/ORG-NEWS | 9 | ||||
| -rw-r--r-- | lisp/ob-python.el | 68 | ||||
| -rw-r--r-- | testing/lisp/test-ob-python.el | 35 |
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. | |||
| 11 | Please send Org bug reports to mailto:emacs-orgmode@gnu.org. | 11 | Please 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 | |||
| 17 | Python blocks with ~:session :results value~ header arguments now only | ||
| 18 | return a value if the last line is a top-level expression statement, | ||
| 19 | otherwise the result is None. Also, None will now show up under | ||
| 20 | "#+RESULTS:", as it already did with ~:results value~ for non-session | ||
| 21 | blocks. | ||
| 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 "\ | ||
| 251 | import ast | ||
| 252 | try: | ||
| 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 | ||
| 264 | except 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 | |||
| 294 | string. If RESULT-TYPE equals `value' then return the value of the | 313 | string. If RESULT-TYPE equals `value' then return the value of the |
| 295 | last statement in BODY, as elisp." | 314 | last 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 | ||
| 144 | value = 'failure' | ||
| 145 | if False: | ||
| 146 | pass | ||
| 147 | else: | ||
| 148 | value = 'success' | ||
| 149 | value | ||
| 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 |