unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Jack Kamm <jackkamm@gmail.com>
To: emacs-devel@gnu.org
Subject: [PATCH] python.el: Returning inferior Python output without inhibiting it
Date: Sun, 01 Oct 2023 09:05:23 -0700	[thread overview]
Message-ID: <87msx2ib0c.fsf@gmail.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 1005 bytes --]

Hello, maintainer of ob-python.el in Org-mode here.

It would be very useful to ob-python, if python.el had a function that
evaluated Python code and returned output, without inhibiting the
output.

So, this patch modifies `python-shell-send-string-no-output', adding
an argument to show the output. I also rename it to
`python-shell-send-string-return-output' since that is a more accurate
name for the new behavior.

The motivation is that ob-python already implements this in
`org-babel-python-send-string', but we have noticed occasional issues
with leaky prompts in our CI [1], and suspect that the
`python-shell-send-string-no-output' implementation may be more
robust. Also, in Org-mode we have a long term goal to be more modular
and integrate better with the rest of Emacs [2]; and replacing custom
ob-python code with python.el functionality would help with that.

[1] https://list.orgmode.org/873506j7ky.fsf@localhost/
[2] https://list.orgmode.org/orgmode/E1kIPh1-0001Lu-Rg@fencepost.gnu.org/


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-python.el-Function-to-return-output-without-necessar.patch --]
[-- Type: text/x-patch, Size: 9283 bytes --]

From ffdbf2961c9d159b2c1d586667469cf7020240c2 Mon Sep 17 00:00:00 2001
From: Jack Kamm <jackkamm@gmail.com>
Date: Sat, 30 Sep 2023 21:04:12 -0700
Subject: [PATCH] python.el: Function to return output without necessarily
 inhibiting

Rename `python-shell-send-string-no-output' to
`python-shell-send-string-return-output', and add option to not
inhibit the output.

*
lisp/progmodes/python.el (python-shell-output-filter-inhibit-output):
Variable to determine whether output currently inhibited.
(python-shell-comint-watch-for-first-prompt-output-filter,
python-shell-internal-send-string,
python-shell-completion-native-setup,
python-shell-completion-get-completions, python-ffap-module-path,
python-eldoc--get-doc-at-point): Replace
`python-shell-send-string-no-output' with
`python-shell-send-string-return-output'.
(python-shell-send-string): Check
`python-shell-output-filter-inhibit-output' instead of
`python-shell-output-filter-in-progress' to determine whether output
is inhibited.
(python-shell-output-filter): Set
`python-shell-output-filter-inhibit-output' to nil after reaching end
of output.
(python-shell-send-string-no-output): Obsolete function alias for
`python-shell-send-string-return-output'.
(python-shell-send-string-return-output): Rename from
`python-shell-send-string-no-output'.  Add function argument on
whether output should be inhibited, and set
`python-shell-output-filter-inhibit-output' accordingly.  If not
inhibited, then the original filter function is run in addition to
`python-shell-output-filter', and `comint-last-prompt' is not reset
after evaluation (since the original output filter will set it
approriately).
---
 lisp/progmodes/python.el | 52 ++++++++++++++++++++++++----------------
 1 file changed, 32 insertions(+), 20 deletions(-)

diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index d3cb5a77e22..67a79192372 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -2736,6 +2736,7 @@ python-shell-dedicated
 
 (defvar python-shell-output-filter-in-progress nil)
 (defvar python-shell-output-filter-buffer nil)
+(defvar python-shell-output-filter-inhibit-output nil)
 
 (defmacro python-shell--add-to-path-with-priority (pathvar paths)
   "Modify PATHVAR and ensure PATHS are added only once at beginning."
@@ -3445,8 +3446,8 @@ python-shell-comint-watch-for-first-prompt-output-filter
                       (format "exec(%s)\n" (python-shell--encode-string string))))))
           ;; Bootstrap: the normal definition of `python-shell-send-string'
           ;; depends on the Python code sent here.
-          (python-shell-send-string-no-output python-shell-eval-setup-code)
-          (python-shell-send-string-no-output python-shell-eval-file-setup-code))
+          (python-shell-send-string-return-output python-shell-eval-setup-code)
+          (python-shell-send-string-return-output python-shell-eval-file-setup-code))
         (with-current-buffer (current-buffer)
           (let ((inhibit-quit nil))
             (run-hooks 'python-shell-first-prompt-hook))))))
@@ -3750,7 +3751,7 @@ python-shell-send-string
                       (python-shell--encode-string string)
                       (python-shell--encode-string (or (buffer-file-name)
                                                        "<string>")))))
-    (unless python-shell-output-filter-in-progress
+    (unless python-shell-output-filter-inhibit-output
       (with-current-buffer (process-buffer process)
         (save-excursion
           (goto-char (process-mark process))
@@ -3766,7 +3767,7 @@ python-shell-send-string
         (python-shell-send-file file-name process temp-file-name t)))))
 
 (defun python-shell-output-filter (string)
-  "Filter used in `python-shell-send-string-no-output' to grab output.
+  "Filter used in `python-shell-send-string-return-output' to grab output.
 STRING is the output received to this point from the process.
 This filter saves received output from the process in
 `python-shell-output-filter-buffer' and stops receiving it after
@@ -3780,6 +3781,7 @@ python-shell-output-filter
     ;; Output ends when `python-shell-output-filter-buffer' contains
     ;; the prompt attached at the end of it.
     (setq python-shell-output-filter-in-progress nil
+          python-shell-output-filter-inhibit-output nil
           python-shell-output-filter-buffer
           (substring python-shell-output-filter-buffer
                      0 (match-beginning 0)))
@@ -3792,15 +3794,23 @@ python-shell-output-filter
             (substring python-shell-output-filter-buffer (match-end 0)))))
   "")
 
-(defun python-shell-send-string-no-output (string &optional process)
-  "Send STRING to PROCESS and inhibit output.
-Return the output."
+(define-obsolete-function-alias
+  'python-shell-send-string-no-output
+  #'python-shell-send-string-return-output "30.1")
+
+(defun python-shell-send-string-return-output (string &optional process show-output)
+  "Send STRING to PROCESS and return the output.
+Inhibit printing the output unless SHOW-OUTPUT is non-nil."
   (or process (setq process (python-shell-get-process-or-error)))
-  (cl-letf* (((process-filter process)
+  (cl-letf* ((original-filter-fn (process-filter process))
+             ((process-filter process)
               (lambda (_proc str)
                 (with-current-buffer (process-buffer process)
-                  (python-shell-output-filter str))))
+                  (python-shell-output-filter str))
+                (when show-output
+                  (funcall original-filter-fn process str))))
              (python-shell-output-filter-in-progress t)
+             (python-shell-output-filter-inhibit-output (not show-output))
              (inhibit-quit t)
              (buffer (process-buffer process))
              (last-prompt (cond ((boundp 'comint-last-prompt-overlay)
@@ -3812,13 +3822,15 @@ python-shell-send-string-no-output
      (with-local-quit
        (unwind-protect
            (python-shell-send-string string process)
-         (when (not (null last-prompt))
-           (with-current-buffer buffer
-             (set last-prompt last-prompt-value))))
+         (unless show-output
+           (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
-         ;; detects end of output.
+         ;; `python-shell-output-filter-in-progress' and
+         ;; `python-shell-output-filter-inhibit-output' to NIL after
+         ;; it detects end of output.
          (accept-process-output process))
        (prog1
            python-shell-output-filter-buffer
@@ -3828,11 +3840,11 @@ python-shell-send-string-no-output
 
 (defun python-shell-internal-send-string (string)
   "Send STRING to the Internal Python interpreter.
-Returns the output.  See `python-shell-send-string-no-output'."
+Returns the output.  See `python-shell-send-string-return-output'."
   ;; XXX Remove `python-shell-internal-last-output' once CEDET is
   ;; updated to support this new mode.
   (setq python-shell-internal-last-output
-        (python-shell-send-string-no-output
+        (python-shell-send-string-return-output
          ;; Makes this function compatible with the old
          ;; python-send-receive. (At least for CEDET).
          (replace-regexp-in-string "_emacs_out +" "" string)
@@ -4217,7 +4229,7 @@ python-shell-completion-native-try
 (defun python-shell-completion-native-setup ()
   "Try to setup native completion, return non-nil on success."
   (let* ((process (python-shell-get-process))
-         (output (python-shell-send-string-no-output "
+         (output (python-shell-send-string-return-output "
 def __PYTHON_EL_native_completion_setup():
     try:
         import readline
@@ -4454,7 +4466,7 @@ python-shell-completion-get-completions
   (with-current-buffer (process-buffer process)
     (let ((completions
            (python-util-strip-string
-            (python-shell-send-string-no-output
+            (python-shell-send-string-return-output
              (format
               "%s\nprint(';'.join(__PYTHON_EL_get_completions(%s)))"
               python-shell-completion-setup-code
@@ -5198,7 +5210,7 @@ python-ffap-module-path
              (ready (python-shell-with-shell-buffer
                       (python-util-comint-end-of-output-p)))
              (module-file
-              (python-shell-send-string-no-output
+              (python-shell-send-string-return-output
                (format "%s\nprint(__FFAP_get_module_path(%s))"
                        python-ffap-setup-code
                        (python-shell--encode-string module)))))
@@ -5327,7 +5339,7 @@ python-eldoc--get-doc-at-point
                 ;; Prevent resizing the echo area when iPython is
                 ;; enabled.  Bug#18794.
                 (python-util-strip-string
-                 (python-shell-send-string-no-output
+                 (python-shell-send-string-return-output
                   (format
                    "%s\nprint(__PYDOC_get_help(%s))"
                    python-eldoc-setup-code
-- 
2.42.0


             reply	other threads:[~2023-10-01 16:05 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-10-01 16:05 Jack Kamm [this message]
2023-10-01 17:00 ` [PATCH] python.el: Returning inferior Python output without inhibiting it Eli Zaretskii
2023-10-01 20:23   ` Jack Kamm
2023-10-02  5:54     ` Eli Zaretskii
2023-10-02 12:42       ` kobarity
2023-10-02 16:10         ` Eli Zaretskii
2023-10-16  0:27           ` Jack Kamm

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87msx2ib0c.fsf@gmail.com \
    --to=jackkamm@gmail.com \
    --cc=emacs-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).