From ae3da38afc6269294faf1e6204128b5be2f40c3f Mon Sep 17 00:00:00 2001 From: Chong Yidong Date: Sun, 21 Nov 2010 12:04:43 -0500 Subject: [PATCH] Backport fixes for Bug#5390 and Bug#5694 from trunk. * progmodes/python.el: Add Ipython support (Bug#5390). (python-shell-prompt-alist) (python-shell-continuation-prompt-alist): New options. (python--set-prompt-regexp): New function. (inferior-python-mode, run-python, python-shell): Require ansi-color. Use python--set-prompt-regexp to set the comint prompt based on the Python interpreter. (python--prompt-regexp): New var. (python-check-comint-prompt) (python-comint-output-filter-function): Use it. (run-python): Use a pipe (Bug#5694). --- lisp/ChangeLog | 14 ++++++ lisp/progmodes/python.el | 95 ++++++++++++++++++++++++++++++---------- 2 files changed, 85 insertions(+), 24 deletions(-) diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 4796239b366..add4b50bfbd 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,17 @@ +2010-11-21 Chong Yidong + + * progmodes/python.el: Add Ipython support (Bug#5390). + (python-shell-prompt-alist) + (python-shell-continuation-prompt-alist): New options. + (python--set-prompt-regexp): New function. + (inferior-python-mode, run-python, python-shell): Require + ansi-color. Use python--set-prompt-regexp to set the comint + prompt based on the Python interpreter. + (python--prompt-regexp): New var. + (python-check-comint-prompt) + (python-comint-output-filter-function): Use it. + (run-python): Use a pipe (Bug#5694). + 2010-11-21 Chong Yidong * progmodes/python.el (run-python): Doc fix. diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 653ef3150e5..6fdaa126b5b 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -570,6 +570,33 @@ having to restart the program." "Queue of Python temp files awaiting execution. Currently-active file is at the head of the list.") +(defcustom python-shell-prompt-alist + '(("ipython" . "^In \\[[0-9]+\\]: *") + (t . "^>>> ")) + "Alist of Python input prompts. +Each element has the form (PROGRAM . REGEXP), where PROGRAM is +the value of `python-python-command' for the python process and +REGEXP is a regular expression matching the Python prompt. +PROGRAM can also be t, which specifies the default when no other +element matches `python-python-command'." + :type 'string + :group 'python + :version "24.1") + +(defcustom python-shell-continuation-prompt-alist + '(("ipython" . "^ [.][.][.]+: *") + (t . "^[.][.][.] ")) + "Alist of Python continued-line prompts. +Each element has the form (PROGRAM . REGEXP), where PROGRAM is +the value of `python-python-command' for the python process and +REGEXP is a regular expression matching the Python prompt for +continued lines. +PROGRAM can also be t, which specifies the default when no other +element matches `python-python-command'." + :type 'string + :group 'python + :version "24.1") + (defvar python-pdbtrack-is-tracking-p nil) (defconst python-pdbtrack-stack-entry-regexp @@ -1302,13 +1329,9 @@ See `python-check-command' for the default." ;;;; Inferior mode stuff (following cmuscheme). -;; Fixme: Make sure we can work with IPython. - (defcustom python-python-command "python" "Shell command to run Python interpreter. -Any arguments can't contain whitespace. -Note that IPython may not work properly; it must at least be used -with the `-cl' flag, i.e. use `ipython -cl'." +Any arguments can't contain whitespace." :group 'python :type 'string) @@ -1386,6 +1409,23 @@ local value.") ;; Autoloaded. (declare-function compilation-shell-minor-mode "compile" (&optional arg)) +(defvar python--prompt-regexp nil) + +(defun python--set-prompt-regexp () + (let ((prompt (cdr-safe (or (assoc python-python-command + python-shell-prompt-alist) + (assq t python-shell-prompt-alist)))) + (cprompt (cdr-safe (or (assoc python-python-command + python-shell-continuation-prompt-alist) + (assq t python-shell-continuation-prompt-alist))))) + (set (make-local-variable 'comint-prompt-regexp) + (concat "\\(" + (mapconcat 'identity + (delq nil (list prompt cprompt "^([Pp]db) ")) + "\\|") + "\\)")) + (set (make-local-variable 'python--prompt-regexp) prompt))) + ;; Fixme: This should inherit some stuff from `python-mode', but I'm ;; not sure how much: at least some keybindings, like C-c C-f; ;; syntax?; font-locking, e.g. for triple-quoted strings? @@ -1408,14 +1448,12 @@ For running multiple processes in multiple buffers, see `run-python' and \\{inferior-python-mode-map}" :group 'python + (require 'ansi-color) ; for ipython (setq mode-line-process '(":%s")) (set (make-local-variable 'comint-input-filter) 'python-input-filter) (add-hook 'comint-preoutput-filter-functions #'python-preoutput-filter nil t) - ;; Still required by `comint-redirect-send-command', for instance - ;; (and we need to match things like `>>> ... >>> '): - (set (make-local-variable 'comint-prompt-regexp) - (rx line-start (1+ (and (or (repeat 3 (any ">.")) "(Pdb)") " ")))) + (python--set-prompt-regexp) (set (make-local-variable 'compilation-error-regexp-alist) python-compilation-regexp-alist) (compilation-shell-minor-mode 1)) @@ -1522,12 +1560,12 @@ Don't save anything for STR matching `inferior-python-filter-regexp'." cmd))) (unless (shell-command-to-string cmd) (error "Can't run Python command `%s'" cmd)) - (let* ((res (shell-command-to-string (concat cmd " --version")))) - (string-match "Python \\([0-9]\\)\\.\\([0-9]\\)" res) - (unless (and (equal "2" (match-string 1 res)) - (match-beginning 2) - (>= (string-to-number (match-string 2 res)) 2)) - (error "Only Python versions >= 2.2 and < 3.0 supported"))) + (let* ((res (shell-command-to-string + (concat cmd + " -c \"from sys import version_info;\ +print version_info >= (2, 2) and version_info < (3, 0)\"")))) + (unless (string-match "True" res) + (error "Only Python versions >= 2.2 and < 3.0 are supported"))) (setq python-version-checked t))) ;;;###autoload @@ -1554,6 +1592,7 @@ behavior, change `python-remove-cwd-from-path' to nil." (interactive (if current-prefix-arg (list (read-string "Run Python: " python-command) nil t) (list python-command))) + (require 'ansi-color) ; for ipython (unless cmd (setq cmd python-command)) (python-check-version cmd) (setq python-command cmd) @@ -1572,8 +1611,10 @@ behavior, change `python-remove-cwd-from-path' to nil." (if path (concat path path-separator)) data-directory) process-environment)) - ;; Suppress use of pager for help output: - (process-connection-type nil)) + ;; If we use a pipe, unicode characters are not printed + ;; correctly (Bug#5794) and IPython does not work at + ;; all (Bug#5390). + (process-connection-type t)) (apply 'make-comint-in-buffer "Python" (generate-new-buffer "*Python*") (car cmdlist) nil (cdr cmdlist))) @@ -1629,7 +1670,12 @@ behavior, change `python-remove-cwd-from-path' to nil." ;; non-ASCII. (interactive "r") (let* ((f (make-temp-file "py")) - (command (format "emacs.eexecfile(%S)" f)) + (command + ;; IPython puts the FakeModule module into __main__ so + ;; emacs.eexecfile becomes useless. + (if (string-match "^ipython" python-command) + (format "execfile %S" f) + (format "emacs.eexecfile(%S)" f))) (orig-start (copy-marker start))) (when (save-excursion (goto-char start) @@ -1829,7 +1875,9 @@ If there isn't, it's probably not appropriate to send input to return Eldoc information etc. If PROC is non-nil, check the buffer for that process." (with-current-buffer (process-buffer (or proc (python-proc))) (save-excursion - (save-match-data (re-search-backward ">>> \\=" nil t))))) + (save-match-data + (re-search-backward (concat python--prompt-regexp " *\\=") + nil t))))) ;; Fixme: Is there anything reasonable we can do with random methods? ;; (Currently only works with functions.) @@ -2545,9 +2593,7 @@ Runs `jython-mode-hook' after `python-mode-hook'." "Watch output for Python prompt and exec next file waiting in queue. This function is appropriate for `comint-output-filter-functions'." ;; TBD: this should probably use split-string - (when (and (or (string-equal string ">>> ") - (and (>= (length string) 5) - (string-equal (substring string -5) "\n>>> "))) + (when (and (string-match python--prompt-regexp string) python-file-queue) (condition-case nil (delete-file (car python-file-queue)) @@ -2759,6 +2805,7 @@ comint believe the user typed this string so that (funcall (process-filter proc) proc msg)) (set-buffer curbuf)) (process-send-string proc cmd))) + ;;;###autoload (defun python-shell (&optional argprompt) "Start an interactive Python interpreter in another window. @@ -2798,6 +2845,7 @@ interaction between undo and process filters; the same problem exists in non-Python process buffers using the default (Emacs-supplied) process filter." (interactive "P") + (require 'ansi-color) ; For ipython ;; Set the default shell if not already set (when (null python-which-shell) (python-toggle-shells python-default-interpreter)) @@ -2814,10 +2862,9 @@ filter." )))) (switch-to-buffer-other-window (apply 'make-comint python-which-bufname python-which-shell nil args)) - (make-local-variable 'comint-prompt-regexp) (set-process-sentinel (get-buffer-process (current-buffer)) 'python-sentinel) - (setq comint-prompt-regexp "^>>> \\|^[.][.][.] \\|^(pdb) ") + (python--set-prompt-regexp) (add-hook 'comint-output-filter-functions 'python-comint-output-filter-function nil t) ;; pdbtrack -- 2.39.2