Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
marvel
GitHub Repository: marvel/qnf
Path: blob/master/elisp/emacs-for-python/rope-dist/rope/contrib/generate.py
1428 views
1
import rope.base.evaluate
2
from rope.base import change, pyobjects, exceptions, pynames, worder, codeanalyze
3
from rope.refactor import sourceutils, importutils, functionutils, suites
4
5
6
def create_generate(kind, project, resource, offset):
7
"""A factory for creating `Generate` objects
8
9
`kind` can be 'variable', 'function', 'class', 'module' or
10
'package'.
11
12
"""
13
generate = eval('Generate' + kind.title())
14
return generate(project, resource, offset)
15
16
17
def create_module(project, name, sourcefolder=None):
18
"""Creates a module and returns a `rope.base.resources.File`"""
19
if sourcefolder is None:
20
sourcefolder = project.root
21
packages = name.split('.')
22
parent = sourcefolder
23
for package in packages[:-1]:
24
parent = parent.get_child(package)
25
return parent.create_file(packages[-1] + '.py')
26
27
def create_package(project, name, sourcefolder=None):
28
"""Creates a package and returns a `rope.base.resources.Folder`"""
29
if sourcefolder is None:
30
sourcefolder = project.root
31
packages = name.split('.')
32
parent = sourcefolder
33
for package in packages[:-1]:
34
parent = parent.get_child(package)
35
made_packages = parent.create_folder(packages[-1])
36
made_packages.create_file('__init__.py')
37
return made_packages
38
39
40
class _Generate(object):
41
42
def __init__(self, project, resource, offset):
43
self.project = project
44
self.resource = resource
45
self.info = self._generate_info(project, resource, offset)
46
self.name = self.info.get_name()
47
self._check_exceptional_conditions()
48
49
def _generate_info(self, project, resource, offset):
50
return _GenerationInfo(project.pycore, resource, offset)
51
52
def _check_exceptional_conditions(self):
53
if self.info.element_already_exists():
54
raise exceptions.RefactoringError(
55
'Element <%s> already exists.' % self.name)
56
if not self.info.primary_is_found():
57
raise exceptions.RefactoringError(
58
'Cannot determine the scope <%s> should be defined in.' % self.name)
59
60
def get_changes(self):
61
changes = change.ChangeSet('Generate %s <%s>' %
62
(self._get_element_kind(), self.name))
63
indents = self.info.get_scope_indents()
64
blanks = self.info.get_blank_lines()
65
base_definition = sourceutils.fix_indentation(self._get_element(), indents)
66
definition = '\n' * blanks[0] + base_definition + '\n' * blanks[1]
67
68
resource = self.info.get_insertion_resource()
69
start, end = self.info.get_insertion_offsets()
70
71
collector = codeanalyze.ChangeCollector(resource.read())
72
collector.add_change(start, end, definition)
73
changes.add_change(change.ChangeContents(
74
resource, collector.get_changed()))
75
return changes
76
77
def get_location(self):
78
return (self.info.get_insertion_resource(),
79
self.info.get_insertion_lineno())
80
81
def _get_element_kind(self):
82
raise NotImplementedError()
83
84
def _get_element(self):
85
raise NotImplementedError()
86
87
88
class GenerateFunction(_Generate):
89
90
def _generate_info(self, project, resource, offset):
91
return _FunctionGenerationInfo(project.pycore, resource, offset)
92
93
def _get_element(self):
94
decorator = ''
95
args = []
96
if self.info.is_static_method():
97
decorator = '@staticmethod\n'
98
if self.info.is_method() or self.info.is_constructor() or \
99
self.info.is_instance():
100
args.append('self')
101
args.extend(self.info.get_passed_args())
102
definition = '%sdef %s(%s):\n pass\n' % (decorator, self.name,
103
', '.join(args))
104
return definition
105
106
def _get_element_kind(self):
107
return 'Function'
108
109
110
class GenerateVariable(_Generate):
111
112
def _get_element(self):
113
return '%s = None\n' % self.name
114
115
def _get_element_kind(self):
116
return 'Variable'
117
118
119
class GenerateClass(_Generate):
120
121
def _get_element(self):
122
return 'class %s(object):\n pass\n' % self.name
123
124
def _get_element_kind(self):
125
return 'Class'
126
127
128
class GenerateModule(_Generate):
129
130
def get_changes(self):
131
package = self.info.get_package()
132
changes = change.ChangeSet('Generate Module <%s>' % self.name)
133
new_resource = self.project.get_file('%s/%s.py' % (package.path, self.name))
134
if new_resource.exists():
135
raise exceptions.RefactoringError(
136
'Module <%s> already exists' % new_resource.path)
137
changes.add_change(change.CreateResource(new_resource))
138
changes.add_change(_add_import_to_module(
139
self.project.pycore, self.resource, new_resource))
140
return changes
141
142
def get_location(self):
143
package = self.info.get_package()
144
return (package.get_child('%s.py' % self.name) , 1)
145
146
147
class GeneratePackage(_Generate):
148
149
def get_changes(self):
150
package = self.info.get_package()
151
changes = change.ChangeSet('Generate Package <%s>' % self.name)
152
new_resource = self.project.get_folder('%s/%s' % (package.path, self.name))
153
if new_resource.exists():
154
raise exceptions.RefactoringError(
155
'Package <%s> already exists' % new_resource.path)
156
changes.add_change(change.CreateResource(new_resource))
157
changes.add_change(_add_import_to_module(
158
self.project.pycore, self.resource, new_resource))
159
child = self.project.get_folder(package.path + '/' + self.name)
160
changes.add_change(change.CreateFile(child, '__init__.py'))
161
return changes
162
163
def get_location(self):
164
package = self.info.get_package()
165
child = package.get_child(self.name)
166
return (child.get_child('__init__.py') , 1)
167
168
169
def _add_import_to_module(pycore, resource, imported):
170
pymodule = pycore.resource_to_pyobject(resource)
171
import_tools = importutils.ImportTools(pycore)
172
module_imports = import_tools.module_imports(pymodule)
173
module_name = pycore.modname(imported)
174
new_import = importutils.NormalImport(((module_name, None), ))
175
module_imports.add_import(new_import)
176
return change.ChangeContents(resource, module_imports.get_changed_source())
177
178
179
class _GenerationInfo(object):
180
181
def __init__(self, pycore, resource, offset):
182
self.pycore = pycore
183
self.resource = resource
184
self.offset = offset
185
self.source_pymodule = self.pycore.resource_to_pyobject(resource)
186
finder = rope.base.evaluate.ScopeNameFinder(self.source_pymodule)
187
self.primary, self.pyname = finder.get_primary_and_pyname_at(offset)
188
self._init_fields()
189
190
def _init_fields(self):
191
self.source_scope = self._get_source_scope()
192
self.goal_scope = self._get_goal_scope()
193
self.goal_pymodule = self._get_goal_module(self.goal_scope)
194
195
def _get_goal_scope(self):
196
if self.primary is None:
197
return self._get_source_scope()
198
pyobject = self.primary.get_object()
199
if isinstance(pyobject, pyobjects.PyDefinedObject):
200
return pyobject.get_scope()
201
elif isinstance(pyobject.get_type(), pyobjects.PyClass):
202
return pyobject.get_type().get_scope()
203
204
def _get_goal_module(self, scope):
205
if scope is None:
206
return
207
while scope.parent is not None:
208
scope = scope.parent
209
return scope.pyobject
210
211
def _get_source_scope(self):
212
module_scope = self.source_pymodule.get_scope()
213
lineno = self.source_pymodule.lines.get_line_number(self.offset)
214
return module_scope.get_inner_scope_for_line(lineno)
215
216
def get_insertion_lineno(self):
217
lines = self.goal_pymodule.lines
218
if self.goal_scope == self.source_scope:
219
line_finder = self.goal_pymodule.logical_lines
220
lineno = lines.get_line_number(self.offset)
221
lineno = line_finder.logical_line_in(lineno)[0]
222
root = suites.ast_suite_tree(self.goal_scope.pyobject.get_ast())
223
suite = root.find_suite(lineno)
224
indents = sourceutils.get_indents(lines, lineno)
225
while self.get_scope_indents() < indents:
226
lineno = suite.get_start()
227
indents = sourceutils.get_indents(lines, lineno)
228
suite = suite.parent
229
return lineno
230
else:
231
return min(self.goal_scope.get_end() + 1, lines.length())
232
233
def get_insertion_resource(self):
234
return self.goal_pymodule.get_resource()
235
236
def get_insertion_offsets(self):
237
if self.goal_scope.get_kind() == 'Class':
238
start, end = sourceutils.get_body_region(self.goal_scope.pyobject)
239
if self.goal_pymodule.source_code[start:end].strip() == 'pass':
240
return start, end
241
lines = self.goal_pymodule.lines
242
start = lines.get_line_start(self.get_insertion_lineno())
243
return (start, start)
244
245
def get_scope_indents(self):
246
if self.goal_scope.get_kind() == 'Module':
247
return 0
248
return sourceutils.get_indents(self.goal_pymodule.lines,
249
self.goal_scope.get_start()) + 4
250
251
def get_blank_lines(self):
252
if self.goal_scope.get_kind() == 'Module':
253
base_blanks = 2
254
if self.goal_pymodule.source_code.strip() == '':
255
base_blanks = 0
256
if self.goal_scope.get_kind() == 'Class':
257
base_blanks = 1
258
if self.goal_scope.get_kind() == 'Function':
259
base_blanks = 0
260
if self.goal_scope == self.source_scope:
261
return (0, base_blanks)
262
return (base_blanks, 0)
263
264
def get_package(self):
265
primary = self.primary
266
if self.primary is None:
267
return self.pycore.get_source_folders()[0]
268
if isinstance(primary.get_object(), pyobjects.PyPackage):
269
return primary.get_object().get_resource()
270
raise exceptions.RefactoringError(
271
'A module/package can be only created in a package.')
272
273
def primary_is_found(self):
274
return self.goal_scope is not None
275
276
def element_already_exists(self):
277
if self.pyname is None or isinstance(self.pyname, pynames.UnboundName):
278
return False
279
return self.get_name() in self.goal_scope.get_defined_names()
280
281
def get_name(self):
282
return worder.get_name_at(self.resource, self.offset)
283
284
285
class _FunctionGenerationInfo(_GenerationInfo):
286
287
def _get_goal_scope(self):
288
if self.is_constructor():
289
return self.pyname.get_object().get_scope()
290
if self.is_instance():
291
return self.pyname.get_object().get_type().get_scope()
292
if self.primary is None:
293
return self._get_source_scope()
294
pyobject = self.primary.get_object()
295
if isinstance(pyobject, pyobjects.PyDefinedObject):
296
return pyobject.get_scope()
297
elif isinstance(pyobject.get_type(), pyobjects.PyClass):
298
return pyobject.get_type().get_scope()
299
300
def element_already_exists(self):
301
if self.pyname is None or isinstance(self.pyname, pynames.UnboundName):
302
return False
303
return self.get_name() in self.goal_scope.get_defined_names()
304
305
def is_static_method(self):
306
return self.primary is not None and \
307
isinstance(self.primary.get_object(), pyobjects.PyClass)
308
309
def is_method(self):
310
return self.primary is not None and \
311
isinstance(self.primary.get_object().get_type(), pyobjects.PyClass)
312
313
def is_constructor(self):
314
return self.pyname is not None and \
315
isinstance(self.pyname.get_object(), pyobjects.PyClass)
316
317
def is_instance(self):
318
if self.pyname is None:
319
return False
320
pyobject = self.pyname.get_object()
321
return isinstance(pyobject.get_type(), pyobjects.PyClass)
322
323
def get_name(self):
324
if self.is_constructor():
325
return '__init__'
326
if self.is_instance():
327
return '__call__'
328
return worder.get_name_at(self.resource, self.offset)
329
330
def get_passed_args(self):
331
result = []
332
source = self.source_pymodule.source_code
333
finder = worder.Worder(source)
334
if finder.is_a_function_being_called(self.offset):
335
start, end = finder.get_primary_range(self.offset)
336
parens_start, parens_end = finder.get_word_parens_range(end - 1)
337
call = source[start:parens_end]
338
parser = functionutils._FunctionParser(call, False)
339
args, keywords = parser.get_parameters()
340
for arg in args:
341
if self._is_id(arg):
342
result.append(arg)
343
else:
344
result.append('arg%d' % len(result))
345
for name, value in keywords:
346
result.append(name)
347
return result
348
349
def _is_id(self, arg):
350
def id_or_underline(c):
351
return c.isalpha() or c == '_'
352
for c in arg:
353
if not id_or_underline(c) and not c.isdigit():
354
return False
355
return id_or_underline(arg[0])
356
357