aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabián Ezequiel Gallina2014-12-27 20:12:00 -0300
committerFabián Ezequiel Gallina2014-12-27 20:12:00 -0300
commit7d1e62d51b51be27b11a67d7828b77f2df9e1eb1 (patch)
tree8404c3b50203c55b9cbffdbe0e0553311ff7df22
parent996ad1b846a0865245df008bdb551093278b3c30 (diff)
downloademacs-7d1e62d51b51be27b11a67d7828b77f2df9e1eb1.tar.gz
emacs-7d1e62d51b51be27b11a67d7828b77f2df9e1eb1.zip
python.el: Enhance shell user interaction and deprecate python-shell-get-or-create-process.
* lisp/progmodes/python.el (python-shell-get-process-or-error): New function. (python-shell-with-shell-buffer): Use it. (python-shell-send-string, python-shell-send-region) (python-shell-send-buffer, python-shell-send-defun) (python-shell-send-file, python-shell-switch-to-shell): Use it. Add argument MSG to display user-friendly message when no process is running. (python-shell-switch-to-shell): Call pop-to-buffer with NORECORD. (python-shell-make-comint): Rename argument SHOW from POP. Use display-buffer instead of pop-to-buffer. (run-python): Doc fix. Return process. (python-shell-get-or-create-process): Make obsolete. * test/automated/python-tests.el (python-shell-get-or-create-process-1) (python-shell-get-or-create-process-2) (python-shell-get-or-create-process-3): Remove tests.
-rw-r--r--lisp/ChangeLog19
-rw-r--r--lisp/progmodes/python.el127
-rw-r--r--test/ChangeLog6
-rw-r--r--test/automated/python-tests.el78
4 files changed, 112 insertions, 118 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 151d9ac9c14..b01988a06af 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,5 +1,24 @@
12014-12-27 Fabián Ezequiel Gallina <fgallina@gnu.org> 12014-12-27 Fabián Ezequiel Gallina <fgallina@gnu.org>
2 2
3 python.el: Enhance shell user interaction and deprecate
4 python-shell-get-or-create-process.
5
6 * progmodes/python.el
7 (python-shell-get-process-or-error): New function.
8 (python-shell-with-shell-buffer): Use it.
9 (python-shell-send-string, python-shell-send-region)
10 (python-shell-send-buffer, python-shell-send-defun)
11 (python-shell-send-file, python-shell-switch-to-shell): Use it.
12 Add argument MSG to display user-friendly message when no process
13 is running.
14 (python-shell-switch-to-shell): Call pop-to-buffer with NORECORD.
15 (python-shell-make-comint): Rename argument SHOW from POP. Use
16 display-buffer instead of pop-to-buffer.
17 (run-python): Doc fix. Return process.
18 (python-shell-get-or-create-process): Make obsolete.
19
202014-12-27 Fabián Ezequiel Gallina <fgallina@gnu.org>
21
3 * progmodes/python.el (python-shell-buffer-substring): Handle 22 * progmodes/python.el (python-shell-buffer-substring): Handle
4 cornercase when region sent starts at point-min. 23 cornercase when region sent starts at point-min.
5 24
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 0b7d9169e6a..8a85763f765 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -2255,11 +2255,9 @@ Avoids `recenter' calls until OUTPUT is completely sent."
2255 "Execute the forms in BODY with the shell buffer temporarily current. 2255 "Execute the forms in BODY with the shell buffer temporarily current.
2256Signals an error if no shell buffer is available for current buffer." 2256Signals an error if no shell buffer is available for current buffer."
2257 (declare (indent 0) (debug t)) 2257 (declare (indent 0) (debug t))
2258 (let ((shell-buffer (make-symbol "shell-buffer"))) 2258 (let ((shell-process (make-symbol "shell-process")))
2259 `(let ((,shell-buffer (python-shell-get-buffer))) 2259 `(let ((,shell-process (python-shell-get-process-or-error)))
2260 (when (not ,shell-buffer) 2260 (with-current-buffer (process-buffer ,shell-process)
2261 (error "No inferior Python buffer available."))
2262 (with-current-buffer ,shell-buffer
2263 ,@body)))) 2261 ,@body))))
2264 2262
2265(defvar python-shell--font-lock-buffer nil) 2263(defvar python-shell--font-lock-buffer nil)
@@ -2471,12 +2469,12 @@ variable.
2471 (python-shell-accept-process-output 2469 (python-shell-accept-process-output
2472 (get-buffer-process (current-buffer)))) 2470 (get-buffer-process (current-buffer))))
2473 2471
2474(defun python-shell-make-comint (cmd proc-name &optional pop internal) 2472(defun python-shell-make-comint (cmd proc-name &optional show internal)
2475 "Create a Python shell comint buffer. 2473 "Create a Python shell comint buffer.
2476CMD is the Python command to be executed and PROC-NAME is the 2474CMD is the Python command to be executed and PROC-NAME is the
2477process name the comint buffer will get. After the comint buffer 2475process name the comint buffer will get. After the comint buffer
2478is created the `inferior-python-mode' is activated. When 2476is created the `inferior-python-mode' is activated. When
2479optional argument POP is non-nil the buffer is shown. When 2477optional argument SHOW is non-nil the buffer is shown. When
2480optional argument INTERNAL is non-nil this process is run on a 2478optional argument INTERNAL is non-nil this process is run on a
2481buffer with a name that starts with a space, following the Emacs 2479buffer with a name that starts with a space, following the Emacs
2482convention for temporary/internal buffers, and also makes sure 2480convention for temporary/internal buffers, and also makes sure
@@ -2505,16 +2503,13 @@ killed."
2505 (mapconcat #'identity args " "))) 2503 (mapconcat #'identity args " ")))
2506 (with-current-buffer buffer 2504 (with-current-buffer buffer
2507 (inferior-python-mode)) 2505 (inferior-python-mode))
2508 (and pop (pop-to-buffer buffer t)) 2506 (when show (display-buffer buffer))
2509 (and internal (set-process-query-on-exit-flag process nil)))) 2507 (and internal (set-process-query-on-exit-flag process nil))))
2510 proc-buffer-name))) 2508 proc-buffer-name)))
2511 2509
2512;;;###autoload 2510;;;###autoload
2513(defun run-python (&optional cmd dedicated show) 2511(defun run-python (&optional cmd dedicated show)
2514 "Run an inferior Python process. 2512 "Run an inferior Python process.
2515Input and output via buffer named after
2516`python-shell-buffer-name'. If there is a process already
2517running in that buffer, just switch to it.
2518 2513
2519Argument CMD defaults to `python-shell-calculate-command' return 2514Argument CMD defaults to `python-shell-calculate-command' return
2520value. When called interactively with `prefix-arg', it allows 2515value. When called interactively with `prefix-arg', it allows
@@ -2522,6 +2517,11 @@ the user to edit such value and choose whether the interpreter
2522should be DEDICATED for the current buffer. When numeric prefix 2517should be DEDICATED for the current buffer. When numeric prefix
2523arg is other than 0 or 4 do not SHOW. 2518arg is other than 0 or 4 do not SHOW.
2524 2519
2520For a given buffer and same values of DEDICATED, if a process is
2521already running for it, it will do nothing. This means that if
2522the current buffer is using a global process, the user is still
2523able to switch it to use a dedicated one.
2524
2525Runs the hook `inferior-python-mode-hook' after 2525Runs the hook `inferior-python-mode-hook' after
2526`comint-mode-hook' is run. (Type \\[describe-mode] in the 2526`comint-mode-hook' is run. (Type \\[describe-mode] in the
2527process buffer for a list of commands.)" 2527process buffer for a list of commands.)"
@@ -2532,10 +2532,10 @@ process buffer for a list of commands.)"
2532 (y-or-n-p "Make dedicated process? ") 2532 (y-or-n-p "Make dedicated process? ")
2533 (= (prefix-numeric-value current-prefix-arg) 4)) 2533 (= (prefix-numeric-value current-prefix-arg) 4))
2534 (list (python-shell-calculate-command) nil t))) 2534 (list (python-shell-calculate-command) nil t)))
2535 (python-shell-make-comint 2535 (get-buffer-process
2536 (or cmd (python-shell-calculate-command)) 2536 (python-shell-make-comint
2537 (python-shell-get-process-name dedicated) show) 2537 (or cmd (python-shell-calculate-command))
2538 dedicated) 2538 (python-shell-get-process-name dedicated) show)))
2539 2539
2540(defun run-python-internal () 2540(defun run-python-internal ()
2541 "Run an inferior Internal Python process. 2541 "Run an inferior Internal Python process.
@@ -2578,6 +2578,21 @@ If current buffer is in `inferior-python-mode', return it."
2578 "Return inferior Python process for current buffer." 2578 "Return inferior Python process for current buffer."
2579 (get-buffer-process (python-shell-get-buffer))) 2579 (get-buffer-process (python-shell-get-buffer)))
2580 2580
2581(defun python-shell-get-process-or-error (&optional interactivep)
2582 "Return inferior Python process for current buffer or signal error.
2583When argument INTERACTIVEP is non-nil, use `user-error' instead
2584of `error' with a user-friendly message."
2585 (or (python-shell-get-process)
2586 (if interactivep
2587 (user-error
2588 "Start a Python process first with `M-x run-python' or `%s'."
2589 ;; Get the binding.
2590 (key-description
2591 (where-is-internal
2592 #'run-python overriding-local-map t)))
2593 (error
2594 "No inferior Python process running."))))
2595
2581(defun python-shell-get-or-create-process (&optional cmd dedicated show) 2596(defun python-shell-get-or-create-process (&optional cmd dedicated show)
2582 "Get or create an inferior Python process for current buffer and return it. 2597 "Get or create an inferior Python process for current buffer and return it.
2583Arguments CMD, DEDICATED and SHOW are those of `run-python' and 2598Arguments CMD, DEDICATED and SHOW are those of `run-python' and
@@ -2593,6 +2608,11 @@ be asked for their values."
2593 (run-python cmd dedicated show))) 2608 (run-python cmd dedicated show)))
2594 (or shell-process (python-shell-get-process)))) 2609 (or shell-process (python-shell-get-process))))
2595 2610
2611(make-obsolete
2612 #'python-shell-get-or-create-process
2613 "Instead call `python-shell-get-process' and create one if returns nil."
2614 "25.1")
2615
2596(defvar python-shell-internal-buffer nil 2616(defvar python-shell-internal-buffer nil
2597 "Current internal shell buffer for the current buffer. 2617 "Current internal shell buffer for the current buffer.
2598This is really not necessary at all for the code to work but it's 2618This is really not necessary at all for the code to work but it's
@@ -2631,10 +2651,14 @@ there for compatibility with CEDET.")
2631 (delete-trailing-whitespace)) 2651 (delete-trailing-whitespace))
2632 temp-file-name)) 2652 temp-file-name))
2633 2653
2634(defun python-shell-send-string (string &optional process) 2654(defun python-shell-send-string (string &optional process msg)
2635 "Send STRING to inferior Python PROCESS." 2655 "Send STRING to inferior Python PROCESS.
2636 (interactive "sPython command: ") 2656When optional argument MSG is non-nil, forces display of a
2637 (let ((process (or process (python-shell-get-or-create-process)))) 2657user-friendly message if there's no process running; defaults to
2658t when called interactively."
2659 (interactive
2660 (list (read-string "Python command: ") nil t))
2661 (let ((process (or process (python-shell-get-process-or-error msg))))
2638 (if (string-match ".\n+." string) ;Multiline. 2662 (if (string-match ".\n+." string) ;Multiline.
2639 (let* ((temp-file-name (python-shell--save-temp-file string)) 2663 (let* ((temp-file-name (python-shell--save-temp-file string))
2640 (file-name (or (buffer-file-name) temp-file-name))) 2664 (file-name (or (buffer-file-name) temp-file-name)))
@@ -2677,7 +2701,7 @@ detecting a prompt at the end of the buffer."
2677(defun python-shell-send-string-no-output (string &optional process) 2701(defun python-shell-send-string-no-output (string &optional process)
2678 "Send STRING to PROCESS and inhibit output. 2702 "Send STRING to PROCESS and inhibit output.
2679Return the output." 2703Return the output."
2680 (let ((process (or process (python-shell-get-or-create-process))) 2704 (let ((process (or process (python-shell-get-process-or-error)))
2681 (comint-preoutput-filter-functions 2705 (comint-preoutput-filter-functions
2682 '(python-shell-output-filter)) 2706 '(python-shell-output-filter))
2683 (python-shell-output-filter-in-progress t) 2707 (python-shell-output-filter-in-progress t)
@@ -2781,35 +2805,43 @@ the python shell:
2781 (line-beginning-position) (line-end-position)))) 2805 (line-beginning-position) (line-end-position))))
2782 (buffer-substring-no-properties (point-min) (point-max))))) 2806 (buffer-substring-no-properties (point-min) (point-max)))))
2783 2807
2784(defun python-shell-send-region (start end &optional send-main) 2808(defun python-shell-send-region (start end &optional send-main msg)
2785 "Send the region delimited by START and END to inferior Python process. 2809 "Send the region delimited by START and END to inferior Python process.
2786When optional argument SEND-MAIN is non-nil, allow execution of 2810When optional argument SEND-MAIN is non-nil, allow execution of
2787code inside blocks delimited by \"if __name__== '__main__':\". 2811code inside blocks delimited by \"if __name__== '__main__':\".
2788When called interactively SEND-MAIN defaults to nil, unless it's 2812When called interactively SEND-MAIN defaults to nil, unless it's
2789called with prefix argument." 2813called with prefix argument. When optional argument MSG is
2790 (interactive "r\nP") 2814non-nil, forces display of a user-friendly message if there's no
2815process running; defaults to t when called interactively."
2816 (interactive
2817 (list (region-beginning) (region-end) current-prefix-arg t))
2791 (let* ((string (python-shell-buffer-substring start end (not send-main))) 2818 (let* ((string (python-shell-buffer-substring start end (not send-main)))
2792 (process (python-shell-get-or-create-process)) 2819 (process (python-shell-get-process-or-error msg))
2793 (original-string (buffer-substring-no-properties start end)) 2820 (original-string (buffer-substring-no-properties start end))
2794 (_ (string-match "\\`\n*\\(.*\\)" original-string))) 2821 (_ (string-match "\\`\n*\\(.*\\)" original-string)))
2795 (message "Sent: %s..." (match-string 1 original-string)) 2822 (message "Sent: %s..." (match-string 1 original-string))
2796 (python-shell-send-string string process))) 2823 (python-shell-send-string string process)))
2797 2824
2798(defun python-shell-send-buffer (&optional send-main) 2825(defun python-shell-send-buffer (&optional send-main msg)
2799 "Send the entire buffer to inferior Python process. 2826 "Send the entire buffer to inferior Python process.
2800When optional argument SEND-MAIN is non-nil, allow execution of 2827When optional argument SEND-MAIN is non-nil, allow execution of
2801code inside blocks delimited by \"if __name__== '__main__':\". 2828code inside blocks delimited by \"if __name__== '__main__':\".
2802When called interactively SEND-MAIN defaults to nil, unless it's 2829When called interactively SEND-MAIN defaults to nil, unless it's
2803called with prefix argument." 2830called with prefix argument. When optional argument MSG is
2804 (interactive "P") 2831non-nil, forces display of a user-friendly message if there's no
2832process running; defaults to t when called interactively."
2833 (interactive (list current-prefix-arg t))
2805 (save-restriction 2834 (save-restriction
2806 (widen) 2835 (widen)
2807 (python-shell-send-region (point-min) (point-max) send-main))) 2836 (python-shell-send-region (point-min) (point-max) send-main msg)))
2808 2837
2809(defun python-shell-send-defun (arg) 2838(defun python-shell-send-defun (&optional arg msg)
2810 "Send the current defun to inferior Python process. 2839 "Send the current defun to inferior Python process.
2811When argument ARG is non-nil do not include decorators." 2840When argument ARG is non-nil do not include decorators. When
2812 (interactive "P") 2841optional argument MSG is non-nil, forces display of a
2842user-friendly message if there's no process running; defaults to
2843t when called interactively."
2844 (interactive (list current-prefix-arg t))
2813 (save-excursion 2845 (save-excursion
2814 (python-shell-send-region 2846 (python-shell-send-region
2815 (progn 2847 (progn
@@ -2825,17 +2857,28 @@ When argument ARG is non-nil do not include decorators."
2825 (progn 2857 (progn
2826 (or (python-nav-end-of-defun) 2858 (or (python-nav-end-of-defun)
2827 (end-of-line 1)) 2859 (end-of-line 1))
2828 (point-marker))))) 2860 (point-marker))
2861 nil ;; noop
2862 msg)))
2829 2863
2830(defun python-shell-send-file (file-name &optional process temp-file-name 2864(defun python-shell-send-file (file-name &optional process temp-file-name
2831 delete) 2865 delete msg)
2832 "Send FILE-NAME to inferior Python PROCESS. 2866 "Send FILE-NAME to inferior Python PROCESS.
2833If TEMP-FILE-NAME is passed then that file is used for processing 2867If TEMP-FILE-NAME is passed then that file is used for processing
2834instead, while internally the shell will continue to use 2868instead, while internally the shell will continue to use
2835FILE-NAME. If TEMP-FILE-NAME and DELETE are non-nil, then 2869FILE-NAME. If TEMP-FILE-NAME and DELETE are non-nil, then
2836TEMP-FILE-NAME is deleted after evaluation is performed." 2870TEMP-FILE-NAME is deleted after evaluation is performed. When
2837 (interactive "fFile to send: ") 2871optional argument MSG is non-nil, forces display of a
2838 (let* ((process (or process (python-shell-get-or-create-process))) 2872user-friendly message if there's no process running; defaults to
2873t when called interactively."
2874 (interactive
2875 (list
2876 (read-file-name "File to send: ") ; file-name
2877 nil ; process
2878 nil ; temp-file-name
2879 nil ; delete
2880 t)) ; msg
2881 (let* ((process (or process (python-shell-get-process-or-error msg)))
2839 (encoding (with-temp-buffer 2882 (encoding (with-temp-buffer
2840 (insert-file-contents 2883 (insert-file-contents
2841 (or temp-file-name file-name)) 2884 (or temp-file-name file-name))
@@ -2860,10 +2903,14 @@ TEMP-FILE-NAME is deleted after evaluation is performed."
2860 (or temp-file-name file-name) encoding encoding file-name) 2903 (or temp-file-name file-name) encoding encoding file-name)
2861 process))) 2904 process)))
2862 2905
2863(defun python-shell-switch-to-shell () 2906(defun python-shell-switch-to-shell (&optional msg)
2864 "Switch to inferior Python process buffer." 2907 "Switch to inferior Python process buffer.
2865 (interactive) 2908When optional argument MSG is non-nil, forces display of a
2866 (pop-to-buffer (process-buffer (python-shell-get-or-create-process)) t)) 2909user-friendly message if there's no process running; defaults to
2910t when called interactively."
2911 (interactive "p")
2912 (pop-to-buffer
2913 (process-buffer (python-shell-get-process-or-error msg)) nil t))
2867 2914
2868(defun python-shell-send-setup-code () 2915(defun python-shell-send-setup-code ()
2869 "Send all setup code for shell. 2916 "Send all setup code for shell.
diff --git a/test/ChangeLog b/test/ChangeLog
index d541910b630..b7861654328 100644
--- a/test/ChangeLog
+++ b/test/ChangeLog
@@ -1,5 +1,11 @@
12014-12-27 Fabián Ezequiel Gallina <fgallina@gnu.org> 12014-12-27 Fabián Ezequiel Gallina <fgallina@gnu.org>
2 2
3 * automated/python-tests.el (python-shell-get-or-create-process-1)
4 (python-shell-get-or-create-process-2)
5 (python-shell-get-or-create-process-3): Remove tests.
6
72014-12-27 Fabián Ezequiel Gallina <fgallina@gnu.org>
8
3 (python-shell-buffer-substring-9): New test. 9 (python-shell-buffer-substring-9): New test.
4 10
52014-12-27 Fabián Ezequiel Gallina <fgallina@gnu.org> 112014-12-27 Fabián Ezequiel Gallina <fgallina@gnu.org>
diff --git a/test/automated/python-tests.el b/test/automated/python-tests.el
index a6ed6808182..90fa79ee966 100644
--- a/test/automated/python-tests.el
+++ b/test/automated/python-tests.el
@@ -2083,84 +2083,6 @@ and `python-shell-interpreter-args' in the new shell buffer."
2083 (ignore-errors (kill-buffer global-shell-buffer)) 2083 (ignore-errors (kill-buffer global-shell-buffer))
2084 (ignore-errors (kill-buffer dedicated-shell-buffer)))))) 2084 (ignore-errors (kill-buffer dedicated-shell-buffer))))))
2085 2085
2086(ert-deftest python-shell-get-or-create-process-1 ()
2087 "Check shell dedicated process creation."
2088 (skip-unless (executable-find python-tests-shell-interpreter))
2089 (python-tests-with-temp-file
2090 ""
2091 (let* ((cmd
2092 (concat (executable-find python-tests-shell-interpreter) " -i"))
2093 (use-dialog-box)
2094 (dedicated-process-name (python-shell-get-process-name t))
2095 (dedicated-process (python-shell-get-or-create-process cmd t))
2096 (dedicated-shell-buffer (process-buffer dedicated-process)))
2097 (unwind-protect
2098 (progn
2099 (set-process-query-on-exit-flag dedicated-process nil)
2100 ;; should be dedicated.
2101 (should (equal (process-name dedicated-process)
2102 dedicated-process-name))
2103 (kill-buffer dedicated-shell-buffer)
2104 ;; Check there are no processes for current buffer.
2105 (should (not (python-shell-get-process))))
2106 (ignore-errors (kill-buffer dedicated-shell-buffer))))))
2107
2108(ert-deftest python-shell-get-or-create-process-2 ()
2109 "Check shell global process creation."
2110 (skip-unless (executable-find python-tests-shell-interpreter))
2111 (python-tests-with-temp-file
2112 ""
2113 (let* ((cmd
2114 (concat (executable-find python-tests-shell-interpreter) " -i"))
2115 (use-dialog-box)
2116 (process-name (python-shell-get-process-name nil))
2117 (process (python-shell-get-or-create-process cmd))
2118 (shell-buffer (process-buffer process)))
2119 (unwind-protect
2120 (progn
2121 (set-process-query-on-exit-flag process nil)
2122 ;; should be global.
2123 (should (equal (process-name process) process-name))
2124 (kill-buffer shell-buffer)
2125 ;; Check there are no processes for current buffer.
2126 (should (not (python-shell-get-process))))
2127 (ignore-errors (kill-buffer shell-buffer))))))
2128
2129(ert-deftest python-shell-get-or-create-process-3 ()
2130 "Check shell dedicated/global process preference."
2131 (skip-unless (executable-find python-tests-shell-interpreter))
2132 (python-tests-with-temp-file
2133 ""
2134 (let* ((cmd
2135 (concat (executable-find python-tests-shell-interpreter) " -i"))
2136 (python-shell-interpreter python-tests-shell-interpreter)
2137 (use-dialog-box)
2138 (dedicated-process-name (python-shell-get-process-name t))
2139 (global-process)
2140 (dedicated-process))
2141 (progn
2142 ;; Create global process
2143 (run-python cmd nil)
2144 (setq global-process (get-buffer-process "*Python*"))
2145 (should global-process)
2146 (set-process-query-on-exit-flag global-process nil)
2147 ;; Create dedicated process
2148 (run-python cmd t)
2149 (setq dedicated-process (get-process dedicated-process-name))
2150 (should dedicated-process)
2151 (set-process-query-on-exit-flag dedicated-process nil)
2152 ;; Prefer dedicated.
2153 (should (equal (python-shell-get-or-create-process)
2154 dedicated-process))
2155 ;; Kill the dedicated so the global takes over.
2156 (kill-buffer (process-buffer dedicated-process))
2157 ;; Detect global.
2158 (should (equal (python-shell-get-or-create-process) global-process))
2159 ;; Kill the global.
2160 (kill-buffer (process-buffer global-process))
2161 ;; Check there are no processes for current buffer.
2162 (should (not (python-shell-get-process)))))))
2163
2164(ert-deftest python-shell-internal-get-or-create-process-1 () 2086(ert-deftest python-shell-internal-get-or-create-process-1 ()
2165 "Check internal shell process creation fallback." 2087 "Check internal shell process creation fallback."
2166 (skip-unless (executable-find python-tests-shell-interpreter)) 2088 (skip-unless (executable-find python-tests-shell-interpreter))