Index: tools/telemetry/third_party/rope/rope/contrib/autoimport.py |
diff --git a/tools/telemetry/third_party/rope/rope/contrib/autoimport.py b/tools/telemetry/third_party/rope/rope/contrib/autoimport.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9670080cf7f3c223f7e84156089c6c45e2a5db5b |
--- /dev/null |
+++ b/tools/telemetry/third_party/rope/rope/contrib/autoimport.py |
@@ -0,0 +1,222 @@ |
+import re |
+ |
+from rope.base import builtins |
+from rope.base import exceptions |
+from rope.base import libutils |
+from rope.base import pynames |
+from rope.base import pyobjects |
+from rope.base import resources |
+from rope.base import resourceobserver |
+from rope.base import taskhandle |
+from rope.refactor import importutils |
+ |
+ |
+class AutoImport(object): |
+ """A class for finding the module that provides a name |
+ |
+ This class maintains a cache of global names in python modules. |
+ Note that this cache is not accurate and might be out of date. |
+ |
+ """ |
+ |
+ def __init__(self, project, observe=True, underlined=False): |
+ """Construct an AutoImport object |
+ |
+ If `observe` is `True`, listen for project changes and update |
+ the cache. |
+ |
+ If `underlined` is `True`, underlined names are cached, too. |
+ """ |
+ self.project = project |
+ self.underlined = underlined |
+ self.names = project.data_files.read_data('globalnames') |
+ if self.names is None: |
+ self.names = {} |
+ project.data_files.add_write_hook(self._write) |
+ # XXX: using a filtered observer |
+ observer = resourceobserver.ResourceObserver( |
+ changed=self._changed, moved=self._moved, removed=self._removed) |
+ if observe: |
+ project.add_observer(observer) |
+ |
+ def import_assist(self, starting): |
+ """Return a list of ``(name, module)`` tuples |
+ |
+ This function tries to find modules that have a global name |
+ that starts with `starting`. |
+ """ |
+ # XXX: breaking if gave up! use generators |
+ result = [] |
+ for module in self.names: |
+ for global_name in self.names[module]: |
+ if global_name.startswith(starting): |
+ result.append((global_name, module)) |
+ return result |
+ |
+ def get_modules(self, name): |
+ """Return the list of modules that have global `name`""" |
+ result = [] |
+ for module in self.names: |
+ if name in self.names[module]: |
+ result.append(module) |
+ return result |
+ |
+ def get_all_names(self): |
+ """Return the list of all cached global names""" |
+ result = set() |
+ for module in self.names: |
+ result.update(set(self.names[module])) |
+ return result |
+ |
+ def get_name_locations(self, name): |
+ """Return a list of ``(resource, lineno)`` tuples""" |
+ result = [] |
+ for module in self.names: |
+ if name in self.names[module]: |
+ try: |
+ pymodule = self.project.get_module(module) |
+ if name in pymodule: |
+ pyname = pymodule[name] |
+ module, lineno = pyname.get_definition_location() |
+ if module is not None: |
+ resource = module.get_module().get_resource() |
+ if resource is not None and lineno is not None: |
+ result.append((resource, lineno)) |
+ except exceptions.ModuleNotFoundError: |
+ pass |
+ return result |
+ |
+ def generate_cache(self, resources=None, underlined=None, |
+ task_handle=taskhandle.NullTaskHandle()): |
+ """Generate global name cache for project files |
+ |
+ If `resources` is a list of `rope.base.resource.File`\s, only |
+ those files are searched; otherwise all python modules in the |
+ project are cached. |
+ |
+ """ |
+ if resources is None: |
+ resources = self.project.get_python_files() |
+ job_set = task_handle.create_jobset( |
+ 'Generatig autoimport cache', len(resources)) |
+ for file in resources: |
+ job_set.started_job('Working on <%s>' % file.path) |
+ self.update_resource(file, underlined) |
+ job_set.finished_job() |
+ |
+ def generate_modules_cache(self, modules, underlined=None, |
+ task_handle=taskhandle.NullTaskHandle()): |
+ """Generate global name cache for modules listed in `modules`""" |
+ job_set = task_handle.create_jobset( |
+ 'Generatig autoimport cache for modules', len(modules)) |
+ for modname in modules: |
+ job_set.started_job('Working on <%s>' % modname) |
+ if modname.endswith('.*'): |
+ mod = self.project.find_module(modname[:-2]) |
+ if mod: |
+ for sub in submodules(mod): |
+ self.update_resource(sub, underlined) |
+ else: |
+ self.update_module(modname, underlined) |
+ job_set.finished_job() |
+ |
+ def clear_cache(self): |
+ """Clear all entries in global-name cache |
+ |
+ It might be a good idea to use this function before |
+ regenerating global names. |
+ |
+ """ |
+ self.names.clear() |
+ |
+ def find_insertion_line(self, code): |
+ """Guess at what line the new import should be inserted""" |
+ match = re.search(r'^(def|class)\s+', code) |
+ if match is not None: |
+ code = code[:match.start()] |
+ try: |
+ pymodule = libutils.get_string_module(self.project, code) |
+ except exceptions.ModuleSyntaxError: |
+ return 1 |
+ testmodname = '__rope_testmodule_rope' |
+ importinfo = importutils.NormalImport(((testmodname, None),)) |
+ module_imports = importutils.get_module_imports(self.project, |
+ pymodule) |
+ module_imports.add_import(importinfo) |
+ code = module_imports.get_changed_source() |
+ offset = code.index(testmodname) |
+ lineno = code.count('\n', 0, offset) + 1 |
+ return lineno |
+ |
+ def update_resource(self, resource, underlined=None): |
+ """Update the cache for global names in `resource`""" |
+ try: |
+ pymodule = self.project.get_pymodule(resource) |
+ modname = self._module_name(resource) |
+ self._add_names(pymodule, modname, underlined) |
+ except exceptions.ModuleSyntaxError: |
+ pass |
+ |
+ def update_module(self, modname, underlined=None): |
+ """Update the cache for global names in `modname` module |
+ |
+ `modname` is the name of a module. |
+ """ |
+ try: |
+ pymodule = self.project.get_module(modname) |
+ self._add_names(pymodule, modname, underlined) |
+ except exceptions.ModuleNotFoundError: |
+ pass |
+ |
+ def _module_name(self, resource): |
+ return libutils.modname(resource) |
+ |
+ def _add_names(self, pymodule, modname, underlined): |
+ if underlined is None: |
+ underlined = self.underlined |
+ globals = [] |
+ if isinstance(pymodule, pyobjects.PyDefinedObject): |
+ attributes = pymodule._get_structural_attributes() |
+ else: |
+ attributes = pymodule.get_attributes() |
+ for name, pyname in attributes.items(): |
+ if not underlined and name.startswith('_'): |
+ continue |
+ if isinstance(pyname, (pynames.AssignedName, pynames.DefinedName)): |
+ globals.append(name) |
+ if isinstance(pymodule, builtins.BuiltinModule): |
+ globals.append(name) |
+ self.names[modname] = globals |
+ |
+ def _write(self): |
+ self.project.data_files.write_data('globalnames', self.names) |
+ |
+ def _changed(self, resource): |
+ if not resource.is_folder(): |
+ self.update_resource(resource) |
+ |
+ def _moved(self, resource, newresource): |
+ if not resource.is_folder(): |
+ modname = self._module_name(resource) |
+ if modname in self.names: |
+ del self.names[modname] |
+ self.update_resource(newresource) |
+ |
+ def _removed(self, resource): |
+ if not resource.is_folder(): |
+ modname = self._module_name(resource) |
+ if modname in self.names: |
+ del self.names[modname] |
+ |
+ |
+def submodules(mod): |
+ if isinstance(mod, resources.File): |
+ if mod.name.endswith('.py') and mod.name != '__init__.py': |
+ return set([mod]) |
+ return set() |
+ if not mod.has_child('__init__.py'): |
+ return set() |
+ result = set([mod]) |
+ for child in mod.get_children(): |
+ result |= submodules(child) |
+ return result |