diff options
| author | Jack Kamm | 2020-01-20 17:40:22 -0800 |
|---|---|---|
| committer | Jack Kamm | 2020-02-03 21:17:38 -0800 |
| commit | cc89d5523fcf6786ea202ec765e656fc0c48b346 (patch) | |
| tree | 95bd2b9d940f82158360eb210485ad0f013718d9 /lisp/ob-python.el | |
| parent | e076ed6e8508d177a6323638b00e4e1661fc83f5 (diff) | |
| download | emacs-cc89d5523fcf6786ea202ec765e656fc0c48b346.tar.gz emacs-cc89d5523fcf6786ea202ec765e656fc0c48b346.zip | |
ob-python: Fix several issues with :session :results value
* lisp/ob-python.el (org-babel-python-evaluate-session): Fix a few
related issues with :session :results value blocks, including broken
if-else statements, indented blocks with blank lines, and returning
the wrong value when underscore has been used.
(org-babel-python--eval-ast): New constant variable, a string
consisting of Python code to execute a source block using ast.
Previously, python blocks with parameters ":session :results value"
were entered line-by-line into the Python session, which could cause
issues around indentation and new lines. Now, such python blocks are
written to temp files, then the built-in ast python module is used to
parse and execute them, and to extract the last line separately to
return as a result. Introduces a change in behavior, requiring that
the last line must be a top-level expression statement if its result
is to be saved (otherwise, the result is None).
Diffstat (limited to 'lisp/ob-python.el')
| -rw-r--r-- | lisp/ob-python.el | 68 |
1 files changed, 41 insertions, 27 deletions
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 |