Merge from emacs-23
[bpt/emacs.git] / lisp / progmodes / python.el
index 9ca11ac..7754dc6 100644 (file)
@@ -1,6 +1,7 @@
 ;;; python.el --- silly walks for Python  -*- coding: iso-8859-1 -*-
 
-;; Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008  Free Software Foundation, Inc.
+;; Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+;;   Free Software Foundation, Inc.
 
 ;; Author: Dave Love <fx@gnu.org>
 ;; Maintainer: FSF
 
 ;; Major mode for editing Python, with support for inferior processes.
 
-;; There is another Python mode, python-mode.el, used by XEmacs and
-;; maintained with Python.  That isn't covered by an FSF copyright
-;; assignment, unlike this code, and seems not to be well-maintained
-;; for Emacs (though I've submitted fixes).  This mode is rather
-;; simpler and is better in other ways.  In particular, using the
-;; syntax functions with text properties maintained by font-lock makes
-;; it more correct with arbitrary string and comment contents.
+;; There is another Python mode, python-mode.el:
+;; http://launchpad.net/python-mode
+;; used by XEmacs, and originally maintained with Python.
+;; That isn't covered by an FSF copyright assignment (?), unlike this
+;; code, and seems not to be well-maintained for Emacs (though I've
+;; submitted fixes).  This mode is rather simpler and is better in
+;; other ways.  In particular, using the syntax functions with text
+;; properties maintained by font-lock makes it more correct with
+;; arbitrary string and comment contents.
 
 ;; This doesn't implement all the facilities of python-mode.el.  Some
 ;; just need doing, e.g. catching exceptions in the inferior Python
@@ -42,7 +45,7 @@
 ;; `forward-into-nomenclature' should be done separately, since it's
 ;; not specific to Python, and I've installed a minor mode to do the
 ;; job properly in Emacs 23.  [CC mode 5.31 contains an incompatible
-;; feature, `c-subword-mode' which is intended to have a similar
+;; feature, `subword-mode' which is intended to have a similar
 ;; effect, but actually only affects word-oriented keybindings.]
 
 ;; Other things seem more natural or canonical here, e.g. the
@@ -70,7 +73,6 @@
   (require 'compile)
   (require 'hippie-exp))
 
-(require 'sym-comp)
 (autoload 'comint-mode "comint")
 
 (defgroup python nil
   :link '(emacs-commentary-link "python"))
 \f
 ;;;###autoload
-(add-to-list 'interpreter-mode-alist '("jython" . jython-mode))
+(add-to-list 'interpreter-mode-alist (cons (purecopy "jython") 'jython-mode))
 ;;;###autoload
-(add-to-list 'interpreter-mode-alist '("python" . python-mode))
+(add-to-list 'interpreter-mode-alist (cons (purecopy "python") 'python-mode))
 ;;;###autoload
-(add-to-list 'auto-mode-alist '("\\.py\\'" . python-mode))
-(add-to-list 'same-window-buffer-names "*Python*")
+(add-to-list 'auto-mode-alist (cons (purecopy "\\.py\\'")  'python-mode))
+(add-to-list 'same-window-buffer-names (purecopy "*Python*"))
 \f
 ;;;; Font lock
 
 (defvar python-font-lock-keywords
   `(,(rx symbol-start
-        ;; From v 2.5 reference, § keywords.
+        ;; From v 2.7 reference, § keywords.
         ;; def and class dealt with separately below
         (or "and" "as" "assert" "break" "continue" "del" "elif" "else"
             "except" "exec" "finally" "for" "from" "global" "if"
              ;; Not real keywords, but close enough to be fontified as such
              "self" "True" "False")
         symbol-end)
-    (,(rx symbol-start "None" symbol-end)      ; see § Keywords in 2.5 manual
+    (,(rx symbol-start "None" symbol-end)      ; see § Keywords in 2.7 manual
      . font-lock-constant-face)
     ;; Definitions
     (,(rx symbol-start (group "class") (1+ space) (group (1+ (or word ?_))))
     ;; Top-level assignments are worth highlighting.
     (,(rx line-start (group (1+ (or word ?_))) (0+ space) "=")
      (1 font-lock-variable-name-face))
-    (,(rx line-start (* (any " \t")) (group "@" (1+ (or word ?_)))) ; decorators
+    ;; Decorators.
+    (,(rx line-start (* (any " \t")) (group "@" (1+ (or word ?_))
+                                           (0+ "." (1+ (or word ?_)))))
      (1 font-lock-type-face))
     ;; Built-ins.  (The next three blocks are from
-    ;; `__builtin__.__dict__.keys()' in Python 2.5.1.)  These patterns
+    ;; `__builtin__.__dict__.keys()' in Python 2.7)  These patterns
     ;; are debateable, but they at least help to spot possible
     ;; shadowing of builtins.
     (,(rx symbol-start (or
          "SystemExit" "TabError" "TypeError" "UnboundLocalError"
          "UnicodeDecodeError" "UnicodeEncodeError" "UnicodeError"
          "UnicodeTranslateError" "UnicodeWarning" "UserWarning"
-         "ValueError" "Warning" "ZeroDivisionError") symbol-end)
+         "ValueError" "Warning" "ZeroDivisionError"
+         ;; Python 2.7
+         "BufferError" "BytesWarning" "WindowsError") symbol-end)
      . font-lock-type-face)
     (,(rx (or line-start (not (any ". \t"))) (* (any " \t")) symbol-start
          (group (or
          "range" "raw_input" "reduce" "reload" "repr" "reversed"
          "round" "set" "setattr" "slice" "sorted" "staticmethod"
          "str" "sum" "super" "tuple" "type" "unichr" "unicode" "vars"
-         "xrange" "zip")) symbol-end)
+         "xrange" "zip"
+         ;; Python 2.7.
+         "bin" "bytearray" "bytes" "format" "memoryview" "next" "print"
+         )) symbol-end)
      (1 font-lock-builtin-face))
     (,(rx symbol-start (or
          ;; other built-ins
          "True" "False" "None" "Ellipsis"
-         "_" "__debug__" "__doc__" "__import__" "__name__") symbol-end)
+         "_" "__debug__" "__doc__" "__import__" "__name__" "__package__")
+          symbol-end)
      . font-lock-builtin-face)))
 
-(defconst python-font-lock-syntactic-keywords
+(defconst python-syntax-propertize-function
   ;; Make outer chars of matching triple-quote sequences into generic
   ;; string delimiters.  Fixme: Is there a better way?
   ;; First avoid a sequence preceded by an odd number of backslashes.
-  `((,(rx (not (any ?\\))
-         ?\\ (* (and ?\\ ?\\))
-         (group (syntax string-quote))
-         (backref 1)
-         (group (backref 1)))
-     (2 ,(string-to-syntax "\"")))     ; dummy
-    (,(rx (group (optional (any "uUrR"))) ; prefix gets syntax property
-         (optional (any "rR"))           ; possible second prefix
-         (group (syntax string-quote))   ; maybe gets property
-         (backref 2)                     ; per first quote
-         (group (backref 2)))            ; maybe gets property
-     (1 (python-quote-syntax 1))
-     (2 (python-quote-syntax 2))
-     (3 (python-quote-syntax 3)))
-    ;; This doesn't really help.
-;;;     (,(rx (and ?\\ (group ?\n))) (1 " "))
-    ))
-
-(defun python-quote-syntax (n)
+  (syntax-propertize-rules
+   (;; ¡Backrefs don't work in syntax-propertize-rules!
+    (concat "\\(?:\\([RUru]\\)[Rr]?\\|^\\|[^\\]\\(?:\\\\.\\)*\\)" ;Prefix.
+              "\\(?:\\('\\)'\\('\\)\\|\\(?2:\"\\)\"\\(?3:\"\\)\\)")
+    (3 (ignore (python-quote-syntax))))
+   ;; This doesn't really help.
+   ;;((rx (and ?\\ (group ?\n))) (1 " "))
+   ))
+
+(defun python-quote-syntax ()
   "Put `syntax-table' property correctly on triple quote.
 Used for syntactic keywords.  N is the match number (1, 2 or 3)."
   ;; Given a triple quote, we have to check the context to know
@@ -196,28 +197,25 @@ Used for syntactic keywords.  N is the match number (1, 2 or 3)."
   ;; x '"""' x """ \"""" x
   (save-excursion
     (goto-char (match-beginning 0))
-    (cond
-     ;; Consider property for the last char if in a fenced string.
-     ((= n 3)
-      (let* ((font-lock-syntactic-keywords nil)
-            (syntax (syntax-ppss)))
-       (when (eq t (nth 3 syntax))     ; after unclosed fence
-         (goto-char (nth 8 syntax))    ; fence position
-         (skip-chars-forward "uUrR")   ; skip any prefix
-         ;; Is it a matching sequence?
-         (if (eq (char-after) (char-after (match-beginning 2)))
-             (eval-when-compile (string-to-syntax "|"))))))
-     ;; Consider property for initial char, accounting for prefixes.
-     ((or (and (= n 2)                 ; leading quote (not prefix)
-              (= (match-beginning 1) (match-end 1))) ; prefix is null
-         (and (= n 1)                  ; prefix
-              (/= (match-beginning 1) (match-end 1)))) ; non-empty
-      (let ((font-lock-syntactic-keywords nil))
-       (unless (eq 'string (syntax-ppss-context (syntax-ppss)))
-         (eval-when-compile (string-to-syntax "|")))))
-     ;; Otherwise (we're in a non-matching string) the property is
-     ;; nil, which is OK.
-     )))
+    (let ((syntax (save-match-data (syntax-ppss))))
+      (cond
+       ((eq t (nth 3 syntax))           ; after unclosed fence
+        ;; Consider property for the last char if in a fenced string.
+        (goto-char (nth 8 syntax))     ; fence position
+        (skip-chars-forward "uUrR")    ; skip any prefix
+        ;; Is it a matching sequence?
+        (if (eq (char-after) (char-after (match-beginning 2)))
+            (put-text-property (match-beginning 3) (match-end 3)
+                               'syntax-table (string-to-syntax "|"))))
+       ((match-end 1)
+        ;; Consider property for initial char, accounting for prefixes.
+        (put-text-property (match-beginning 1) (match-end 1)
+                           'syntax-table (string-to-syntax "|")))
+       (t
+        ;; Consider property for initial char, accounting for prefixes.
+        (put-text-property (match-beginning 2) (match-end 2)
+                           'syntax-table (string-to-syntax "|"))))
+      )))
 
 ;; This isn't currently in `font-lock-defaults' as probably not worth
 ;; it -- we basically only mess with a few normally-symbol characters.
@@ -265,7 +263,7 @@ Used for syntactic keywords.  N is the match number (1, 2 or 3)."
     (define-key map "\C-c\C-z" 'python-switch-to-python)
     (define-key map "\C-c\C-m" 'python-load-file)
     (define-key map "\C-c\C-l" 'python-load-file) ; a la cmuscheme
-    (substitute-key-definition 'complete-symbol 'symbol-complete
+    (substitute-key-definition 'complete-symbol 'completion-at-point
                               map global-map)
     (define-key map "\C-c\C-i" 'python-find-imports)
     (define-key map "\C-c\C-t" 'python-expand-template)
@@ -316,7 +314,7 @@ Used for syntactic keywords.  N is the match number (1, 2 or 3)."
        "-"
        ["Help on symbol" python-describe-symbol
         :help "Use pydoc on symbol at point"]
-       ["Complete symbol" symbol-complete
+       ["Complete symbol" completion-at-point
         :help "Complete (qualified) symbol before point"]
        ["Find function" python-find-function
         :help "Try to find source definition of function at point"]
@@ -569,10 +567,37 @@ 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
-  "^> \\(.*\\)(\\([0-9]+\\))\\([?a-zA-Z0-9_]+\\)()"
+  "^> \\(.*\\)(\\([0-9]+\\))\\([?a-zA-Z0-9_<>]+\\)()"
   "Regular expression pdbtrack uses to find a stack trace entry.")
 
 (defconst python-pdbtrack-input-prompt "\n[(<]*[Pp]db[>)]+ "
@@ -745,7 +770,7 @@ Set `python-indent' locally to the value guessed."
   '(("else" "if" "elif" "while" "for" "try" "except")
     ("elif" "if" "elif")
     ("except" "try" "except")
-    ("finally" "try" "except"))
+    ("finally" "else" "try" "except"))
   "Alist of keyword matches.
 The car of an element is a keyword introducing a statement which
 can close a block opened by a keyword in the cdr.")
@@ -1301,13 +1326,9 @@ See `python-check-command' for the default."
 \f
 ;;;; 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)
 
@@ -1385,6 +1406,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?
@@ -1407,14 +1445,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))
@@ -1511,12 +1547,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
@@ -1539,6 +1575,7 @@ buffer for a list of commands.)"
   (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)
@@ -1547,15 +1584,24 @@ buffer for a list of commands.)"
   ;; invoked.  Would support multiple processes better.
   (when (or new (not (comint-check-proc python-buffer)))
     (with-current-buffer
-       (let* ((cmdlist (append (python-args-to-list cmd) '("-i")))
+       (let* ((cmdlist
+               (append (python-args-to-list cmd)
+                        ;; It's easy for the user to cause the process to be
+                       ;; started without realizing it (e.g. to perform
+                       ;; completion); for this reason loading files from the
+                       ;; current directory is a security risk.  See
+                       ;; http://article.gmane.org/gmane.emacs.devel/103569
+                       '("-i" "-c" "import sys; sys.path.remove('')")))
               (path (getenv "PYTHONPATH"))
               (process-environment     ; to import emacs.py
                (cons (concat "PYTHONPATH="
                              (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)))
@@ -1611,7 +1657,12 @@ buffer for a list of commands.)"
   ;; 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)
@@ -1780,9 +1831,10 @@ will."
     (with-output-to-temp-buffer (help-buffer)
       (with-current-buffer standard-output
        ;; Fixme: Is this actually useful?
-       (help-setup-xref (list 'python-describe-symbol symbol) (interactive-p))
+       (help-setup-xref (list 'python-describe-symbol symbol)
+                        (called-interactively-p 'interactive))
        (set (make-local-variable 'comint-redirect-subvert-readonly) t)
-       (print-help-return-message))))
+       (help-print-return-message))))
   (comint-redirect-send-command-to-process (format "emacs.ehelp(%S, %s)"
                                                   symbol python-imports)
    "*Help*" (python-proc) nil nil))
@@ -1810,7 +1862,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.)
@@ -2003,9 +2057,10 @@ COUNT defaults to `python-indent'.  If region isn't active, just shift
 current line.  The region shifted includes the lines in which START and
 END lie.  It is an error if any lines in the region are indented less than
 COUNT columns."
-  (interactive (if mark-active
-                  (list (region-beginning) (region-end) current-prefix-arg)
-                (list (point) (point) current-prefix-arg)))
+  (interactive
+   (if mark-active
+       (list (region-beginning) (region-end) current-prefix-arg)
+     (list (line-beginning-position) (line-end-position) current-prefix-arg)))
   (if count
       (setq count (prefix-numeric-value count))
     (setq count python-indent))
@@ -2026,9 +2081,10 @@ COUNT columns."
 COUNT defaults to `python-indent'.  If region isn't active, just shift
 current line.  The region shifted includes the lines in which START and
 END lie."
-  (interactive (if mark-active
-                  (list (region-beginning) (region-end) current-prefix-arg)
-                (list (point) (point) current-prefix-arg)))
+  (interactive
+   (if mark-active
+       (list (region-beginning) (region-end) current-prefix-arg)
+     (list (line-beginning-position) (line-end-position) current-prefix-arg)))
   (if count
       (setq count (prefix-numeric-value count))
     (setq count python-indent))
@@ -2139,20 +2195,21 @@ Uses `python-beginning-of-block', `python-end-of-block'."
   "Return a list of completions of the string SYMBOL from Python process.
 The list is sorted.
 Uses `python-imports' to load modules against which to complete."
-  (when symbol
+  (when (stringp symbol)
     (let ((completions
           (condition-case ()
               (car (read-from-string
                     (python-send-receive
-                     (format "emacs.complete(%S,%s)" symbol python-imports))))
+                     (format "emacs.complete(%S,%s)"
+                             (substring-no-properties symbol)
+                             python-imports))))
             (error nil))))
       (sort
        ;; We can get duplicates from the above -- don't know why.
        (delete-dups completions)
        #'string<))))
 
-(defun python-partial-symbol ()
-  "Return the partial symbol before point (for completion)."
+(defun python-completion-at-point ()
   (let ((end (point))
        (start (save-excursion
                 (and (re-search-backward
@@ -2160,7 +2217,9 @@ Uses `python-imports' to load modules against which to complete."
                           (group (1+ (regexp "[[:alnum:]._]"))) point)
                       nil t)
                      (match-beginning 1)))))
-    (if start (buffer-substring-no-properties start end))))
+    (when start
+      (list start end
+            (completion-table-dynamic 'python-symbol-completions)))))
 \f
 ;;;; FFAP support
 
@@ -2196,10 +2255,18 @@ Interactively, prompt for name."
     (unless file (error "Don't know where `%s' is defined" name))
     (pop-to-buffer (find-file-noselect file))
     (when (integerp line)
-      (goto-line line))))
+      (goto-char (point-min))
+      (forward-line (1- line)))))
 \f
 ;;;; Skeletons
 
+(defcustom python-use-skeletons nil
+  "Non-nil means template skeletons will be automagically inserted.
+This happens when pressing \"if<SPACE>\", for example, to prompt for
+the if condition."
+  :type 'boolean
+  :group 'python)
+
 (define-abbrev-table 'python-mode-abbrev-table ()
   "Abbrev table for Python mode."
   :case-fixed t
@@ -2211,18 +2278,19 @@ Interactively, prompt for name."
 (eval-when-compile
   ;; Define a user-level skeleton and add it to the abbrev table.
 (defmacro def-python-skeleton (name &rest elements)
+  (declare (indent 2))
   (let* ((name (symbol-name name))
         (function (intern (concat "python-insert-" name))))
     `(progn
        ;; Usual technique for inserting a skeleton, but expand
        ;; to the original abbrev instead if in a comment or string.
-       (define-abbrev python-mode-abbrev-table ,name ""
-        ',function
-        nil t)                         ; system abbrev
+       (when python-use-skeletons
+         (define-abbrev python-mode-abbrev-table ,name ""
+           ',function
+           nil t))                      ; system abbrev
        (define-skeleton ,function
         ,(format "Insert Python \"%s\" template." name)
         ,@elements)))))
-(put 'def-python-skeleton 'lisp-indent-function 2)
 
 ;; From `skeleton-further-elements' set below:
 ;;  `<': outdent a level;
@@ -2420,12 +2488,12 @@ with skeleton expansions for compound statement templates.
   :group 'python
   (set (make-local-variable 'font-lock-defaults)
        '(python-font-lock-keywords nil nil nil nil
-                                  (font-lock-syntactic-keywords
-                                   . python-font-lock-syntactic-keywords)
-                                  ;; This probably isn't worth it.
-                                  ;; (font-lock-syntactic-face-function
-                                  ;;  . python-font-lock-syntactic-face-function)
-                                  ))
+         ;; This probably isn't worth it.
+         ;; (font-lock-syntactic-face-function
+         ;;  . python-font-lock-syntactic-face-function)
+         ))
+  (set (make-local-variable 'syntax-propertize-function)
+       python-syntax-propertize-function)
   (set (make-local-variable 'parse-sexp-lookup-properties) t)
   (set (make-local-variable 'parse-sexp-ignore-comments) t)
   (set (make-local-variable 'comment-start) "# ")
@@ -2454,10 +2522,8 @@ with skeleton expansions for compound statement templates.
   (add-hook 'eldoc-mode-hook
            (lambda () (run-python nil t)) ; need it running
            nil t)
-  (set (make-local-variable 'symbol-completion-symbol-function)
-       'python-partial-symbol)
-  (set (make-local-variable 'symbol-completion-completions-function)
-       'python-symbol-completions)
+  (add-hook 'completion-at-point-functions
+            'python-completion-at-point nil 'local)
   ;; Fixme: should be in hideshow.  This seems to be of limited use
   ;; since it isn't (can't be) indentation-based.  Also hide-level
   ;; doesn't seem to work properly.
@@ -2471,10 +2537,6 @@ with skeleton expansions for compound statement templates.
        '((< '(backward-delete-char-untabify (min python-indent
                                                 (current-column))))
         (^ '(- (1+ (current-indentation))))))
-  (if (featurep 'hippie-exp)
-      (set (make-local-variable 'hippie-expand-try-functions-list)
-          (cons 'symbol-completion-try-complete
-                hippie-expand-try-functions-list)))
   ;; Python defines TABs as being 8-char wide.
   (set (make-local-variable 'tab-width) 8)
   (unless font-lock-mode (font-lock-mode 1))
@@ -2518,9 +2580,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))
@@ -2538,7 +2598,7 @@ This function is appropriate for `comint-output-filter-functions'."
               overlay-arrow-string "=>"
               python-pdbtrack-is-tracking-p t)
         (set-marker overlay-arrow-position
-                    (save-excursion (beginning-of-line) (point))
+                    (line-beginning-position)
                     (current-buffer)))
     (setq overlay-arrow-position nil
           python-pdbtrack-is-tracking-p nil)))
@@ -2597,7 +2657,8 @@ find it."
                   target_buffer (cadr target)
                   target_fname (buffer-file-name target_buffer))
             (switch-to-buffer-other-window target_buffer)
-            (goto-line target_lineno)
+            (goto-char (point-min))
+            (forward-line (1- target_lineno))
             (message "pdbtrack: line %s, file %s" target_lineno target_fname)
             (python-pdbtrack-overlay-arrow t)
             (pop-to-buffer origbuf t)
@@ -2634,8 +2695,7 @@ problem."
                  ;; Add in number of lines for leading '##' comments:
                  (setq lineno
                        (+ lineno
-                          (save-excursion
-                            (set-buffer funcbuffer)
+                          (with-current-buffer funcbuffer
                             (if (equal (point-min)(point-max))
                                 0
                               (count-lines
@@ -2663,13 +2723,12 @@ problem."
     (while (and buffers (not got))
       (setq buf (car buffers)
             buffers (cdr buffers))
-      (if (and (save-excursion (set-buffer buf)
-                               (string= major-mode "python-mode"))
+      (if (and (with-current-buffer buf
+                 (string= major-mode "python-mode"))
                (or (string-match funcname (buffer-name buf))
                    (string-match (concat "^\\s-*\\(def\\|class\\)\\s-+"
                                          funcname "\\s-*(")
-                                 (save-excursion
-                                   (set-buffer buf)
+                                 (with-current-buffer buf
                                    (buffer-substring (point-min)
                                                      (point-max))))))
           (setq got buf)))
@@ -2727,13 +2786,13 @@ comint believe the user typed this string so that
         ;; add some comment, so that we can filter it out of history
        (cmd (format "execfile(r'%s') # PYTHON-MODE\n" filename)))
     (unwind-protect
-       (save-excursion
-         (set-buffer procbuf)
+       (with-current-buffer procbuf
          (goto-char (point-max))
          (move-marker (process-mark proc) (point))
          (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.
@@ -2748,7 +2807,7 @@ command is used to switch to an existing process, only when a new
 process is started.  If you use this, you will probably want to ensure
 that the current arguments are retained (they will be included in the
 prompt).  This argument is ignored when this function is called
-programmatically, or when running in Emacs 19.34 or older.
+programmatically.
 
 Note: You can toggle between using the CPython interpreter and the
 JPython interpreter by hitting \\[python-toggle-shells].  This toggles
@@ -2773,12 +2832,13 @@ 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))
   (let ((args python-which-args))
     (when (and argprompt
-              (interactive-p)
+              (called-interactively-p 'interactive)
               (fboundp 'split-string))
       ;; TBD: Perhaps force "-i" in the final list?
       (setq args (split-string
@@ -2789,10 +2849,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
@@ -2825,5 +2884,4 @@ filter."
 (provide 'python)
 (provide 'python-21)
 
-;; arch-tag: 6fce1d99-a704-4de9-ba19-c6e4912b0554
 ;;; python.el ends here