Path: blob/master/elisp/emacs-for-python/rope-dist/rope/contrib/autoimport.py
1496 views
import re12from rope.base import (exceptions, pynames, resourceobserver,3taskhandle, pyobjects, builtins, resources)4from rope.refactor import importutils567class AutoImport(object):8"""A class for finding the module that provides a name910This class maintains a cache of global names in python modules.11Note that this cache is not accurate and might be out of date.1213"""1415def __init__(self, project, observe=True, underlined=False):16"""Construct an AutoImport object1718If `observe` is `True`, listen for project changes and update19the cache.2021If `underlined` is `True`, underlined names are cached, too.22"""23self.project = project24self.underlined = underlined25self.names = project.data_files.read_data('globalnames')26if self.names is None:27self.names = {}28project.data_files.add_write_hook(self._write)29# XXX: using a filtered observer30observer = resourceobserver.ResourceObserver(31changed=self._changed, moved=self._moved, removed=self._removed)32if observe:33project.add_observer(observer)3435def import_assist(self, starting):36"""Return a list of ``(name, module)`` tuples3738This function tries to find modules that have a global name39that starts with `starting`.40"""41# XXX: breaking if gave up! use generators42result = []43for module in self.names:44for global_name in self.names[module]:45if global_name.startswith(starting):46result.append((global_name, module))47return result4849def get_modules(self, name):50"""Return the list of modules that have global `name`"""51result = []52for module in self.names:53if name in self.names[module]:54result.append(module)55return result5657def get_all_names(self):58"""Return the list of all cached global names"""59result = set()60for module in self.names:61result.update(set(self.names[module]))62return result6364def get_name_locations(self, name):65"""Return a list of ``(resource, lineno)`` tuples"""66result = []67pycore = self.project.pycore68for module in self.names:69if name in self.names[module]:70try:71pymodule = pycore.get_module(module)72if name in pymodule:73pyname = pymodule[name]74module, lineno = pyname.get_definition_location()75if module is not None:76resource = module.get_module().get_resource()77if resource is not None and lineno is not None:78result.append((resource, lineno))79except exceptions.ModuleNotFoundError:80pass81return result8283def generate_cache(self, resources=None, underlined=None,84task_handle=taskhandle.NullTaskHandle()):85"""Generate global name cache for project files8687If `resources` is a list of `rope.base.resource.File`\s, only88those files are searched; otherwise all python modules in the89project are cached.9091"""92if resources is None:93resources = self.project.pycore.get_python_files()94job_set = task_handle.create_jobset(95'Generatig autoimport cache', len(resources))96for file in resources:97job_set.started_job('Working on <%s>' % file.path)98self.update_resource(file, underlined)99job_set.finished_job()100101def generate_modules_cache(self, modules, underlined=None,102task_handle=taskhandle.NullTaskHandle()):103"""Generate global name cache for modules listed in `modules`"""104job_set = task_handle.create_jobset(105'Generatig autoimport cache for modules', len(modules))106for modname in modules:107job_set.started_job('Working on <%s>' % modname)108if modname.endswith('.*'):109mod = self.project.pycore.find_module(modname[:-2])110if mod:111for sub in submodules(mod):112self.update_resource(sub, underlined)113else:114self.update_module(modname, underlined)115job_set.finished_job()116117def clear_cache(self):118"""Clear all entries in global-name cache119120It might be a good idea to use this function before121regenerating global names.122123"""124self.names.clear()125126def find_insertion_line(self, code):127"""Guess at what line the new import should be inserted"""128match = re.search(r'^(def|class)\s+', code)129if match is not None:130code = code[:match.start()]131try:132pymodule = self.project.pycore.get_string_module(code)133except exceptions.ModuleSyntaxError:134return 1135testmodname = '__rope_testmodule_rope'136importinfo = importutils.NormalImport(((testmodname, None),))137module_imports = importutils.get_module_imports(138self.project.pycore, pymodule)139module_imports.add_import(importinfo)140code = module_imports.get_changed_source()141offset = code.index(testmodname)142lineno = code.count('\n', 0, offset) + 1143return lineno144145def update_resource(self, resource, underlined=None):146"""Update the cache for global names in `resource`"""147try:148pymodule = self.project.pycore.resource_to_pyobject(resource)149modname = self._module_name(resource)150self._add_names(pymodule, modname, underlined)151except exceptions.ModuleSyntaxError:152pass153154def update_module(self, modname, underlined=None):155"""Update the cache for global names in `modname` module156157`modname` is the name of a module.158"""159try:160pymodule = self.project.pycore.get_module(modname)161self._add_names(pymodule, modname, underlined)162except exceptions.ModuleNotFoundError:163pass164165def _module_name(self, resource):166return self.project.pycore.modname(resource)167168def _add_names(self, pymodule, modname, underlined):169if underlined is None:170underlined = self.underlined171globals = []172if isinstance(pymodule, pyobjects.PyDefinedObject):173attributes = pymodule._get_structural_attributes()174else:175attributes = pymodule.get_attributes()176for name, pyname in attributes.items():177if not underlined and name.startswith('_'):178continue179if isinstance(pyname, (pynames.AssignedName, pynames.DefinedName)):180globals.append(name)181if isinstance(pymodule, builtins.BuiltinModule):182globals.append(name)183self.names[modname] = globals184185def _write(self):186self.project.data_files.write_data('globalnames', self.names)187188def _changed(self, resource):189if not resource.is_folder():190self.update_resource(resource)191192def _moved(self, resource, newresource):193if not resource.is_folder():194modname = self._module_name(resource)195if modname in self.names:196del self.names[modname]197self.update_resource(newresource)198199def _removed(self, resource):200if not resource.is_folder():201modname = self._module_name(resource)202if modname in self.names:203del self.names[modname]204205206def submodules(mod):207if isinstance(mod, resources.File):208if mod.name.endswith('.py') and mod.name != '__init__.py':209return set([mod])210return set()211if not mod.has_child('__init__.py'):212return set()213result = set([mod])214for child in mod.get_children():215result |= submodules(child)216return result217218219