| 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)
|
|
|