I don't seem to have advertised the python.el support for Python 3 I did
some time ago, but the previous implementation with separate major modes
for the language versions turned out to be unfortunate.  In this version
the major mode is always `python-mode', but you can still use
`python-2-mode' and `python-3-mode' in file-local variables.  The
support for the inferior shell works with version 2 or 3 interpreters.

It's often said, without good justification I've heard, that this is
somehow bad compared with the old python-mode.el.  I'll try to address
reports of actual bugs or missing features that aren't inappropriate to
include, as discussed in the commentary.

An Emacs 21 version is at http://www.loveshack.ukfsn.org/emacs/python-21.el.

Attachment: python.el.gz
Description: GNU Zip compressed data

"""Definitions used by commands sent to inferior Python in python.el."""

# Copyright (C) 2004, 2005, 2006, 2007, 2008  Free Software Foundation, Inc.
# Author: Dave Love <f...@gnu.org>

# This file is part of GNU Emacs.

# 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 3 of the License, 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
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.

# $Revision: 1.17 $

import os, sys, traceback, inspect, keyword

### Python 2.3 compatibility

# Python 2.3 doesn't have `set'.  Avoid a warning in Python 2.6 or
# error in 3.0.  (We can't put the import in a conditional block to
# avoid the warning.)
try: set
except: from sets import Set as set # Python 2.3

### Python 2/3 compatibility

# According to the Python doc, you're meant to maintain separate
# version 2 and version 3 files, deriving the latter from the former
# with `2to3'.  However, we have a dynamic language, and using
# compatibility functions like this is more maintainable once you've
# fixed the things 2to3 complains about that aren't syntax errors.

# Use the Python 3 function, if available.

if not keyword.iskeyword ("print"): # Python 3
    # This avoids syntax errors in Python 2.
    printit = eval ("print")
    execit = eval ("exec")
else:				# Python 2
    # execit
    eval (compile ("""
def execit (string, dict = None):
    "Compatibility with Python 3's `exec' for Python 2."
    if dict:
        exec string in dict
        exec string
    # printit
    eval (compile ("""
def printit (obj, end = '\\n'):
    "Partial compatibility with Python 3's `print' for Python 2."
    if end == ' ':
        print obj,
    elif end == '\\n':
        print obj
        # Not really right because of the trailing space, but not
        # called such that that matters.
        print '%s%s' % (obj, end),
""", 'emacs.py', 'single'))

### main code

__all__ = ["eexecfile", "eargs", "complete", "ehelp", "eimport",
	   "modpath", "location_of"]

def eexecfile (file):
    """Execute FILE and then remove it.
    If we get an exception, print a traceback with the top frame
    (ourselves) excluded."""
    import __main__

            # Note that the file is closed by the (quasi-?) GC since
            # there's no reference to it kept.
            execit (open(file).read(), __main__.__dict__)
            (etype, value, tb) = sys.exc_info ()
            # Lose the stack frame for this location and for execit.
            tb = tb.tb_next.tb_next
            if tb is None:      # print_exception won't do it
                printit ("Traceback (most recent call last):")
            traceback.print_exception (etype, value, tb)
        try: os.remove (file)
        except: pass            # e.g. file not found, but shouldn't happen

def eargs (name, imports):
    """Get arglist of NAME for Eldoc &c.
    Exec IMPORTS first."""
        try:                    # don't give up if the imports fail
            if imports: execit (imports)
            parts = name.split ('.')
            if len (parts) > 1:
                execit ('import ' + parts[0])
        except: pass
        func = eval (name)
        if inspect.isbuiltin (func) or inspect.isclass (func):
            doc = func.__doc__
            if doc.find (' ->') != -1:
                printit ('_emacs_out ' + doc.split (' ->')[0])
            elif doc.find ('\n') != -1:
                printit ('_emacs_out ' + doc.split ('\n')[0])
                raise RuntimeError
        if inspect.ismethod (func):
                func = func.im_func # Python 2
                func = func.__func__ # Python 3
        if not inspect.isfunction (func):
            raise RuntimeError
        (args, varargs, varkw, defaults) = inspect.getargspec (func)
        # No space between name and arglist for consistency with builtins.
        printit ('_emacs_out ' + \
                 func.__name__ + \
                 inspect.formatargspec (args, varargs, varkw, defaults))
    except: printit ('_emacs_out ')

def all_names (object):
    """Return (an approximation to) a list of all possible attribute
    names reachable via the attributes of OBJECT, i.e. roughly the
    leaves of the dictionary tree under it."""

    def do_object (object, names):
        if inspect.ismodule (object):
            do_module (object, names)
        elif inspect.isclass (object):
            do_class (object, names)
        # Might have an object without its class in scope.
        elif hasattr (object, '__class__'):
            names.add ('__class__')
            do_class (object.__class__, names)
        # Probably not a good idea to try to enumerate arbitrary
        # dictionaries...
        return names

    def do_module (module, names):
        if hasattr (module, '__all__'): # limited export list
            names.update (module.__all__)
            for i in module.__all__:
                do_object (getattr (module, i), names)
        else:                   # use all names
            names.update (dir (module))
            for i in dir (module):
                do_object (getattr (module, i), names)
        return names

    def do_class (object, names):
        names.update (dir (object))
        if hasattr (object, '__bases__'): # superclasses
            for i in object.__bases__: do_object (i, names)
        return names

    return do_object (object, set ([]))

# Fixme:  Should do multiple dotted components -- see rlcompleter.
def complete (name, imports):
    """Complete NAME and print a Lisp list of completions.
    Exec IMPORTS first."""
    import __main__

    def class_members (object):
        names = dir (object)
        if hasattr (object, '__bases__'):
            for superc in object.__bases__:
                names = class_members (superc)
        return names    

    names = set ([])
    base = None
        dic = __main__.__dict__.copy()
        if imports:
            try: execit (imports, dic)
            except: pass
        l = len (name)
        dot = name.rfind ('.')
        if dot == -1:
            for elts in [__builtins__, keyword.kwlist,
                         list (dic.keys())]: # `list' for Python 3
                for elt in elts:
                    if elt[:l] == name: names.add (elt)
            base = name[:dot]
            name = name[dot+1:]
                obj = eval (base, dic)
                names = set (dir (obj))
                if hasattr (obj, '__class__'):
                    names.add ('__class__')
                    names.update (class_members (obj))
            except: names = all_names (dic)
    except: return []
    l = len (name)
    printit ('_emacs_out (', end=' ')
    for n in names:
        if name == n[:l]:
            if base: printit ('"%s.%s"' % (base, n), end = ' ')
            else: printit ('"%s"' % n, end = ' ')
    printit (')')

# Fixme:  This could try to look up methods/attributes applied to
# variables by generating possibilities like the completion code.  The
# trouble is that could be misleading if it gets the wrong one.
def ehelp (name, imports):
    """Get help on string NAME.
    First try to eval name, e.g. for user definitions where we need
    the object.  Otherwise try the string form.
    Exec IMPORTS first."""
    locls = {}
    if imports:
        try: execit (imports, locls)
        except: pass
    try: help (eval (name, globals (), locls))
    except: help (name)

def eimport (mod, dir):
    """Import module MOD with directory DIR at the head of the search path.
    NB doesn't load from DIR if MOD shadows a system module."""
    from __main__ import __dict__

    path0 = sys.path[0]         # will be ''
    sys.path[0] = dir
            if mod in __dict__ and inspect.ismodule (__dict__[mod]):
                reload (__dict__[mod])
                __dict__[mod] = __import__ (mod)
            (etype, value, tb) = sys.exc_info ()
            printit ("Traceback (most recent call last):")
            traceback.print_exception (etype, value, tb.tb_next)
        sys.path[0] = path0

def modpath (module):
    """Get the source file for the given MODULE (or nil)."""
    locls = {}
        __import__ (module)
        printit ("_emacs_out " + \
            inspect.getsourcefile (eval (module, globals (), locls)))
        printit ("_emacs_out ()")

def location_of (name, imports):
    """Get location at which NAME is defined (or nil).
    Provides a pair (PATH, LINE), where LINE is the start of the definition
    in path name PATH.
    Exec IMPORTS first."""
    locls = {}
    if imports:
        try: execit (imports, locls)
        except: pass
        obj = eval (name, globals (), locls)
        # Bug: (in Python 2.5) `getsourcefile' only works with modules,
        # hence the `getmodule' here.
        srcfile = inspect.getsourcefile (inspect.getmodule (obj))
        _, line = inspect.getsourcelines (obj)
        printit ('_emacs_out ("%s" . %d)' % (srcfile, line))
        printit ("_emacs_out ()")

# print '_emacs_ok'             # ready for input and can call continuation

# arch-tag: d90408f3-90e2-4de4-99c2-6eb9c7b9ca46
* Revision 1.29 (emacs.py 1.17, python-21.el 1.77)

  * Change the mechanism for Python2/Python3:  `python-2-mode' and
    `python-3-mode' are no longer proper major modes, but just invoke
    `python-mode' with `python-default-version' bound appropriately.
    Thus you can use, say, python-3-mode in file-local variables, but
    `major-mode' is always `python-mode' to avoid breaking things
    which test `major-mode'.

  * Bug fixes:

    * Allow trailing comments on outline headings.
    * `python-load-file' produces a stack trace with the file name.

* Revision 1.25 (emacs.py 1.16, python-21.el 1.73)

  * Fix hideshow to work with the new major modes, and make it
    consistent with outlining.

* Revision 1.24 (emacs.py 1.16, python-21.el 1.72)

  * Flymake support (via `python-check-command').

  * Bug/infelicity fixes:

    * Error with backslash-continued line inside parens.
    * Allow "else" after "finally".
    * Set `comment-start-skip', `local-abbrev-table'.
    * Allow non-ASCII ids with Eldoc.
    * `python-process-kill-without-query' allows silent killing of
      inferior process.
    * Avoid possible bad indentation of try block clauses.

* Revision 1.18 (emacs.py 1.16, python-21.el 1.66)

  * Support for Python 3:

    * New modes, `python-3-mode' and `python-2-mode'.  `python-mode'
      changed just to invoke the one determined by
    * emacs.py should work with python 2.3 up.  (Only 2.5 and 3.0

  * Bug fixes:

    * Completion works again in python 2.4 up.
    * `python-beginning-of-block' moves to the beginning properly when
      point is in the first statement of an outer block.
    * `python-mark-block' is more sensible because of the above.
    * `python-mark-block' doesn't push marks or deactivate the region
      if it doesn't find a block.
    * `indent-tabs-mode' is now normally nil -- Python 3 objects to
      typical mixed tab/space indentation otherwise.  The indentation
      isn't guessed if it's set file-locally.
    * Avoid error in `python-find-function'.
    * Try harder to find Info files for info-look.
    * Customizing `python-python-command' updates things which depend
      on it.
    * Fix Imenu finding nested definitions.
    * Fix python-beginning-of-defun with nested definitions.

  * Other changes
    * Imenu generation is sanitized:  class names are indicated by a
      suffix, not a prefix; sorting is sorted; the default
      `imenu-sort-function' gives an overall order of module variables,
      class definitions, and function definitions.
    * In Emacs 21, bind C-M-h and C-x n d usefully to work around
      problems with normal bindings.
gnu-emacs-sources mailing list

Reply via email to