Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
marvel
GitHub Repository: marvel/qnf
Path: blob/master/elisp/emacs-for-python/rope-dist/rope/refactor/usefunction.py
1415 views
1
from rope.base import (change, taskhandle, evaluate,
2
exceptions, pyobjects, pynames, ast)
3
from rope.refactor import restructure, sourceutils, similarfinder, importutils
4
5
6
class UseFunction(object):
7
"""Try to use a function wherever possible"""
8
9
def __init__(self, project, resource, offset):
10
self.project = project
11
self.offset = offset
12
this_pymodule = project.pycore.resource_to_pyobject(resource)
13
pyname = evaluate.eval_location(this_pymodule, offset)
14
if pyname is None:
15
raise exceptions.RefactoringError('Unresolvable name selected')
16
self.pyfunction = pyname.get_object()
17
if not isinstance(self.pyfunction, pyobjects.PyFunction) or \
18
not isinstance(self.pyfunction.parent, pyobjects.PyModule):
19
raise exceptions.RefactoringError(
20
'Use function works for global functions, only.')
21
self.resource = self.pyfunction.get_module().get_resource()
22
self._check_returns()
23
24
def _check_returns(self):
25
node = self.pyfunction.get_ast()
26
if _yield_count(node):
27
raise exceptions.RefactoringError('Use function should not '
28
'be used on generators.')
29
returns = _return_count(node)
30
if returns > 1:
31
raise exceptions.RefactoringError('usefunction: Function has more '
32
'than one return statement.')
33
if returns == 1 and not _returns_last(node):
34
raise exceptions.RefactoringError('usefunction: return should '
35
'be the last statement.')
36
37
def get_changes(self, resources=None,
38
task_handle=taskhandle.NullTaskHandle()):
39
if resources is None:
40
resources = self.project.pycore.get_python_files()
41
changes = change.ChangeSet('Using function <%s>' %
42
self.pyfunction.get_name())
43
if self.resource in resources:
44
newresources = list(resources)
45
newresources.remove(self.resource)
46
for c in self._restructure(newresources, task_handle).changes:
47
changes.add_change(c)
48
if self.resource in resources:
49
for c in self._restructure([self.resource], task_handle,
50
others=False).changes:
51
changes.add_change(c)
52
return changes
53
54
def get_function_name(self):
55
return self.pyfunction.get_name()
56
57
def _restructure(self, resources, task_handle, others=True):
58
body = self._get_body()
59
pattern = self._make_pattern()
60
goal = self._make_goal(import_=others)
61
imports = None
62
if others:
63
imports = ['import %s' % self._module_name()]
64
65
body_region = sourceutils.get_body_region(self.pyfunction)
66
args_value = {'skip': (self.resource, body_region)}
67
args = {'': args_value}
68
69
restructuring = restructure.Restructure(
70
self.project, pattern, goal, args=args, imports=imports)
71
return restructuring.get_changes(resources=resources,
72
task_handle=task_handle)
73
74
def _find_temps(self):
75
return find_temps(self.project, self._get_body())
76
77
def _module_name(self):
78
return self.project.pycore.modname(self.resource)
79
80
def _make_pattern(self):
81
params = self.pyfunction.get_param_names()
82
body = self._get_body()
83
body = restructure.replace(body, 'return', 'pass')
84
wildcards = list(params)
85
wildcards.extend(self._find_temps())
86
if self._does_return():
87
if self._is_expression():
88
replacement = '${%s}' % self._rope_returned
89
else:
90
replacement = '%s = ${%s}' % (self._rope_result,
91
self._rope_returned)
92
body = restructure.replace(
93
body, 'return ${%s}' % self._rope_returned,
94
replacement)
95
wildcards.append(self._rope_result)
96
return similarfinder.make_pattern(body, wildcards)
97
98
def _get_body(self):
99
return sourceutils.get_body(self.pyfunction)
100
101
def _make_goal(self, import_=False):
102
params = self.pyfunction.get_param_names()
103
function_name = self.pyfunction.get_name()
104
if import_:
105
function_name = self._module_name() + '.' + function_name
106
goal = '%s(%s)' % (function_name,
107
', ' .join(('${%s}' % p) for p in params))
108
if self._does_return() and not self._is_expression():
109
goal = '${%s} = %s' % (self._rope_result, goal)
110
return goal
111
112
def _does_return(self):
113
body = self._get_body()
114
removed_return = restructure.replace(body, 'return ${result}', '')
115
return removed_return != body
116
117
def _is_expression(self):
118
return len(self.pyfunction.get_ast().body) == 1
119
120
_rope_result = '_rope__result'
121
_rope_returned = '_rope__returned'
122
123
124
def find_temps(project, code):
125
code = 'def f():\n' + sourceutils.indent_lines(code, 4)
126
pymodule = project.pycore.get_string_module(code)
127
result = []
128
function_scope = pymodule.get_scope().get_scopes()[0]
129
for name, pyname in function_scope.get_names().items():
130
if isinstance(pyname, pynames.AssignedName):
131
result.append(name)
132
return result
133
134
135
def _returns_last(node):
136
return node.body and isinstance(node.body[-1], ast.Return)
137
138
def _yield_count(node):
139
visitor = _ReturnOrYieldFinder()
140
visitor.start_walking(node)
141
return visitor.yields
142
143
def _return_count(node):
144
visitor = _ReturnOrYieldFinder()
145
visitor.start_walking(node)
146
return visitor.returns
147
148
class _ReturnOrYieldFinder(object):
149
150
def __init__(self):
151
self.returns = 0
152
self.yields = 0
153
154
def _Return(self, node):
155
self.returns += 1
156
157
def _Yield(self, node):
158
self.yields += 1
159
160
def _FunctionDef(self, node):
161
pass
162
163
def _ClassDef(self, node):
164
pass
165
166
def start_walking(self, node):
167
nodes = [node]
168
if isinstance(node, ast.FunctionDef):
169
nodes = ast.get_child_nodes(node)
170
for child in nodes:
171
ast.walk(child, self)
172
173