PageRenderTime 50ms CodeModel.GetById 35ms app.highlight 12ms RepoModel.GetById 1ms app.codeStats 0ms

/nose/importer.py

https://bitbucket.org/jpellerin/nose/
Python | 154 lines | 114 code | 15 blank | 25 comment | 20 complexity | 21e2d2549ef8e87454a9d8bea9b6ad08 MD5 | raw file
  1"""Implements an importer that looks only in specific path (ignoring
  2sys.path), and uses a per-path cache in addition to sys.modules. This is
  3necessary because test modules in different directories frequently have the
  4same names, which means that the first loaded would mask the rest when using
  5the builtin importer.
  6"""
  7import logging
  8import os
  9import sys
 10from nose.config import Config
 11
 12from imp import find_module, load_module, acquire_lock, release_lock
 13
 14log = logging.getLogger(__name__)
 15
 16class Importer(object):
 17    """An importer class that does only path-specific imports. That
 18    is, the given module is not searched for on sys.path, but only at
 19    the path or in the directory specified.
 20    """
 21    def __init__(self, config=None):
 22        if config is None:
 23            config = Config()
 24        self.config = config
 25
 26    def importFromPath(self, path, fqname):
 27        """Import a dotted-name package whose tail is at path. In other words,
 28        given foo.bar and path/to/foo/bar.py, import foo from path/to/foo then
 29        bar from path/to/foo/bar, returning bar.
 30        """
 31        # find the base dir of the package
 32        path_parts = os.path.normpath(os.path.abspath(path)).split(os.sep)
 33        name_parts = fqname.split('.')
 34        if path_parts[-1].startswith('__init__'):
 35            path_parts.pop()
 36        path_parts = path_parts[:-(len(name_parts))]
 37        dir_path = os.sep.join(path_parts)
 38        # then import fqname starting from that dir
 39        return self.importFromDir(dir_path, fqname)                
 40
 41    def importFromDir(self, dir, fqname):
 42        """Import a module *only* from path, ignoring sys.path and
 43        reloading if the version in sys.modules is not the one we want.
 44        """
 45        dir = os.path.normpath(os.path.abspath(dir))
 46        log.debug("Import %s from %s", fqname, dir)
 47
 48        # FIXME reimplement local per-dir cache?
 49        
 50        # special case for __main__
 51        if fqname == '__main__':
 52            return sys.modules[fqname]
 53        
 54        if self.config.addPaths:
 55            add_path(dir, self.config)
 56            
 57        path = [dir]
 58        parts = fqname.split('.')
 59        part_fqname = ''
 60        mod = parent = fh = None
 61
 62        for part in parts:
 63            if part_fqname == '':
 64                part_fqname = part
 65            else:
 66                part_fqname = "%s.%s" % (part_fqname, part)
 67            try:
 68                acquire_lock()
 69                log.debug("find module part %s (%s) in %s",
 70                          part, part_fqname, path)
 71                fh, filename, desc = find_module(part, path)
 72                old = sys.modules.get(part_fqname)
 73                if old is not None:
 74                    # test modules frequently have name overlap; make sure
 75                    # we get a fresh copy of anything we are trying to load
 76                    # from a new path
 77                    log.debug("sys.modules has %s as %s", part_fqname, old)
 78                    if (self.sameModule(old, filename)
 79                        or (self.config.firstPackageWins and
 80                            getattr(old, '__path__', None))):
 81                        mod = old
 82                    else:
 83                        del sys.modules[part_fqname]
 84                        mod = load_module(part_fqname, fh, filename, desc)
 85                else:
 86                    mod = load_module(part_fqname, fh, filename, desc)
 87            finally:
 88                if fh:
 89                    fh.close()
 90                release_lock()
 91            if parent:
 92                setattr(parent, part, mod)
 93            if hasattr(mod, '__path__'):
 94                path = mod.__path__
 95            parent = mod
 96        return mod
 97
 98    def sameModule(self, mod, filename):
 99        mod_paths = []
100        if hasattr(mod, '__path__'):
101            for path in mod.__path__:
102                mod_paths.append(os.path.dirname(
103                    os.path.normpath(
104                    os.path.abspath(path))))
105        elif hasattr(mod, '__file__'):
106            mod_paths.append(os.path.dirname(
107                os.path.normpath(
108                os.path.abspath(mod.__file__))))
109        else:
110            # builtin or other module-like object that
111            # doesn't have __file__; must be new
112            return False
113        new_path = os.path.dirname(os.path.normpath(filename))
114        for mod_path in mod_paths:
115            log.debug(
116                "module already loaded? mod: %s new: %s",
117                mod_path, new_path)
118            if mod_path == new_path:
119                return True
120        return False
121
122
123def add_path(path, config=None):
124    """Ensure that the path, or the root of the current package (if
125    path is in a package), is in sys.path.
126    """
127
128    # FIXME add any src-looking dirs seen too... need to get config for that
129    
130    log.debug('Add path %s' % path)    
131    if not path:
132        return []
133    added = []
134    parent = os.path.dirname(path)
135    if (parent
136        and os.path.exists(os.path.join(path, '__init__.py'))):
137        added.extend(add_path(parent, config))
138    elif not path in sys.path:
139        log.debug("insert %s into sys.path", path)
140        sys.path.insert(0, path)
141        added.append(path)
142    if config and config.srcDirs:
143        for dirname in config.srcDirs:
144            dirpath = os.path.join(path, dirname)
145            if os.path.isdir(dirpath):
146                sys.path.insert(0, dirpath)
147                added.append(dirpath)
148    return added
149
150
151def remove_path(path):
152    log.debug('Remove path %s' % path)
153    if path in sys.path:
154        sys.path.remove(path)