Hello Bram!
Hallo Aaron!

E836: This Vim cannot execute :python after using :py3
This happened to me several times already when changing to a buffer with python code, e.g. to my .vimrc file. The reason was that omnifunc was set to pythoncomplete#Complete.

Python 2.6 and python 2.7 have had changes to make it more compatible with python3. It is now quite easy to write code that works for both, python2.x and python3.x.

With the error message triggering and the latter thought I tried to go for the following: forward to the other python command.

- The first time vim realizes that it cannot run python (py3) because of the RTLD problem this error message is issued:

E836: This Vim cannot execute :python after using :py3. Will forward future calls to :py3.
or
E837: This Vim cannot execute :py3 after using :python. Will forward future calls to python.

  (Of course, I've not changed the translations of the these messages.)
- The second time the python command is forwarded to the other python command - If the system/python version allows both, python 2.x and python 3.x, there is no forwarding.
- This applies only if vim is configure for both python versions

If somebody has a script, which uses either :py3 or :python, the following sequence in the .vimrc as first python interaction will redirect all calls to :py3.

    "redirect :python to :py3
    py3 3
    python 2
    "argument has no meaning

If the order is changed :py3 calls will be redirected to :python.

@Bram: If you agree, please merge it to the main vim line.

@Aaron: Since the changes in python3complete.vim do work for python 2.6 and python 2.7 (I've test it), I have moved it to pythoncomplete.vim. The patch contains this move. The original pythoncomplete.vim I've moved to python25complete.vim with according internal renaming. It is attached.
Do you agree with this proceeding?
I'm moving to ArchLinux and in the vim package there pythoncomplete.vim is separate. The same version is already included in the vim sources.

- Roland

--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
diff -r 3edc4535acfa runtime/autoload/pythoncomplete.vim
--- a/runtime/autoload/pythoncomplete.vim       Wed Aug 10 18:36:54 2011 +0200
+++ b/runtime/autoload/pythoncomplete.vim       Wed Aug 17 05:35:24 2011 +0200
@@ -3,6 +3,9 @@
 " Version: 0.9
 " Last Updated: 18 Jun 2009
 "
+" Roland Puntaier: this file contains adaptations for python3, which also work 
for python2.6 onward.
+" The original pythoncomplete.vim has been moved to python25complete.vim with 
according internal renamings
+"
 " Changes
 " TODO:
 " 'info' item output can use some formatting work
@@ -41,8 +44,8 @@
 "  rewrite.
 "
 
-if !has('python')
-    echo "Error: Required vim compiled with +python"
+if !has('python3') && !has('python')
+    echo "Error: Required vim compiled with +python or +python3"
     finish
 endif
 
@@ -88,42 +91,23 @@
 
 function! s:DefPython()
 python << PYTHONEOF
-import sys, tokenize, cStringIO, types
+import sys, tokenize, io, types
 from token import NAME, DEDENT, NEWLINE, STRING
 
 debugstmts=[]
 def dbg(s): debugstmts.append(s)
 def showdbg():
-    for d in debugstmts: print "DBG: %s " % d
+    for d in debugstmts: print("DBG: %s " % d)
 
 def vimcomplete(context,match):
     global debugstmts
     debugstmts = []
     try:
         import vim
-        def complsort(x,y):
-            try:
-                xa = x['abbr']
-                ya = y['abbr']
-                if xa[0] == '_':
-                    if xa[1] == '_' and ya[0:2] == '__':
-                        return xa > ya
-                    elif ya[0:2] == '__':
-                        return -1
-                    elif y[0] == '_':
-                        return xa > ya
-                    else:
-                        return 1
-                elif ya[0] == '_':
-                    return -1
-                else:
-                   return xa > ya
-            except:
-                return 0
         cmpl = Completer()
         cmpl.evalsource('\n'.join(vim.current.buffer),vim.eval("line('.')"))
         all = cmpl.get_completions(context,match)
-        all.sort(complsort)
+        all.sort(key=lambda x:x['abbr'].replace('_','z'))
         dictstr = '['
         # have to do this for double quoting
         for cmpl in all:
@@ -147,44 +131,43 @@
         sc = self.parser.parse(text,line)
         src = sc.get_code()
         dbg("source: %s" % src)
-        try: exec(src) in self.compldict
+        try: exec(src,self.compldict)
         except: dbg("parser: %s, %s" % (sys.exc_info()[0],sys.exc_info()[1]))
         for l in sc.locals:
-            try: exec(l) in self.compldict
+            try: exec(l,self.compldict)
             except: dbg("locals: %s, %s [%s]" % 
(sys.exc_info()[0],sys.exc_info()[1],l))
 
     def _cleanstr(self,doc):
         return doc.replace('"',' ').replace("'",' ')
 
     def get_arguments(self,func_obj):
-        def _ctor(obj):
-            try: return class_ob.__init__.im_func
+        def _ctor(class_ob):
+            try: return class_ob.__init__
             except AttributeError:
                 for base in class_ob.__bases__:
-                    rc = _find_constructor(base)
+                    rc = _ctor(base)
                     if rc is not None: return rc
             return None
 
         arg_offset = 1
-        if type(func_obj) == types.ClassType: func_obj = _ctor(func_obj)
-        elif type(func_obj) == types.MethodType: func_obj = func_obj.im_func
+        if type(func_obj) == type: func_obj = _ctor(func_obj)
+        elif type(func_obj) == types.MethodType: arg_offset = 1
         else: arg_offset = 0
-        
+
         arg_text=''
-        if type(func_obj) in [types.FunctionType, types.LambdaType]:
+        if type(func_obj) in [types.FunctionType, 
types.LambdaType,types.MethodType]:
             try:
-                cd = func_obj.func_code
+                cd = func_obj.__code__
                 real_args = cd.co_varnames[arg_offset:cd.co_argcount]
-                defaults = func_obj.func_defaults or ''
-                defaults = map(lambda name: "=%s" % name, defaults)
+                defaults = func_obj.__defaults__ or []
+                defaults = ["=%s" % name for name in defaults]
                 defaults = [""] * (len(real_args)-len(defaults)) + defaults
-                items = map(lambda a,d: a+d, real_args, defaults)
-                if func_obj.func_code.co_flags & 0x4:
+                items = [a+d for a,d in zip(real_args,defaults)]
+                if func_obj.__code__.co_flags & 0x4:
                     items.append("...")
-                if func_obj.func_code.co_flags & 0x8:
+                if func_obj.__code__.co_flags & 0x8:
                     items.append("***")
                 arg_text = (','.join(items)) + ')'
-
             except:
                 dbg("arg completion: %s: %s" % 
(sys.exc_info()[0],sys.exc_info()[1]))
                 pass
@@ -206,7 +189,7 @@
         return arg_text
 
     def get_completions(self,context,match):
-        dbg("get_completions('%s','%s')" % (context,match))
+        #dbg("get_completions('%s','%s')" % (context,match))
         stmt = ''
         if context: stmt += str(context)
         if match: stmt += str(match)
@@ -257,7 +240,7 @@
                             c['abbr'] += '(' + 
self._cleanstr(self.get_arguments(inst))
                         elif "module" in typestr:
                             c['word'] += '.'
-                        elif "class" in typestr:
+                        elif "type" in typestr:
                             c['word'] += '('
                             c['abbr'] += '('
                         completions.append(c)
@@ -280,7 +263,7 @@
         self.indent = indent
 
     def add(self,sub):
-        #print 'push scope: [%s@%s]' % (sub.name,sub.indent)
+        #print('push scope: [%s@%s]' % (sub.name,sub.indent))
         sub.parent = self
         self.subscopes.append(sub)
         return sub
@@ -325,18 +308,18 @@
         return str
 
     def pop(self,indent):
-        #print 'pop scope: [%s] to [%s]' % (self.indent,indent)
+        #print('pop scope: [%s] to [%s]' % (self.indent,indent))
         outer = self
         while outer.parent != None and outer.indent >= indent:
             outer = outer.parent
         return outer
 
     def currentindent(self):
-        #print 'parse current indent: %s' % self.indent
+        #print('parse current indent: %s' % self.indent)
         return '    '*self.indent
 
     def childindent(self):
-        #print 'parse child indent: [%s]' % (self.indent+1)
+        #print('parse child indent: [%s]' % (self.indent+1))
         return '    '*(self.indent+1)
 
 class Class(Scope):
@@ -382,15 +365,15 @@
         #returns (dottedname, nexttoken)
         name = []
         if pre is None:
-            tokentype, token, indent = self.next()
+            tokentype, token, indent = self.donext()
             if tokentype != NAME and token != '*':
                 return ('', token)
         else: token = pre
         name.append(token)
         while True:
-            tokentype, token, indent = self.next()
+            tokentype, token, indent = self.donext()
             if token != '.': break
-            tokentype, token, indent = self.next()
+            tokentype, token, indent = self.donext()
             if tokentype != NAME: break
             name.append(token)
         return (".".join(name), token)
@@ -404,7 +387,7 @@
             if token == 'as': name2, token = self._parsedotname()
             imports.append((name, name2))
             while token != "," and "\n" not in token:
-                tokentype, token, indent = self.next()
+                tokentype, token, indent = self.donext()
             if token != ",": break
         return imports
 
@@ -413,7 +396,7 @@
         names = []
         level = 1
         while True:
-            tokentype, token, indent = self.next()
+            tokentype, token, indent = self.donext()
             if token in (')', ',') and level == 1:
                 if '=' not in name: name = name.replace(' ', '')
                 names.append(name.strip())
@@ -433,34 +416,34 @@
 
     def _parsefunction(self,indent):
         self.scope=self.scope.pop(indent)
-        tokentype, fname, ind = self.next()
+        tokentype, fname, ind = self.donext()
         if tokentype != NAME: return None
 
-        tokentype, open, ind = self.next()
+        tokentype, open, ind = self.donext()
         if open != '(': return None
         params=self._parenparse()
 
-        tokentype, colon, ind = self.next()
+        tokentype, colon, ind = self.donext()
         if colon != ':': return None
 
         return Function(fname,params,indent)
 
     def _parseclass(self,indent):
         self.scope=self.scope.pop(indent)
-        tokentype, cname, ind = self.next()
+        tokentype, cname, ind = self.donext()
         if tokentype != NAME: return None
 
         super = []
-        tokentype, next, ind = self.next()
-        if next == '(':
+        tokentype, thenext, ind = self.donext()
+        if thenext == '(':
             super=self._parenparse()
-        elif next != ':': return None
+        elif thenext != ':': return None
 
         return Class(cname,super,indent)
 
     def _parseassignment(self):
         assign=''
-        tokentype, token, indent = self.next()
+        tokentype, token, indent = self.donext()
         if tokentype == tokenize.STRING or token == 'str':  
             return '""'
         elif token == '(' or token == 'tuple':
@@ -481,7 +464,7 @@
             assign += token
             level = 0
             while True:
-                tokentype, token, indent = self.next()
+                tokentype, token, indent = self.donext()
                 if token in ('(','{','['):
                     level += 1
                 elif token in (']','}',')'):
@@ -492,10 +475,10 @@
                     assign += token
         return "%s" % assign
 
-    def next(self):
-        type, token, (lineno, indent), end, self.parserline = self.gen.next()
+    def donext(self):
+        type, token, (lineno, indent), end, self.parserline = next(self.gen)
         if lineno == self.curline:
-            #print 'line found [%s] scope=%s' % 
(line.replace('\n',''),self.scope.name)
+            #print('line found [%s] scope=%s' % 
(line.replace('\n',''),self.scope.name))
             self.currentscope = self.scope
         return (type, token, indent)
 
@@ -541,14 +524,14 @@
     #p.parse(vim.current.buffer[:],vim.eval("line('.')"))
     def parse(self,text,curline=0):
         self.curline = int(curline)
-        buf = cStringIO.StringIO(''.join(text) + '\n')
+        buf = io.StringIO(''.join(text) + '\n')
         self.gen = tokenize.generate_tokens(buf.readline)
         self.currentscope = self.scope
 
         try:
             freshscope=True
             while True:
-                tokentype, token, indent = self.next()
+                tokentype, token, indent = self.donext()
                 #dbg( 'main: token=[%s] indent=[%s]' % (token,indent))
 
                 if tokentype == DEDENT or token == "pass":
@@ -556,7 +539,7 @@
                 elif token == 'def':
                     func = self._parsefunction(indent)
                     if func is None:
-                        print "function: syntax error..."
+                        print("function: syntax error...")
                         continue
                     dbg("new scope: function")
                     freshscope = True
@@ -564,7 +547,7 @@
                 elif token == 'class':
                     cls = self._parseclass(indent)
                     if cls is None:
-                        print "class: syntax error..."
+                        print("class: syntax error...")
                         continue
                     freshscope = True
                     dbg("new scope: class")
@@ -580,7 +563,7 @@
                 elif token == 'from':
                     mod, token = self._parsedotname()
                     if not mod or token != "import":
-                        print "from: syntax error..."
+                        print("from: syntax error...")
                         continue
                     names = self._parseimportlist()
                     for name, alias in names:
@@ -622,4 +605,3 @@
 endfunction
 
 call s:DefPython()
-" vim: set et ts=4:
diff -r 3edc4535acfa src/if_python.c
--- a/src/if_python.c   Wed Aug 10 18:36:54 2011 +0200
+++ b/src/if_python.c   Wed Aug 17 05:35:24 2011 +0200
@@ -352,6 +352,8 @@
     }
 }
 
+static int forward_to_py3 = 0;
+
 /*
  * Load library and get all pointers.
  * Parameter 'libname' provides name of DLL.
@@ -366,10 +368,12 @@
     /* Can't have Python and Python3 loaded at the same time.
      * It cause a crash, because RTLD_GLOBAL is needed for
      * standard C extension libraries of one or both python versions. */
+    if (forward_to_py3) return OK;
     if (python3_loaded())
     {
-       EMSG(_("E836: This Vim cannot execute :python after using :py3"));
-       return FAIL;
+       EMSG(_("E836: This Vim cannot execute :python after using :py3. Will 
forward future calls to :py3."));
+       forward_to_py3 = 1;
+       return OK;
     }
 #endif
 
@@ -515,6 +519,9 @@
     if (recurse != 0)
        return;
 
+    if (forward_to_py3) 
+        return;
+
     ++recurse;
 
 #ifdef DYNAMIC_PYTHON
@@ -554,6 +561,8 @@
            EMSG(_("E263: Sorry, this command is disabled, the Python library 
could not be loaded."));
            goto fail;
        }
+        if (forward_to_py3) 
+            return -1;
 #endif
 
 #ifdef PYTHON_HOME
@@ -690,6 +699,8 @@
 {
     char_u *script;
 
+    if (forward_to_py3) ex_py3(eap);
+
     script = script_get(eap, eap->arg);
     if (!eap->skip)
     {
@@ -713,6 +724,8 @@
     const char *file = (char *)eap->arg;
     char *p;
 
+    if (forward_to_py3) ex_py3file(eap);
+
     /* Have to do it like this. PyRun_SimpleFile requires you to pass a
      * stdio file pointer, but Vim and the Python DLL are compiled with
      * different options under Windows, meaning that stdio pointers aren't
diff -r 3edc4535acfa src/if_python3.c
--- a/src/if_python3.c  Wed Aug 10 18:36:54 2011 +0200
+++ b/src/if_python3.c  Wed Aug 17 05:35:24 2011 +0200
@@ -350,6 +350,8 @@
     }
 }
 
+static int forward_to_python = 0;
+
 /*
  * Load library and get all pointers.
  * Parameter 'libname' provides name of DLL.
@@ -365,10 +367,12 @@
     /* Can't have Python and Python3 loaded at the same time.
      * It cause a crash, because RTLD_GLOBAL is needed for
      * standard C extension libraries of one or both python versions. */
+    if (forward_to_python) return OK;
     if (python_loaded())
     {
-       EMSG(_("E837: This Vim cannot execute :py3 after using :python"));
-       return FAIL;
+       EMSG(_("E837: This Vim cannot execute :py3 after using :python. Will 
forward future calls to python."));
+        forward_to_python = 1;
+       return OK;
     }
 # endif
 
@@ -521,6 +525,9 @@
 {
     static int recurse = 0;
 
+    if (forward_to_python) 
+        return;
+
     /* If a crash occurs while doing this, don't try again. */
     if (recurse != 0)
        return;
@@ -565,11 +572,12 @@
            EMSG(_("E263: Sorry, this command is disabled, the Python library 
could not be loaded."));
            goto fail;
        }
+        if (forward_to_python) 
+            return -1;
 #endif
 
        init_structs();
 
-
 #ifdef PYTHON3_HOME
        Py_SetPythonHome(PYTHON3_HOME);
 #endif
@@ -703,6 +711,8 @@
 {
     char_u *script;
 
+    if (forward_to_python) ex_python(eap);
+
     script = script_get(eap, eap->arg);
     if (!eap->skip)
     {
@@ -727,6 +737,8 @@
     char *p;
     int i;
 
+    if (forward_to_python) ex_pyfile(eap);
+
     /* Have to do it like this. PyRun_SimpleFile requires you to pass a
      * stdio file pointer, but Vim and the Python DLL are compiled with
      * different options under Windows, meaning that stdio pointers aren't
"python25complete.vim - Omni Completion for python 2.5 or earlier
" Maintainer: Aaron Griffin <[email protected]>
" Version: 0.9
" Last Updated: 18 Jun 2009
"
" Roland Puntaier 2011_08_17: I've renamed to *25* in this file, nothing else.
"
" Changes
" TODO:
" 'info' item output can use some formatting work
" Add an "unsafe eval" mode, to allow for return type evaluation
" Complete basic syntax along with import statements
"   i.e. "import url<c-x,c-o>"
" Continue parsing on invalid line??
"
" v 0.9
"   * Fixed docstring parsing for classes and functions
"   * Fixed parsing of *args and **kwargs type arguments
"   * Better function param parsing to handle things like tuples and
"     lambda defaults args
"
" v 0.8
"   * Fixed an issue where the FIRST assignment was always used instead of
"   using a subsequent assignment for a variable
"   * Fixed a scoping issue when working inside a parameterless function
"
"
" v 0.7
"   * Fixed function list sorting (_ and __ at the bottom)
"   * Removed newline removal from docs.  It appears vim handles these better in
"   recent patches
"
" v 0.6:
"   * Fixed argument completion
"   * Removed the 'kind' completions, as they are better indicated
"   with real syntax
"   * Added tuple assignment parsing (whoops, that was forgotten)
"   * Fixed import handling when flattening scope
"
" v 0.5:
" Yeah, I skipped a version number - 0.4 was never public.
"  It was a bugfix version on top of 0.3.  This is a complete
"  rewrite.
"

if !has('python')
    echo "Error: Required vim compiled with +python"
    finish
endif

function! python25complete#Complete(findstart, base)
    "findstart = 1 when we need to get the text length
    if a:findstart == 1
        let line = getline('.')
        let idx = col('.')
        while idx > 0
            let idx -= 1
            let c = line[idx]
            if c =~ '\w'
                continue
            elseif ! c =~ '\.'
                let idx = -1
                break
            else
                break
            endif
        endwhile

        return idx
    "findstart = 0 when we need to return the list of completions
    else
        "vim no longer moves the cursor upon completion... fix that
        let line = getline('.')
        let idx = col('.')
        let cword = ''
        while idx > 0
            let idx -= 1
            let c = line[idx]
            if c =~ '\w' || c =~ '\.'
                let cword = c . cword
                continue
            elseif strlen(cword) > 0 || idx == 0
                break
            endif
        endwhile
        execute "python vimcomplete('" . cword . "', '" . a:base . "')"
        return g:python25complete_completions
    endif
endfunction

function! s:DefPython()
python << PYTHONEOF
import sys, tokenize, cStringIO, types
from token import NAME, DEDENT, NEWLINE, STRING

debugstmts=[]
def dbg(s): debugstmts.append(s)
def showdbg():
    for d in debugstmts: print "DBG: %s " % d

def vimcomplete(context,match):
    global debugstmts
    debugstmts = []
    try:
        import vim
        def complsort(x,y):
            try:
                xa = x['abbr']
                ya = y['abbr']
                if xa[0] == '_':
                    if xa[1] == '_' and ya[0:2] == '__':
                        return xa > ya
                    elif ya[0:2] == '__':
                        return -1
                    elif y[0] == '_':
                        return xa > ya
                    else:
                        return 1
                elif ya[0] == '_':
                    return -1
                else:
                   return xa > ya
            except:
                return 0
        cmpl = Completer()
        cmpl.evalsource('\n'.join(vim.current.buffer),vim.eval("line('.')"))
        all = cmpl.get_completions(context,match)
        all.sort(complsort)
        dictstr = '['
        # have to do this for double quoting
        for cmpl in all:
            dictstr += '{'
            for x in cmpl: dictstr += '"%s":"%s",' % (x,cmpl[x])
            dictstr += '"icase":0},'
        if dictstr[-1] == ',': dictstr = dictstr[:-1]
        dictstr += ']'
        #dbg("dict: %s" % dictstr)
        vim.command("silent let g:python25complete_completions = %s" % dictstr)
        #dbg("Completion dict:\n%s" % all)
    except vim.error:
        dbg("VIM Error: %s" % vim.error)

class Completer(object):
    def __init__(self):
       self.compldict = {}
       self.parser = PyParser()

    def evalsource(self,text,line=0):
        sc = self.parser.parse(text,line)
        src = sc.get_code()
        dbg("source: %s" % src)
        try: exec(src) in self.compldict
        except: dbg("parser: %s, %s" % (sys.exc_info()[0],sys.exc_info()[1]))
        for l in sc.locals:
            try: exec(l) in self.compldict
            except: dbg("locals: %s, %s [%s]" % 
(sys.exc_info()[0],sys.exc_info()[1],l))

    def _cleanstr(self,doc):
        return doc.replace('"',' ').replace("'",' ')

    def get_arguments(self,func_obj):
        def _ctor(obj):
            try: return class_ob.__init__.im_func
            except AttributeError:
                for base in class_ob.__bases__:
                    rc = _find_constructor(base)
                    if rc is not None: return rc
            return None

        arg_offset = 1
        if type(func_obj) == types.ClassType: func_obj = _ctor(func_obj)
        elif type(func_obj) == types.MethodType: func_obj = func_obj.im_func
        else: arg_offset = 0
        
        arg_text=''
        if type(func_obj) in [types.FunctionType, types.LambdaType]:
            try:
                cd = func_obj.func_code
                real_args = cd.co_varnames[arg_offset:cd.co_argcount]
                defaults = func_obj.func_defaults or ''
                defaults = map(lambda name: "=%s" % name, defaults)
                defaults = [""] * (len(real_args)-len(defaults)) + defaults
                items = map(lambda a,d: a+d, real_args, defaults)
                if func_obj.func_code.co_flags & 0x4:
                    items.append("...")
                if func_obj.func_code.co_flags & 0x8:
                    items.append("***")
                arg_text = (','.join(items)) + ')'

            except:
                dbg("arg completion: %s: %s" % 
(sys.exc_info()[0],sys.exc_info()[1]))
                pass
        if len(arg_text) == 0:
            # The doc string sometimes contains the function signature
            #  this works for alot of C modules that are part of the
            #  standard library
            doc = func_obj.__doc__
            if doc:
                doc = doc.lstrip()
                pos = doc.find('\n')
                if pos > 0:
                    sigline = doc[:pos]
                    lidx = sigline.find('(')
                    ridx = sigline.find(')')
                    if lidx > 0 and ridx > 0:
                        arg_text = sigline[lidx+1:ridx] + ')'
        if len(arg_text) == 0: arg_text = ')'
        return arg_text

    def get_completions(self,context,match):
        dbg("get_completions('%s','%s')" % (context,match))
        stmt = ''
        if context: stmt += str(context)
        if match: stmt += str(match)
        try:
            result = None
            all = {}
            ridx = stmt.rfind('.')
            if len(stmt) > 0 and stmt[-1] == '(':
                result = eval(_sanitize(stmt[:-1]), self.compldict)
                doc = result.__doc__
                if doc is None: doc = ''
                args = self.get_arguments(result)
                return 
[{'word':self._cleanstr(args),'info':self._cleanstr(doc)}]
            elif ridx == -1:
                match = stmt
                all = self.compldict
            else:
                match = stmt[ridx+1:]
                stmt = _sanitize(stmt[:ridx])
                result = eval(stmt, self.compldict)
                all = dir(result)

            dbg("completing: stmt:%s" % stmt)
            completions = []

            try: maindoc = result.__doc__
            except: maindoc = ' '
            if maindoc is None: maindoc = ' '
            for m in all:
                if m == "_PyCmplNoType": continue #this is internal
                try:
                    dbg('possible completion: %s' % m)
                    if m.find(match) == 0:
                        if result is None: inst = all[m]
                        else: inst = getattr(result,m)
                        try: doc = inst.__doc__
                        except: doc = maindoc
                        typestr = str(inst)
                        if doc is None or doc == '': doc = maindoc

                        wrd = m[len(match):]
                        c = {'word':wrd, 'abbr':m,  'info':self._cleanstr(doc)}
                        if "function" in typestr:
                            c['word'] += '('
                            c['abbr'] += '(' + 
self._cleanstr(self.get_arguments(inst))
                        elif "method" in typestr:
                            c['word'] += '('
                            c['abbr'] += '(' + 
self._cleanstr(self.get_arguments(inst))
                        elif "module" in typestr:
                            c['word'] += '.'
                        elif "class" in typestr:
                            c['word'] += '('
                            c['abbr'] += '('
                        completions.append(c)
                except:
                    i = sys.exc_info()
                    dbg("inner completion: %s,%s [stmt='%s']" % 
(i[0],i[1],stmt))
            return completions
        except:
            i = sys.exc_info()
            dbg("completion: %s,%s [stmt='%s']" % (i[0],i[1],stmt))
            return []

class Scope(object):
    def __init__(self,name,indent,docstr=''):
        self.subscopes = []
        self.docstr = docstr
        self.locals = []
        self.parent = None
        self.name = name
        self.indent = indent

    def add(self,sub):
        #print 'push scope: [%s@%s]' % (sub.name,sub.indent)
        sub.parent = self
        self.subscopes.append(sub)
        return sub

    def doc(self,str):
        """ Clean up a docstring """
        d = str.replace('\n',' ')
        d = d.replace('\t',' ')
        while d.find('  ') > -1: d = d.replace('  ',' ')
        while d[0] in '"\'\t ': d = d[1:]
        while d[-1] in '"\'\t ': d = d[:-1]
        dbg("Scope(%s)::docstr = %s" % (self,d))
        self.docstr = d

    def local(self,loc):
        self._checkexisting(loc)
        self.locals.append(loc)

    def copy_decl(self,indent=0):
        """ Copy a scope's declaration only, at the specified indent level - 
not local variables """
        return Scope(self.name,indent,self.docstr)

    def _checkexisting(self,test):
        "Convienance function... keep out duplicates"
        if test.find('=') > -1:
            var = test.split('=')[0].strip()
            for l in self.locals:
                if l.find('=') > -1 and var == l.split('=')[0].strip():
                    self.locals.remove(l)

    def get_code(self):
        str = ""
        if len(self.docstr) > 0: str += '"""'+self.docstr+'"""\n'
        for l in self.locals:
            if l.startswith('import'): str += l+'\n'
        str += 'class _PyCmplNoType:\n    def __getattr__(self,name):\n        
return None\n'
        for sub in self.subscopes:
            str += sub.get_code()
        for l in self.locals:
            if not l.startswith('import'): str += l+'\n'

        return str

    def pop(self,indent):
        #print 'pop scope: [%s] to [%s]' % (self.indent,indent)
        outer = self
        while outer.parent != None and outer.indent >= indent:
            outer = outer.parent
        return outer

    def currentindent(self):
        #print 'parse current indent: %s' % self.indent
        return '    '*self.indent

    def childindent(self):
        #print 'parse child indent: [%s]' % (self.indent+1)
        return '    '*(self.indent+1)

class Class(Scope):
    def __init__(self, name, supers, indent, docstr=''):
        Scope.__init__(self,name,indent, docstr)
        self.supers = supers
    def copy_decl(self,indent=0):
        c = Class(self.name,self.supers,indent, self.docstr)
        for s in self.subscopes:
            c.add(s.copy_decl(indent+1))
        return c
    def get_code(self):
        str = '%sclass %s' % (self.currentindent(),self.name)
        if len(self.supers) > 0: str += '(%s)' % ','.join(self.supers)
        str += ':\n'
        if len(self.docstr) > 0: str += 
self.childindent()+'"""'+self.docstr+'"""\n'
        if len(self.subscopes) > 0:
            for s in self.subscopes: str += s.get_code()
        else:
            str += '%spass\n' % self.childindent()
        return str


class Function(Scope):
    def __init__(self, name, params, indent, docstr=''):
        Scope.__init__(self,name,indent, docstr)
        self.params = params
    def copy_decl(self,indent=0):
        return Function(self.name,self.params,indent, self.docstr)
    def get_code(self):
        str = "%sdef %s(%s):\n" % \
            (self.currentindent(),self.name,','.join(self.params))
        if len(self.docstr) > 0: str += 
self.childindent()+'"""'+self.docstr+'"""\n'
        str += "%spass\n" % self.childindent()
        return str

class PyParser:
    def __init__(self):
        self.top = Scope('global',0)
        self.scope = self.top

    def _parsedotname(self,pre=None):
        #returns (dottedname, nexttoken)
        name = []
        if pre is None:
            tokentype, token, indent = self.next()
            if tokentype != NAME and token != '*':
                return ('', token)
        else: token = pre
        name.append(token)
        while True:
            tokentype, token, indent = self.next()
            if token != '.': break
            tokentype, token, indent = self.next()
            if tokentype != NAME: break
            name.append(token)
        return (".".join(name), token)

    def _parseimportlist(self):
        imports = []
        while True:
            name, token = self._parsedotname()
            if not name: break
            name2 = ''
            if token == 'as': name2, token = self._parsedotname()
            imports.append((name, name2))
            while token != "," and "\n" not in token:
                tokentype, token, indent = self.next()
            if token != ",": break
        return imports

    def _parenparse(self):
        name = ''
        names = []
        level = 1
        while True:
            tokentype, token, indent = self.next()
            if token in (')', ',') and level == 1:
                if '=' not in name: name = name.replace(' ', '')
                names.append(name.strip())
                name = ''
            if token == '(':
                level += 1
                name += "("
            elif token == ')':
                level -= 1
                if level == 0: break
                else: name += ")"
            elif token == ',' and level == 1:
                pass
            else:
                name += "%s " % str(token)
        return names

    def _parsefunction(self,indent):
        self.scope=self.scope.pop(indent)
        tokentype, fname, ind = self.next()
        if tokentype != NAME: return None

        tokentype, open, ind = self.next()
        if open != '(': return None
        params=self._parenparse()

        tokentype, colon, ind = self.next()
        if colon != ':': return None

        return Function(fname,params,indent)

    def _parseclass(self,indent):
        self.scope=self.scope.pop(indent)
        tokentype, cname, ind = self.next()
        if tokentype != NAME: return None

        super = []
        tokentype, next, ind = self.next()
        if next == '(':
            super=self._parenparse()
        elif next != ':': return None

        return Class(cname,super,indent)

    def _parseassignment(self):
        assign=''
        tokentype, token, indent = self.next()
        if tokentype == tokenize.STRING or token == 'str':  
            return '""'
        elif token == '(' or token == 'tuple':
            return '()'
        elif token == '[' or token == 'list':
            return '[]'
        elif token == '{' or token == 'dict':
            return '{}'
        elif tokentype == tokenize.NUMBER:
            return '0'
        elif token == 'open' or token == 'file':
            return 'file'
        elif token == 'None':
            return '_PyCmplNoType()'
        elif token == 'type':
            return 'type(_PyCmplNoType)' #only for method resolution
        else:
            assign += token
            level = 0
            while True:
                tokentype, token, indent = self.next()
                if token in ('(','{','['):
                    level += 1
                elif token in (']','}',')'):
                    level -= 1
                    if level == 0: break
                elif level == 0:
                    if token in (';','\n'): break
                    assign += token
        return "%s" % assign

    def next(self):
        type, token, (lineno, indent), end, self.parserline = self.gen.next()
        if lineno == self.curline:
            #print 'line found [%s] scope=%s' % 
(line.replace('\n',''),self.scope.name)
            self.currentscope = self.scope
        return (type, token, indent)

    def _adjustvisibility(self):
        newscope = Scope('result',0)
        scp = self.currentscope
        while scp != None:
            if type(scp) == Function:
                slice = 0
                #Handle 'self' params
                if scp.parent != None and type(scp.parent) == Class:
                    slice = 1
                    newscope.local('%s = %s' % (scp.params[0],scp.parent.name))
                for p in scp.params[slice:]:
                    i = p.find('=')
                    if len(p) == 0: continue
                    pvar = ''
                    ptype = ''
                    if i == -1:
                        pvar = p
                        ptype = '_PyCmplNoType()'
                    else:
                        pvar = p[:i]
                        ptype = _sanitize(p[i+1:])
                    if pvar.startswith('**'):
                        pvar = pvar[2:]
                        ptype = '{}'
                    elif pvar.startswith('*'):
                        pvar = pvar[1:]
                        ptype = '[]'

                    newscope.local('%s = %s' % (pvar,ptype))

            for s in scp.subscopes:
                ns = s.copy_decl(0)
                newscope.add(ns)
            for l in scp.locals: newscope.local(l)
            scp = scp.parent

        self.currentscope = newscope
        return self.currentscope

    #p.parse(vim.current.buffer[:],vim.eval("line('.')"))
    def parse(self,text,curline=0):
        self.curline = int(curline)
        buf = cStringIO.StringIO(''.join(text) + '\n')
        self.gen = tokenize.generate_tokens(buf.readline)
        self.currentscope = self.scope

        try:
            freshscope=True
            while True:
                tokentype, token, indent = self.next()
                #dbg( 'main: token=[%s] indent=[%s]' % (token,indent))

                if tokentype == DEDENT or token == "pass":
                    self.scope = self.scope.pop(indent)
                elif token == 'def':
                    func = self._parsefunction(indent)
                    if func is None:
                        print "function: syntax error..."
                        continue
                    dbg("new scope: function")
                    freshscope = True
                    self.scope = self.scope.add(func)
                elif token == 'class':
                    cls = self._parseclass(indent)
                    if cls is None:
                        print "class: syntax error..."
                        continue
                    freshscope = True
                    dbg("new scope: class")
                    self.scope = self.scope.add(cls)
                    
                elif token == 'import':
                    imports = self._parseimportlist()
                    for mod, alias in imports:
                        loc = "import %s" % mod
                        if len(alias) > 0: loc += " as %s" % alias
                        self.scope.local(loc)
                    freshscope = False
                elif token == 'from':
                    mod, token = self._parsedotname()
                    if not mod or token != "import":
                        print "from: syntax error..."
                        continue
                    names = self._parseimportlist()
                    for name, alias in names:
                        loc = "from %s import %s" % (mod,name)
                        if len(alias) > 0: loc += " as %s" % alias
                        self.scope.local(loc)
                    freshscope = False
                elif tokentype == STRING:
                    if freshscope: self.scope.doc(token)
                elif tokentype == NAME:
                    name,token = self._parsedotname(token) 
                    if token == '=':
                        stmt = self._parseassignment()
                        dbg("parseassignment: %s = %s" % (name, stmt))
                        if stmt != None:
                            self.scope.local("%s = %s" % (name,stmt))
                    freshscope = False
        except StopIteration: #thrown on EOF
            pass
        except:
            dbg("parse error: %s, %s @ %s" %
                (sys.exc_info()[0], sys.exc_info()[1], self.parserline))
        return self._adjustvisibility()

def _sanitize(str):
    val = ''
    level = 0
    for c in str:
        if c in ('(','{','['):
            level += 1
        elif c in (']','}',')'):
            level -= 1
        elif level == 0:
            val += c
    return val

sys.path.extend(['.','..'])
PYTHONEOF
endfunction

call s:DefPython()
" vim: set et ts=4:

Raspunde prin e-mail lui