Index: tools/telemetry/third_party/rope/rope/refactor/rename.py |
diff --git a/tools/telemetry/third_party/rope/rope/refactor/rename.py b/tools/telemetry/third_party/rope/rope/refactor/rename.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..3f1f5b7e6d61271124767e29a8be067c96e5199a |
--- /dev/null |
+++ b/tools/telemetry/third_party/rope/rope/refactor/rename.py |
@@ -0,0 +1,220 @@ |
+import warnings |
+ |
+from rope.base import (exceptions, pyobjects, pynames, taskhandle, |
+ evaluate, worder, codeanalyze, libutils) |
+from rope.base.change import ChangeSet, ChangeContents, MoveResource |
+from rope.refactor import occurrences |
+ |
+ |
+class Rename(object): |
+ """A class for performing rename refactoring |
+ |
+ It can rename everything: classes, functions, modules, packages, |
+ methods, variables and keyword arguments. |
+ |
+ """ |
+ |
+ def __init__(self, project, resource, offset=None): |
+ """If `offset` is None, the `resource` itself will be renamed""" |
+ self.project = project |
+ self.resource = resource |
+ if offset is not None: |
+ self.old_name = worder.get_name_at(self.resource, offset) |
+ this_pymodule = self.project.get_pymodule(self.resource) |
+ self.old_instance, self.old_pyname = \ |
+ evaluate.eval_location2(this_pymodule, offset) |
+ if self.old_pyname is None: |
+ raise exceptions.RefactoringError( |
+ 'Rename refactoring should be performed' |
+ ' on resolvable python identifiers.') |
+ else: |
+ if not resource.is_folder() and resource.name == '__init__.py': |
+ resource = resource.parent |
+ dummy_pymodule = libutils.get_string_module(self.project, '') |
+ self.old_instance = None |
+ self.old_pyname = pynames.ImportedModule(dummy_pymodule, |
+ resource=resource) |
+ if resource.is_folder(): |
+ self.old_name = resource.name |
+ else: |
+ self.old_name = resource.name[:-3] |
+ |
+ def get_old_name(self): |
+ return self.old_name |
+ |
+ def get_changes(self, new_name, in_file=None, in_hierarchy=False, |
+ unsure=None, docs=False, resources=None, |
+ task_handle=taskhandle.NullTaskHandle()): |
+ """Get the changes needed for this refactoring |
+ |
+ Parameters: |
+ |
+ - `in_hierarchy`: when renaming a method this keyword forces |
+ to rename all matching methods in the hierarchy |
+ - `docs`: when `True` rename refactoring will rename |
+ occurrences in comments and strings where the name is |
+ visible. Setting it will make renames faster, too. |
+ - `unsure`: decides what to do about unsure occurrences. |
+ If `None`, they are ignored. Otherwise `unsure` is |
+ called with an instance of `occurrence.Occurrence` as |
+ parameter. If it returns `True`, the occurrence is |
+ considered to be a match. |
+ - `resources` can be a list of `rope.base.resources.File`\s to |
+ apply this refactoring on. If `None`, the restructuring |
+ will be applied to all python files. |
+ - `in_file`: this argument has been deprecated; use |
+ `resources` instead. |
+ |
+ """ |
+ if unsure in (True, False): |
+ warnings.warn( |
+ 'unsure parameter should be a function that returns ' |
+ 'True or False', DeprecationWarning, stacklevel=2) |
+ |
+ def unsure_func(value=unsure): |
+ return value |
+ unsure = unsure_func |
+ if in_file is not None: |
+ warnings.warn( |
+ '`in_file` argument has been deprecated; use `resources` ' |
+ 'instead. ', DeprecationWarning, stacklevel=2) |
+ if in_file: |
+ resources = [self.resource] |
+ if _is_local(self.old_pyname): |
+ resources = [self.resource] |
+ if resources is None: |
+ resources = self.project.get_python_files() |
+ changes = ChangeSet('Renaming <%s> to <%s>' % |
+ (self.old_name, new_name)) |
+ finder = occurrences.create_finder( |
+ self.project, self.old_name, self.old_pyname, unsure=unsure, |
+ docs=docs, instance=self.old_instance, |
+ in_hierarchy=in_hierarchy and self.is_method()) |
+ job_set = task_handle.create_jobset('Collecting Changes', |
+ len(resources)) |
+ for file_ in resources: |
+ job_set.started_job(file_.path) |
+ new_content = rename_in_module(finder, new_name, resource=file_) |
+ if new_content is not None: |
+ changes.add_change(ChangeContents(file_, new_content)) |
+ job_set.finished_job() |
+ if self._is_renaming_a_module(): |
+ resource = self.old_pyname.get_object().get_resource() |
+ if self._is_allowed_to_move(resources, resource): |
+ self._rename_module(resource, new_name, changes) |
+ return changes |
+ |
+ def _is_allowed_to_move(self, resources, resource): |
+ if resource.is_folder(): |
+ try: |
+ return resource.get_child('__init__.py') in resources |
+ except exceptions.ResourceNotFoundError: |
+ return False |
+ else: |
+ return resource in resources |
+ |
+ def _is_renaming_a_module(self): |
+ if isinstance(self.old_pyname.get_object(), pyobjects.AbstractModule): |
+ return True |
+ return False |
+ |
+ def is_method(self): |
+ pyname = self.old_pyname |
+ return isinstance(pyname, pynames.DefinedName) and \ |
+ isinstance(pyname.get_object(), pyobjects.PyFunction) and \ |
+ isinstance(pyname.get_object().parent, pyobjects.PyClass) |
+ |
+ def _rename_module(self, resource, new_name, changes): |
+ if not resource.is_folder(): |
+ new_name = new_name + '.py' |
+ parent_path = resource.parent.path |
+ if parent_path == '': |
+ new_location = new_name |
+ else: |
+ new_location = parent_path + '/' + new_name |
+ changes.add_change(MoveResource(resource, new_location)) |
+ |
+ |
+class ChangeOccurrences(object): |
+ """A class for changing the occurrences of a name in a scope |
+ |
+ This class replaces the occurrences of a name. Note that it only |
+ changes the scope containing the offset passed to the constructor. |
+ What's more it does not have any side-effects. That is for |
+ example changing occurrences of a module does not rename the |
+ module; it merely replaces the occurrences of that module in a |
+ scope with the given expression. This class is useful for |
+ performing many custom refactorings. |
+ |
+ """ |
+ |
+ def __init__(self, project, resource, offset): |
+ self.project = project |
+ self.resource = resource |
+ self.offset = offset |
+ self.old_name = worder.get_name_at(resource, offset) |
+ self.pymodule = project.get_pymodule(self.resource) |
+ self.old_pyname = evaluate.eval_location(self.pymodule, offset) |
+ |
+ def get_old_name(self): |
+ word_finder = worder.Worder(self.resource.read()) |
+ return word_finder.get_primary_at(self.offset) |
+ |
+ def _get_scope_offset(self): |
+ lines = self.pymodule.lines |
+ scope = self.pymodule.get_scope().\ |
+ get_inner_scope_for_line(lines.get_line_number(self.offset)) |
+ start = lines.get_line_start(scope.get_start()) |
+ end = lines.get_line_end(scope.get_end()) |
+ return start, end |
+ |
+ def get_changes(self, new_name, only_calls=False, reads=True, writes=True): |
+ changes = ChangeSet('Changing <%s> occurrences to <%s>' % |
+ (self.old_name, new_name)) |
+ scope_start, scope_end = self._get_scope_offset() |
+ finder = occurrences.create_finder( |
+ self.project, self.old_name, self.old_pyname, |
+ imports=False, only_calls=only_calls) |
+ new_contents = rename_in_module( |
+ finder, new_name, pymodule=self.pymodule, replace_primary=True, |
+ region=(scope_start, scope_end), reads=reads, writes=writes) |
+ if new_contents is not None: |
+ changes.add_change(ChangeContents(self.resource, new_contents)) |
+ return changes |
+ |
+ |
+def rename_in_module(occurrences_finder, new_name, resource=None, |
+ pymodule=None, replace_primary=False, region=None, |
+ reads=True, writes=True): |
+ """Returns the changed source or `None` if there is no changes""" |
+ if resource is not None: |
+ source_code = resource.read() |
+ else: |
+ source_code = pymodule.source_code |
+ change_collector = codeanalyze.ChangeCollector(source_code) |
+ for occurrence in occurrences_finder.find_occurrences(resource, pymodule): |
+ if replace_primary and occurrence.is_a_fixed_primary(): |
+ continue |
+ if replace_primary: |
+ start, end = occurrence.get_primary_range() |
+ else: |
+ start, end = occurrence.get_word_range() |
+ if (not reads and not occurrence.is_written()) or \ |
+ (not writes and occurrence.is_written()): |
+ continue |
+ if region is None or region[0] <= start < region[1]: |
+ change_collector.add_change(start, end, new_name) |
+ return change_collector.get_changed() |
+ |
+ |
+def _is_local(pyname): |
+ module, lineno = pyname.get_definition_location() |
+ if lineno is None: |
+ return False |
+ scope = module.get_scope().get_inner_scope_for_line(lineno) |
+ if isinstance(pyname, pynames.DefinedName) and \ |
+ scope.get_kind() in ('Function', 'Class'): |
+ scope = scope.parent |
+ return scope.get_kind() == 'Function' and \ |
+ pyname in scope.get_names().values() and \ |
+ isinstance(pyname, pynames.AssignedName) |