Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Update Python-mode 6.0.8.

supply Pymacs to go with Pycomplete (needs manual install by user.)
  • Loading branch information...
commit 6299dad84a97bcc5c3c6a4d1540615b3e66d9b90 1 parent cc48072
@davidswelt authored
View
7 aquamacs/doc/latex/changelog.tex
@@ -1,4 +1,8 @@
\subsection{Changes--- dev}
+
+\textbf{To all supporters: thank you so much for your donations. See also: \url{http://aquamacs.org/donations.shtml}. To all users and hackers: Thank You for your support! We appreciate your bug reports, success stories, ideas and code. }
+\vskip2em
+
\begin{itemize}
\item S-tab no longer mapped to C-y (ASCII 25, end of medium). Now properly handled as backtab. Patch by Gracjan Polak.
@@ -14,6 +18,7 @@ \subsection{Changes--- dev}
\item Frames are now reliably moved inside the current display again when entering the minibuffer (and at other times).
\item Switching applications while Aquamacs is in full-screen mode no longer creates unwanted empty frames.
Reported by Christian H\"o{}ltje, Rory Kirchner and Milan Mitrovi\'c{}.
+\item Python-mode upgraded to 6.0.8. (Previous version was 6.0.2.) See here for changes: \url{https://launchpad.net/python-mode/+announcements}
\end{itemize}
\subsection{Changes--- 2.4}
@@ -119,8 +124,6 @@ \subsection{Changes--- 2.2}
\subsection{Changes--- 2.1}
-\textbf{To all supporters: thank you so much for your donations. See also: \url{http://aquamacs.org/donations.shtml}. To all users and hackers: Thank You for your support! We appreciate your bug reports, success stories, ideas and code. }
-\vskip2em
\begin{itemize}
View
31 aquamacs/src/site-lisp/edit-modes/python-mode/Pymacs/__init__.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright © 2002, 2003 Progiciels Bourbeau-Pinard inc.
+# François Pinard <pinard@iro.umontreal.ca>, 2002.
+
+# 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 - Module initialisation.
+
+A few symbols are moved in here so they appear to be defined at this level.
+"""
+
+from pymacs import Let, lisp
+
+# Identification of version.
+
+__package__ = 'Pymacs'
+__version__ = '0.24-beta1'
View
697 aquamacs/src/site-lisp/edit-modes/python-mode/Pymacs/pymacs.py
@@ -0,0 +1,697 @@
+#!/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 other Python modules.
+See the Pymacs documentation (check `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
+
+## 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. The Interrupt signal is temporary enabled, however,
+ # while executing any Python code received from the Lisp side.
+ if signal is not None:
+
+ # See the comment for IO_ERRORS_WITH_SIGNALS in p4config.py.
+ self.original_handler = signal.signal(
+ signal.SIGINT, self.interrupt_handler)
+
+ self.inhibit_quit = True
+
+ # Re-open standard input and output in binary mode.
+ sys.stdin = os.fdopen(sys.stdin.fileno(), 'rb')
+ sys.stdout = os.fdopen(sys.stdout.fileno(), 'wb')
+
+ # Start protocol and services.
+ lisp._protocol.send('version', '"0.24-beta2"')
+ lisp._protocol.loop()
+
+ def generic_handler(self, number, frame):
+ if self.signal_file:
+ handle = open(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 = open(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
+
+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:
+
+ 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] != '>':
+
+ raise ProtocolError("`>' expected.")
+ while prefix[-1] != '\t':
+ character = sys.stdin.read(1)
+ if not character:
+
+ raise ProtocolError("Empty stdin read.")
+ prefix += character
+ text = sys.stdin.read(int(prefix[1:-1]))
+ if run.debug_file is not None:
+ handle = open(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 = open(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():
+
+ 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):
+
+ 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] == '_':
+
+ raise AttributeError(name)
+ return self[name.replace('_', '-')]
+
+ def __setattr__(self, name, value):
+ if name[0] == '_':
+
+ 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:])
View
39 aquamacs/src/site-lisp/edit-modes/python-mode/README
@@ -1,10 +1,37 @@
-This project contains basically:
+Customize default Python shell as `py-shell-name'
- * python-mode.el - major mode for editing Python source
- * doctest-mode.el - major mode for editing doctest files
- * pycomplete.el - support for Python symbol completion (Emacs Lisp side)
- * pycomplete.py - support for Python symbol completion (Python side)
+`py-shell-name' might be an installed executable as
+shell command `type' would display, but also a
+PATH/TO/(I)PYTHON, of a virtualenv for example
+To change the Python default shell see also INSTALL
-Remaining files provide tests for developers
+Most python-mode.el commands start with prefix `py-'
+`M-x py- TAB'
+displays a list of them in completion-buffer.
+See also commands list delivered in directory doc.
+
+List virtualenv related `M-x virtualenv- TAB'
+resp. Pymacs commands `M-x pymacs-'
+
+Commands related to a specific shell start with
+it's name as `ipython-complete'.
+Open an installed shell by
+
+M-x SHELL
+
+With prefix C-u user is prompted to specify a PATH-TO-LOCAL-SHELL
+Also evaluating
+
+(py-shell nil DEDICATED PATH-TO-LOCAL-SHELL)
+
+if DEDICATED is set to `t', shell will get an unique name.
+
+Install a local shell by evaluating
+
+(defun MY-LOCAL-SHELL ()
+ (interactive)
+ (py-shell nil DEDICATED PATH-TO-LOCAL-SHELL))
+
+If `py-complete-function' is set, it takes precedence
View
45 aquamacs/src/site-lisp/edit-modes/python-mode/completion/pycomplete.el
@@ -18,15 +18,27 @@
;; of things and a short description of what to expect.
(require 'pymacs)
-(require 'python-mode)
(pymacs-load "pycomplete")
-(defun py-complete ()
- (interactive)
- (let ((pymacs-forget-mutability t))
- (insert (pycomplete-pycomplete (py-symbol-near-point)
- (py-find-global-imports)))))
+(defun py-symbol-near-point ()
+ "Return the first textual item to the nearest point."
+ ;; alg stolen from etag.el
+ (save-excursion
+ (with-syntax-table py-dotted-expression-syntax-table
+ (if (or (bobp) (not (memq (char-syntax (char-before)) '(?w ?_))))
+ (while (not (looking-at "\\sw\\|\\s_\\|\\'"))
+ (forward-char 1)))
+ (while (looking-at "\\sw\\|\\s_")
+ (forward-char 1))
+ (if (re-search-backward "\\sw\\|\\s_" nil t)
+ (progn (forward-char 1)
+ (buffer-substring (point)
+ (progn (forward-sexp -1)
+ (while (looking-at "\\s'")
+ (forward-char 1))
+ (point))))
+ nil))))
(defun py-find-global-imports ()
(save-excursion
@@ -45,6 +57,25 @@
(match-end 0))))))
imports)))
-(define-key py-mode-map "\M-\C-i" 'py-complete)
+(defun py-complete ()
+ (interactive)
+ (let* ((pymacs-forget-mutability t)
+ (symbol (py-symbol-near-point))
+ (completions
+ (pycomplete-pycomplete symbol
+ (py-find-global-imports))))
+ (cond ((null completions) ; no matching symbol
+ (message "Can't find completion for \"%s\"" symbol)
+ (ding))
+ ((null (cdr completions))
+ (if (string= "" (car completions))
+ (tab-to-tab-stop)
+ ;; sole completion
+ (insert (car completions))))
+ (t
+ (message "Making completion list...")
+ (with-output-to-temp-buffer "*PythonCompletions*"
+ (display-completion-list completions))
+ (message "Making completion list...%s" "done")))))
(provide 'pycomplete)
View
15 aquamacs/src/site-lisp/edit-modes/python-mode/completion/pycomplete.py
@@ -34,6 +34,8 @@
# Along with pycomplete.el this file allows programmers to complete Python
# symbols within the current buffer.
+# import pdb
+# pdb.set_trace()
import sys
import os.path
@@ -88,8 +90,17 @@ def get_all_completions(s, imports=None):
def pycomplete(s, imports=None):
completions = get_all_completions(s, imports)
- dots = s.split(".")
- return os.path.commonprefix([k[len(dots[-1]):] for k in completions])
+ if len(completions) == 0:
+ return None
+ else:
+ dots = s.split(".")
+ prefix = os.path.commonprefix([k for k in completions])
+ if len(completions)==1 or len(prefix)>len(dots[-1]):
+ return [prefix[len(dots[-1]):]]
+
+ return completions
+
+ # return os.path.commonprefix([k[len(dots[-1]):] for k in completions])
if __name__ == "__main__":
print "<empty> ->", pycomplete("")
View
96 aquamacs/src/site-lisp/edit-modes/python-mode/pars-part-output.el
@@ -1,96 +0,0 @@
-;;; pars-part-output.el --- `parse-partial-sexp' and `syntax-ppss'
-
-
-;; Author: Andreas Roehler <andreas.roehler@easy-emacs.de>, unless indicated otherwise
-
-;; Keywords: tools, lisp
-
-;; This file 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 file 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 GNU Emacs; see the file COPYING. If not, write to
-;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
-
-;;; Commentary:
-
-;; Prints commented output
-
-;; (global-set-key (kbd "<M-f6>") 'parse-partial-sexp-iac)
-;; (global-set-key (kbd "<M-f7>") 'syntax-ppss-iac)
-
-(defun parse-partial-sexp-commentstop (&optional arg)
- "Interactive form of parse-partial-sexp
-output listed with documentation"
- (interactive "P")
- (save-excursion
- (ppse-documented-base 'parse-partial-sexp (point) arg t)))
-
-(defun parse-partial-sexp-iac (&optional arg)
- "Interactive form of parse-partial-sexp
-output listed with documentation"
- (interactive "P")
- (save-excursion
- (ppse-documented-base 'parse-partial-sexp (point) arg nil)))
-
-(defun syntax-ppss-iac (&optional arg)
- "Syntax-ppss made interactive output listed with documentation"
- (interactive "P")
- (ppse-documented-base 'syntax-ppss (point) arg nil))
-
-(defun ppse-documented-base (funktion end arg commentstop)
- "Parse partial symbolic expression
-list results below its documentation "
- (let* ((start (point-min))
- (scan (condition-case nil
- (scan-lists (point) 1 0)
- (error nil)))
- (rekord
- (if
- (or (eq this-command 'parse-partial-sexp-iac)
- (eq this-command 'parse-partial-sexp-commentstop))
- (funcall funktion start end nil nil nil commentstop)
- (funcall funktion end)))
- (doku (documentation 'parse-partial-sexp))
- (doku-abr (list (substring doku (1+ (string-match ":" doku))))))
- (if arg
- (what-cursor-position)
- (message "%s LEND: %s" rekord scan))
- (with-output-to-temp-buffer (concat (format "%s" funktion) "-output")
- (set-buffer standard-output)
- (insert (car doku-abr))
- (goto-char (point-min))
- (re-search-forward "^ [0-9]+\." nil t 1)
- (replace-match "-")
- (forward-line 1)
- (split-line)
- (dolist (elt rekord)
- (insert (format "\t ====> %s <====" elt))
- (re-search-forward "^ [0-9]+\." nil t 1)
- (replace-match "-")
- (end-of-line)
- (newline))))
- (goto-char (point-min))
- (toggle-read-only -1)
- (unless (featurep 'xemacs)
- (set-window-text-height (selected-window) 4))
- (while (not (eobp))
- (if (looking-at "^[ \t]*$")
- (delete-region (point) (progn (forward-line) (point)))
- (forward-line))))
-
-(defun scan-lists-iac ()
- " "
- (interactive)
- (message "%s" (scan-lists (defun-beginning-position) (point) 0)))
-
-(provide 'pars-part-output)
-;;; pars-part-output.el ends here
View
759 aquamacs/src/site-lisp/edit-modes/python-mode/pymacs.el
@@ -0,0 +1,759 @@
+;;; 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-auto-restart 'ask
+ "Should the Pymacs helper be restarted whenever it dies?
+Possible values are nil, t or ask.")
+
+(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.
+
+;; This page tries to increase the integration seamlessness of Pymacs
+;; with the reminder of Emacs.
+
+;; Module "desktop" savagely kills `*Pymacs*' in some circumstances.
+;; Let's avoid such damage.
+
+(eval-after-load 'desktop
+ '(push "\\*Pymacs\\*" desktop-clear-preserve-buffers))
+
+;; Python functions and modules should ideally look like Lisp
+;; functions and modules.
+
+(when t
+
+ (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.")
+
+;; This is set whenever the Pymacs helper successfully starts, and is
+;; also used to later detect the death of a previous helper. If
+;; pymacs-use-hash-tables is unset, this variable receives `t' when
+;; the helper starts, so the detection works nevertheless.
+(defvar pymacs-weak-hash nil
+ "Weak hash table, meant to find out which IDs are still needed.")
+
+(defvar pymacs-gc-wanted nil
+ "Flag that it is desirable to clean up unused IDs on the Python side.")
+
+(defvar pymacs-gc-inhibit nil
+ "Flag that a new Pymacs garbage collection should just not run now.")
+
+(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-inhibit
+ (setq pymacs-gc-wanted t)))
+
+(defun pymacs-garbage-collect ()
+ ;; Clean up unused IDs on the Python side.
+ (when (and pymacs-use-hash-tables (not pymacs-gc-inhibit))
+ (let ((pymacs-gc-inhibit 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 ".encode('ISO-8859-1').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
+ ;; Erase the buffer in case some previous incarnation of the
+ ;; Pymacs helper died. Otherwise, the "(goto-char (point-min))"
+ ;; below might not find the proper synchronising reply and later
+ ;; trigger a spurious "Protocol error" diagnostic.
+ (erase-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, os;"
+ ; Leave '' as first element in Python's path
+ " sys.path.insert(1, os.getenv('PYMACS_INSTALL_DIR'));"
+ " 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) "0.24-beta2")
+ (pymacs-report-error
+ "Pymacs Lisp version is 0.24-beta2, Python is %s"
+ (cadr reply)))
+ (pymacs-report-error "Pymacs got an invalid initial reply")))))
+ (if (not pymacs-use-hash-tables)
+ (setq pymacs-weak-hash t)
+ (when pymacs-used-ids
+ ;; A previous Pymacs session occurred in this Emacs session,
+ ;; some IDs hang around which do not correspond to anything on
+ ;; the Python side. Python should not recycle such IDs for
+ ;; new objects.
+ (let ((pymacs-transit-buffer buffer)
+ (pymacs-forget-mutability t)
+ (pymacs-gc-inhibit t))
+ (pymacs-apply "zombie_python" pymacs-used-ids))
+ (setq pymacs-used-ids nil))
+ (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-inhibit 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))
+ (when pymacs-weak-hash
+ (unless (or (eq pymacs-auto-restart t)
+ (and (eq pymacs-auto-restart 'ask)
+ (yes-or-no-p "The Pymacs helper died. Restart it? ")))
+ (pymacs-report-error "There is no Pymacs helper!")))
+ (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)
View
20,276 aquamacs/src/site-lisp/edit-modes/python-mode/python-mode.el
15,554 additions, 4,722 deletions not shown
View
14 aquamacs/src/site-lisp/edit-modes/python-mode/setup.py
@@ -0,0 +1,14 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from distutils.core import setup
+
+setup(name='python-mode.el',
+ version='6.0.8',
+ url='http://launchpad.net/python-mode',
+ maintainer_email='andreas.roehler@online.de',
+ maintainer='Andreas Roehler',
+ description='Major mode for editing Python programs',
+ download_url='http://launchpad.net/python-mode/trunk/6.0.8/+download/python-mode.el-6.0.8.tar.gz',
+ license='GNU GPLv3, Python License',
+ )
Please sign in to comment.
Something went wrong with that request. Please try again.