From b0585441a321a144f2bbdc269b3cfc6c14bab7cf Mon Sep 17 00:00:00 2001 From: kobarity Date: Mon, 10 Oct 2022 22:24:17 +0900 Subject: [PATCH] Fix Python completion failure under certain conditions * lisp/progmodes/python.el (python-shell-send-string-no-output): Save and restore `comint-last-prompt-overlay' or `comint-last-prompt'. * test/lisp/progmodes/python-tests.el (python-tests-shell-wait-for-prompt): New helper function. (python-tests-with-temp-buffer-with-shell): New helper macro. (python-shell-completion-1, python-shell-completion-native-1) (python-shell-completion-native-with-ffap-1) (python-shell-completion-native-with-eldoc-1): New tests (bug#58389). --- lisp/progmodes/python.el | 26 +++++--- test/lisp/progmodes/python-tests.el | 93 +++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 8 deletions(-) diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 80c5b31b6ea..680b57fc3ef 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -3413,15 +3413,25 @@ detecting a prompt at the end of the buffer." "Send STRING to PROCESS and inhibit output. Return the output." (or process (setq process (python-shell-get-process-or-error))) - (cl-letf (((process-filter process) - (lambda (_proc str) - (with-current-buffer (process-buffer process) - (python-shell-output-filter str)))) - (python-shell-output-filter-in-progress t) - (inhibit-quit t)) + (cl-letf* (((process-filter process) + (lambda (_proc str) + (with-current-buffer (process-buffer process) + (python-shell-output-filter str)))) + (python-shell-output-filter-in-progress t) + (inhibit-quit t) + (buffer (process-buffer process)) + (last-prompt (cond ((boundp 'comint-last-prompt-overlay) + 'comint-last-prompt-overlay) + ((boundp 'comint-last-prompt) + 'comint-last-prompt))) + (last-prompt-value (buffer-local-value last-prompt buffer))) (or (with-local-quit - (python-shell-send-string string process) + (unwind-protect + (python-shell-send-string string process) + (when (not (null last-prompt)) + (with-current-buffer buffer + (set last-prompt last-prompt-value)))) (while python-shell-output-filter-in-progress ;; `python-shell-output-filter' takes care of setting ;; `python-shell-output-filter-in-progress' to NIL after it @@ -3430,7 +3440,7 @@ Return the output." (prog1 python-shell-output-filter-buffer (setq python-shell-output-filter-buffer nil))) - (with-current-buffer (process-buffer process) + (with-current-buffer buffer (comint-interrupt-subjob))))) (defun python-shell-internal-send-string (string) diff --git a/test/lisp/progmodes/python-tests.el b/test/lisp/progmodes/python-tests.el index fdaedb5fd7a..60ff9bb613a 100644 --- a/test/lisp/progmodes/python-tests.el +++ b/test/lisp/progmodes/python-tests.el @@ -43,6 +43,37 @@ always located at the beginning of buffer." (goto-char (point-min)) ,@body))) +(defun python-tests-shell-wait-for-prompt () + "Wait for the prompt in the shell buffer." + (python-shell-with-shell-buffer + (while (not (if-let ((prompt (python-util-comint-last-prompt))) + (python-shell-comint-end-of-output-p + (buffer-substring-no-properties + (car prompt) (cdr prompt))))) + (sit-for 0.1)))) + +(defmacro python-tests-with-temp-buffer-with-shell (contents &rest body) + "Create a `python-mode' enabled temp buffer with CONTENTS and `run-python'. +BODY is code to be executed within the temp buffer. Point is +always located at the beginning of buffer. Native completion is +turned off. Shell buffer will be killed on exit." + (declare (indent 1) (debug t)) + `(with-temp-buffer + (let ((python-indent-guess-indent-offset nil) + (python-shell-completion-native-enable nil)) + (python-mode) + (unwind-protect + (progn + (run-python nil t) + (insert ,contents) + (goto-char (point-min)) + (python-tests-shell-wait-for-prompt) + ,@body) + (when (python-shell-get-buffer) + (python-shell-with-shell-buffer + (let (kill-buffer-hook kill-buffer-query-functions) + (kill-buffer)))))))) + (defmacro python-tests-with-temp-file (contents &rest body) "Create a `python-mode' enabled file with CONTENTS. BODY is code to be executed within the temp buffer. Point is @@ -4365,6 +4396,68 @@ def foo(): (python-shell-interpreter "/some/path/to/bin/pypy")) (should (python-shell-completion-native-interpreter-disabled-p)))) +(ert-deftest python-shell-completion-1 () + (skip-unless (executable-find python-tests-shell-interpreter)) + (python-tests-with-temp-buffer-with-shell + " +import abc +" + (let ((inhibit-message t)) + (python-shell-send-buffer) + (python-tests-shell-wait-for-prompt) + (goto-char (point-max)) + (insert "abc.") + (should (completion-at-point)) + (insert "A") + (should (completion-at-point))))) + +(ert-deftest python-shell-completion-native-1 () + (skip-unless (executable-find python-tests-shell-interpreter)) + (python-tests-with-temp-buffer-with-shell + " +import abc +" + (let ((inhibit-message t)) + (python-shell-completion-native-turn-on) + (python-shell-send-buffer) + (python-tests-shell-wait-for-prompt) + (goto-char (point-max)) + (insert "abc.") + (should (completion-at-point)) + (insert "A") + (should (completion-at-point))))) + +(ert-deftest python-shell-completion-native-with-ffap-1 () + (skip-unless (executable-find python-tests-shell-interpreter)) + (python-tests-with-temp-buffer-with-shell + " +import abc +" + (let ((inhibit-message t)) + (python-shell-completion-native-turn-on) + (python-shell-send-buffer) + (python-tests-shell-wait-for-prompt) + (goto-char (point-max)) + (insert "abc.") + ;; This is called when FFAP is enabled and a find-file function is called. + (python-ffap-module-path "abc.") + (should (completion-at-point))))) + +(ert-deftest python-shell-completion-native-with-eldoc-1 () + (skip-unless (executable-find python-tests-shell-interpreter)) + (python-tests-with-temp-buffer-with-shell + " +import abc +" + (let ((inhibit-message t)) + (python-shell-completion-native-turn-on) + (python-shell-send-buffer) + (python-tests-shell-wait-for-prompt) + (goto-char (point-max)) + (insert "abc.") + ;; This is called by idle-timer when ElDoc is enabled. + (python-eldoc-function) + (should (completion-at-point))))) -- 2.39.5