#1
  1. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2003
    Posts
    133
    Rep Power
    11

    On rlcompleter and completing the import statement


    So ... I like rlcompleter. It really makes experimenting in the Python interpreter easier. But it won't complete import statements.

    Now: I've looked around but I haven't found an alternative to rlcompleter that will also complete import statements. This is irritating! So I wrote my own.

    It's very much a hack and there's probably something better that I just haven't found. But for those that (like me) haven't found anything better, here's an extension to rlcompleter that also completes import statements:
    Code:
    import re
    import readline
    from rlcompleter import Completer as _Completer
    
    __all__ = ["Completer"]
    
    class Completer(_Completer):
        def __init__(self):
            _Completer.__init__(self)
        
        def complete(self, text, state):
            buffer = readline.get_line_buffer()
            
            if state == 0:
                if re.search(r"(import|from) ([\w_.]+)?$", buffer):
                    self.matches = self.import_matches(buffer, text)
                else:
                    _Completer.complete(self, text, state)
            try:
                return self.matches[state]
            except IndexError:
                return None
    
        def import_matches(self, buffer, text):
            mods = Modules()
            
            if re.search(r"from [\w_.]+ import", buffer):
                return
            if '.' in text:
                return self._subpkgs(mods, text)
                
            return [mod for mod in mods.completions
                        if mod.startswith(text)]
        
        def _subpkgs(self, mods, text):
            from os import path
        
            parts = text.split('.')
            basepath = ''
            for pth, pkg in mods.rawmods:
                if parts[0] == pkg:
                    basepath = pth
                    break
            if not basepath:
                return
    
            for part in parts[1:-1]:
                basepath = path.join(basepath, part)
                if not path.isdir(basepath):
                    return
            
            basename = '.'.join(parts[:-1])
            rawmods = mods.modscan.submodules((basepath, basename))
            completions = get_package_members(rawmods)
            
            return ['.'.join([basename, mod]) for mod in completions
                        if mod.startswith(parts[-1])]
            
    class Modules(object):
        
        def __new__(cls, *args, **kwds):
            inst = cls.__dict__.get("__inst__")
            if inst is not None:
                return inst["instance"]
            cls.__inst__ = {}
            cls.__inst__['instance'] = inst = object.__new__(cls)
            return inst
        
        def __init__(self):
            if self.__inst__.get("initialized"):
                return
            self.__inst__['initialized'] = True
            
            from pydoc import ModuleScanner
        
            self.completions = ["errno", "exceptions", "gc", "imp",
                                "marshal", "posix", "signal", "sys",
                                "thread", "xxsubtype", "zipimport"]
            self.modscan = ModuleScanner()
            self.roots = self.modscan.roots
            
            self.rawmods = self.flattened([self.modscan.submodules(mod)
                                           for mod in self.roots[1:]])
            self.completions.extend(get_package_members(self.rawmods))
        
        def flattened(self, seq):
            """Flattens a sequence with nested sequences.
            
            flattened() is non-recursive and does not flatten nested
            sequences.
            """
            result = []
            for item in seq:
                if not isinstance(item, basestring) and \
                        (hasattr(item, '__getitem__') or hasattr(item, '__iter__')):
                    result.extend(item)
                else:
                    result.append(item)
            return result
    
    def get_package_members(rawmods):
        completions = []
    
        for pth, pkg in rawmods:
            pth = pth.split('/')[-1]
            pkg = pkg.split('.')[-1]
        
            if pth == pkg:
                completions.append(pkg)
            elif pth.endswith('.py') or pth.endswith('.so') \
                    and not pth.endswith('__init__.py'):
                if not pth.startswith('_'):
                    completions.append(pth[:-3])
        return completions
    
    readline.set_completer(Completer().complete)
    This code will only work if you use Python on a *nix computer.
    Last edited by percivall; June 11th, 2004 at 12:12 PM.

IMN logo majestic logo threadwatch logo seochat tools logo