diff options
author | Emile Anclin <emile.anclin@logilab.fr> | 2010-03-17 12:56:04 +0100 |
---|---|---|
committer | Emile Anclin <emile.anclin@logilab.fr> | 2010-03-17 12:56:04 +0100 |
commit | a246fa3f8175bdcba9282439b8cab413de285a43 (patch) | |
tree | e43611815377cfa6b998dd91741137939e789e66 | |
parent | e1bbf73e1b36b5039f86769edbd2faf2988d2b38 (diff) | |
download | astroid-a246fa3f8175bdcba9282439b8cab413de285a43.tar.gz |
refactor cache handling to avoid infinite recursion error while infering assignments during build, which potentially tried to rebuild a module we were currently building
-rw-r--r-- | builder.py | 13 | ||||
-rw-r--r-- | manager.py | 87 | ||||
-rw-r--r-- | rebuilder.py | 10 |
3 files changed, 55 insertions, 55 deletions
@@ -62,7 +62,7 @@ class ASTNGBuilder: self._module = None self._file = None self._done = None - self.rebuilder = TreeRebuilder() + self.rebuilder = TreeRebuilder(manager) self._dyn_modname_map = {'gtk': 'gtk._gtk'} def module_build(self, module, modname=None): @@ -87,10 +87,12 @@ class ASTNGBuilder: because it's a built-in module or because the .py is not available) """ self._module = module - node = build_module(modname or module.__name__, module.__doc__) + if modname is None: + modname = module.__name__ + node = build_module(modname, module.__doc__) node.file = node.path = path and abspath(path) or path if self._manager is not None: - self._manager._cache[node.file] = self._manager._cache[node.name] = node + self._manager._cache[modname] = node node.package = hasattr(module, '__path__') self._done = {} self.object_build(node, module) @@ -144,11 +146,6 @@ class ASTNGBuilder: newnode.pure_python = True newnode.package = package newnode.file = newnode.path = node_file - newnode.name = modname - if self._manager is not None: - self._manager._cache[newnode.file] = newnode - if self._file: - self._manager._cache[abspath(self._file)] = newnode return newnode # astng from living objects ############################################### @@ -28,7 +28,7 @@ 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, \ + file_from_modpath, load_module_from_name, modpath_from_file, \ get_module_files, get_source_file, zipimport from logilab.common.configuration import OptionsProviderMixIn @@ -101,6 +101,7 @@ class ASTNGManager(OptionsProviderMixIn): def set_cache_size(self, cache_size): """set the cache size (flush it as a side effect!)""" + # NOTE: cache entries are added by the [re]builder self._cache = {} #Cache(cache_size) self._mod_file_cache = {} @@ -117,52 +118,51 @@ class ASTNGManager(OptionsProviderMixIn): source = True except NoSourceFile: source = False + if modname is None: + modname = '.'.join(modpath_from_file(filepath)) + print >>sys.stderr, 'astng from file', filepath, modname try: - return self._cache[filepath] + return self._cache[modname] except KeyError: - if source: - try: - from logilab.astng.builder import ASTNGBuilder - astng = ASTNGBuilder(self).file_build(filepath, modname) - except (SyntaxError, KeyboardInterrupt, SystemExit): - raise - except Exception, ex: - if __debug__: - print 'error while building astng for', filepath - import traceback - traceback.print_exc() - msg = 'Unable to load module %s (%s)' % (modname, ex) - raise ASTNGBuildingException(msg), None, sys.exc_info()[-1] - elif fallback and modname: - return self.astng_from_module_name(modname) - else: - raise ASTNGBuildingException('unable to get astng for file %s' % - filepath) - self._cache[filepath] = astng - return astng - - from_file = astng_from_file # backward compat + pass + if source: + try: + from logilab.astng.builder import ASTNGBuilder + return ASTNGBuilder(self).file_build(filepath, modname) + except (SyntaxError, KeyboardInterrupt, SystemExit): + raise + except Exception, ex: + raise + if __debug__: + print 'error while building astng for', filepath + import traceback + traceback.print_exc() + msg = 'Unable to load module %s (%s)' % (modname, ex) + raise ASTNGBuildingException(msg), None, sys.exc_info()[-1] + 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""" + try: + return self._cache[modname] + except KeyError: + pass 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): - try: - return self._cache[filepath] - except KeyError: - data, zmodname = zip_import_data(filepath) - if data is not None: - from logilab.astng.builder import ASTNGBuilder - try: - astng = ASTNGBuilder(self).string_build(data, zmodname, filepath) - except (SyntaxError, KeyboardInterrupt, SystemExit): - raise - self._cache[filepath] = astng - return astng + data, zmodname = zip_import_data(filepath) + if data is not None: + from logilab.astng.builder import ASTNGBuilder + try: + return ASTNGBuilder(self).string_build(data, zmodname, filepath) + except (SyntaxError, KeyboardInterrupt, SystemExit): + raise if filepath is None or not is_python_source(filepath): try: module = load_module_from_name(modname) @@ -192,6 +192,10 @@ class ASTNGManager(OptionsProviderMixIn): def astng_from_module(self, module, modname=None): """given an imported module, return the astng object""" modname = modname or module.__name__ + try: + return self._cache[modname] + except KeyError: + pass filepath = modname try: # some builtin modules don't have __file__ attribute @@ -200,15 +204,8 @@ class ASTNGManager(OptionsProviderMixIn): return self.astng_from_file(filepath, modname) except AttributeError: pass - try: - return self._cache[filepath] - except KeyError: - from logilab.astng.builder import ASTNGBuilder - astng = ASTNGBuilder(self).module_build(module, modname) - # update caches (filepath and astng.file are not necessarily the - # same (.pyc pb)) - self._cache[filepath] = self._cache[astng.file] = astng - return astng + 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""" diff --git a/rebuilder.py b/rebuilder.py index b527ce0..2cab247 100644 --- a/rebuilder.py +++ b/rebuilder.py @@ -30,7 +30,8 @@ from logilab.astng.bases import YES, Instance class RebuildVisitor(ASTVisitor): """Visitor to transform an AST to an ASTNG """ - def __init__(self): + def __init__(self, manager): + self._manager = manager self.asscontext = None self._metaclass = None self._global_names = None @@ -183,7 +184,12 @@ class RebuildVisitor(ASTVisitor): """visit an Module node to become astng""" self._metaclass = [''] self._global_names = [] - return self._visit_module(node, parent) + module = self._visit_module(node, parent) + # init module cache here else we may get some infinite recursion + # errors while infering delayed assignments + if self._manager is not None: + self._manager._cache[module.name] = module + return module def visit_pass(self, node, parent): """visit a Pass node by returning a fresh instance of it""" |