Path: blob/master/elisp/emacs-for-python/rope-dist/rope/refactor/introduce_parameter.py
1415 views
import rope.base.change1from rope.base import exceptions, evaluate, worder, codeanalyze2from rope.refactor import functionutils, sourceutils, occurrences345class IntroduceParameter(object):6"""Introduce parameter refactoring78This refactoring adds a new parameter to a function and replaces9references to an expression in it with the new parameter.1011The parameter finding part is different from finding similar12pieces in extract refactorings. In this refactoring parameters13are found based on the object they reference to. For instance14in::1516class A(object):17var = None1819class B(object):20a = A()2122b = B()23a = b.a2425def f(a):26x = b.a.var + a.var2728using this refactoring on ``a.var`` with ``p`` as the new29parameter name, will result in::3031def f(p=a.var):32x = p + p3334"""3536def __init__(self, project, resource, offset):37self.pycore = project.pycore38self.resource = resource39self.offset = offset40self.pymodule = self.pycore.resource_to_pyobject(self.resource)41scope = self.pymodule.get_scope().get_inner_scope_for_offset(offset)42if scope.get_kind() != 'Function':43raise exceptions.RefactoringError(44'Introduce parameter should be performed inside functions')45self.pyfunction = scope.pyobject46self.name, self.pyname = self._get_name_and_pyname()47if self.pyname is None:48raise exceptions.RefactoringError(49'Cannot find the definition of <%s>' % self.name)5051def _get_primary(self):52word_finder = worder.Worder(self.resource.read())53return word_finder.get_primary_at(self.offset)5455def _get_name_and_pyname(self):56return (worder.get_name_at(self.resource, self.offset),57evaluate.eval_location(self.pymodule, self.offset))5859def get_changes(self, new_parameter):60definition_info = functionutils.DefinitionInfo.read(self.pyfunction)61definition_info.args_with_defaults.append((new_parameter,62self._get_primary()))63collector = codeanalyze.ChangeCollector(self.resource.read())64header_start, header_end = self._get_header_offsets()65body_start, body_end = sourceutils.get_body_region(self.pyfunction)66collector.add_change(header_start, header_end,67definition_info.to_string())68self._change_function_occurances(collector, body_start,69body_end, new_parameter)70changes = rope.base.change.ChangeSet('Introduce parameter <%s>' %71new_parameter)72change = rope.base.change.ChangeContents(self.resource,73collector.get_changed())74changes.add_change(change)75return changes7677def _get_header_offsets(self):78lines = self.pymodule.lines79start_line = self.pyfunction.get_scope().get_start()80end_line = self.pymodule.logical_lines.\81logical_line_in(start_line)[1]82start = lines.get_line_start(start_line)83end = lines.get_line_end(end_line)84start = self.pymodule.source_code.find('def', start) + 485end = self.pymodule.source_code.rfind(':', start, end)86return start, end8788def _change_function_occurances(self, collector, function_start,89function_end, new_name):90finder = occurrences.create_finder(self.pycore, self.name, self.pyname)91for occurrence in finder.find_occurrences(resource=self.resource):92start, end = occurrence.get_primary_range()93if function_start <= start < function_end:94collector.add_change(start, end, new_name)959697