Index: tools/telemetry/third_party/rope/rope/refactor/usefunction.py |
diff --git a/tools/telemetry/third_party/rope/rope/refactor/usefunction.py b/tools/telemetry/third_party/rope/rope/refactor/usefunction.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..85896a98f7750150fea34e9bd9049e88f2b601be |
--- /dev/null |
+++ b/tools/telemetry/third_party/rope/rope/refactor/usefunction.py |
@@ -0,0 +1,174 @@ |
+from rope.base import (change, taskhandle, evaluate, |
+ exceptions, pyobjects, pynames, ast) |
+from rope.base import libutils |
+from rope.refactor import restructure, sourceutils, similarfinder |
+ |
+ |
+class UseFunction(object): |
+ """Try to use a function wherever possible""" |
+ |
+ def __init__(self, project, resource, offset): |
+ self.project = project |
+ self.offset = offset |
+ this_pymodule = project.get_pymodule(resource) |
+ pyname = evaluate.eval_location(this_pymodule, offset) |
+ if pyname is None: |
+ raise exceptions.RefactoringError('Unresolvable name selected') |
+ self.pyfunction = pyname.get_object() |
+ if not isinstance(self.pyfunction, pyobjects.PyFunction) or \ |
+ not isinstance(self.pyfunction.parent, pyobjects.PyModule): |
+ raise exceptions.RefactoringError( |
+ 'Use function works for global functions, only.') |
+ self.resource = self.pyfunction.get_module().get_resource() |
+ self._check_returns() |
+ |
+ def _check_returns(self): |
+ node = self.pyfunction.get_ast() |
+ if _yield_count(node): |
+ raise exceptions.RefactoringError('Use function should not ' |
+ 'be used on generators.') |
+ returns = _return_count(node) |
+ if returns > 1: |
+ raise exceptions.RefactoringError('usefunction: Function has more ' |
+ 'than one return statement.') |
+ if returns == 1 and not _returns_last(node): |
+ raise exceptions.RefactoringError('usefunction: return should ' |
+ 'be the last statement.') |
+ |
+ def get_changes(self, resources=None, |
+ task_handle=taskhandle.NullTaskHandle()): |
+ if resources is None: |
+ resources = self.project.get_python_files() |
+ changes = change.ChangeSet('Using function <%s>' % |
+ self.pyfunction.get_name()) |
+ if self.resource in resources: |
+ newresources = list(resources) |
+ newresources.remove(self.resource) |
+ for c in self._restructure(newresources, task_handle).changes: |
+ changes.add_change(c) |
+ if self.resource in resources: |
+ for c in self._restructure([self.resource], task_handle, |
+ others=False).changes: |
+ changes.add_change(c) |
+ return changes |
+ |
+ def get_function_name(self): |
+ return self.pyfunction.get_name() |
+ |
+ def _restructure(self, resources, task_handle, others=True): |
+ pattern = self._make_pattern() |
+ goal = self._make_goal(import_=others) |
+ imports = None |
+ if others: |
+ imports = ['import %s' % self._module_name()] |
+ |
+ body_region = sourceutils.get_body_region(self.pyfunction) |
+ args_value = {'skip': (self.resource, body_region)} |
+ args = {'': args_value} |
+ |
+ restructuring = restructure.Restructure( |
+ self.project, pattern, goal, args=args, imports=imports) |
+ return restructuring.get_changes(resources=resources, |
+ task_handle=task_handle) |
+ |
+ def _find_temps(self): |
+ return find_temps(self.project, self._get_body()) |
+ |
+ def _module_name(self): |
+ return libutils.modname(self.resource) |
+ |
+ def _make_pattern(self): |
+ params = self.pyfunction.get_param_names() |
+ body = self._get_body() |
+ body = restructure.replace(body, 'return', 'pass') |
+ wildcards = list(params) |
+ wildcards.extend(self._find_temps()) |
+ if self._does_return(): |
+ if self._is_expression(): |
+ replacement = '${%s}' % self._rope_returned |
+ else: |
+ replacement = '%s = ${%s}' % (self._rope_result, |
+ self._rope_returned) |
+ body = restructure.replace( |
+ body, 'return ${%s}' % self._rope_returned, |
+ replacement) |
+ wildcards.append(self._rope_result) |
+ return similarfinder.make_pattern(body, wildcards) |
+ |
+ def _get_body(self): |
+ return sourceutils.get_body(self.pyfunction) |
+ |
+ def _make_goal(self, import_=False): |
+ params = self.pyfunction.get_param_names() |
+ function_name = self.pyfunction.get_name() |
+ if import_: |
+ function_name = self._module_name() + '.' + function_name |
+ goal = '%s(%s)' % (function_name, |
+ ', ' .join(('${%s}' % p) for p in params)) |
+ if self._does_return() and not self._is_expression(): |
+ goal = '${%s} = %s' % (self._rope_result, goal) |
+ return goal |
+ |
+ def _does_return(self): |
+ body = self._get_body() |
+ removed_return = restructure.replace(body, 'return ${result}', '') |
+ return removed_return != body |
+ |
+ def _is_expression(self): |
+ return len(self.pyfunction.get_ast().body) == 1 |
+ |
+ _rope_result = '_rope__result' |
+ _rope_returned = '_rope__returned' |
+ |
+ |
+def find_temps(project, code): |
+ code = 'def f():\n' + sourceutils.indent_lines(code, 4) |
+ pymodule = libutils.get_string_module(project, code) |
+ result = [] |
+ function_scope = pymodule.get_scope().get_scopes()[0] |
+ for name, pyname in function_scope.get_names().items(): |
+ if isinstance(pyname, pynames.AssignedName): |
+ result.append(name) |
+ return result |
+ |
+ |
+def _returns_last(node): |
+ return node.body and isinstance(node.body[-1], ast.Return) |
+ |
+ |
+def _yield_count(node): |
+ visitor = _ReturnOrYieldFinder() |
+ visitor.start_walking(node) |
+ return visitor.yields |
+ |
+ |
+def _return_count(node): |
+ visitor = _ReturnOrYieldFinder() |
+ visitor.start_walking(node) |
+ return visitor.returns |
+ |
+ |
+class _ReturnOrYieldFinder(object): |
+ |
+ def __init__(self): |
+ self.returns = 0 |
+ self.yields = 0 |
+ |
+ def _Return(self, node): |
+ self.returns += 1 |
+ |
+ def _Yield(self, node): |
+ self.yields += 1 |
+ |
+ def _FunctionDef(self, node): |
+ pass |
+ |
+ def _ClassDef(self, node): |
+ pass |
+ |
+ def start_walking(self, node): |
+ nodes = [node] |
+ if isinstance(node, ast.FunctionDef): |
+ nodes = ast.get_child_nodes(node) |
+ for child in nodes: |
+ ast.walk(child, self) |