def analyze_file(self, pyfile, use_cache=False): # don't analyze the file again if we've already done it and it hasn't # changed. If `use_cache` is True then lookup/record in global cache. mtime = os.path.getmtime(pyfile) if pyfile in self.fileinfo: if mtime <= self.fileinfo[pyfile][1]: return self.fileinfo[pyfile][0] if use_cache: info = _FileInfoCache.lookup(pyfile) if info is not None and mtime <= info[1]: self.fileinfo[pyfile] = info return info[0] logger.info("analyzing %s", pyfile) myvisitor = PythonSourceFileAnalyser(pyfile, self) self.modinfo[get_module_path(pyfile)] = myvisitor self.fileinfo[myvisitor.fname] = (myvisitor, os.path.getmtime(myvisitor.fname)) self.files_count += 1 if use_cache: _FileInfoCache.record(pyfile, (myvisitor, os.path.getmtime(myvisitor.fname))) return myvisitor
def analyze_file(self, pyfile, use_cache=False): # don't analyze the file again if we've already done it and it hasn't # changed. If `use_cache` is True then lookup/record in global cache. mtime = os.path.getmtime(pyfile) if pyfile in self.fileinfo: if mtime <= self.fileinfo[pyfile][1]: return self.fileinfo[pyfile][0] if use_cache: info = _FileInfoCache.lookup(pyfile) if info is not None and mtime <= info[1]: self.fileinfo[pyfile] = info return info[0] logger.info("analyzing %s", pyfile) myvisitor = PythonSourceFileAnalyser(pyfile, self) self.modinfo[get_module_path(pyfile)] = myvisitor self.fileinfo[myvisitor.fname] = (myvisitor, os.path.getmtime(myvisitor.fname)) self.files_count += 1 if use_cache: _FileInfoCache.record( pyfile, (myvisitor, os.path.getmtime(myvisitor.fname))) return myvisitor
def __init__(self, fname, tree_analyser): ast.NodeVisitor.__init__(self) self.fname = os.path.abspath(os.path.expanduser(fname)) self.modpath = get_module_path(fname) self.classes = {} self.localnames = {} # map of local names to package names self.starimports = [] self.unresolved_classes = set() self.tree_analyser = tree_analyser # in order to get this to work with the 'ast' lib, I have # to read using universal newlines and append a newline # to the string I read for some files. The 'compiler' lib # didn't have this problem. :( f = open(self.fname, "Ur") try: contents = f.read() if len(contents) > 0 and contents[-1] != "\n": contents += "\n" for node in ast.walk(ast.parse(contents, self.fname)): self.visit(node) finally: f.close() self.update_graph(self.tree_analyser.graph) self.update_ifaces(self.tree_analyser.graph) self.tree_analyser = None
def __init__(self, fname, tree_analyser): ast.NodeVisitor.__init__(self) self.fname = os.path.abspath(os.path.expanduser(fname)) self.modpath = get_module_path(fname) self.classes = {} self.localnames = {} # map of local names to package names self.starimports = [] self.unresolved_classes = set() self.tree_analyser = tree_analyser # in order to get this to work with the 'ast' lib, I have # to read using universal newlines and append a newline # to the string I read for some files. The 'compiler' lib # didn't have this problem. :( f = open(self.fname, 'Ur') try: contents = f.read() if len(contents) > 0 and contents[-1] != '\n': contents += '\n' for node in ast.walk(ast.parse(contents, self.fname)): self.visit(node) finally: f.close() self.update_graph(self.tree_analyser.graph) self.update_ifaces(self.tree_analyser.graph) self.tree_analyser = None
def __init__(self, fpath): self.fpath = fpath self.modpath = get_module_path(fpath) self.modtime = os.path.getmtime(fpath) __import__(self.modpath) module = sys.modules[self.modpath] self.version = getattr(module, '__version__', None) self._update_class_info()
def update(self, added, changed, removed): """File may have changed on disk, update information and return sets of added, removed and (possibly) changed classes. """ self.modpath = get_module_path(self.fpath) startset = set(self.classes.keys()) self._reload() keys = set(self.classes.keys()) added.update(keys - startset) changed.update(startset & keys) removed.update(startset - keys)
def execfile(self, fname, digest=None): # first, make sure file has been imported __import__(get_module_path(fname)) newdigest = file_md5(fname) if digest and digest != newdigest: logger.warning("file '%s' has been modified since the last time" " it was exec'd" % fname) with open(fname) as f: contents = f.read() node = add_init_monitors(parse(contents, fname, mode='exec')) exec compile(node, fname, 'exec') in self._model_globals # make the recorded execfile command use the current md5 hash self._cmds_to_save.append("execfile('%s', '%s')" % (fname, newdigest))
def analyze_file(self, pyfile): # don't analyze the file again if we've already done it and it hasn't changed if pyfile in self.fileinfo: if os.path.getmtime(pyfile) <= self.fileinfo[pyfile][1]: return self.fileinfo[pyfile][0] myvisitor = PythonSourceFileAnalyser(pyfile, self) self.modinfo[get_module_path(pyfile)] = myvisitor self.fileinfo[myvisitor.fname] = (myvisitor, os.path.getmtime(myvisitor.fname)) self.files_count += 1 return myvisitor
def remove_file(self, fname): fvisitor = self.fileinfo[fname][0] del self.fileinfo[fname] del self.modinfo[fvisitor.modpath] nodes = [] for klass, cinfo in self.class_map.items(): if isinstance(cinfo, ClassInfo): if cinfo.fname == fname: nodes.append(klass) else: modname = get_module_path(fname) + '.' if klass.startswith(modname) or cinfo.startswith(modname): nodes.append(klass) self.graph.remove_nodes_from(nodes) for klass in nodes: del self.class_map[klass]
def remove_file(self, fname): fvisitor = self.fileinfo[fname][0] del self.fileinfo[fname] del self.modinfo[fvisitor.modpath] nodes = [] for klass, cinfo in self.class_map.items(): if isinstance(cinfo, ClassInfo): if cinfo.fname == fname: nodes.append(klass) else: modname = get_module_path(fname) + "." if klass.startswith(modname) or cinfo.startswith(modname): nodes.append(klass) self.graph.remove_nodes_from(nodes) for klass in nodes: del self.class_map[klass]
def _analyze(self): """Gather import and class inheritance information from the source trees under the specified set of starting directories. """ fileinfo = {} # gather python files from the specified starting directories # and parse them, extracting class and import information for pyfile in find_files(self.startdirs, "*.py", self.exclude): myvisitor = PythonSourceFileAnalyser(pyfile) # in order to get this to work with the 'ast' lib, I have # to read using universal newlines and append a newline # to the string I read for some files. The 'compiler' lib # didn't have this problem. :( f = open(pyfile, 'Ur') try: for node in ast.walk(ast.parse(f.read()+'\n', pyfile)): myvisitor.visit(node) finally: f.close() fileinfo[get_module_path(pyfile)] = myvisitor # now translate any indirect imports into absolute module pathnames # NOTE: only indirect imports within the set of specified source # trees will be fully resolved, i.e., if a file in your set # of source trees imports # openmdao.main.api but you don't include openmdao.main in your # list of source trees, any imports within openmdao.main.api # won't be included in the translation. This means that # openmdao.main.api.Component will not be translated to # openmdao.main.component.Component like it should. for visitor in fileinfo.values(): visitor.translate(fileinfo) # build the inheritance/interface graph for visitor in fileinfo.values(): for classname, classinfo in visitor.classes.items(): for base in classinfo.bases: self.graph.add_edge(classname, base) for impl in classinfo.impls: self.ifaces.add(impl) self.graph.add_edge(classname, impl) # flip orientation of inheritance graph so we can find all classes # that inherit from a particular base more easily self.graph = self.graph.reverse(copy=False)
def _analyze(self): """Gather import and class inheritance information from the source trees under the specified set of starting directories. """ fileinfo = {} # gather python files from the specified starting directories # and parse them, extracting class and import information for pyfile in find_files(self.startdirs, "*.py", self.exclude): myvisitor = PythonSourceFileAnalyser(pyfile) # in order to get this to work with the 'ast' lib, I have # to read using universal newlines and append a newline # to the string I read for some files. The 'compiler' lib # didn't have this problem. :( f = open(pyfile, 'Ur') try: for node in ast.walk(ast.parse(f.read() + '\n', pyfile)): myvisitor.visit(node) finally: f.close() fileinfo[get_module_path(pyfile)] = myvisitor # now translate any indirect imports into absolute module pathnames # NOTE: only indirect imports within the set of specified source # trees will be fully resolved, i.e., if a file in your set # of source trees imports # openmdao.main.api but you don't include openmdao.main in your # list of source trees, any imports within openmdao.main.api # won't be included in the translation. This means that # openmdao.main.api.Component will not be translated to # openmdao.main.component.Component like it should. for visitor in fileinfo.values(): visitor.translate(fileinfo) # build the inheritance/interface graph for visitor in fileinfo.values(): for classname, classinfo in visitor.classes.items(): for base in classinfo.bases: self.graph.add_edge(classname, base) for impl in classinfo.impls: self.ifaces.add(impl) self.graph.add_edge(classname, impl) # flip orientation of inheritance graph so we can find all classes # that inherit from a particular base more easily self.graph = self.graph.reverse(copy=False)
def on_modified(self, fpath, added_set, changed_set, deleted_set): if os.path.isdir(fpath): return with self._lock: imported = False if fpath in self.analyzer.fileinfo: # file has been previously scanned visitor = self.analyzer.fileinfo[fpath][0] pre_set = set(visitor.classes.keys()) if fpath in self.imported: # we imported it earlier imported = True sys.path = [os.path.dirname(fpath) ] + sys.path # add fpath location to sys.path try: reload(self.imported[fpath][0]) except ImportError as err: return None finally: sys.path = sys.path[1:] # restore original sys.path #self.imported[fpath] = (m, self.imported[fpath][1]) elif os.path.getmtime( fpath) > self.analyzer.fileinfo[fpath][1]: modpath = get_module_path(fpath) if modpath in sys.modules: reload(sys.modules[modpath]) self.on_deleted(fpath, set()) # clean up old refs else: # it's a new file pre_set = set() visitor = self.analyzer.analyze_file(fpath) post_set = set(visitor.classes.keys()) deleted_set.update(pre_set - post_set) added_set.update(post_set - pre_set) if imported: changed_set.update(pre_set.intersection(post_set))
def __init__(self, fname, stop_classes=None): ast.NodeVisitor.__init__(self) self.fname = os.path.abspath(fname) self.modpath = get_module_path(fname) self.classes = {} self.localnames = {} # map of local names to package names
def tearDown(self): for pyfile in find_files(self.tdir, "*.py"): modpath = get_module_path(pyfile) if modpath in sys.modules: del sys.modules[modpath] shutil.rmtree(self.tdir, onerror=onerror)