def real_code(source): """Simplify `source` for analysis It replaces: * comments with spaces * strs with a new str filled with spaces * implicit and explicit continuations with spaces * tabs and semicolons with spaces The resulting code is a lot easier to analyze if we are interested only in offsets. """ collector = codeanalyze.ChangeCollector(source) for start, end in ignored_regions(source): if source[start] == '#': replacement = ' ' * (end - start) else: replacement = '"%s"' % (' ' * (end - start - 2)) collector.add_change(start, end, replacement) source = collector.get_changed() or source collector = codeanalyze.ChangeCollector(source) parens = 0 for match in _parens.finditer(source): i = match.start() c = match.group() if c in '({[': parens += 1 if c in ')}]': parens -= 1 if c == '\n' and parens > 0: collector.add_change(i, i + 1, ' ') source = collector.get_changed() or source return source.replace('\\\n', ' ').replace('\t', ' ').replace(';', '\n')
def get_changes( self, dest_attr, new_name=None, resources=None, task_handle=taskhandle.NullTaskHandle(), ): """Return the changes needed for this refactoring Parameters: - `dest_attr`: the name of the destination attribute - `new_name`: the name of the new method; if `None` uses the old name - `resources` can be a list of `rope.base.resources.File` to apply this refactoring on. If `None`, the restructuring will be applied to all python files. """ changes = ChangeSet("Moving method <%s>" % self.method_name) if resources is None: resources = self.project.get_python_files() if new_name is None: new_name = self.get_method_name() resource1, start1, end1, new_content1 = self._get_changes_made_by_old_class( dest_attr, new_name) collector1 = codeanalyze.ChangeCollector(resource1.read()) collector1.add_change(start1, end1, new_content1) resource2, start2, end2, new_content2 = self._get_changes_made_by_new_class( dest_attr, new_name) if resource1 == resource2: collector1.add_change(start2, end2, new_content2) else: collector2 = codeanalyze.ChangeCollector(resource2.read()) collector2.add_change(start2, end2, new_content2) result = collector2.get_changed() import_tools = importutils.ImportTools(self.project) new_imports = self._get_used_imports(import_tools) if new_imports: goal_pymodule = libutils.get_string_module( self.project, result, resource2) result = _add_imports_to_module(import_tools, goal_pymodule, new_imports) if resource2 in resources: changes.add_change(ChangeContents(resource2, result)) if resource1 in resources: changes.add_change( ChangeContents(resource1, collector1.get_changed())) 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 get_changes(self, classname=None, new_class_name=None): if new_class_name is not None: warnings.warn( "new_class_name parameter is deprecated; use classname", DeprecationWarning, stacklevel=2, ) classname = new_class_name collector = codeanalyze.ChangeCollector(self.pymodule.source_code) start, end = sourceutils.get_body_region(self.pyfunction) indents = sourceutils.get_indents( self.pymodule.lines, self.pyfunction.get_scope().get_start()) + sourceutils.get_indent( self.project) new_contents = " " * indents + "return %s(%s)()\n" % ( classname, ", ".join(self._get_parameter_names()), ) collector.add_change(start, end, new_contents) insertion = self._get_class_insertion_point() collector.add_change(insertion, insertion, "\n\n" + self.get_new_class(classname)) changes = change.ChangeSet( "Replace method with method object refactoring") changes.add_change( change.ChangeContents(self.resource, collector.get_changed())) return changes
def substitute(self, mapping): collector = codeanalyze.ChangeCollector(self.template) for name, occurrences in self.names.items(): for region in occurrences: collector.add_change(region[0], region[1], mapping[name]) result = collector.get_changed() if result is None: return self.template return result
def extract(self): extract_info = self._collect_info() content = codeanalyze.ChangeCollector(self.info.source) definition = extract_info.definition lineno, indents = extract_info.definition_location offset = self.info.lines.get_line_start(lineno) indented = sourceutils.fix_indentation(definition, indents) content.add_change(offset, offset, indented) self._replace_occurrences(content, extract_info) return content.get_changed()
def get_changes(self, new_parameter): definition_info = functionutils.DefinitionInfo.read(self.pyfunction) definition_info.args_with_defaults.append((new_parameter, self._get_primary())) collector = codeanalyze.ChangeCollector(self.resource.read()) header_start, header_end = self._get_header_offsets() body_start, body_end = sourceutils.get_body_region(self.pyfunction) collector.add_change(header_start, header_end, definition_info.to_string()) self._change_function_occurrences( collector, body_start, body_end, new_parameter ) changes = rope.base.change.ChangeSet("Introduce parameter <%s>" % new_parameter) change = rope.base.change.ChangeContents(self.resource, collector.get_changed()) changes.add_change(change) return changes
def make_pattern(code, variables): variables = set(variables) collector = codeanalyze.ChangeCollector(code) def does_match(node, name): return isinstance(node, ast.Name) and node.id == name finder = RawSimilarFinder(code, does_match=does_match) for variable in variables: for match in finder.get_matches('${%s}' % variable): start, end = match.get_region() collector.add_change(start, end, '${%s}' % variable) result = collector.get_changed() return result if result is not None else code
def _get_node_text(self, node, force=False): if not force and node in self.matched_asts: return self._get_matched_text(self.matched_asts[node]) start, end = patchedast.node_region(node) main_text = self.source[start:end] collector = codeanalyze.ChangeCollector(main_text) for node in self._get_nearest_roots(node): sub_start, sub_end = patchedast.node_region(node) collector.add_change(sub_start - start, sub_end - start, self._get_node_text(node)) result = collector.get_changed() if result is None: return main_text return result
def real_code(source): """Simplify `source` for analysis It replaces: * comments with spaces * strs with a new str filled with spaces * implicit and explicit continuations with spaces * tabs and semicolons with spaces The resulting code is a lot easier to analyze if we are interested only in offsets. """ collector = codeanalyze.ChangeCollector(source) for start, end, matchgroups in ignored_regions(source): if source[start] == "#": replacement = " " * (end - start) elif "f" in matchgroups.get("prefix", "").lower(): replacement = None else: replacement = '"%s"' % (" " * (end - start - 2)) if replacement is not None: collector.add_change(start, end, replacement) source = collector.get_changed() or source collector = codeanalyze.ChangeCollector(source) parens = 0 for match in _parens.finditer(source): i = match.start() c = match.group() if c in "({[": parens += 1 if c in ")}]": parens -= 1 if c == "\n" and parens > 0: collector.add_change(i, i + 1, " ") source = collector.get_changed() or source return source.replace("\\\n", " ").replace("\t", " ").replace(";", "\n")
def get_changed_module(self): source = self.resource.read() change_collector = codeanalyze.ChangeCollector(source) if self.replacement is not None: change_collector.add_change(self.skip_start, self.skip_end, self.replacement) for occurrence in self.occurrence_finder.find_occurrences(self.resource): start, end = occurrence.get_primary_range() if self.skip_start <= start < self.skip_end: self.handle.occurred_inside_skip(change_collector, occurrence) else: self.handle.occurred_outside_skip(change_collector, occurrence) result = change_collector.get_changed() if result is not None and result != source: return result
def get_changes(self): changes = change.ChangeSet('Generate %s <%s>' % (self._get_element_kind(), self.name)) indents = self.info.get_scope_indents() blanks = self.info.get_blank_lines() base_definition = sourceutils.fix_indentation(self._get_element(), indents) definition = '\n' * blanks[0] + base_definition + '\n' * blanks[1] resource = self.info.get_insertion_resource() start, end = self.info.get_insertion_offsets() collector = codeanalyze.ChangeCollector(resource.read()) collector.add_change(start, end, definition) changes.add_change(change.ChangeContents( resource, collector.get_changed())) return changes
def get_changed(self): if self._is_expression(): result = self._get_node_text(self.ast) if result == self.source: return None return result else: collector = codeanalyze.ChangeCollector(self.source) last_end = -1 for match in self.matches: start, end = match.get_region() if start < last_end: if not self._is_expression(): continue last_end = end replacement = self._get_matched_text(match) collector.add_change(start, end, replacement) return collector.get_changed()
def get_changed_module(self): word_finder = worder.Worder(self.source) change_collector = codeanalyze.ChangeCollector(self.source) for occurrence in self.occurrence_finder.find_occurrences( self.resource): if not occurrence.is_called() and not occurrence.is_defined(): continue start, end = occurrence.get_primary_range() begin_parens, end_parens = word_finder.get_word_parens_range(end - 1) if occurrence.is_called(): primary, pyname = occurrence.get_primary_and_pyname() changed_call = self.call_changer.change_call( primary, pyname, self.source[start:end_parens]) else: changed_call = self.call_changer.change_definition( self.source[start:end_parens]) if changed_call is not None: change_collector.add_change(start, end_parens, changed_call) return change_collector.get_changed()
def get_changes(self): changes = change.ChangeSet("Generate %s <%s>" % (self._get_element_kind(), self.name)) indents = self.info.get_scope_indents() blanks = self.info.get_blank_lines() base_definition = sourceutils.fix_indentation(self._get_element(), indents) definition = "\n" * blanks[0] + base_definition + "\n" * blanks[1] resource = self.info.get_insertion_resource() start, end = self.info.get_insertion_offsets() collector = codeanalyze.ChangeCollector(resource.read()) collector.add_change(start, end, definition) changes.add_change( change.ChangeContents(resource, collector.get_changed())) if self.goal_resource: relative_import = _add_relative_import_to_module( self.project, self.resource, self.goal_resource, self.name) changes.add_change(relative_import) return changes