From b5709ddc98a450b13b475f7d2f59b5fd91320dbb Mon Sep 17 00:00:00 2001 From: Jack Kamm Date: Fri, 4 Sep 2020 17:37:05 -0700 Subject: [PATCH] ob-python: Fix session blocks not waiting for value result * lisp/ob-python.el (py-shell-send-string): Remove unneeded declare-function. (org-babel-python-eoe-indicator): Add back this const. (org-babel-python--eval-ast): Fix spacing so the block can be entered line by line into the interpreter. (org-babel-python-evaluate-session): Revert previous changes to evaluation for value results. * testing/lisp/test-ob-python.el (test-ob-python/session-value-sleep): New test for session blocks that don't return immediately. 632ceabb1 introduced a bug where session blocks don't wait for value results if not returned immediately. This reverts part of that commit, specifically for evaluation of session blocks with value results. It leaves other parts of that commit alone, for example the changes to session output results, which is more robust handling prompts from ipython interpreter. A downside of reverting the session value implementation is that the ugly code from org-babel-python--eval-ast is again printed in the console, but this is less bad than the new bug that was introduced by the change, hence reverted. --- lisp/ob-python.el | 53 +++++++++++++++++++++------------- testing/lisp/test-ob-python.el | 10 +++++++ 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/lisp/ob-python.el b/lisp/ob-python.el index 44e1b63e0..21a58f106 100644 --- a/lisp/ob-python.el +++ b/lisp/ob-python.el @@ -33,7 +33,6 @@ (declare-function py-shell "ext:python-mode" (&rest args)) (declare-function py-toggle-shells "ext:python-mode" (arg)) -(declare-function py-shell-send-string "ext:python-mode" (strg &optional process)) (declare-function py-send-string-no-output "ext:python-mode" (strg &optional process buffer-name)) (defvar org-babel-tangle-lang-exts) @@ -225,6 +224,9 @@ then create. Return the initialized session." (org-babel-python-session-buffer (org-babel-python-initiate-session-by-key session)))) +(defvar org-babel-python-eoe-indicator "'org_babel_python_eoe'" + "A string to indicate that evaluation has completed.") + (defconst org-babel-python-wrapper-method " def main(): @@ -249,9 +251,9 @@ to evaluate.") (defconst org-babel-python--eval-ast "\ import ast - with open('%s') as f: __org_babel_python_ast = ast.parse(f.read()) + __org_babel_python_final = __org_babel_python_ast.body[-1] if isinstance(__org_babel_python_final, ast.Expr): @@ -326,13 +328,18 @@ last statement in BODY, as elisp." If RESULT-TYPE equals `output' then return standard output as a string. If RESULT-TYPE equals `value' then return the value of the last statement in BODY, as elisp." - (require org-babel-python-mode) (let* ((python-shell-buffer-name (org-babel-python-without-earmuffs session)) + (send-wait (lambda () (comint-send-input nil t) (sleep-for 0 5))) + (input-body (lambda (body) + (dolist (line (split-string body "[\r\n]")) + (insert line) + (funcall send-wait)) + (funcall send-wait))) (tmp-src-file (org-babel-temp-file "python-")) - (results + (results (progn (with-temp-file tmp-src-file (insert body)) - (pcase result-type + (pcase result-type (`output (let ((src-str (format org-babel-python--exec-tmpfile (org-babel-process-file-name @@ -341,21 +348,27 @@ last statement in BODY, as elisp." (py-send-string-no-output src-str (get-buffer-process session) session) (python-shell-send-string-no-output src-str)))) - (`value - (let* ((results-file (org-babel-temp-file "python-")) - (src-str (format - org-babel-python--eval-ast - (org-babel-process-file-name tmp-src-file 'noquote) - (org-babel-process-file-name results-file 'noquote) - (org-babel-python-var-to-python result-params)))) - (if (eq 'python-mode org-babel-python-mode) - (py-shell-send-string src-str (get-buffer-process session)) - (python-shell-send-string src-str)) - (sleep-for 0 5) - (org-babel-eval-read-file results-file))))))) - (org-babel-result-cond result-params - results - (org-babel-python-table-or-string results)))) + (`value + (let* ((tmp-results-file (org-babel-temp-file "python-")) + (body (format org-babel-python--eval-ast + (org-babel-process-file-name + tmp-src-file 'noquote) + (org-babel-process-file-name + tmp-results-file 'noquote) + (if (member "pp" result-params) + "True" "False")))) + (org-babel-comint-with-output + (session org-babel-python-eoe-indicator nil body) + (let ((comint-process-echoes nil)) + (funcall input-body body) + (funcall send-wait) (funcall send-wait) + (insert org-babel-python-eoe-indicator) + (funcall send-wait))) + (org-babel-eval-read-file tmp-results-file))))))) + (unless (string= (substring org-babel-python-eoe-indicator 1 -1) results) + (org-babel-result-cond result-params + results + (org-babel-python-table-or-string results))))) (defun org-babel-python-read-string (string) "Strip \\='s from around Python string." diff --git a/testing/lisp/test-ob-python.el b/testing/lisp/test-ob-python.el index e3f0571a1..d558d07e0 100644 --- a/testing/lisp/test-ob-python.el +++ b/testing/lisp/test-ob-python.el @@ -200,6 +200,16 @@ return text #+end_src" (org-babel-execute-src-block))))) +(ert-deftest test-ob-python/session-value-sleep () + (should + (equal "success" + (org-test-with-temp-text "#+begin_src python :session :results value +import time +time.sleep(.1) +'success' +#+end_src" + (org-babel-execute-src-block))))) + (provide 'test-ob-python) ;;; test-ob-python.el ends here