diff -r 6d998a43102b Lib/importlib/_bootstrap.py --- a/Lib/importlib/_bootstrap.py Thu Aug 29 14:37:47 2013 +0100 +++ b/Lib/importlib/_bootstrap.py Thu Aug 29 13:57:14 2013 -0400 @@ -1369,63 +1369,64 @@ self._loaders = loaders # Base (directory) path self.path = path or '.' - self._path_mtime = -1 - self._path_cache = set() - self._relaxed_path_cache = set() def invalidate_caches(self): """Invalidate the directory mtime.""" - self._path_mtime = -1 + self._path_mtime[self.path] = -1 find_module = _find_module_shim + _path_mtime = {} + _path_cache = {} + _relaxed_path_cache = {} + def find_loader(self, fullname): """Try to find a loader for the specified module, or the namespace package portions. Returns (loader, list-of-portions).""" is_namespace = False tail_module = fullname.rpartition('.')[2] - try: - mtime = _os.stat(self.path).st_mtime - except OSError: - mtime = -1 - if mtime != self._path_mtime: - self._fill_cache() - self._path_mtime = mtime + self._fill_cache() # tail_module keeps the original casing, for __file__ and friends if _relax_case(): - cache = self._relaxed_path_cache + cache = self._relaxed_path_cache[self.path] cache_module = tail_module.lower() else: - cache = self._path_cache + cache = self._path_cache[self.path] cache_module = tail_module # Check if the module is the name of a directory (and thus a package). if cache_module in cache: base_path = _path_join(self.path, tail_module) - if _path_isdir(base_path): - for suffix, loader in self._loaders: - init_filename = '__init__' + suffix - full_path = _path_join(base_path, init_filename) - if _path_isfile(full_path): - return (loader(fullname, full_path), [base_path]) - else: - # A namespace package, return the path if we don't also - # find a module in the next section. - is_namespace = True + self._fill_cache(base_path) + for suffix, loader in self._loaders: + init_filename = '__init__' + suffix + full_path = _path_join(base_path, init_filename) + if init_filename in self._path_cache[base_path]: + return loader(fullname, full_path), [base_path] + else: + # A namespace package, return the path if we don't also + # find a module in the next section. + is_namespace = True # Check for a file w/ a proper suffix exists. for suffix, loader in self._loaders: full_path = _path_join(self.path, tail_module + suffix) _verbose_message('trying {}'.format(full_path), verbosity=2) if cache_module + suffix in cache: - if _path_isfile(full_path): - return (loader(fullname, full_path), []) + return loader(fullname, full_path), [] if is_namespace: _verbose_message('possible namespace for {}'.format(base_path)) - return (None, [base_path]) - return (None, []) + return None, [base_path] + return None, [] - def _fill_cache(self): + def _fill_cache(self, path=None): """Fill the cache of potential modules and packages for this directory.""" - path = self.path + path = path if path is not None else self.path + try: + mtime = _os.stat(path).st_mtime + except OSError: + mtime = -1 + if mtime == self._path_mtime.get(path, 0): + return + self._path_mtime[path] = mtime try: contents = _os.listdir(path) except (FileNotFoundError, PermissionError, NotADirectoryError): @@ -1435,7 +1436,7 @@ # We store two cached versions, to handle runtime changes of the # PYTHONCASEOK environment variable. if not sys.platform.startswith('win'): - self._path_cache = set(contents) + self._path_cache[path] = set(contents) else: # Windows users can import modules with case-insensitive file # suffixes (for legacy reasons). Make the suffix lowercase here @@ -1450,9 +1451,9 @@ else: new_name = name lower_suffix_contents.add(new_name) - self._path_cache = lower_suffix_contents + self._path_cache[path] = lower_suffix_contents if sys.platform.startswith(_CASE_INSENSITIVE_PLATFORMS): - self._relaxed_path_cache = {fn.lower() for fn in contents} + self._relaxed_path_cache[path] = {fn.lower() for fn in contents} @classmethod def path_hook(cls, *loader_details):