| Index: third_party/logilab/astng/manager.py
 | 
| ===================================================================
 | 
| --- third_party/logilab/astng/manager.py	(revision 292986)
 | 
| +++ third_party/logilab/astng/manager.py	(working copy)
 | 
| @@ -1,299 +0,0 @@
 | 
| -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 | 
| -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 | 
| -# copyright 2003-2010 Sylvain Thenault, all rights reserved.
 | 
| -# contact mailto:thenault@gmail.com
 | 
| -#
 | 
| -# This file is part of logilab-astng.
 | 
| -#
 | 
| -# logilab-astng is free software: you can redistribute it and/or modify it
 | 
| -# under the terms of the GNU Lesser General Public License as published by the
 | 
| -# Free Software Foundation, either version 2.1 of the License, or (at your
 | 
| -# option) any later version.
 | 
| -#
 | 
| -# logilab-astng 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 Lesser General Public License
 | 
| -# for more details.
 | 
| -#
 | 
| -# You should have received a copy of the GNU Lesser General Public License along
 | 
| -# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
 | 
| -"""astng manager: avoid multiple astng build of a same module when
 | 
| -possible by providing a class responsible to get astng representation
 | 
| -from various source and using a cache of built modules)
 | 
| -"""
 | 
| -
 | 
| -__docformat__ = "restructuredtext en"
 | 
| -
 | 
| -import sys
 | 
| -import os
 | 
| -from os.path import dirname, basename, abspath, join, isdir, exists
 | 
| -
 | 
| -from logilab.common.modutils import NoSourceFile, is_python_source, \
 | 
| -     file_from_modpath, load_module_from_name, modpath_from_file, \
 | 
| -     get_module_files, get_source_file, zipimport
 | 
| -from logilab.common.configuration import OptionsProviderMixIn
 | 
| -
 | 
| -from logilab.astng.exceptions import ASTNGBuildingException
 | 
| -
 | 
| -def astng_wrapper(func, modname):
 | 
| -    """wrapper to give to ASTNGManager.project_from_files"""
 | 
| -    print 'parsing %s...' % modname
 | 
| -    try:
 | 
| -        return func(modname)
 | 
| -    except ASTNGBuildingException, exc:
 | 
| -        print exc
 | 
| -    except Exception, exc:
 | 
| -        import traceback
 | 
| -        traceback.print_exc()
 | 
| -
 | 
| -def _silent_no_wrap(func, modname):
 | 
| -    """silent wrapper that doesn't do anything; can be used for tests"""
 | 
| -    return func(modname)
 | 
| -
 | 
| -def safe_repr(obj):
 | 
| -    try:
 | 
| -        return repr(obj)
 | 
| -    except:
 | 
| -        return '???'
 | 
| -
 | 
| -
 | 
| -
 | 
| -class ASTNGManager(OptionsProviderMixIn):
 | 
| -    """the astng manager, responsible to build astng from files
 | 
| -     or modules.
 | 
| -
 | 
| -    Use the Borg pattern.
 | 
| -    """
 | 
| -
 | 
| -    name = 'astng loader'
 | 
| -    options = (("ignore",
 | 
| -                {'type' : "csv", 'metavar' : "<file>",
 | 
| -                 'dest' : "black_list", "default" : ('CVS',),
 | 
| -                 'help' : "add <file> (may be a directory) to the black list\
 | 
| -. It should be a base name, not a path. You may set this option multiple times\
 | 
| -."}),
 | 
| -               ("project",
 | 
| -                {'default': "No Name", 'type' : 'string', 'short': 'p',
 | 
| -                 'metavar' : '<project name>',
 | 
| -                 'help' : 'set the project name.'}),
 | 
| -               )
 | 
| -    brain = {}
 | 
| -    def __init__(self):
 | 
| -        self.__dict__ = ASTNGManager.brain
 | 
| -        if not self.__dict__:
 | 
| -            OptionsProviderMixIn.__init__(self)
 | 
| -            self.load_defaults()
 | 
| -            # NOTE: cache entries are added by the [re]builder
 | 
| -            self.astng_cache = {}
 | 
| -            self._mod_file_cache = {}
 | 
| -            self.transformers = []
 | 
| -
 | 
| -    def astng_from_file(self, filepath, modname=None, fallback=True, source=False):
 | 
| -        """given a module name, return the astng object"""
 | 
| -        try:
 | 
| -            filepath = get_source_file(filepath, include_no_ext=True)
 | 
| -            source = True
 | 
| -        except NoSourceFile:
 | 
| -            pass
 | 
| -        if modname is None:
 | 
| -            try:
 | 
| -                modname = '.'.join(modpath_from_file(filepath))
 | 
| -            except ImportError:
 | 
| -                modname = filepath
 | 
| -        if modname in self.astng_cache:
 | 
| -            return self.astng_cache[modname]
 | 
| -        if source:
 | 
| -            from logilab.astng.builder import ASTNGBuilder
 | 
| -            return ASTNGBuilder(self).file_build(filepath, modname)
 | 
| -        elif fallback and modname:
 | 
| -            return self.astng_from_module_name(modname)
 | 
| -        raise ASTNGBuildingException('unable to get astng for file %s' %
 | 
| -                                     filepath)
 | 
| -
 | 
| -    def astng_from_module_name(self, modname, context_file=None):
 | 
| -        """given a module name, return the astng object"""
 | 
| -        if modname in self.astng_cache:
 | 
| -            return self.astng_cache[modname]
 | 
| -        if modname == '__main__':
 | 
| -            from logilab.astng.builder import ASTNGBuilder
 | 
| -            return ASTNGBuilder(self).string_build('', modname)
 | 
| -        old_cwd = os.getcwd()
 | 
| -        if context_file:
 | 
| -            os.chdir(dirname(context_file))
 | 
| -        try:
 | 
| -            filepath = self.file_from_module_name(modname, context_file)
 | 
| -            if filepath is not None and not is_python_source(filepath):
 | 
| -                module = self.zip_import_data(filepath)
 | 
| -                if module is not None:
 | 
| -                    return module
 | 
| -            if filepath is None or not is_python_source(filepath):
 | 
| -                try:
 | 
| -                    module = load_module_from_name(modname)
 | 
| -                except Exception, ex:
 | 
| -                    msg = 'Unable to load module %s (%s)' % (modname, ex)
 | 
| -                    raise ASTNGBuildingException(msg)
 | 
| -                return self.astng_from_module(module, modname)
 | 
| -            return self.astng_from_file(filepath, modname, fallback=False)
 | 
| -        finally:
 | 
| -            os.chdir(old_cwd)
 | 
| -
 | 
| -    def zip_import_data(self, filepath):
 | 
| -        if zipimport is None:
 | 
| -            return None
 | 
| -        from logilab.astng.builder import ASTNGBuilder
 | 
| -        builder = ASTNGBuilder(self)
 | 
| -        for ext in ('.zip', '.egg'):
 | 
| -            try:
 | 
| -                eggpath, resource = filepath.rsplit(ext + '/', 1)
 | 
| -            except ValueError:
 | 
| -                continue
 | 
| -            try:
 | 
| -                importer = zipimport.zipimporter(eggpath + ext)
 | 
| -                zmodname = resource.replace('/', '.')
 | 
| -                if importer.is_package(resource):
 | 
| -                    zmodname =  zmodname + '.__init__'
 | 
| -                module = builder.string_build(importer.get_source(resource),
 | 
| -                                              zmodname, filepath)
 | 
| -                return module
 | 
| -            except:
 | 
| -                continue
 | 
| -        return None
 | 
| -
 | 
| -    def file_from_module_name(self, modname, contextfile):
 | 
| -        try:
 | 
| -            value = self._mod_file_cache[(modname, contextfile)]
 | 
| -        except KeyError:
 | 
| -            try:
 | 
| -                value = file_from_modpath(modname.split('.'),
 | 
| -                                          context_file=contextfile)
 | 
| -            except ImportError, ex:
 | 
| -                msg = 'Unable to load module %s (%s)' % (modname, ex)
 | 
| -                value = ASTNGBuildingException(msg)
 | 
| -            self._mod_file_cache[(modname, contextfile)] = value
 | 
| -        if isinstance(value, ASTNGBuildingException):
 | 
| -            raise value
 | 
| -        return value
 | 
| -
 | 
| -    def astng_from_module(self, module, modname=None):
 | 
| -        """given an imported module, return the astng object"""
 | 
| -        modname = modname or module.__name__
 | 
| -        if modname in self.astng_cache:
 | 
| -            return self.astng_cache[modname]
 | 
| -        try:
 | 
| -            # some builtin modules don't have __file__ attribute
 | 
| -            filepath = module.__file__
 | 
| -            if is_python_source(filepath):
 | 
| -                return self.astng_from_file(filepath, modname)
 | 
| -        except AttributeError:
 | 
| -            pass
 | 
| -        from logilab.astng.builder import ASTNGBuilder
 | 
| -        return ASTNGBuilder(self).module_build(module, modname)
 | 
| -
 | 
| -    def astng_from_class(self, klass, modname=None):
 | 
| -        """get astng for the given class"""
 | 
| -        if modname is None:
 | 
| -            try:
 | 
| -                modname = klass.__module__
 | 
| -            except AttributeError:
 | 
| -                raise ASTNGBuildingException(
 | 
| -                    'Unable to get module for class %s' % safe_repr(klass))
 | 
| -        modastng = self.astng_from_module_name(modname)
 | 
| -        return modastng.getattr(klass.__name__)[0] # XXX
 | 
| -
 | 
| -
 | 
| -    def infer_astng_from_something(self, obj, context=None):
 | 
| -        """infer astng for the given class"""
 | 
| -        if hasattr(obj, '__class__') and not isinstance(obj, type):
 | 
| -            klass = obj.__class__
 | 
| -        else:
 | 
| -            klass = obj
 | 
| -        try:
 | 
| -            modname = klass.__module__
 | 
| -        except AttributeError:
 | 
| -            raise ASTNGBuildingException(
 | 
| -                'Unable to get module for %s' % safe_repr(klass))
 | 
| -        except Exception, ex:
 | 
| -            raise ASTNGBuildingException(
 | 
| -                'Unexpected error while retrieving module for %s: %s'
 | 
| -                % (safe_repr(klass), ex))
 | 
| -        try:
 | 
| -            name = klass.__name__
 | 
| -        except AttributeError:
 | 
| -            raise ASTNGBuildingException(
 | 
| -                'Unable to get name for %s' % safe_repr(klass))
 | 
| -        except Exception, ex:
 | 
| -            raise ASTNGBuildingException(
 | 
| -                'Unexpected error while retrieving name for %s: %s'
 | 
| -                % (safe_repr(klass), ex))
 | 
| -        # take care, on living object __module__ is regularly wrong :(
 | 
| -        modastng = self.astng_from_module_name(modname)
 | 
| -        if klass is obj:
 | 
| -            for  infered in modastng.igetattr(name, context):
 | 
| -                yield infered
 | 
| -        else:
 | 
| -            for infered in modastng.igetattr(name, context):
 | 
| -                yield infered.instanciate_class()
 | 
| -
 | 
| -    def project_from_files(self, files, func_wrapper=astng_wrapper,
 | 
| -                           project_name=None, black_list=None):
 | 
| -        """return a Project from a list of files or modules"""
 | 
| -        # build the project representation
 | 
| -        project_name = project_name or self.config.project
 | 
| -        black_list = black_list or self.config.black_list
 | 
| -        project = Project(project_name)
 | 
| -        for something in files:
 | 
| -            if not exists(something):
 | 
| -                fpath = file_from_modpath(something.split('.'))
 | 
| -            elif isdir(something):
 | 
| -                fpath = join(something, '__init__.py')
 | 
| -            else:
 | 
| -                fpath = something
 | 
| -            astng = func_wrapper(self.astng_from_file, fpath)
 | 
| -            if astng is None:
 | 
| -                continue
 | 
| -            # XXX why is first file defining the project.path ?
 | 
| -            project.path = project.path or astng.file
 | 
| -            project.add_module(astng)
 | 
| -            base_name = astng.name
 | 
| -            # recurse in package except if __init__ was explicitly given
 | 
| -            if astng.package and something.find('__init__') == -1:
 | 
| -                # recurse on others packages / modules if this is a package
 | 
| -                for fpath in get_module_files(dirname(astng.file),
 | 
| -                                              black_list):
 | 
| -                    astng = func_wrapper(self.astng_from_file, fpath)
 | 
| -                    if astng is None or astng.name == base_name:
 | 
| -                        continue
 | 
| -                    project.add_module(astng)
 | 
| -        return project
 | 
| -
 | 
| -    def register_transformer(self, transformer):
 | 
| -        self.transformers.append(transformer)
 | 
| -
 | 
| -class Project:
 | 
| -    """a project handle a set of modules / packages"""
 | 
| -    def __init__(self, name=''):
 | 
| -        self.name = name
 | 
| -        self.path = None
 | 
| -        self.modules = []
 | 
| -        self.locals = {}
 | 
| -        self.__getitem__ = self.locals.__getitem__
 | 
| -        self.__iter__ = self.locals.__iter__
 | 
| -        self.values = self.locals.values
 | 
| -        self.keys = self.locals.keys
 | 
| -        self.items = self.locals.items
 | 
| -
 | 
| -    def add_module(self, node):
 | 
| -        self.locals[node.name] = node
 | 
| -        self.modules.append(node)
 | 
| -
 | 
| -    def get_module(self, name):
 | 
| -        return self.locals[name]
 | 
| -
 | 
| -    def get_children(self):
 | 
| -        return self.modules
 | 
| -
 | 
| -    def __repr__(self):
 | 
| -        return '<Project %r at %s (%s modules)>' % (self.name, id(self),
 | 
| -                                                    len(self.modules))
 | 
| -
 | 
| -
 | 
| 
 |