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/encapsulate_field.py
1421 views
1
from rope.base import pynames, taskhandle, evaluate, exceptions, worder, utils
2
from rope.base.change import ChangeSet, ChangeContents
3
from rope.refactor import sourceutils, occurrences
4
5
6
class EncapsulateField(object):
7
8
def __init__(self, project, resource, offset):
9
self.pycore = project.pycore
10
self.name = worder.get_name_at(resource, offset)
11
this_pymodule = self.pycore.resource_to_pyobject(resource)
12
self.pyname = evaluate.eval_location(this_pymodule, offset)
13
if not self._is_an_attribute(self.pyname):
14
raise exceptions.RefactoringError(
15
'Encapsulate field should be performed on class attributes.')
16
self.resource = self.pyname.get_definition_location()[0].get_resource()
17
18
def get_changes(self, getter=None, setter=None, resources=None,
19
task_handle=taskhandle.NullTaskHandle()):
20
"""Get the changes this refactoring makes
21
22
If `getter` is not `None`, that will be the name of the
23
getter, otherwise ``get_${field_name}`` will be used. The
24
same is true for `setter` and if it is None set_${field_name} is
25
used.
26
27
`resources` can be a list of `rope.base.resource.File`\s that
28
the refactoring should be applied on; if `None` all python
29
files in the project are searched.
30
31
"""
32
if resources is None:
33
resources = self.pycore.get_python_files()
34
changes = ChangeSet('Encapsulate field <%s>' % self.name)
35
job_set = task_handle.create_jobset('Collecting Changes',
36
len(resources))
37
if getter is None:
38
getter = 'get_' + self.name
39
if setter is None:
40
setter = 'set_' + self.name
41
renamer = GetterSetterRenameInModule(
42
self.pycore, self.name, self.pyname, getter, setter)
43
for file in resources:
44
job_set.started_job(file.path)
45
if file == self.resource:
46
result = self._change_holding_module(changes, renamer,
47
getter, setter)
48
changes.add_change(ChangeContents(self.resource, result))
49
else:
50
result = renamer.get_changed_module(file)
51
if result is not None:
52
changes.add_change(ChangeContents(file, result))
53
job_set.finished_job()
54
return changes
55
56
def get_field_name(self):
57
"""Get the name of the field to be encapsulated"""
58
return self.name
59
60
def _is_an_attribute(self, pyname):
61
if pyname is not None and isinstance(pyname, pynames.AssignedName):
62
pymodule, lineno = self.pyname.get_definition_location()
63
scope = pymodule.get_scope().\
64
get_inner_scope_for_line(lineno)
65
if scope.get_kind() == 'Class':
66
return pyname in scope.get_names().values()
67
parent = scope.parent
68
if parent is not None and parent.get_kind() == 'Class':
69
return pyname in parent.get_names().values()
70
return False
71
72
def _get_defining_class_scope(self):
73
defining_scope = self._get_defining_scope()
74
if defining_scope.get_kind() == 'Function':
75
defining_scope = defining_scope.parent
76
return defining_scope
77
78
def _get_defining_scope(self):
79
pymodule, line = self.pyname.get_definition_location()
80
return pymodule.get_scope().get_inner_scope_for_line(line)
81
82
def _change_holding_module(self, changes, renamer, getter, setter):
83
pymodule = self.pycore.resource_to_pyobject(self.resource)
84
class_scope = self._get_defining_class_scope()
85
defining_object = self._get_defining_scope().pyobject
86
start, end = sourceutils.get_body_region(defining_object)
87
88
new_source = renamer.get_changed_module(pymodule=pymodule,
89
skip_start=start, skip_end=end)
90
if new_source is not None:
91
pymodule = self.pycore.get_string_module(new_source, self.resource)
92
class_scope = pymodule.get_scope().\
93
get_inner_scope_for_line(class_scope.get_start())
94
indents = sourceutils.get_indent(self.pycore) * ' '
95
getter = 'def %s(self):\n%sreturn self.%s' % \
96
(getter, indents, self.name)
97
setter = 'def %s(self, value):\n%sself.%s = value' % \
98
(setter, indents, self.name)
99
new_source = sourceutils.add_methods(pymodule, class_scope,
100
[getter, setter])
101
return new_source
102
103
104
class GetterSetterRenameInModule(object):
105
106
def __init__(self, pycore, name, pyname, getter, setter):
107
self.pycore = pycore
108
self.name = name
109
self.finder = occurrences.create_finder(pycore, name, pyname)
110
self.getter = getter
111
self.setter = setter
112
113
def get_changed_module(self, resource=None, pymodule=None,
114
skip_start=0, skip_end=0):
115
change_finder = _FindChangesForModule(self, resource, pymodule,
116
skip_start, skip_end)
117
return change_finder.get_changed_module()
118
119
120
class _FindChangesForModule(object):
121
122
def __init__(self, finder, resource, pymodule, skip_start, skip_end):
123
self.pycore = finder.pycore
124
self.finder = finder.finder
125
self.getter = finder.getter
126
self.setter = finder.setter
127
self.resource = resource
128
self.pymodule = pymodule
129
self.last_modified = 0
130
self.last_set = None
131
self.set_index = None
132
self.skip_start = skip_start
133
self.skip_end = skip_end
134
135
def get_changed_module(self):
136
result = []
137
for occurrence in self.finder.find_occurrences(self.resource,
138
self.pymodule):
139
start, end = occurrence.get_word_range()
140
if self.skip_start <= start < self.skip_end:
141
continue
142
self._manage_writes(start, result)
143
result.append(self.source[self.last_modified:start])
144
if self._is_assigned_in_a_tuple_assignment(occurrence):
145
raise exceptions.RefactoringError(
146
'Cannot handle tuple assignments in encapsulate field.')
147
if occurrence.is_written():
148
assignment_type = self.worder.get_assignment_type(start)
149
if assignment_type == '=':
150
result.append(self.setter + '(')
151
else:
152
var_name = self.source[occurrence.get_primary_range()[0]:
153
start] + self.getter + '()'
154
result.append(self.setter + '(' + var_name
155
+ ' %s ' % assignment_type[:-1])
156
current_line = self.lines.get_line_number(start)
157
start_line, end_line = self.pymodule.logical_lines.\
158
logical_line_in(current_line)
159
self.last_set = self.lines.get_line_end(end_line)
160
end = self.source.index('=', end) + 1
161
self.set_index = len(result)
162
else:
163
result.append(self.getter + '()')
164
self.last_modified = end
165
if self.last_modified != 0:
166
self._manage_writes(len(self.source), result)
167
result.append(self.source[self.last_modified:])
168
return ''.join(result)
169
return None
170
171
def _manage_writes(self, offset, result):
172
if self.last_set is not None and self.last_set <= offset:
173
result.append(self.source[self.last_modified:self.last_set])
174
set_value = ''.join(result[self.set_index:]).strip()
175
del result[self.set_index:]
176
result.append(set_value + ')')
177
self.last_modified = self.last_set
178
self.last_set = None
179
180
def _is_assigned_in_a_tuple_assignment(self, occurance):
181
offset = occurance.get_word_range()[0]
182
return self.worder.is_assigned_in_a_tuple_assignment(offset)
183
184
@property
185
@utils.saveit
186
def source(self):
187
if self.resource is not None:
188
return self.resource.read()
189
else:
190
return self.pymodule.source_code
191
192
@property
193
@utils.saveit
194
def lines(self):
195
if self.pymodule is None:
196
self.pymodule = self.pycore.resource_to_pyobject(self.resource)
197
return self.pymodule.lines
198
199
@property
200
@utils.saveit
201
def worder(self):
202
return worder.Worder(self.source)
203
204