(Emacs) python-mode.el patch, vers 1.06x -> 1.06

Tim Peters (tim@ksr.com)
Mon, 06 Apr 92 23:45:50 -0400

Congratulations to Guido on another fine release!

If you're using the Emacs Python mode, the patch below will change the
experimental version (1.06x) distributed with the Python 0.9.6 release
to the "official" version (1.06). Note that 1.06x and 1.06 both
*require* Python 0.9.6 for proper functioning (they rely on the nifty
new "execfile" builtin).

There are few visible changes from 1.05; in general, some things are
just slicker, and there are a few new user-settable variables to control
scrolling behavior and to name a temp directory.

One bugfix: 1.05 & 1.06x didn't realize that "while" loops can have an
"else" clause. 1.06 does.

One outstanding bug: Sometimes when you're at the end of a file that
doesn't end with a newline, the indentation commands just don't work
right. E.g., DEL may move back a single column when it should close a
block, or TAB may move to the wrong column. Nobody has been able to
reproduce this reliably -- it seems to be a function of both the file
content and the history of inserts and deletes. On three occasions when
this happened, I have definitely seen Emacs returning a wrong value for
the Elisp function `current-indentation', and this does appear to be the
underlying cause. Since it a appears to be a flaky bug in some versions
of Emacs out there, there's not much I can do about it. If it happens
to you, make sure your file ends with a newline and try again -- that's
fixed it every time for me. And if you stumble into a reproducible case,
please send it on & I'll try to follow up on it.

have-fun!-ly y'rs - tim

Tim Peters Kendall Square Research Corp
tim@ksr.com, ksr!tim@uunet.uu.net

Change log:

Mon Apr 6 23:14:18 1992 tim
version 1.06
beefed up py-shell docs
point to emacs shell-mode docs
warn against changing python's ps[12]
fixed set-up of syntax table to stop changing the syntax of the buffer
py-mode is loaded from (probably only affects py-mode developers
doing eval-current-buffer)
changed set-up of py-mode-map just to look more like the others
junked py-process-send-string-wait
rewrote py-execute-region, py-process-filter, py-append-to-process-buffer
to add new scrolling features, and to use python's new execfile
command
new vrbl py-temp-directory
new vrbl py-scroll-process-buffer
new secret vrbl py-file-queue
new helper py-make-temp-name
new helper py-execute-file
new helper py-delete-file-silently
new hair to try to make sure the temp files are deleted when emacs
exits (in case buffer dies/gets_killed while files pending)
new secret vrbl py-inherited-kill-emacs-hook
new helper py-kill-emacs-hook
changed py-append-to-process-buffer to muck with shell-mode's "last
input" markers so that C-c C-r and C-c C-o work intuitively
after sending a region
added bizarre hack to py-process-filter -- major mysteries here, but
emacs segfaults (etc) without this
added warning about trying to undo edits that occurred while process
output is arriving -- this one's certainly Emacs's fault
heck, maybe it's a feature!
changed py-shell to make the process buffer use python's syntax table
taught py-mark-block that 'while' loops can have an 'else' clause

Patch (1.06x -> 1.06):

*** python-mode.el Mon Apr 6 23:10:28 1992
--- ../python-mode.el Mon Apr 6 23:08:51 1992
***************
*** 1,4 ****
! ;;; Major mode for editing Python programs, version 1.06x
;; by: Michael A. Guravage
;; Guido van Rossum <guido@cwi.nl>
;; Tim Peters <tim@ksr.com>
--- 1,4 ----
! ;;; Major mode for editing Python programs, version 1.06
;; by: Michael A. Guravage
;; Guido van Rossum <guido@cwi.nl>
;; Tim Peters <tim@ksr.com>
***************
*** 100,113 ****
(funcall ok "/usr/tmp")
(funcall ok "/tmp")
(funcall ok ".")
! (error "Couldn't find a usable temp directory")))
"*Directory used for temp files created by a *Python* process.
By default, the first directory from this list that exists and that you
can write into: the value (if any) of the environment variable TMPDIR,
/usr/tmp, /tmp, or the current directory.")

! (defvar py-active-file nil)
! (defvar py-pending-files nil)

;; arrange to kill temp files no matter what
;; have to trust that other people are as respectful of our hook
--- 100,116 ----
(funcall ok "/usr/tmp")
(funcall ok "/tmp")
(funcall ok ".")
! (error
! "Couldn't find a usable temp directory -- set py-temp-directory")))
"*Directory used for temp files created by a *Python* process.
By default, the first directory from this list that exists and that you
can write into: the value (if any) of the environment variable TMPDIR,
/usr/tmp, /tmp, or the current directory.")

! ;; have to bind py-file-queue before installing the kill-emacs hook
! (defvar py-file-queue nil
! "Queue of Python temp files awaiting execution.
! Currently-active file is at the head of the list.")

;; arrange to kill temp files no matter what
;; have to trust that other people are as respectful of our hook
***************
*** 293,307 ****
your output from Python's output, and assumes that `>>> ' at the start
of a line is a prompt from Python. Similarly, the Emacs Shell mode code
assumes that both `>>> ' and `... ' at the start of a line are Python
! prompts. Bad things can happen if you fool either mode."
(interactive)
(require 'shell)
(switch-to-buffer-other-window (make-shell "Python" py-python-command))
(make-local-variable 'shell-prompt-pattern)
(setq shell-prompt-pattern "^>>> \\|^\\.\\.\\. ")
! (set-process-filter
! (get-buffer-process (current-buffer))
! 'py-process-filter))

(defun py-execute-region (start end)
"Send the region between START and END to a Python interpreter.
--- 296,318 ----
your output from Python's output, and assumes that `>>> ' at the start
of a line is a prompt from Python. Similarly, the Emacs Shell mode code
assumes that both `>>> ' and `... ' at the start of a line are Python
! prompts. Bad things can happen if you fool either mode.
!
! Warning: If you do any editing *in* the process buffer *while* the
! buffer is accepting output from Python, do NOT attempt to `undo' the
! changes. Some of the output (nowhere near the parts you changed!) may
! be lost if you do. This appears to be an Emacs bug, an unfortunate
! interaction between undo and process filters; the same problem exists in
! non-Python process buffers using the default (Emacs-supplied) process
! filter."
(interactive)
(require 'shell)
(switch-to-buffer-other-window (make-shell "Python" py-python-command))
(make-local-variable 'shell-prompt-pattern)
(setq shell-prompt-pattern "^>>> \\|^\\.\\.\\. ")
! (set-process-filter (get-buffer-process (current-buffer))
! 'py-process-filter)
! (set-syntax-table py-mode-syntax-table))

(defun py-execute-region (start end)
"Send the region between START and END to a Python interpreter.
***************
*** 331,358 ****
limit is (indirectly) inherited from libc's mktemp(3). Python mode does
not try to protect you from exceeding the limit. It's extremely
unlikely that you'll get anywhere close to the limit in practice, unless
! you're trying to be a jerk <grin>."
(interactive "r")
(or (< start end) (error "Region is empty"))
(let ( (pyproc (get-process "Python"))
! fname)
! (if (null pyproc)
! (shell-command-on-region start end py-python-command)
! ;; else feed it thru a temp file
! (setq fname (py-make-temp-name))
! (write-region start end fname nil 'no-msg)
! (if py-active-file
! (progn
! (setq py-pending-files
! (append py-pending-files (list fname)))
! (message "File %s queued for execution" fname))
! ;; else
! (py-execute-file pyproc fname)))))

(defun py-execute-file (pyproc fname)
- (if py-active-file
- (error "Internal error in py-execute-file"))
- (setq py-active-file fname)
(py-append-to-process-buffer
pyproc
(format "## working on region in file %s ...\n" fname))
--- 342,366 ----
limit is (indirectly) inherited from libc's mktemp(3). Python mode does
not try to protect you from exceeding the limit. It's extremely
unlikely that you'll get anywhere close to the limit in practice, unless
! you're trying to be a jerk <grin>.
!
! See the `\\[py-shell]' docs for additional warnings."
(interactive "r")
(or (< start end) (error "Region is empty"))
(let ( (pyproc (get-process "Python"))
! fname)
! (if (null pyproc)
! (shell-command-on-region start end py-python-command)
! ;; else feed it thru a temp file
! (setq fname (py-make-temp-name))
! (write-region start end fname nil 'no-msg)
! (setq py-file-queue (append py-file-queue (list fname)))
! (if (cdr py-file-queue)
! (message "File %s queued for execution" fname)
! ;; else
! (py-execute-file pyproc fname)))))

(defun py-execute-file (pyproc fname)
(py-append-to-process-buffer
pyproc
(format "## working on region in file %s ...\n" fname))
***************
*** 390,396 ****
(insert string)
(move-marker pmark (point))
(setq file-finished
! (and py-active-file
(equal ">>> "
(buffer-substring
(prog2 (beginning-of-line) (point)
--- 398,404 ----
(insert string)
(move-marker pmark (point))
(setq file-finished
! (and py-file-queue
(equal ">>> "
(buffer-substring
(prog2 (beginning-of-line) (point)
***************
*** 405,416 ****
(set-buffer curbuf)
(if file-finished
(progn
! (py-delete-files-silently py-active-file)
! (setq py-active-file nil)
! (if py-pending-files
! (progn
! (py-execute-file pyproc (car py-pending-files))
! (setq py-pending-files (cdr py-pending-files))))))))

(defun py-execute-buffer ()
"Send the contents of the buffer to a Python interpreter.
--- 413,422 ----
(set-buffer curbuf)
(if file-finished
(progn
! (py-delete-file-silently (car py-file-queue))
! (setq py-file-queue (cdr py-file-queue))
! (if py-file-queue
! (py-execute-file pyproc (car py-file-queue)))))))

(defun py-execute-buffer ()
"Send the contents of the buffer to a Python interpreter.
***************
*** 839,849 ****
\tif elif else try except finally for while def class
the region will be set to the body of the structure, including
following blocks that `belong' to it, but excluding trailing blank
! and comment lines. E.g., if on a `try' statement, the `try' block and
! all (if any) of the following `except' and `finally' blocks that
belong to the `try' structure will be in the region. Ditto for
! if/elif/else and for/else structures, and (a bit degenerate, since
! they're always one-block structures) while, def and class blocks.

- Else if no prefix argument is given, and the line begins a Python
block (see list above), and the block is not a `one-liner' (i.e., the
--- 845,856 ----
\tif elif else try except finally for while def class
the region will be set to the body of the structure, including
following blocks that `belong' to it, but excluding trailing blank
! and comment lines. E.g., if on a `try' statement, the `try' block
! and all (if any) of the following `except' and `finally' blocks that
belong to the `try' structure will be in the region. Ditto for
! if/elif/else, for/else and while/else structures, and (a bit
! degenerate, since they're always one-block structures) def and class
! blocks.

- Else if no prefix argument is given, and the line begins a Python
block (see list above), and the block is not a `one-liner' (i.e., the
***************
*** 883,890 ****
(followers
'( (if elif else) (elif elif else) (else)
(try except finally) (except except finally) (finally)
! (for else)
! (def) (class) (while) ) )
first-symbol next-symbol)

(cond
--- 890,897 ----
(followers
'( (if elif else) (elif elif else) (else)
(try except finally) (except except finally) (finally)
! (for else) (while else)
! (def) (class) ) )
first-symbol next-symbol)

(cond
***************
*** 1057,1070 ****
%c:py-execute-region
%c:py-shell

- Warning: If you do any editing *in* the process buffer *while* the
- buffer is accepting output from Python, do NOT attempt to `undo' the
- changes. Some of the output (nowhere near the parts you changed!) may
- be lost if you do. This appears to be an Emacs bug, an unfortunate
- interaction between undo and process filters; the same problem exists in
- non-Python process buffers using the default (Emacs-supplied) process
- filter.
-
@VARIABLES

py-indent-offset\tindentation increment
--- 1064,1069 ----
***************
*** 1397,1418 ****
(make-temp-name
(concat (file-name-as-directory py-temp-directory) "python")))

! ;; pass a mixture of strings and lists of strings
! (defun py-delete-files-silently (&rest args)
! (let (onearg)
! (while args
! (setq onearg (car args)
! args (cdr args))
! (if (stringp onearg) (setq onearg (list onearg)))
! (while onearg
! (condition-case nil
! (delete-file (car onearg))
! (error nil))
! (setq onearg (cdr onearg))))))

(defun py-kill-emacs-hook ()
;; delete our temp files
! (py-delete-files-silently py-active-file py-pending-files)
;; run the hook we inherited, if any
(and py-inherited-kill-emacs-hook
(funcall py-inherited-kill-emacs-hook)))
--- 1396,1411 ----
(make-temp-name
(concat (file-name-as-directory py-temp-directory) "python")))

! (defun py-delete-file-silently (fname)
! (condition-case nil
! (delete-file fname)
! (error nil)))

(defun py-kill-emacs-hook ()
;; delete our temp files
! (while py-file-queue
! (py-delete-file-silently (car py-file-queue))
! (setq py-file-queue (cdr py-file-queue)))
;; run the hook we inherited, if any
(and py-inherited-kill-emacs-hook
(funcall py-inherited-kill-emacs-hook)))

>>> END OF MSG