1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-24 06:20:43 -08:00

Fix Python PDB tracking for remote inferior Python

* lisp/progmodes/python.el (python-shell-local-prefix): New
constant.
(python-shell--convert-file-name-to-send): New function to
add/remove prefix.
(python-shell-send-string, python-shell-send-file): Changed to
use 'python-shell--convert-file-name-to-send'.
(python-pdbtrack-set-tracked-buffer): Changed to add/remove
prefix.
* test/lisp/progmodes/python-tests.el
(python-shell--convert-file-name-to-send-1): New test.
(Bug#79036)
This commit is contained in:
kobarity 2025-08-06 23:08:49 +09:00 committed by Eli Zaretskii
parent 68aaeb3519
commit 3d8fbb0716
2 changed files with 66 additions and 8 deletions

View file

@ -3698,6 +3698,10 @@ def __PYTHON_EL_eval_file(filename, tempname, delete):
"Code used to evaluate files in inferior Python processes. "Code used to evaluate files in inferior Python processes.
The coding cookie regexp is specified in PEP 263.") The coding cookie regexp is specified in PEP 263.")
(defconst python-shell-local-prefix "/local:"
"A prefix used to indicate that a file is local.
It is used when sending file names to remote Python processes.")
(defun python-shell-comint-watch-for-first-prompt-output-filter (output) (defun python-shell-comint-watch-for-first-prompt-output-filter (output)
"Run `python-shell-first-prompt-hook' when first prompt is found in OUTPUT." "Run `python-shell-first-prompt-hook' when first prompt is found in OUTPUT."
(when (not python-shell--first-prompt-received) (when (not python-shell--first-prompt-received)
@ -4016,6 +4020,27 @@ there for compatibility with CEDET.")
(signal 'wrong-type-argument (list 'stringp text))))) (signal 'wrong-type-argument (list 'stringp text)))))
"Encode TEXT as a valid Python string.") "Encode TEXT as a valid Python string.")
(defun python-shell--convert-file-name-to-send (process file-name)
"Convert the FILE-NAME for sending to the inferior Python PROCESS.
If PROCESS is local and FILE-NAME is prefixed with
`python-shell-local-prefix', remove the prefix. If PROCESS is remote
and the FILE-NAME is not prefixed, prepend `python-shell-local-prefix'.
If PROCESS is remote and the file is on the same remote host, remove the
remote prefix. Otherwise, return the file name as is."
(when file-name
(let ((process-prefix
(file-remote-p
(with-current-buffer (process-buffer process) default-directory)))
(local-prefix (string-prefix-p python-shell-local-prefix file-name)))
(cond
((and (not process-prefix) local-prefix)
(string-remove-prefix python-shell-local-prefix file-name))
((and process-prefix (not (or local-prefix (file-remote-p file-name))))
(concat python-shell-local-prefix (file-local-name file-name)))
((and process-prefix (string= (file-remote-p file-name) process-prefix))
(file-local-name file-name))
(t file-name)))))
(defun python-shell-send-string (string &optional process msg) (defun python-shell-send-string (string &optional process msg)
"Send STRING to inferior Python PROCESS. "Send STRING to inferior Python PROCESS.
When optional argument MSG is non-nil, forces display of a When optional argument MSG is non-nil, forces display of a
@ -4023,11 +4048,13 @@ user-friendly message if there's no process running; defaults to
t when called interactively." t when called interactively."
(interactive (interactive
(list (read-string "Python command: ") nil t)) (list (read-string "Python command: ") nil t))
(let ((process (or process (python-shell-get-process-or-error msg))) (let* ((process (or process (python-shell-get-process-or-error msg)))
(code (format "__PYTHON_EL_eval(%s, %s)\n" (code (format "__PYTHON_EL_eval(%s, %s)\n"
(python-shell--encode-string string) (python-shell--encode-string string)
(python-shell--encode-string (or (buffer-file-name) (python-shell--encode-string
"<string>"))))) (or (python-shell--convert-file-name-to-send
process (buffer-file-name))
"<string>")))))
(unless python-shell-output-filter-in-progress (unless python-shell-output-filter-in-progress
(with-current-buffer (process-buffer process) (with-current-buffer (process-buffer process)
(save-excursion (save-excursion
@ -4358,7 +4385,8 @@ t when called interactively."
temp-file-name (with-temp-buffer temp-file-name (with-temp-buffer
(insert-file-contents file-name) (insert-file-contents file-name)
(python-shell--save-temp-file (current-buffer)))))) (python-shell--save-temp-file (current-buffer))))))
(let* ((file-name (file-local-name (expand-file-name file-name))) (let* ((file-name (python-shell--convert-file-name-to-send
process (expand-file-name file-name)))
(temp-file-name (when temp-file-name (temp-file-name (when temp-file-name
(file-local-name (expand-file-name (file-local-name (expand-file-name
temp-file-name))))) temp-file-name)))))
@ -5082,8 +5110,12 @@ Never set this variable directly, use
"Set the buffer for FILE-NAME as the tracked buffer. "Set the buffer for FILE-NAME as the tracked buffer.
Internally it uses the `python-pdbtrack-tracked-buffer' variable. Internally it uses the `python-pdbtrack-tracked-buffer' variable.
Returns the tracked buffer." Returns the tracked buffer."
(let* ((file-name-prospect (concat (file-remote-p default-directory) (let* ((file-name-prospect
file-name)) (if (string-prefix-p python-shell-local-prefix file-name)
(string-remove-prefix python-shell-local-prefix file-name)
(if (file-remote-p file-name)
file-name
(concat (file-remote-p default-directory) file-name))))
(file-buffer (get-file-buffer file-name-prospect))) (file-buffer (get-file-buffer file-name-prospect)))
(unless file-buffer (unless file-buffer
(cond (cond

View file

@ -4602,6 +4602,32 @@ and `python-shell-interpreter-args' in the new shell buffer."
"^\\(o\\.t \\|\\)"))) "^\\(o\\.t \\|\\)")))
(ignore-errors (delete-file startup-file)))))) (ignore-errors (delete-file startup-file))))))
(ert-deftest python-shell--convert-file-name-to-send-1 ()
"Test parameters consist of a list of the following three elements.
1. The variable `default-directory' of the process.
2. FILE-NAME argument.
3. The expected return value."
(python-tests-with-temp-buffer-with-shell
""
(let* ((path "/tmp/tmp.py")
(local-path (concat python-shell-local-prefix path))
(remote1-path (concat "/ssh:remote1:" path))
(remote2-path (concat "/ssh:remote2:" path))
(process (python-shell-get-process)))
(dolist
(test (list (list "/tmp" nil nil)
(list "/tmp" path path)
(list "/tmp" local-path path)
(list "/ssh:remote1:/tmp" path local-path)
(list "/ssh:remote1:/tmp" local-path local-path)
(list "/ssh:remote1:/tmp" remote1-path path)
(list "/ssh:remote1:/tmp" remote2-path remote2-path)))
(with-current-buffer (process-buffer process)
(setq default-directory (nth 0 test)))
(should (string= (python-shell--convert-file-name-to-send
process (nth 1 test))
(nth 2 test)))))))
(ert-deftest python-shell-buffer-substring-1 () (ert-deftest python-shell-buffer-substring-1 ()
"Selecting a substring of the whole buffer must match its contents." "Selecting a substring of the whole buffer must match its contents."
(python-tests-with-temp-buffer (python-tests-with-temp-buffer