Vasil Vangelovski avatar Vasil Vangelovski committed 0120328

pymacs, rope, ropemacs"

Comments (0)

Files changed (88)

Pymacs/__init__.py

+__version__ = "custom"
+;;; Interface between Emacs Lisp and Python - Lisp part.    -*- emacs-lisp -*-
+;;; Copyright © 2001, 2002, 2003 Progiciels Bourbeau-Pinard inc.
+;;; François Pinard <pinard@iro.umontreal.ca>, 2001.
+
+;;; This program is free software; you can redistribute it and/or modify
+;;; it under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 2, or (at your option)
+;;; any later version.
+;;;
+;;; This program is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with this program; if not, write to the Free Software Foundation,
+;;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.  */
+
+;;; Portability stunts.
+
+(defvar pymacs-use-hash-tables
+  (and (fboundp 'make-hash-table) (fboundp 'gethash) (fboundp 'puthash))
+  "Set to t if hash tables are available.")
+
+(eval-and-compile
+
+  ;; pymacs-cancel-timer
+  (defalias 'pymacs-cancel-timer
+    (cond ((fboundp 'cancel-timer) 'cancel-timer)
+          ;; XEmacs case - yet having post-gc-hook, this is unused.
+          ((fboundp 'delete-itimer) 'delete-itimer)
+          (t 'ignore)))
+
+  ;; pymacs-kill-without-query
+  (if (fboundp 'set-process-query-on-exit-flag)
+      (defun pymacs-kill-without-query (process)
+        "Tell recent Emacs how to quickly destroy PROCESS while exiting."
+        (set-process-query-on-exit-flag process nil))
+    (defalias 'pymacs-kill-without-query
+      (if (fboundp 'process-kill-without-query-process)
+          'process-kill-without-query-process
+        'ignore)))
+
+  ;; pymacs-multibyte-string-p
+  (cond ((fboundp 'multibyte-string-p)
+         (defalias 'pymacs-multibyte-string-p 'multibyte-string-p))
+        ((fboundp 'find-charset-string)
+         (defun pymacs-multibyte-string-p (string)
+           "Tell XEmacs if STRING should be handled as multibyte."
+           (not (member (find-charset-string string) '(nil (ascii))))))
+        (t
+         ; Tell XEmacs that STRING is unibyte, when Mule is not around!
+         (defalias 'pymacs-multibyte-string-p 'ignore)))
+
+  ;; pymacs-report-error
+  (defalias 'pymacs-report-error (symbol-function 'error))
+
+  ;; pymacs-set-buffer-multibyte
+  (if (fboundp 'set-buffer-multibyte)
+      (defalias 'pymacs-set-buffer-multibyte 'set-buffer-multibyte)
+    (defun pymacs-set-buffer-multibyte (flag)
+      "For use in Emacs 20.2 or earlier.  Under XEmacs: no operation."
+      (setq enable-multibyte-characters flag)))
+
+  ;; pymacs-timerp
+  (defalias 'pymacs-timerp
+    (cond ((fboundp 'timerp) 'timerp)
+         ; XEmacs case - yet having post-gc-hook, this is unused.
+          ((fboundp 'itimerp) 'itimerp)
+          (t 'ignore)))
+
+  )
+
+;;; Published variables and functions.
+
+(defvar pymacs-load-path nil
+  "List of additional directories to search for Python modules.
+The directories listed will be searched first, in the order given.")
+
+(defvar pymacs-trace-transit '(5000 . 30000)
+  "Keep the communication buffer growing, for debugging.
+When this variable is nil, the `*Pymacs*' communication buffer gets erased
+before each communication round-trip.  Setting it to `t' guarantees that
+the full communication is saved, which is useful for debugging.
+It could also be given as (KEEP . LIMIT): whenever the buffer exceeds LIMIT
+bytes, it is reduced to approximately KEEP bytes.")
+
+(defvar pymacs-forget-mutability nil
+  "Transmit copies to Python instead of Lisp handles, as much as possible.
+When this variable is nil, most mutable objects are transmitted as handles.
+This variable is meant to be temporarily rebound to force copies.")
+
+(defvar pymacs-mutable-strings nil
+  "Prefer transmitting Lisp strings to Python as handles.
+When this variable is nil, strings are transmitted as copies, and the
+Python side thus has no way for modifying the original Lisp strings.
+This variable is ignored whenever `forget-mutability' is set.")
+
+(defvar pymacs-timeout-at-start 30
+  "Maximum reasonable time, in seconds, for starting the Pymacs helper.
+A machine should be pretty loaded before one needs to increment this.")
+
+(defvar pymacs-timeout-at-reply 5
+  "Expected maximum time, in seconds, to get the first line of a reply.
+The status of the Pymacs helper is checked at every such timeout.")
+
+(defvar pymacs-timeout-at-line 2
+  "Expected maximum time, in seconds, to get another line of a reply.
+The status of the Pymacs helper is checked at every such timeout.")
+
+(defvar pymacs-dreadful-zombies nil
+  "If zombies should trigger hard errors, whenever they get called.
+If `nil', calling a zombie will merely produce a diagnostic message.")
+
+(defun pymacs-load (module &optional prefix noerror)
+  "Import the Python module named MODULE into Emacs.
+Each function in the Python module is made available as an Emacs function.
+The Lisp name of each function is the concatenation of PREFIX with
+the Python name, in which underlines are replaced by dashes.  If PREFIX is
+not given, it defaults to MODULE followed by a dash.
+If NOERROR is not nil, do not raise error when the module is not found."
+  (interactive
+   (let* ((module (read-string "Python module? "))
+          (default (concat (car (last (split-string module "\\."))) "-"))
+          (prefix (read-string (format "Prefix? [%s] " default)
+                               nil nil default)))
+     (list module prefix)))
+  (message "Pymacs loading %s..." module)
+  (let ((lisp-code (pymacs-call "pymacs_load_helper" module prefix)))
+    (cond (lisp-code (let ((result (eval lisp-code)))
+                       (message "Pymacs loading %s...done" module)
+                       result))
+          (noerror (message "Pymacs loading %s...failed" module) nil)
+          (t (pymacs-report-error "Pymacs loading %s...failed" module)))))
+
+(defun pymacs-eval (text)
+  "Compile TEXT as a Python expression, and return its value."
+  (interactive "sPython expression? ")
+  (let ((value (pymacs-serve-until-reply "eval" `(princ ,text))))
+    (when (interactive-p)
+      (message "%S" value))
+    value))
+
+(defun pymacs-exec (text)
+  "Compile and execute TEXT as a sequence of Python statements.
+This functionality is experimental, and does not appear to be useful."
+  (interactive "sPython statements? ")
+  (let ((value (pymacs-serve-until-reply "exec" `(princ ,text))))
+    (when (interactive-p)
+      (message "%S" value))
+    value))
+
+(defun pymacs-call (function &rest arguments)
+  "Return the result of calling a Python function FUNCTION over ARGUMENTS.
+FUNCTION is a string denoting the Python function, ARGUMENTS are separate
+Lisp expressions, one per argument.  Immutable Lisp constants are converted
+to Python equivalents, other structures are converted into Lisp handles."
+  (pymacs-serve-until-reply
+   "eval" `(pymacs-print-for-apply ',function ',arguments)))
+
+(defun pymacs-apply (function arguments)
+  "Return the result of calling a Python function FUNCTION over ARGUMENTS.
+FUNCTION is a string denoting the Python function, ARGUMENTS is a list of
+Lisp expressions.  Immutable Lisp constants are converted to Python
+equivalents, other structures are converted into Lisp handles."
+  (pymacs-serve-until-reply
+   "eval" `(pymacs-print-for-apply ',function ',arguments)))
+
+;;; Integration details.
+
+;; Python functions and modules should ideally look like Lisp functions and
+;; modules.  This page tries to increase the integration seamlessness.
+
+(defadvice documentation (around pymacs-ad-documentation activate)
+  ;; Integration of doc-strings.
+  (let* ((reference (pymacs-python-reference function))
+         (python-doc (when reference
+                       (pymacs-eval (format "doc_string(%s)" reference)))))
+    (if (or reference python-doc)
+        (setq ad-return-value
+              (concat
+               "It interfaces to a Python function.\n\n"
+               (when python-doc
+                 (if raw python-doc (substitute-command-keys python-doc)))))
+      ad-do-it)))
+
+(defun pymacs-python-reference (object)
+  ;; Return the text reference of a Python object if possible, else nil.
+  (when (functionp object)
+    (let* ((definition (indirect-function object))
+           (body (and (pymacs-proper-list-p definition)
+                      (> (length definition) 2)
+                      (eq (car definition) 'lambda)
+                      (cddr definition))))
+      (when (and body (listp (car body)) (eq (caar body) 'interactive))
+        ;; Skip the interactive specification of a function.
+        (setq body (cdr body)))
+      (when (and body
+                 ;; Advised functions start with a string.
+                 (not (stringp (car body)))
+                 ;; Python trampolines hold exactly one expression.
+                 (= (length body) 1))
+        (let ((expression (car body)))
+          ;; EXPRESSION might now hold something like:
+          ;;    (pymacs-apply (quote (pymacs-python . N)) ARGUMENT-LIST)
+          (when (and (pymacs-proper-list-p expression)
+                     (= (length expression) 3)
+                     (eq (car expression) 'pymacs-apply)
+                     (eq (car (cadr expression)) 'quote))
+            (setq object (cadr (cadr expression))))))))
+  (when (eq (car-safe object) 'pymacs-python)
+    (format "python[%d]" (cdr object))))
+
+;; The following functions are experimental -- they are not satisfactory yet.
+
+(defun pymacs-file-handler (operation &rest arguments)
+  ;; Integration of load-file, autoload, etc.
+  ;; Emacs might want the contents of some `MODULE.el' which does not exist,
+  ;; while there is a `MODULE.py' or `MODULE.pyc' file in the same directory.
+  ;; The goal is to generate a virtual contents for this `MODULE.el' file, as
+  ;; a set of Lisp trampoline functions to the Python module functions.
+  ;; Python modules can then be loaded or autoloaded as if they were Lisp.
+  (cond ((and (eq operation 'file-readable-p)
+              (let ((module (substring (car arguments) 0 -3)))
+                (or (pymacs-file-force operation arguments)
+                    (file-readable-p (concat module ".py"))
+                    (file-readable-p (concat module ".pyc"))))))
+        ((and (eq operation 'load)
+              (not (pymacs-file-force
+                    'file-readable-p (list (car arguments))))
+              (file-readable-p (car arguments)))
+         (let ((lisp-code (pymacs-call "pymacs_load_helper"
+                                       (substring (car arguments) 0 -3)
+                                       nil)))
+           (unless lisp-code
+             (pymacs-report-error "Python import error"))
+           (eval lisp-code)))
+        ((and (eq operation 'insert-file-contents)
+              (not (pymacs-file-force
+                    'file-readable-p (list (car arguments))))
+              (file-readable-p (car arguments)))
+         (let ((lisp-code (pymacs-call "pymacs_load_helper"
+                                       (substring (car arguments) 0 -3)
+                                       nil)))
+           (unless lisp-code
+             (pymacs-report-error "Python import error"))
+           (insert (prin1-to-string lisp-code))))
+        (t (pymacs-file-force operation arguments))))
+
+(defun pymacs-file-force (operation arguments)
+  ;; Bypass the file handler.
+  (let ((inhibit-file-name-handlers
+         (cons 'pymacs-file-handler
+               (and (eq inhibit-file-name-operation operation)
+                    inhibit-file-name-handlers)))
+        (inhibit-file-name-operation operation))
+    (apply operation arguments)))
+
+;(add-to-list 'file-name-handler-alist '("\\.el\\'" . pymacs-file-handler))
+
+;;; Gargabe collection of Python IDs.
+
+;; Python objects which have no Lisp representation are allocated on the
+;; Python side as `python[INDEX]', and INDEX is transmitted to Emacs, with
+;; the value to use on the Lisp side for it.  Whenever Lisp does not need a
+;; Python object anymore, it should be freed on the Python side.  The
+;; following variables and functions are meant to fill this duty.
+
+(defvar pymacs-used-ids nil
+  "List of received IDs, currently allocated on the Python side.")
+
+(defvar pymacs-weak-hash nil
+  "Weak hash table, meant to find out which IDs are still needed.")
+
+(defvar pymacs-gc-wanted nil
+  "Flag if it is time to clean up unused IDs on the Python side.")
+
+(defvar pymacs-gc-running nil
+  "Flag telling that a Pymacs garbage collection is in progress.")
+
+(defvar pymacs-gc-timer nil
+  "Timer to trigger Pymacs garbage collection at regular time intervals.
+The timer is used only if `post-gc-hook' is not available.")
+
+(defun pymacs-schedule-gc (&optional xemacs-list)
+  (unless pymacs-gc-running
+    (setq pymacs-gc-wanted t)))
+
+(defun pymacs-garbage-collect ()
+  ;; Clean up unused IDs on the Python side.
+  (when pymacs-use-hash-tables
+    (let ((pymacs-gc-running t)
+          (pymacs-forget-mutability t)
+          (ids pymacs-used-ids)
+          used-ids unused-ids)
+      (while ids
+        (let ((id (car ids)))
+          (setq ids (cdr ids))
+          (if (gethash id pymacs-weak-hash)
+              (setq used-ids (cons id used-ids))
+            (setq unused-ids (cons id unused-ids)))))
+      (setq pymacs-used-ids used-ids
+            pymacs-gc-wanted nil)
+      (when unused-ids
+        (pymacs-apply "free_python" unused-ids)))))
+
+(defun pymacs-defuns (arguments)
+  ;; Take one argument, a list holding a number of items divisible by 3.  The
+  ;; first argument is an INDEX, the second is a NAME, the third is the
+  ;; INTERACTION specification, and so forth.  Register Python INDEX with a
+  ;; function with that NAME and INTERACTION on the Lisp side.  The strange
+  ;; calling convention is to minimise quoting at call time.
+  (while (>= (length arguments) 3)
+    (let ((index (nth 0 arguments))
+          (name (nth 1 arguments))
+          (interaction (nth 2 arguments)))
+      (fset name (pymacs-defun index interaction))
+      (setq arguments (nthcdr 3 arguments)))))
+
+(defun pymacs-defun (index interaction)
+  ;; Register INDEX on the Lisp side with a Python object that is a function,
+  ;; and return a lambda form calling that function.  If the INTERACTION
+  ;; specification is nil, the function is not interactive.  Otherwise, the
+  ;; function is interactive, INTERACTION is then either a string, or the
+  ;; index of an argument-less Python function returning the argument list.
+  (let ((object (pymacs-python index)))
+    (cond ((null interaction)
+           `(lambda (&rest arguments)
+              (pymacs-apply ',object arguments)))
+          ((stringp interaction)
+           `(lambda (&rest arguments)
+              (interactive ,interaction)
+              (pymacs-apply ',object arguments)))
+          (t `(lambda (&rest arguments)
+                (interactive (pymacs-call ',(pymacs-python interaction)))
+                (pymacs-apply ',object arguments))))))
+
+(defun pymacs-python (index)
+  ;; Register on the Lisp side a Python object having INDEX, and return it.
+  ;; The result is meant to be recognised specially by `print-for-eval', and
+  ;; in the function position by `print-for-apply'.
+  (let ((object (cons 'pymacs-python index)))
+    (when pymacs-use-hash-tables
+      (puthash index object pymacs-weak-hash)
+      (setq pymacs-used-ids (cons index pymacs-used-ids)))
+    object))
+
+;;; Generating Python code.
+
+;; Many Lisp expressions cannot fully be represented in Python, at least
+;; because the object is mutable on the Lisp side.  Such objects are allocated
+;; somewhere into a vector of handles, and the handle index is used for
+;; communication instead of the expression itself.
+
+(defvar pymacs-lisp nil
+  "Vector of handles to hold transmitted expressions.")
+
+(defvar pymacs-freed-list nil
+  "List of unallocated indices in Lisp.")
+
+;; When the Python GC is done with a Lisp object, a communication occurs so to
+;; free the object on the Lisp side as well.
+
+(defun pymacs-allocate-lisp (expression)
+  ;; This function allocates some handle for an EXPRESSION, and return its
+  ;; index.
+  (unless pymacs-freed-list
+    (let* ((previous pymacs-lisp)
+           (old-size (length previous))
+           (new-size (if (zerop old-size) 100 (+ old-size (/ old-size 2))))
+           (counter new-size))
+      (setq pymacs-lisp (make-vector new-size nil))
+      (while (> counter 0)
+        (setq counter (1- counter))
+        (if (< counter old-size)
+            (aset pymacs-lisp counter (aref previous counter))
+          (setq pymacs-freed-list (cons counter pymacs-freed-list))))))
+  (let ((index (car pymacs-freed-list)))
+    (setq pymacs-freed-list (cdr pymacs-freed-list))
+    (aset pymacs-lisp index expression)
+    index))
+
+(defun pymacs-free-lisp (indices)
+  ;; This function is triggered from Python side for Lisp handles which lost
+  ;; their last reference.  These references should be cut on the Lisp side as
+  ;; well, or else, the objects will never be garbage-collected.
+  (while indices
+    (let ((index (car indices)))
+      (aset pymacs-lisp index nil)
+      (setq pymacs-freed-list (cons index pymacs-freed-list)
+            indices (cdr indices)))))
+
+(defun pymacs-print-for-apply (function arguments)
+  ;; This function prints a Python expression calling FUNCTION, which is a
+  ;; string naming a Python function, or a Python reference, over all its
+  ;; ARGUMENTS, which are Lisp expressions.
+  (let ((separator "")
+        argument)
+    (if (eq (car-safe function) 'pymacs-python)
+        (princ (format "python[%d]" (cdr function)))
+      (princ function))
+    (princ "(")
+    (while arguments
+      (setq argument (car arguments)
+            arguments (cdr arguments))
+      (princ separator)
+      (setq separator ", ")
+      (pymacs-print-for-eval argument))
+    (princ ")")))
+
+(defun pymacs-print-for-eval (expression)
+  ;; This function prints a Python expression out of a Lisp EXPRESSION.
+  (let (done)
+    (cond ((not expression)
+           (princ "None")
+           (setq done t))
+          ((eq expression t)
+           (princ "True")
+           (setq done t))
+          ((numberp expression)
+           (princ expression)
+           (setq done t))
+          ((stringp expression)
+           (when (or pymacs-forget-mutability
+                     (not pymacs-mutable-strings))
+             (let* ((multibyte (pymacs-multibyte-string-p expression))
+                    (text (if multibyte
+                              (encode-coding-string expression 'utf-8)
+                            (copy-sequence expression))))
+               (set-text-properties 0 (length text) nil text)
+               (princ (mapconcat 'identity
+                                 (split-string (prin1-to-string text) "\n")
+                                 "\\n"))
+               (when multibyte
+                 (princ ".decode('UTF-8')")))
+             (setq done t)))
+          ((symbolp expression)
+           (let ((name (symbol-name expression)))
+             ;; The symbol can only be transmitted when in the main oblist.
+             (when (eq expression (intern-soft name))
+               (princ "lisp[")
+               (prin1 name)
+               (princ "]")
+               (setq done t))))
+          ((vectorp expression)
+           (when pymacs-forget-mutability
+             (let ((limit (length expression))
+                   (counter 0))
+               (princ "(")
+               (while (< counter limit)
+                 (unless (zerop counter)
+                   (princ ", "))
+                 (pymacs-print-for-eval (aref expression counter))
+                 (setq counter (1+ counter)))
+               (when (= limit 1)
+                 (princ ","))
+               (princ ")")
+               (setq done t))))
+          ((eq (car-safe expression) 'pymacs-python)
+           (princ "python[")
+           (princ (cdr expression))
+           (princ "]")
+           (setq done t))
+          ((pymacs-proper-list-p expression)
+           (when pymacs-forget-mutability
+             (princ "[")
+             (pymacs-print-for-eval (car expression))
+             (while (setq expression (cdr expression))
+               (princ ", ")
+               (pymacs-print-for-eval (car expression)))
+             (princ "]")
+             (setq done t))))
+    (unless done
+      (let ((class (cond ((vectorp expression) "Vector")
+                         ((and pymacs-use-hash-tables
+                               (hash-table-p expression))
+                          "Table")
+                         ((bufferp expression) "Buffer")
+                         ((pymacs-proper-list-p expression) "List")
+                         (t "Lisp"))))
+        (princ class)
+        (princ "(")
+        (princ (pymacs-allocate-lisp expression))
+        (princ ")")))))
+
+;;; Communication protocol.
+
+(defvar pymacs-transit-buffer nil
+  "Communication buffer between Emacs and Python.")
+
+;; The principle behind the communication protocol is that it is easier to
+;; generate than parse, and that each language already has its own parser.
+;; So, the Emacs side generates Python text for the Python side to interpret,
+;; while the Python side generates Lisp text for the Lisp side to interpret.
+;; About nothing but expressions are transmitted, which are evaluated on
+;; arrival.  The pseudo `reply' function is meant to signal the final result
+;; of a series of exchanges following a request, while the pseudo `error'
+;; function is meant to explain why an exchange could not have been completed.
+
+;; The protocol itself is rather simple, and contains human readable text
+;; only.  A message starts at the beginning of a line in the communication
+;; buffer, either with `>' for the Lisp to Python direction, or `<' for the
+;; Python to Lisp direction.  This is followed by a decimal number giving the
+;; length of the message text, a TAB character, and the message text itself.
+;; Message direction alternates systematically between messages, it never
+;; occurs that two successive messages are sent in the same direction.  The
+;; first message is received from the Python side, it is `(version VERSION)'.
+
+(defun pymacs-start-services ()
+  ;; This function gets called automatically, as needed.
+  (let ((buffer (get-buffer-create "*Pymacs*")))
+    (with-current-buffer buffer
+      (buffer-disable-undo)
+      (pymacs-set-buffer-multibyte nil)
+      (set-buffer-file-coding-system 'raw-text)
+      (save-match-data
+        ;; Launch the Pymacs helper.
+        (let ((process
+               (apply 'start-process "pymacs" buffer
+                      (let ((python (getenv "PYMACS_PYTHON")))
+                        (if (or (null python) (equal python ""))
+                            "python"
+                          python))
+                      "-c" (concat "import sys;"
+                                   " from Pymacs.pymacs import main;"
+				   " main(*sys.argv[1:])")
+		      (append
+		       (and (>= emacs-major-version 24) '("-f"))
+		       (mapcar 'expand-file-name pymacs-load-path)))))
+          (pymacs-kill-without-query process)
+          ;; Receive the synchronising reply.
+          (while (progn
+                   (goto-char (point-min))
+                   (not (re-search-forward "<\\([0-9]+\\)\t" nil t)))
+            (unless (accept-process-output process pymacs-timeout-at-start)
+              (pymacs-report-error
+               "Pymacs helper did not start within %d seconds"
+                     pymacs-timeout-at-start)))
+          (let ((marker (process-mark process))
+                (limit-position (+ (match-end 0)
+                                   (string-to-number (match-string 1)))))
+            (while (< (marker-position marker) limit-position)
+              (unless (accept-process-output process pymacs-timeout-at-start)
+                (pymacs-report-error
+                 "Pymacs helper probably was interrupted at start")))))
+        ;; Check that synchronisation occurred.
+        (goto-char (match-end 0))
+        (let ((reply (read (current-buffer))))
+          (if (and (pymacs-proper-list-p reply)
+                   (= (length reply) 2)
+                   (eq (car reply) 'version))
+              (unless (string-equal (cadr reply) "custom")
+                (pymacs-report-error
+                 "Pymacs Lisp version is custom, Python is %s"
+                 (cadr reply)))
+            (pymacs-report-error "Pymacs got an invalid initial reply")))))
+    (when pymacs-use-hash-tables
+      (if pymacs-weak-hash
+          ;; A previous Pymacs session occurred in *this* Emacs session.  Some
+          ;; IDs may hang around, which do not correspond to anything on the
+          ;; Python side.  Python should not recycle such IDs for new objects.
+          (when pymacs-used-ids
+            (let ((pymacs-transit-buffer buffer)
+                  (pymacs-forget-mutability t))
+              (pymacs-apply "zombie_python" pymacs-used-ids)))
+        (setq pymacs-weak-hash (make-hash-table :weakness 'value)))
+      (if (boundp 'post-gc-hook)
+          (add-hook 'post-gc-hook 'pymacs-schedule-gc)
+        (setq pymacs-gc-timer (run-at-time 20 20 'pymacs-schedule-gc))))
+    ;; If nothing failed, only then declare that Pymacs has started!
+    (setq pymacs-transit-buffer buffer)))
+
+(defun pymacs-terminate-services ()
+  ;; This function is mainly provided for documentation purposes.
+  (interactive)
+  (garbage-collect)
+  (pymacs-garbage-collect)
+  (when (or (not pymacs-used-ids)
+            (yes-or-no-p "\
+Killing the Pymacs helper might create zombie objects.  Kill? "))
+    (cond ((boundp 'post-gc-hook)
+           (remove-hook 'post-gc-hook 'pymacs-schedule-gc))
+          ((pymacs-timerp pymacs-gc-timer)
+           (pymacs-cancel-timer pymacs-gc-timer)))
+    (when pymacs-transit-buffer
+      (kill-buffer pymacs-transit-buffer))
+    (setq pymacs-gc-running nil
+          pymacs-gc-timer nil
+          pymacs-transit-buffer nil
+          pymacs-lisp nil
+          pymacs-freed-list nil)))
+
+(defun pymacs-serve-until-reply (action inserter)
+  ;; This function builds a Python request by printing ACTION and
+  ;; evaluating INSERTER, which itself prints an argument.  It then
+  ;; sends the request to the Pymacs helper, and serves all
+  ;; sub-requests coming from the Python side, until either a reply or
+  ;; an error is finally received.
+  (unless (and pymacs-transit-buffer
+               (buffer-name pymacs-transit-buffer)
+               (get-buffer-process pymacs-transit-buffer))
+    (pymacs-start-services))
+  (when pymacs-gc-wanted
+    (pymacs-garbage-collect))
+  (let ((inhibit-quit t)
+        done value)
+    (while (not done)
+      (let ((form (pymacs-round-trip action inserter)))
+        (setq action (car form))
+        (when (eq action 'free)
+          (pymacs-free-lisp (cadr form))
+          (setq form (cddr form)
+                action (car form)))
+        (let* ((pair (pymacs-interruptible-eval (cadr form)))
+               (success (cdr pair)))
+          (setq value (car pair))
+          (cond ((eq action 'eval)
+                 (if success
+                     (setq action "return"
+                           inserter `(pymacs-print-for-eval ',value))
+                   (setq action "raise"
+                         inserter `(let ((pymacs-forget-mutability t))
+                                     (pymacs-print-for-eval ,value)))))
+                ((eq action 'expand)
+                 (if success
+                     (setq action "return"
+                           inserter `(let ((pymacs-forget-mutability t))
+                                       (pymacs-print-for-eval ,value)))
+                   (setq action "raise"
+                         inserter `(let ((pymacs-forget-mutability t))
+                                     (pymacs-print-for-eval ,value)))))
+                ((eq action 'return)
+                 (if success
+                     (setq done t)
+                   (pymacs-report-error "%s" value)))
+                ((eq action 'raise)
+                 (if success
+                     (pymacs-report-error "Python: %s" value)
+                   (pymacs-report-error "%s" value)))
+                (t (pymacs-report-error "Protocol error: %s" form))))))
+    value))
+
+(defun pymacs-round-trip (action inserter)
+  ;; This function produces a Python request by printing and
+  ;; evaluating INSERTER, which itself prints an argument.  It sends
+  ;; the request to the Pymacs helper, awaits for any kind of reply,
+  ;; and returns it.
+  (with-current-buffer pymacs-transit-buffer
+    ;; Possibly trim the beginning of the transit buffer.
+    (cond ((not pymacs-trace-transit)
+           (erase-buffer))
+          ((consp pymacs-trace-transit)
+           (when (> (buffer-size) (cdr pymacs-trace-transit))
+             (let ((cut (- (buffer-size) (car pymacs-trace-transit))))
+               (when (> cut 0)
+                 (save-excursion
+                   (goto-char cut)
+                   (unless (memq (preceding-char) '(0 ?\n))
+                     (forward-line 1))
+                   (delete-region (point-min) (point))))))))
+    ;; Send the request, wait for a reply, and process it.
+    (let* ((process (get-buffer-process pymacs-transit-buffer))
+           (status (process-status process))
+           (marker (process-mark process))
+           (moving (= (point) marker))
+           send-position reply-position reply)
+      (save-excursion
+        (save-match-data
+          ;; Encode request.
+          (setq send-position (marker-position marker))
+          (let ((standard-output marker))
+            (princ action)
+            (princ " ")
+            (eval inserter))
+          (goto-char marker)
+          (unless (= (preceding-char) ?\n)
+            (princ "\n" marker))
+          ;; Send request text.
+          (goto-char send-position)
+          (insert (format ">%d\t" (- marker send-position)))
+          (setq reply-position (marker-position marker))
+          (process-send-region process send-position marker)
+          ;; Receive reply text.
+          (while (and (eq status 'run)
+                      (progn
+                        (goto-char reply-position)
+                        (not (re-search-forward "<\\([0-9]+\\)\t" nil t))))
+            (unless (accept-process-output process pymacs-timeout-at-reply)
+              (setq status (process-status process))))
+          (when (eq status 'run)
+            (let ((limit-position (+ (match-end 0)
+                                     (string-to-number (match-string 1)))))
+              (while (and (eq status 'run)
+                          (< (marker-position marker) limit-position))
+                (unless (accept-process-output process pymacs-timeout-at-line)
+                  (setq status (process-status process))))))
+          ;; Decode reply.
+          (if (not (eq status 'run))
+              (pymacs-report-error "Pymacs helper status is `%S'" status)
+            (goto-char (match-end 0))
+            (setq reply (read (current-buffer))))))
+      (when (and moving (not pymacs-trace-transit))
+        (goto-char marker))
+      reply)))
+
+(defun pymacs-interruptible-eval (expression)
+  ;; This function produces a pair (VALUE . SUCCESS) for EXPRESSION.
+  ;; A cautious evaluation of EXPRESSION is attempted, and any
+  ;; error while evaluating is caught, including Emacs quit (C-g).
+  ;; Any Emacs quit also gets forward as a SIGINT to the Pymacs handler.
+  ;; With SUCCESS being true, VALUE is the expression value.
+  ;; With SUCCESS being false, VALUE is an interruption diagnostic.
+  (condition-case info
+      (cons (let ((inhibit-quit nil)) (eval expression)) t)
+    (quit (setq quit-flag t)
+          (interrupt-process pymacs-transit-buffer)
+          (cons "*Interrupted!*" nil))
+    (error (cons (prin1-to-string info) nil))))
+
+(defun pymacs-proper-list-p (expression)
+  ;; Tell if a list is proper, id est, that it is `nil' or ends with `nil'.
+  (cond ((not expression))
+        ((consp expression) (not (cdr (last expression))))))
+
+(provide 'pymacs)
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright © 2001, 2002, 2003 Progiciels Bourbeau-Pinard inc.
+# François Pinard <pinard@iro.umontreal.ca>, 2001.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.  */
+
+"""\
+Interface between Emacs Lisp and Python - Python part.
+
+Emacs may launch this module as a stand-alone program, in which case it
+acts as a server of Python facilities for that Emacs session, reading
+requests from standard input and writing replies on standard output.
+When used in this way, the program is called "the Pymacs helper".
+
+This module may also be usefully imported by those other Python modules.
+See the Pymacs documentation (in `README') for more information.
+"""
+
+__metaclass__ = type
+import os, sys
+
+
+def fixup_icanon():
+    # otherwise sys.stdin.read hangs for large inputs in emacs 24
+    # see comment in emacs source code sysdep.c
+    import termios
+    a = termios.tcgetattr(1)
+    a[3] &= ~termios.ICANON
+    termios.tcsetattr(1, termios.TCSANOW, a)
+
+try:
+    import signal
+except ImportError:
+    # Jython does not have signal.
+    signal = None
+
+old_style_exception = not isinstance(Exception, type)
+
+## Python services for Emacs applications.
+
+class Main:
+    debug_file = None
+    signal_file = None
+
+    def main(self, *arguments):
+        """\
+Execute Python services for Emacs, and Emacs services for Python.
+This program is meant to be called from Emacs, using `pymacs.el'.
+
+Debugging options:
+    -d FILE  Debug the protocol to FILE.
+    -s FILE  Trace received signals to FILE.
+
+Arguments are added to the search path for Python modules.
+"""
+        # Decode options.
+        arguments = (os.environ.get('PYMACS_OPTIONS', '').split()
+                     + list(arguments))
+        import getopt
+        options, arguments = getopt.getopt(arguments, 'fd:s:')
+        for option, value in options:
+            if option == '-d':
+                self.debug_file = value
+            elif option == '-s':
+                self.signal_file = value
+            elif option == '-f':
+                try:
+                    fixup_icanon()
+                except:
+                    pass
+
+        arguments.reverse()
+        for argument in arguments:
+            if os.path.isdir(argument):
+                sys.path.insert(0, argument)
+        # Inhibit signals.
+        if signal is not None:
+            self.original_handler = signal.signal(
+                    signal.SIGINT, self.interrupt_handler)
+            for counter in range(1, signal.NSIG):
+                if counter == signal.SIGINT:
+                    self.original_handler = signal.signal(
+                            counter, self.interrupt_handler)
+
+                # The following few lines of code are reported to create IO
+                # problems within the Pymacs helper itself, so I merely comment
+                # them for now, until we know better.
+
+                #else:
+                #    try:
+                #        signal.signal(counter, self.generic_handler)
+                #    except RuntimeError:
+                #        pass
+        self.inhibit_quit = True
+        # Start protocol and services.
+        from Pymacs import __version__
+        lisp._protocol.send('version', '"%s"' % __version__)
+        lisp._protocol.loop()
+
+    def generic_handler(self, number, frame):
+        if self.signal_file:
+            handle = file(self.signal_file, 'a')
+            handle.write('%d\n' % number)
+            handle.close()
+
+    def interrupt_handler(self, number, frame):
+        if self.signal_file:
+            star = (' *', '')[self.inhibit_quit]
+            handle = file(self.signal_file, 'a')
+            handle.write('%d%s\n' % (number, star))
+            handle.close()
+        if not self.inhibit_quit:
+            self.original_handler(number, frame)
+
+run = Main()
+main = run.main
+
+if old_style_exception:
+    ProtocolError = 'ProtocolError'
+    ZombieError = 'ZombieError'
+else:
+    class error(Exception): pass
+    class ProtocolError(error): pass
+    class ZombieError(error): pass
+
+class Protocol:
+
+    # All exec's and eval's triggered from the Emacs side are all executed
+    # within the "loop" method below, so all user context is kept as
+    # local variables within this single routine.  Different instances
+    # of this Protocol class would yield independant evaluation contexts.
+    # But in the usual case, there is only one such instance kept within a
+    # Lisp_Interface instance, and the "lisp" global variable within this
+    # module holds such a Lisp_Interface instance.
+
+    def __init__(self):
+        self.freed = []
+
+    def loop(self):
+        # The server loop repeatedly receives a request from Emacs and
+        # returns a response, which is either the value of the received
+        # Python expression, or the Python traceback if an error occurs
+        # while evaluating the expression.
+
+        # The server loop may also be executed, as a recursive invocation,
+        # in the context of Emacs serving a Python request.  In which
+        # case, we might also receive a notification from Emacs telling
+        # that the reply has been transmitted, or that an error occurred.
+        # A reply notification from Emacs interrupts the loop: the result
+        # of this function is then the value returned from Emacs.
+        done = False
+        while not done:
+            try:
+                action, text = self.receive()
+                if action == 'eval':
+                    action = 'return'
+                    try:
+                        run.inhibit_quit = False
+                        value = eval(text)
+                    finally:
+                        run.inhibit_quit = True
+                elif action == 'exec':
+                    action = 'return'
+                    value = None
+                    try:
+                        run.inhibit_quit = False
+                        exec text
+                    finally:
+                        run.inhibit_quit = True
+                elif action == 'return':
+                    done = True
+                    try:
+                        run.inhibit_quit = False
+                        value = eval(text)
+                    finally:
+                        run.inhibit_quit = True
+                elif action == 'raise':
+                    action = 'raise'
+                    value = 'Emacs: ' + text
+                else:
+                    if old_style_exception:
+                        raise ProtocolError, "Unknown action %r" % action
+                    raise ProtocolError("Unknown action %r" % action)
+            except KeyboardInterrupt:
+                if done:
+                    raise
+                action = 'raise'
+                value = '*Interrupted*'
+            except ProtocolError, exception:
+                sys.exit("Protocol error: %s\n" % exception)
+            except:
+                import StringIO, traceback
+                buffer = StringIO.StringIO()
+                traceback.print_exc(file=buffer)
+                action = 'raise'
+                value = buffer.getvalue()
+            if not done:
+                fragments = []
+                print_lisp(value, fragments.append, True)
+                self.send(action, ''.join(fragments))
+        return value
+
+    def receive(self):
+        # Receive a Python expression from Emacs, return (ACTION, TEXT).
+        prefix = sys.stdin.read(3)
+        if not prefix or prefix[0] != '>':
+            if old_style_exception:
+                raise ProtocolError, "`>' expected."
+            raise ProtocolError("`>' expected.")
+        while prefix[-1] != '\t':
+            character = sys.stdin.read(1)
+            if not character:
+                if old_style_exception:
+                    raise ProtocolError, "Empty stdin read."
+                raise ProtocolError("Empty stdin read.")
+            prefix += character
+        text = sys.stdin.read(int(prefix[1:-1]))
+        if run.debug_file is not None:
+            handle = file(run.debug_file, 'a')
+            handle.write(prefix + text)
+            handle.close()
+        return text.split(None, 1)
+
+    def send(self, action, text):
+        # Send ACTION and its TEXT argument to Emacs.
+        if self.freed:
+            # All delayed Lisp cleanup is piggied back on the transmission.
+            text = ('(free (%s) %s %s)\n'
+                    % (' '.join(map(str, self.freed)), action, text))
+            self.freed = []
+        else:
+            text = '(%s %s)\n' % (action, text)
+        prefix = '<%d\t' % len(text)
+        if run.debug_file is not None:
+            handle = file(run.debug_file, 'a')
+            handle.write(prefix + text)
+            handle.close()
+        sys.stdout.write(prefix + text)
+        sys.stdout.flush()
+
+def pymacs_load_helper(file_without_extension, prefix):
+    # This function imports a Python module, then returns a Lisp expression
+    # which, when later evaluated, will install trampoline definitions
+    # in Emacs for accessing the Python module facilities.  Module, given
+    # through FILE_WITHOUT_EXTENSION, may be a full path, yet without the
+    # `.py' or `.pyc' suffix, in which case the directory is temporarily
+    # added to the Python search path for the sole duration of that import.
+    # All defined symbols on the Lisp side have have PREFIX prepended,
+    # and have Python underlines in Python turned into dashes.  If PREFIX
+    # is None, it then defaults to the base name of MODULE with underlines
+    # turned to dashes, followed by a dash.
+    directory, module_name = os.path.split(file_without_extension)
+    module_components = module_name.split('.')
+    if prefix is None:
+        prefix = module_components[-1].replace('_', '-') + '-'
+    try:
+        object = sys.modules.get(module_name)
+        if object:
+            reload(object)
+        else:
+            try:
+                if directory:
+                    sys.path.insert(0, directory)
+                object = __import__(module_name)
+            finally:
+                if directory:
+                    del sys.path[0]
+            # Whenever MODULE_NAME is of the form [PACKAGE.]...MODULE,
+            # __import__ returns the outer PACKAGE, not the module.
+            for component in module_components[1:]:
+                object = getattr(object, component)
+    except ImportError:
+        return None
+    load_hook = object.__dict__.get('pymacs_load_hook')
+    if load_hook:
+        load_hook()
+    interactions = object.__dict__.get('interactions', {})
+    if not isinstance(interactions, dict):
+        interactions = {}
+    arguments = []
+    for name, value in object.__dict__.items():
+        if callable(value) and value is not lisp:
+            arguments.append(allocate_python(value))
+            arguments.append(lisp[prefix + name.replace('_', '-')])
+            try:
+                interaction = value.interaction
+            except AttributeError:
+                interaction = interactions.get(value)
+            if callable(interaction):
+                arguments.append(allocate_python(interaction))
+            else:
+                arguments.append(interaction)
+    if arguments:
+        return [lisp.progn,
+                [lisp.pymacs_defuns, [lisp.quote, arguments]],
+                object]
+    return [lisp.quote, object]
+
+def doc_string(object):
+    if hasattr(object, '__doc__'):
+        return object.__doc__
+
+## Garbage collection matters.
+
+# Many Python types do not have direct Lisp equivalents, and may not be
+# directly returned to Lisp for this reason.  They are rather allocated in
+# a list of handles, below, and a handle index is used for communication
+# instead of the Python value.  Whenever such a handle is freed from the
+# Lisp side, its index is added of a freed list for later reuse.
+
+python = []
+freed_list = []
+
+def allocate_python(value):
+    assert not isinstance(value, str), (type(value), repr(value))
+    # Allocate some handle to hold VALUE, return its index.
+    if freed_list:
+        index = freed_list[-1]
+        del freed_list[-1]
+        python[index] = value
+    else:
+        index = len(python)
+        python.append(value)
+    return index
+
+def free_python(*indices):
+    # Return many handles to the pool.
+    for index in indices:
+        python[index] = None
+        freed_list.append(index)
+
+def zombie_python(*indices):
+    # Ensure that some handles are _not_ in the pool.
+    for index in indices:
+        while index >= len(python):
+            freed_list.append(len(python))
+            python.append(None)
+        python[index] = zombie
+        freed_list.remove(index)
+    # Merely to make `*Pymacs*' a bit more readable.
+    freed_list.sort()
+
+def zombie(*arguments):
+    # This catch-all function is set as the value for any function which
+    # disappeared with a previous Pymacs helper process, so calling
+    # such a function from Emacs will trigger a decipherable diagnostic.
+    diagnostic = "Object vanished when the Pymacs helper was killed"
+    if lisp.pymacs_dreadful_zombies.value():
+        if old_style_exception:
+            raise ZombieError, diagnostic
+        raise ZombieError(diagnostic)
+    lisp.message(diagnostic)
+
+## Emacs services for Python applications.
+
+class Let:
+
+    def __init__(self, **keywords):
+        # The stack holds (METHOD, DATA) pairs, where METHOD is the expected
+        # unbound pop_* method, and DATA holds information to be restored.
+        # METHOD may not be bound to the instance, as this would induce
+        # reference cycles, and then, __del__ would not be called timely.
+        self.stack = []
+        if keywords:
+            self.push(**keywords)
+
+    def __del__(self):
+        self.pops()
+
+    def __nonzero__(self):
+        # So stylistic `if let:' executes faster.
+        return True
+
+    def pops(self):
+        while self.stack:
+            self.stack[-1][0](self)
+
+    def push(self, **keywords):
+        data = []
+        for name, value in keywords.items():
+            data.append((name, getattr(lisp, name).value()))
+            setattr(lisp, name, value)
+        self.stack.append((Let.pop, data))
+        return self
+
+    def pop(self):
+        method, data = self.stack.pop()
+        assert method == Let.pop, (method, data)
+        for name, value in data:
+            setattr(lisp, name, value)
+
+    def push_excursion(self):
+        self.stack.append((Let.pop_excursion, (lisp.current_buffer(),
+                                               lisp.point_marker(),
+                                               lisp.mark_marker())))
+        return self
+
+    def pop_excursion(self):
+        method, data = self.stack.pop()
+        assert method == Let.pop_excursion, (method, data)
+        buffer, point_marker, mark_marker = data
+        lisp.set_buffer(buffer)
+        lisp.goto_char(point_marker)
+        lisp.set_mark(mark_marker)
+        lisp.set_marker(point_marker, None)
+        lisp.set_marker(mark_marker, None)
+
+    def push_match_data(self):
+        self.stack.append((Let.pop_match_data, lisp.match_data()))
+        return self
+
+    def pop_match_data(self):
+        method, data = self.stack.pop()
+        assert method == Let.pop_match_data, (method, data)
+        lisp.set_match_data(data)
+
+    def push_restriction(self):
+        self.stack.append((Let.pop_restriction, (lisp.point_min_marker(),
+                                                 lisp.point_max_marker())))
+        return self
+
+    def pop_restriction(self):
+        method, data = self.stack.pop()
+        assert method == Let.pop_restriction, (method, data)
+        point_min_marker, point_max_marker = data
+        lisp.narrow_to_region(point_min_marker, point_max_marker)
+        lisp.set_marker(point_min_marker, None)
+        lisp.set_marker(point_max_marker, None)
+
+    def push_selected_window(self):
+        self.stack.append((Let.pop_selected_window, lisp.selected_window()))
+        return self
+
+    def pop_selected_window(self):
+        method, data = self.stack.pop()
+        assert method == Let.pop_selected_window, (method, data)
+        lisp.select_window(data)
+
+    def push_window_excursion(self):
+        self.stack.append((Let.pop_window_excursion,
+                           lisp.current_window_configuration()))
+        return self
+
+    def pop_window_excursion(self):
+        method, data = self.stack.pop()
+        assert method == Let.pop_window_excursion, (method, data)
+        lisp.set_window_configuration(data)
+
+class Symbol:
+
+    def __init__(self, text):
+        self.text = text
+
+    def __repr__(self):
+        return 'lisp[%s]' % repr(self.text)
+
+    def __str__(self):
+        return '\'' + self.text
+
+    def value(self):
+        return lisp._eval(self.text)
+
+    def copy(self):
+        return lisp._expand(self.text)
+
+    def set(self, value):
+        if value is None:
+            lisp._eval('(setq %s nil)' % self.text)
+        else:
+            fragments = []
+            write = fragments.append
+            write('(progn (setq %s ' % self.text)
+            print_lisp(value, write, True)
+            write(') nil)')
+            lisp._eval(''.join(fragments))
+
+    def __call__(self, *arguments):
+        fragments = []
+        write = fragments.append
+        write('(%s' % self.text)
+        for argument in arguments:
+            write(' ')
+            print_lisp(argument, write, True)
+        write(')')
+        return lisp._eval(''.join(fragments))
+
+class Lisp:
+
+    def __init__(self, index):
+        self.index = index
+
+    def __del__(self):
+        lisp._protocol.freed.append(self.index)
+
+    def __repr__(self):
+        return ('lisp(%s)' % repr(lisp('(prin1-to-string %s)' % self)))
+
+    def __str__(self):
+        return '(aref pymacs-lisp %d)' % self.index
+
+    def value(self):
+        return self
+
+    def copy(self):
+        return lisp._expand(str(self))
+
+class Buffer(Lisp):
+    pass
+
+    #def write(text):
+    #    # So you could do things like
+    #    # print >>lisp.current_buffer(), "Hello World"
+    #    lisp.insert(text, self)
+
+    #def point(self):
+    #    return lisp.point(self)
+
+class List(Lisp):
+
+    def __call__(self, *arguments):
+        fragments = []
+        write = fragments.append
+        write('(%s' % self)
+        for argument in arguments:
+            write(' ')
+            print_lisp(argument, write, True)
+        write(')')
+        return lisp._eval(''.join(fragments))
+
+    def __len__(self):
+        return lisp._eval('(length %s)' % self)
+
+    def __getitem__(self, key):
+        value = lisp._eval('(nth %d %s)' % (key, self))
+        if value is None and key >= len(self):
+            if old_style_exception:
+                raise IndexError, key
+            raise IndexError(key)
+        return value
+
+    def __setitem__(self, key, value):
+        fragments = []
+        write = fragments.append
+        write('(setcar (nthcdr %d %s) ' % (key, self))
+        print_lisp(value, write, True)
+        write(')')
+        lisp._eval(''.join(fragments))
+
+class Table(Lisp):
+
+    def __getitem__(self, key):
+        fragments = []
+        write = fragments.append
+        write('(gethash ')
+        print_lisp(key, write, True)
+        write(' %s)' % self)
+        return lisp._eval(''.join(fragments))
+
+    def __setitem__(self, key, value):
+        fragments = []
+        write = fragments.append
+        write('(puthash ')
+        print_lisp(key, write, True)
+        write(' ')
+        print_lisp(value, write, True)
+        write(' %s)' % self)
+        lisp._eval(''.join(fragments))
+
+class Vector(Lisp):
+
+    def __len__(self):
+        return lisp._eval('(length %s)' % self)
+
+    def __getitem__(self, key):
+        return lisp._eval('(aref %s %d)' % (self, key))
+
+    def __setitem__(self, key, value):
+        fragments = []
+        write = fragments.append
+        write('(aset %s %d ' % (self, key))
+        print_lisp(value, write, True)
+        write(')')
+        lisp._eval(''.join(fragments))
+
+class Lisp_Interface:
+
+    def __init__(self):
+        self.__dict__['_cache'] = {'nil': None}
+        self.__dict__['_protocol'] = Protocol()
+
+    def __call__(self, text):
+        return self._eval('(progn %s)' % text)
+
+    def _eval(self, text):
+        self._protocol.send('eval', text)
+        return self._protocol.loop()
+
+    def _expand(self, text):
+        self._protocol.send('expand', text)
+        return self._protocol.loop()
+
+    def __getattr__(self, name):
+        if name[0] == '_':
+            if old_style_exception:
+                raise AttributeError, name
+            raise AttributeError(name)
+        return self[name.replace('_', '-')]
+
+    def __setattr__(self, name, value):
+        if name[0] == '_':
+            if old_style_exception:
+                raise AttributeError, name
+            raise AttributeError(name)
+        self[name.replace('_', '-')] = value
+
+    def __getitem__(self, name):
+        try:
+            return self._cache[name]
+        except KeyError:
+            symbol = self._cache[name] = Symbol(name)
+            return symbol
+
+    def __setitem__(self, name, value):
+        try:
+            symbol = self._cache[name]
+        except KeyError:
+            symbol = self._cache[name] = Symbol(name)
+        symbol.set(value)
+
+lisp = Lisp_Interface()
+
+print_lisp_quoted_specials = {
+        '"': '\\"', '\\': '\\\\', '\b': '\\b', '\f': '\\f',
+        '\n': '\\n', '\r': '\\r', '\t': '\\t'}
+
+def print_lisp(value, write, quoted):
+    if value is None:
+        write('nil')
+    elif isinstance(bool, type) and isinstance(value, bool):
+        write(('nil', 't')[value])
+    elif isinstance(value, int):
+        write(repr(value))
+    elif isinstance(value, float):
+        write(repr(value))
+    elif isinstance(value, basestring):
+        multibyte = False
+        if isinstance(value, unicode):
+            try:
+                value = value.encode('ASCII')
+            except UnicodeError:
+                value = value.encode('UTF-8')
+                multibyte = True
+        if multibyte:
+            write('(decode-coding-string ')
+        write('"')
+        for character in value:
+            special = print_lisp_quoted_specials.get(character)
+            if special is not None:
+                write(special)
+            elif 32 <= ord(character) < 127:
+                write(character)
+            else:
+                write('\\%.3o' % ord(character))
+        write('"')
+        if multibyte:
+            write(' \'utf-8)')
+    elif isinstance(value, list):
+        if quoted:
+            write("'")
+        if len(value) == 0:
+            write('nil')
+        elif len(value) == 2 and value[0] == lisp.quote:
+            write("'")
+            print_lisp(value[1], write, False)
+        else:
+            write('(')
+            print_lisp(value[0], write, False)
+            for sub_value in value[1:]:
+                write(' ')
+                print_lisp(sub_value, write, False)
+            write(')')
+    elif isinstance(value, tuple):
+        write('[')
+        if len(value) > 0:
+            print_lisp(value[0], write, False)
+            for sub_value in value[1:]:
+                write(' ')
+                print_lisp(sub_value, write, False)
+        write(']')
+    elif isinstance(value, Lisp):
+        write(str(value))
+    elif isinstance(value, Symbol):
+        if quoted:
+            write("'")
+        write(value.text)
+    elif callable(value):
+        write('(pymacs-defun %d nil)' % allocate_python(value))
+    else:
+        write('(pymacs-python %d)' % allocate_python(value))
+
+if __name__ == '__main__':
+    main(*sys.argv[1:])
-((("set-variable" .
+((("cwd" .
+   [1 0 0])
+  (":" .
+   [1])
+  ("getenv" .
+   [0 0 0 0 0 2])
+  ("set-variable" .
    [0 0 0 0 0 0 1 0 0 0 0 0])
   ("lambda" .
    [0 0 0 0 1 0])
   ("color" .
    [1 0 0 0 0])
   ("*" .
-   [1])
+   [2])
   ("add-to-" .
    [0 0 0 0 0 0 1])
   ("load" .
   ("color-theme-twilight" .
    [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0])
   ("GranularPermissionsBackend" .
-   [0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0])))
+   [0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0])
+  ("setenv-internal" .
+   [0 0 0 0 0 0 0 0 0 0 0 0 0 0 1])
+  ("setenv" .
+   [0 0 0 0 0 1])
+  ("__version__" .
+   [0 0 0 0 0 1 0 0 0 0 0])))

emacs-starter-kit/places

 ;;; -*- coding: utf-8 -*-
 
-nil
+(("/Users/vasilvangelovski/.emacs.d/init.el" . 739))
+
+(setq cwd (file-name-directory
+                    (or (buffer-file-name) load-file-name)))
 (add-to-list 'load-path "~/.emacs.d/")
 (add-to-list 'load-path "~/.emacs.d/python-mode/")
+(add-to-list 'load-path "~/.emacs.d/Pymacs/")
+(setenv "PYTHONPATH" cwd)
 
 ;; emacs-starter-kit
 (add-to-list 'load-path "~/.emacs.d/emacs-starter-kit/")
 
 
 
-;; python intentation
+;; python indentation
 (require 'python-mode)
+
 (add-hook 'python-mode-hook
           (lambda ()
             (set-variable 'py-indent-offset 4)

pymacs.el

-;;; Interface between Emacs Lisp and Python - Lisp part.    -*- emacs-lisp -*-
-;;; Copyright © 2001, 2002, 2003 Progiciels Bourbeau-Pinard inc.
-;;; François Pinard <pinard@iro.umontreal.ca>, 2001.
-
-;;; This program is free software; you can redistribute it and/or modify
-;;; it under the terms of the GNU General Public License as published by
-;;; the Free Software Foundation; either version 2, or (at your option)
-;;; any later version.
-;;;
-;;; This program is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;;; GNU General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU General Public License
-;;; along with this program; if not, write to the Free Software Foundation,
-;;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.  */
-
-;;; Portability stunts.
-
-(defvar pymacs-use-hash-tables
-  (and (fboundp 'make-hash-table) (fboundp 'gethash) (fboundp 'puthash))
-  "Set to t if hash tables are available.")
-
-(eval-and-compile
-
-  (if (fboundp 'multibyte-string-p)
-      (defalias 'pymacs-multibyte-string-p 'multibyte-string-p)
-    (defun pymacs-multibyte-string-p (string)
-      "Tell XEmacs if STRING should be handled as multibyte."
-      (not (equal (find-charset-string string) '(ascii))))))
-
-(defalias 'pymacs-report-error (symbol-function 'error))
-
-;;; Published variables and functions.
-
-(defvar pymacs-load-path nil
-  "List of additional directories to search for Python modules.
-The directories listed will be searched first, in the order given.")
-
-(defvar pymacs-trace-transit '(5000 . 30000)
-  "Keep the communication buffer growing, for debugging.
-When this variable is nil, the `*Pymacs*' communication buffer gets erased
-before each communication round-trip.  Setting it to `t' guarantees that
-the full communication is saved, which is useful for debugging.
-It could also be given as (KEEP . LIMIT): whenever the buffer exceeds LIMIT
-bytes, it is reduced to approximately KEEP bytes.")
-
-(defvar pymacs-forget-mutability nil
-  "Transmit copies to Python instead of Lisp handles, as much as possible.
-When this variable is nil, most mutable objects are transmitted as handles.
-This variable is meant to be temporarily rebound to force copies.")
-
-(defvar pymacs-mutable-strings nil
-  "Prefer transmitting Lisp strings to Python as handles.
-When this variable is nil, strings are transmitted as copies, and the
-Python side thus has no way for modifying the original Lisp strings.
-This variable is ignored whenever `forget-mutability' is set.")
-
-(defvar pymacs-timeout-at-start 30
-  "Maximum reasonable time, in seconds, for starting the Pymacs helper.
-A machine should be pretty loaded before one needs to increment this.")
-
-(defvar pymacs-timeout-at-reply 5
-  "Expected maximum time, in seconds, to get the first line of a reply.
-The status of the Pymacs helper is checked at every such timeout.")
-
-(defvar pymacs-timeout-at-line 2
-  "Expected maximum time, in seconds, to get another line of a reply.
-The status of the Pymacs helper is checked at every such timeout.")
-
-(defvar pymacs-dreadful-zombies nil
-  "If zombies should trigger hard errors, whenever they get called.
-If `nil', calling a zombie will merely produce a diagnostic message.")
-
-(defun pymacs-load (module &optional prefix noerror)
-  "Import the Python module named MODULE into Emacs.
-Each function in the Python module is made available as an Emacs function.
-The Lisp name of each function is the concatenation of PREFIX with
-the Python name, in which underlines are replaced by dashes.  If PREFIX is
-not given, it defaults to MODULE followed by a dash.
-If NOERROR is not nil, do not raise error when the module is not found."
-  (interactive
-   (let* ((module (read-string "Python module? "))
-          (default (concat (car (last (split-string module "\\."))) "-"))
-          (prefix (read-string (format "Prefix? [%s] " default)
-                               nil nil default)))
-     (list module prefix)))
-  (message "Pymacs loading %s..." module)
-  (let ((lisp-code (pymacs-call "pymacs_load_helper" module prefix)))
-    (cond (lisp-code (let ((result (eval lisp-code)))
-                       (message "Pymacs loading %s...done" module)
-                       result))
-          (noerror (message "Pymacs loading %s...failed" module) nil)
-          (t (pymacs-report-error "Pymacs loading %s...failed" module)))))
-
-(defun pymacs-eval (text)
-  "Compile TEXT as a Python expression, and return its value."
-  (interactive "sPython expression? ")
-  (let ((value (pymacs-serve-until-reply "eval" `(princ ,text))))
-    (when (interactive-p)
-      (message "%S" value))
-    value))
-
-(defun pymacs-exec (text)
-  "Compile and execute TEXT as a sequence of Python statements.
-This functionality is experimental, and does not appear to be useful."
-  (interactive "sPython statements? ")
-  (let ((value (pymacs-serve-until-reply "exec" `(princ ,text))))
-    (when (interactive-p)
-      (message "%S" value))
-    value))
-
-(defun pymacs-call (function &rest arguments)
-  "Return the result of calling a Python function FUNCTION over ARGUMENTS.
-FUNCTION is a string denoting the Python function, ARGUMENTS are separate
-Lisp expressions, one per argument.  Immutable Lisp constants are converted
-to Python equivalents, other structures are converted into Lisp handles."
-  (pymacs-serve-until-reply
-   "eval" `(pymacs-print-for-apply ',function ',arguments)))
-
-(defun pymacs-apply (function arguments)
-  "Return the result of calling a Python function FUNCTION over ARGUMENTS.
-FUNCTION is a string denoting the Python function, ARGUMENTS is a list of
-Lisp expressions.  Immutable Lisp constants are converted to Python
-equivalents, other structures are converted into Lisp handles."
-  (pymacs-serve-until-reply
-   "eval" `(pymacs-print-for-apply ',function ',arguments)))
-
-;;; Integration details.
-
-;; Python functions and modules should ideally look like Lisp functions and
-;; modules.  This page tries to increase the integration seamlessness.
-
-(defadvice documentation (around pymacs-ad-documentation activate)
-  ;; Integration of doc-strings.
-  (let* ((reference (pymacs-python-reference function))
-         (python-doc (when reference
-                       (pymacs-eval (format "doc_string(%s)" reference)))))
-    (if (or reference python-doc)
-        (setq ad-return-value
-              (concat
-               "It interfaces to a Python function.\n\n"
-               (when python-doc
-                 (if raw python-doc (substitute-command-keys python-doc)))))
-      ad-do-it)))
-
-(defun pymacs-python-reference (object)
-  ;; Return the text reference of a Python object if possible, else nil.
-  (when (functionp object)
-    (let* ((definition (indirect-function object))
-           (body (and (pymacs-proper-list-p definition)
-                      (> (length definition) 2)
-                      (eq (car definition) 'lambda)
-                      (cddr definition))))
-      (when (and body (listp (car body)) (eq (caar body) 'interactive))
-        ;; Skip the interactive specification of a function.
-        (setq body (cdr body)))
-      (when (and body
-                 ;; Advised functions start with a string.
-                 (not (stringp (car body)))
-                 ;; Python trampolines hold exactly one expression.
-                 (= (length body) 1))
-        (let ((expression (car body)))
-          ;; EXPRESSION might now hold something like:
-          ;;    (pymacs-apply (quote (pymacs-python . N)) ARGUMENT-LIST)
-          (when (and (pymacs-proper-list-p expression)
-                     (= (length expression) 3)
-                     (eq (car expression) 'pymacs-apply)
-                     (eq (car (cadr expression)) 'quote))
-            (setq object (cadr (cadr expression))))))))
-  (when (eq (car-safe object) 'pymacs-python)
-    (format "python[%d]" (cdr object))))
-
-;; The following functions are experimental -- they are not satisfactory yet.
-
-(defun pymacs-file-handler (operation &rest arguments)
-  ;; Integration of load-file, autoload, etc.
-  ;; Emacs might want the contents of some `MODULE.el' which does not exist,
-  ;; while there is a `MODULE.py' or `MODULE.pyc' file in the same directory.
-  ;; The goal is to generate a virtual contents for this `MODULE.el' file, as
-  ;; a set of Lisp trampoline functions to the Python module functions.
-  ;; Python modules can then be loaded or autoloaded as if they were Lisp.
-  (cond ((and (eq operation 'file-readable-p)
-              (let ((module (substring (car arguments) 0 -3)))
-                (or (pymacs-file-force operation arguments)
-                    (file-readable-p (concat module ".py"))
-                    (file-readable-p (concat module ".pyc"))))))
-        ((and (eq operation 'load)
-              (not (pymacs-file-force
-                    'file-readable-p (list (car arguments))))
-              (file-readable-p (car arguments)))
-         (let ((lisp-code (pymacs-call "pymacs_load_helper"
-                                       (substring (car arguments) 0 -3)
-                                       nil)))
-           (unless lisp-code
-             (pymacs-report-error "Python import error"))
-           (eval lisp-code)))
-        ((and (eq operation 'insert-file-contents)
-              (not (pymacs-file-force
-                    'file-readable-p (list (car arguments))))
-              (file-readable-p (car arguments)))
-         (let ((lisp-code (pymacs-call "pymacs_load_helper"
-                                       (substring (car arguments) 0 -3)
-                                       nil)))
-           (unless lisp-code
-             (pymacs-report-error "Python import error"))
-           (insert (prin1-to-string lisp-code))))
-        (t (pymacs-file-force operation arguments))))
-
-(defun pymacs-file-force (operation arguments)
-  ;; Bypass the file handler.
-  (let ((inhibit-file-name-handlers
-         (cons 'pymacs-file-handler
-               (and (eq inhibit-file-name-operation operation)
-                    inhibit-file-name-handlers)))
-        (inhibit-file-name-operation operation))
-    (apply operation arguments)))
-
-;(add-to-list 'file-name-handler-alist '("\\.el\\'" . pymacs-file-handler))
-
-;;; Gargabe collection of Python IDs.
-
-;; Python objects which have no Lisp representation are allocated on the
-;; Python side as `python[INDEX]', and INDEX is transmitted to Emacs, with
-;; the value to use on the Lisp side for it.  Whenever Lisp does not need a
-;; Python object anymore, it should be freed on the Python side.  The
-;; following variables and functions are meant to fill this duty.
-
-(defvar pymacs-used-ids nil
-  "List of received IDs, currently allocated on the Python side.")
-
-(defvar pymacs-weak-hash nil
-  "Weak hash table, meant to find out which IDs are still needed.")
-
-(defvar pymacs-gc-wanted nil
-  "Flag if it is time to clean up unused IDs on the Python side.")
-
-(defvar pymacs-gc-running nil
-  "Flag telling that a Pymacs garbage collection is in progress.")
-
-(defvar pymacs-gc-timer nil
-  "Timer to trigger Pymacs garbage collection at regular time intervals.
-The timer is used only if `post-gc-hook' is not available.")
-
-(defun pymacs-schedule-gc (&optional xemacs-list)
-  (unless pymacs-gc-running
-    (setq pymacs-gc-wanted t)))
-
-(defun pymacs-garbage-collect ()
-  ;; Clean up unused IDs on the Python side.
-  (when pymacs-use-hash-tables
-    (let ((pymacs-gc-running t)
-          (pymacs-forget-mutability t)
-          (ids pymacs-used-ids)
-          used-ids unused-ids)
-      (while ids
-        (let ((id (car ids)))
-          (setq ids (cdr ids))
-          (if (gethash id pymacs-weak-hash)
-              (setq used-ids (cons id used-ids))
-            (setq unused-ids (cons id unused-ids)))))
-      (setq pymacs-used-ids used-ids
-            pymacs-gc-wanted nil)
-      (when unused-ids
-        (pymacs-apply "free_python" unused-ids)))))
-
-(defun pymacs-defuns (arguments)
-  ;; Take one argument, a list holding a number of items divisible by 3.  The
-  ;; first argument is an INDEX, the second is a NAME, the third is the
-  ;; INTERACTION specification, and so forth.  Register Python INDEX with a
-  ;; function with that NAME and INTERACTION on the Lisp side.  The strange
-  ;; calling convention is to minimise quoting at call time.
-  (while (>= (length arguments) 3)
-    (let ((index (nth 0 arguments))
-          (name (nth 1 arguments))
-          (interaction (nth 2 arguments)))
-      (fset name (pymacs-defun index interaction))
-      (setq arguments (nthcdr 3 arguments)))))
-
-(defun pymacs-defun (index interaction)
-  ;; Register INDEX on the Lisp side with a Python object that is a function,
-  ;; and return a lambda form calling that function.  If the INTERACTION
-  ;; specification is nil, the function is not interactive.  Otherwise, the
-  ;; function is interactive, INTERACTION is then either a string, or the
-  ;; index of an argument-less Python function returning the argument list.
-  (let ((object (pymacs-python index)))
-    (cond ((null interaction)
-           `(lambda (&rest arguments)
-              (pymacs-apply ',object arguments)))
-          ((stringp interaction)
-           `(lambda (&rest arguments)
-              (interactive ,interaction)
-              (pymacs-apply ',object arguments)))
-          (t `(lambda (&rest arguments)
-                (interactive (pymacs-call ',(pymacs-python interaction)))
-                (pymacs-apply ',object arguments))))))
-
-(defun pymacs-python (index)
-  ;; Register on the Lisp side a Python object having INDEX, and return it.
-  ;; The result is meant to be recognised specially by `print-for-eval', and
-  ;; in the function position by `print-for-apply'.
-  (let ((object (cons 'pymacs-python index)))
-    (when pymacs-use-hash-tables
-      (puthash index object pymacs-weak-hash)
-      (setq pymacs-used-ids (cons index pymacs-used-ids)))
-    object))
-
-;;; Generating Python code.
-
-;; Many Lisp expressions cannot fully be represented in Python, at least
-;; because the object is mutable on the Lisp side.  Such objects are allocated
-;; somewhere into a vector of handles, and the handle index is used for
-;; communication instead of the expression itself.
-
-(defvar pymacs-lisp nil
-  "Vector of handles to hold transmitted expressions.")
-
-(defvar pymacs-freed-list nil
-  "List of unallocated indices in Lisp.")
-
-;; When the Python GC is done with a Lisp object, a communication occurs so to
-;; free the object on the Lisp side as well.
-
-(defun pymacs-allocate-lisp (expression)
-  ;; This function allocates some handle for an EXPRESSION, and return its
-  ;; index.
-  (unless pymacs-freed-list
-    (let* ((previous pymacs-lisp)
-           (old-size (length previous))
-           (new-size (if (zerop old-size) 100 (+ old-size (/ old-size 2))))
-           (counter new-size))
-      (setq pymacs-lisp (make-vector new-size nil))
-      (while (> counter 0)
-        (setq counter (1- counter))
-        (if (< counter old-size)
-            (aset pymacs-lisp counter (aref previous counter))
-          (setq pymacs-freed-list (cons counter pymacs-freed-list))))))
-  (let ((index (car pymacs-freed-list)))
-    (setq pymacs-freed-list (cdr pymacs-freed-list))
-    (aset pymacs-lisp index expression)
-    index))
-
-(defun pymacs-free-lisp (indices)
-  ;; This function is triggered from Python side for Lisp handles which lost
-  ;; their last reference.  These references should be cut on the Lisp side as
-  ;; well, or else, the objects will never be garbage-collected.
-  (while indices
-    (let ((index (car indices)))
-      (aset pymacs-lisp index nil)
-      (setq pymacs-freed-list (cons index pymacs-freed-list)
-            indices (cdr indices)))))
-
-(defun pymacs-print-for-apply (function arguments)
-  ;; This function prints a Python expression calling FUNCTION, which is a
-  ;; string naming a Python function, or a Python reference, over all its
-  ;; ARGUMENTS, which are Lisp expressions.
-  (let ((separator "")
-        argument)
-    (if (eq (car-safe function) 'pymacs-python)
-        (princ (format "python[%d]" (cdr function)))
-      (princ function))
-    (princ "(")
-    (while arguments
-      (setq argument (car arguments)
-            arguments (cdr arguments))
-      (princ separator)
-      (setq separator ", ")
-      (pymacs-print-for-eval argument))
-    (princ ")")))
-
-(defun pymacs-print-for-eval (expression)
-  ;; This function prints a Python expression out of a Lisp EXPRESSION.
-  (let (done)
-    (cond ((not expression)
-           (princ "None")
-           (setq done t))
-          ((eq expression t)
-           (princ "True")
-           (setq done t))
-          ((numberp expression)
-           (princ expression)
-           (setq done t))
-          ((stringp expression)
-           (when (or pymacs-forget-mutability
-                     (not pymacs-mutable-strings))
-             (let* ((multibyte (pymacs-multibyte-string-p expression))
-                    (text (if multibyte
-                              (encode-coding-string expression 'utf-8)
-                            (copy-sequence expression))))
-               (set-text-properties 0 (length text) nil text)
-               (princ (mapconcat 'identity
-                                 (split-string (prin1-to-string text) "\n")
-                                 "\\n"))
-               (when (and multibyte
-                          (not (equal (find-charset-string text) '(ascii))))
-                 (princ ".decode('UTF-8')")))
-             (setq done t)))
-          ((symbolp expression)
-           (let ((name (symbol-name expression)))
-             ;; The symbol can only be transmitted when in the main oblist.
-             (when (eq expression (intern-soft name))
-               (princ "lisp[")
-               (prin1 name)
-               (princ "]")
-               (setq done t))))
-          ((vectorp expression)
-           (when pymacs-forget-mutability
-             (let ((limit (length expression))
-                   (counter 0))
-               (princ "(")
-               (while (< counter limit)
-                 (unless (zerop counter)
-                   (princ ", "))
-                 (pymacs-print-for-eval (aref expression counter))
-                 (setq counter (1+ counter)))
-               (when (= limit 1)
-                 (princ ","))
-               (princ ")")
-               (setq done t))))
-          ((eq (car-safe expression) 'pymacs-python)
-           (princ "python[")
-           (princ (cdr expression))
-           (princ "]")
-           (setq done t))
-          ((pymacs-proper-list-p expression)
-           (when pymacs-forget-mutability
-             (princ "[")
-             (pymacs-print-for-eval (car expression))
-             (while (setq expression (cdr expression))
-               (princ ", ")
-               (pymacs-print-for-eval (car expression)))
-             (princ "]")
-             (setq done t))))
-    (unless done
-      (let ((class (cond ((vectorp expression) "Vector")
-                         ((and pymacs-use-hash-tables
-                               (hash-table-p expression))
-                          "Table")
-                         ((bufferp expression) "Buffer")
-                         ((pymacs-proper-list-p expression) "List")
-                         (t "Lisp"))))
-        (princ class)
-        (princ "(")
-        (princ (pymacs-allocate-lisp expression))
-        (princ ")")))))
-
-;;; Communication protocol.
-
-(defvar pymacs-transit-buffer nil
-  "Communication buffer between Emacs and Python.")
-
-;; The principle behind the communication protocol is that it is easier to
-;; generate than parse, and that each language already has its own parser.
-;; So, the Emacs side generates Python text for the Python side to interpret,
-;; while the Python side generates Lisp text for the Lisp side to interpret.
-;; About nothing but expressions are transmitted, which are evaluated on
-;; arrival.  The pseudo `reply' function is meant to signal the final result
-;; of a series of exchanges following a request, while the pseudo `error'
-;; function is meant to explain why an exchange could not have been completed.
-
-;; The protocol itself is rather simple, and contains human readable text
-;; only.  A message starts at the beginning of a line in the communication
-;; buffer, either with `>' for the Lisp to Python direction, or `<' for the
-;; Python to Lisp direction.  This is followed by a decimal number giving the
-;; length of the message text, a TAB character, and the message text itself.
-;; Message direction alternates systematically between messages, it never
-;; occurs that two successive messages are sent in the same direction.  The
-;; first message is received from the Python side, it is `(version VERSION)'.
-
-(defun pymacs-start-services ()
-  ;; This function gets called automatically, as needed.
-  (let ((buffer (get-buffer-create "*Pymacs*")))
-    (with-current-buffer buffer
-      (buffer-disable-undo)
-      (set-buffer-multibyte nil)
-      (set-buffer-file-coding-system 'raw-text)
-      (save-match-data
-        ;; Launch the Pymacs helper.
-        (let ((process
-               (apply 'start-process "pymacs" buffer
-                      (let ((python (getenv "PYMACS_PYTHON")))
-                        (if (or (null python) (equal python ""))
-                            "python"
-                          python))
-                      "-c" (concat "import sys;"
-                                   " from Pymacs.pymacs import main;"
-                                   " main(*sys.argv[1:])")
-                      (mapcar 'expand-file-name pymacs-load-path))))
-          (cond ((fboundp 'set-process-query-on-exit-flag)
-                 (set-process-query-on-exit-flag process nil))
-                ((fboundp 'process-kill-without-query-process)
-                 (process-kill-without-query process)))
-          ;; Receive the synchronising reply.
-          (while (progn
-                   (goto-char (point-min))
-                   (not (re-search-forward "<\\([0-9]+\\)\t" nil t)))
-            (unless (accept-process-output process pymacs-timeout-at-start)
-              (pymacs-report-error
-               "Pymacs helper did not start within %d seconds"
-                     pymacs-timeout-at-start)))
-          (let ((marker (process-mark process))
-                (limit-position (+ (match-end 0)
-                                   (string-to-number (match-string 1)))))
-            (while (< (marker-position marker) limit-position)
-              (unless (accept-process-output process pymacs-timeout-at-start)
-                (pymacs-report-error
-                 "Pymacs helper probably was interrupted at start")))))
-        ;; Check that synchronisation occurred.
-        (goto-char (match-end 0))
-        (let ((reply (read (current-buffer))))
-          (if (and (pymacs-proper-list-p reply)
-                   (= (length reply) 2)
-                   (eq (car reply) 'version))
-              (unless (string-equal (cadr reply) "0.23")
-                (pymacs-report-error
-                 "Pymacs Lisp version is 0.23, Python is %s"
-                 (cadr reply)))
-            (pymacs-report-error "Pymacs got an invalid initial reply")))))
-    (when pymacs-use-hash-tables
-      (if pymacs-weak-hash
-          ;; A previous Pymacs session occurred in *this* Emacs session.  Some
-          ;; IDs may hang around, which do not correspond to anything on the
-          ;; Python side.  Python should not recycle such IDs for new objects.
-          (when pymacs-used-ids
-            (let ((pymacs-transit-buffer buffer)
-                  (pymacs-forget-mutability t))
-              (pymacs-apply "zombie_python" pymacs-used-ids)))
-        (setq pymacs-weak-hash (make-hash-table :weakness 'value)))
-      (if (boundp 'post-gc-hook)
-          (add-hook 'post-gc-hook 'pymacs-schedule-gc)
-        (setq pymacs-gc-timer (run-at-time 20 20 'pymacs-schedule-gc))))
-    ;; If nothing failed, only then declare that Pymacs has started!
-    (setq pymacs-transit-buffer buffer)))
-
-(defun pymacs-terminate-services ()
-  ;; This function is mainly provided for documentation purposes.
-  (interactive)
-  (garbage-collect)
-  (pymacs-garbage-collect)
-  (when (or (not pymacs-used-ids)
-            (yes-or-no-p "\
-Killing the Pymacs helper might create zombie objects.  Kill? "))
-    (cond ((boundp 'post-gc-hook)
-           (remove-hook 'post-gc-hook 'pymacs-schedule-gc))
-          ((timerp pymacs-gc-timer)
-           (cancel-timer pymacs-gc-timer)))
-    (when pymacs-transit-buffer
-      (kill-buffer pymacs-transit-buffer))
-    (setq pymacs-gc-running nil
-          pymacs-gc-timer nil
-          pymacs-transit-buffer nil
-          pymacs-lisp nil
-          pymacs-freed-list nil)))
-
-(defun pymacs-serve-until-reply (action inserter)
-  ;; This function builds a Python request by printing ACTION and
-  ;; evaluating INSERTER, which itself prints an argument.  It then
-  ;; sends the request to the Pymacs helper, and serves all
-  ;; sub-requests coming from the Python side, until either a reply or
-  ;; an error is finally received.
-  (unless (and pymacs-transit-buffer
-               (buffer-name pymacs-transit-buffer)
-               (get-buffer-process pymacs-transit-buffer))
-    (pymacs-start-services))
-  (when pymacs-gc-wanted
-    (pymacs-garbage-collect))
-  (let ((inhibit-quit t)
-        done value)
-    (while (not done)
-      (let ((form (pymacs-round-trip action inserter)))
-        (setq action (car form))
-        (when (eq action 'free)
-          (pymacs-free-lisp (cadr form))
-          (setq form (cddr form)
-                action (car form)))
-        (let* ((pair (pymacs-interruptible-eval (cadr form)))
-               (success (cdr pair)))
-          (setq value (car pair))
-          (cond ((eq action 'eval)
-                 (if success
-                     (setq action "return"
-                           inserter `(pymacs-print-for-eval ',value))
-                   (setq action "raise"
-                         inserter `(let ((pymacs-forget-mutability t))
-                                     (pymacs-print-for-eval ,value)))))
-                ((eq action 'expand)
-                 (if success
-                     (setq action "return"
-                           inserter `(let ((pymacs-forget-mutability t))
-                                       (pymacs-print-for-eval ,value)))
-                   (setq action "raise"
-                         inserter `(let ((pymacs-forget-mutability t))
-                                     (pymacs-print-for-eval ,value)))))
-                ((eq action 'return)
-                 (if success
-                     (setq done t)
-                   (pymacs-report-error "%s" value)))
-                ((eq action 'raise)
-                 (if success
-                     (pymacs-report-error "Python: %s" value)
-                   (pymacs-report-error "%s" value)))
-                (t (pymacs-report-error "Protocol error: %s" form))))))
-    value))
-
-(defun pymacs-round-trip (action inserter)
-  ;; This function produces a Python request by printing and
-  ;; evaluating INSERTER, which itself prints an argument.  It sends
-  ;; the request to the Pymacs helper, awaits for any kind of reply,
-  ;; and returns it.
-  (with-current-buffer pymacs-transit-buffer
-    ;; Possibly trim the beginning of the transit buffer.
-    (cond ((not pymacs-trace-transit)
-           (erase-buffer))
-          ((consp pymacs-trace-transit)
-           (when (> (buffer-size) (cdr pymacs-trace-transit))
-             (let ((cut (- (buffer-size) (car pymacs-trace-transit))))
-               (when (> cut 0)
-                 (save-excursion
-                   (goto-char cut)
-                   (unless (memq (preceding-char) '(0 ?\n))
-                     (forward-line 1))
-                   (delete-region (point-min) (point))))))))
-    ;; Send the request, wait for a reply, and process it.
-    (let* ((process (get-buffer-process pymacs-transit-buffer))
-           (status (process-status process))
-           (marker (process-mark process))
-           (moving (= (point) marker))
-           send-position reply-position reply)
-      (save-excursion
-        (save-match-data
-          ;; Encode request.
-          (setq send-position (marker-position marker))
-          (let ((standard-output marker))
-            (princ action)
-            (princ " ")
-            (eval inserter))
-          (goto-char marker)
-          (unless (= (preceding-char) ?\n)
-            (princ "\n" marker))
-          ;; Send request text.
-          (goto-char send-position)
-          (insert (format ">%d\t" (- marker send-position)))
-          (setq reply-position (marker-position marker))
-          (process-send-region process send-position marker)
-          ;; Receive reply text.
-          (while (and (eq status 'run)
-                      (progn
-                        (goto-char reply-position)
-                        (not (re-search-forward "<\\([0-9]+\\)\t" nil t))))
-            (unless (accept-process-output process pymacs-timeout-at-reply)
-              (setq status (process-status process))))
-          (when (eq status 'run)
-            (let ((limit-position (+ (match-end 0)
-                                     (string-to-number (match-string 1)))))
-              (while (and (eq status 'run)
-                          (< (marker-position marker) limit-position))
-                (unless (accept-process-output process pymacs-timeout-at-line)
-                  (setq status (process-status process))))))
-          ;; Decode reply.
-          (if (not (eq status 'run))
-              (pymacs-report-error "Pymacs helper status is `%S'" status)
-            (goto-char (match-end 0))
-            (setq reply (read (current-buffer))))))
-      (when (and moving (not pymacs-trace-transit))
-        (goto-char marker))
-      reply)))
-
-(defun pymacs-interruptible-eval (expression)
-  ;; This function produces a pair (VALUE . SUCCESS) for EXPRESSION.
-  ;; A cautious evaluation of EXPRESSION is attempted, and any
-  ;; error while evaluating is caught, including Emacs quit (C-g).
-  ;; Any Emacs quit also gets forward as a SIGINT to the Pymacs handler.
-  ;; With SUCCESS being true, VALUE is the expression value.
-  ;; With SUCCESS being false, VALUE is an interruption diagnostic.
-  (condition-case info
-      (cons (let ((inhibit-quit nil)) (eval expression)) t)
-    (quit (setq quit-flag t)
-          (interrupt-process pymacs-transit-buffer)
-          (cons "*Interrupted!*" nil))
-    (error (cons (prin1-to-string info) nil))))
-
-(defun pymacs-proper-list-p (expression)
-  ;; Tell if a list is proper, id est, that it is `nil' or ends with `nil'.
-  (cond ((not expression))
-        ((consp expression) (not (cdr (last expression))))))
-
-(provide 'pymacs)
+"""rope, a python refactoring library"