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/occurrences.py
1419 views
1
import re
2
3
import rope.base.pynames
4
from rope.base import pynames, pyobjects, codeanalyze, evaluate, exceptions, utils, worder
5
6
7
class Finder(object):
8
"""For finding occurrences of a name
9
10
The constructor takes a `filters` argument. It should be a list
11
of functions that take a single argument. For each possible
12
occurrence, these functions are called in order with the an
13
instance of `Occurrence`:
14
15
* If it returns `None` other filters are tried.
16
* If it returns `True`, the occurrence will be a match.
17
* If it returns `False`, the occurrence will be skipped.
18
* If all of the filters return `None`, it is skipped also.
19
20
"""
21
22
def __init__(self, pycore, name, filters=[lambda o: True], docs=False):
23
self.pycore = pycore
24
self.name = name
25
self.docs = docs
26
self.filters = filters
27
self._textual_finder = _TextualFinder(name, docs=docs)
28
29
def find_occurrences(self, resource=None, pymodule=None):
30
"""Generate `Occurrence` instances"""
31
tools = _OccurrenceToolsCreator(self.pycore, resource=resource,
32
pymodule=pymodule, docs=self.docs)
33
for offset in self._textual_finder.find_offsets(tools.source_code):
34
occurrence = Occurrence(tools, offset)
35
for filter in self.filters:
36
result = filter(occurrence)
37
if result is None:
38
continue
39
if result:
40
yield occurrence
41
break
42
43
44
def create_finder(pycore, name, pyname, only_calls=False, imports=True,
45
unsure=None, docs=False, instance=None, in_hierarchy=False):
46
"""A factory for `Finder`
47
48
Based on the arguments it creates a list of filters. `instance`
49
argument is needed only when you want implicit interfaces to be
50
considered.
51
52
"""
53
pynames = set([pyname])
54
filters = []
55
if only_calls:
56
filters.append(CallsFilter())
57
if not imports:
58
filters.append(NoImportsFilter())
59
if isinstance(instance, rope.base.pynames.ParameterName):
60
for pyobject in instance.get_objects():
61
try:
62
pynames.add(pyobject[name])
63
except exceptions.AttributeNotFoundError:
64
pass
65
for pyname in pynames:
66
filters.append(PyNameFilter(pyname))
67
if in_hierarchy:
68
filters.append(InHierarchyFilter(pyname))
69
if unsure:
70
filters.append(UnsureFilter(unsure))
71
return Finder(pycore, name, filters=filters, docs=docs)
72
73
74
class Occurrence(object):
75
76
def __init__(self, tools, offset):
77
self.tools = tools
78
self.offset = offset
79
self.resource = tools.resource
80
81
@utils.saveit
82
def get_word_range(self):
83
return self.tools.word_finder.get_word_range(self.offset)
84
85
@utils.saveit
86
def get_primary_range(self):
87
return self.tools.word_finder.get_primary_range(self.offset)
88
89
@utils.saveit
90
def get_pyname(self):
91
try:
92
return self.tools.name_finder.get_pyname_at(self.offset)
93
except exceptions.BadIdentifierError:
94
pass
95
96
@utils.saveit
97
def get_primary_and_pyname(self):
98
try:
99
return self.tools.name_finder.get_primary_and_pyname_at(self.offset)
100
except exceptions.BadIdentifierError:
101
pass
102
103
@utils.saveit
104
def is_in_import_statement(self):
105
return (self.tools.word_finder.is_from_statement(self.offset) or
106
self.tools.word_finder.is_import_statement(self.offset))
107
108
def is_called(self):
109
return self.tools.word_finder.is_a_function_being_called(self.offset)
110
111
def is_defined(self):
112
return self.tools.word_finder.is_a_class_or_function_name_in_header(self.offset)
113
114
def is_a_fixed_primary(self):
115
return self.tools.word_finder.is_a_class_or_function_name_in_header(self.offset) or \
116
self.tools.word_finder.is_a_name_after_from_import(self.offset)
117
118
def is_written(self):
119
return self.tools.word_finder.is_assigned_here(self.offset)
120
121
def is_unsure(self):
122
return unsure_pyname(self.get_pyname())
123
124
@property
125
@utils.saveit
126
def lineno(self):
127
offset = self.get_word_range()[0]
128
return self.tools.pymodule.lines.get_line_number(offset)
129
130
131
def same_pyname(expected, pyname):
132
"""Check whether `expected` and `pyname` are the same"""
133
if expected is None or pyname is None:
134
return False
135
if expected == pyname:
136
return True
137
if type(expected) not in (pynames.ImportedModule, pynames.ImportedName) and \
138
type(pyname) not in (pynames.ImportedModule, pynames.ImportedName):
139
return False
140
return expected.get_definition_location() == pyname.get_definition_location() and \
141
expected.get_object() == pyname.get_object()
142
143
def unsure_pyname(pyname, unbound=True):
144
"""Return `True` if we don't know what this name references"""
145
if pyname is None:
146
return True
147
if unbound and not isinstance(pyname, pynames.UnboundName):
148
return False
149
if pyname.get_object() == pyobjects.get_unknown():
150
return True
151
152
153
class PyNameFilter(object):
154
"""For finding occurrences of a name"""
155
156
def __init__(self, pyname):
157
self.pyname = pyname
158
159
def __call__(self, occurrence):
160
if same_pyname(self.pyname, occurrence.get_pyname()):
161
return True
162
163
164
class InHierarchyFilter(object):
165
"""For finding occurrences of a name"""
166
167
def __init__(self, pyname, implementations_only=False):
168
self.pyname = pyname
169
self.impl_only = implementations_only
170
self.pyclass = self._get_containing_class(pyname)
171
if self.pyclass is not None:
172
self.name = pyname.get_object().get_name()
173
self.roots = self._get_root_classes(self.pyclass, self.name)
174
else:
175
self.roots = None
176
177
def __call__(self, occurrence):
178
if self.roots is None:
179
return
180
pyclass = self._get_containing_class(occurrence.get_pyname())
181
if pyclass is not None:
182
roots = self._get_root_classes(pyclass, self.name)
183
if self.roots.intersection(roots):
184
return True
185
186
def _get_containing_class(self, pyname):
187
if isinstance(pyname, pynames.DefinedName):
188
scope = pyname.get_object().get_scope()
189
parent = scope.parent
190
if parent is not None and parent.get_kind() == 'Class':
191
return parent.pyobject
192
193
def _get_root_classes(self, pyclass, name):
194
if self.impl_only and pyclass == self.pyclass:
195
return set([pyclass])
196
result = set()
197
for superclass in pyclass.get_superclasses():
198
if name in superclass:
199
result.update(self._get_root_classes(superclass, name))
200
if not result:
201
return set([pyclass])
202
return result
203
204
205
class UnsureFilter(object):
206
207
def __init__(self, unsure):
208
self.unsure = unsure
209
210
def __call__(self, occurrence):
211
if occurrence.is_unsure() and self.unsure(occurrence):
212
return True
213
214
215
class NoImportsFilter(object):
216
217
def __call__(self, occurrence):
218
if occurrence.is_in_import_statement():
219
return False
220
221
222
class CallsFilter(object):
223
224
def __call__(self, occurrence):
225
if not occurrence.is_called():
226
return False
227
228
229
class _TextualFinder(object):
230
231
def __init__(self, name, docs=False):
232
self.name = name
233
self.docs = docs
234
self.comment_pattern = _TextualFinder.any('comment', [r'#[^\n]*'])
235
self.string_pattern = _TextualFinder.any(
236
'string', [codeanalyze.get_string_pattern()])
237
self.pattern = self._get_occurrence_pattern(self.name)
238
239
def find_offsets(self, source):
240
if not self._fast_file_query(source):
241
return
242
if self.docs:
243
searcher = self._normal_search
244
else:
245
searcher = self._re_search
246
for matched in searcher(source):
247
yield matched
248
249
def _re_search(self, source):
250
for match in self.pattern.finditer(source):
251
for key, value in match.groupdict().items():
252
if value and key == 'occurrence':
253
yield match.start(key)
254
255
def _normal_search(self, source):
256
current = 0
257
while True:
258
try:
259
found = source.index(self.name, current)
260
current = found + len(self.name)
261
if (found == 0 or not self._is_id_char(source[found - 1])) and \
262
(current == len(source) or not self._is_id_char(source[current])):
263
yield found
264
except ValueError:
265
break
266
267
def _is_id_char(self, c):
268
return c.isalnum() or c == '_'
269
270
def _fast_file_query(self, source):
271
try:
272
source.index(self.name)
273
return True
274
except ValueError:
275
return False
276
277
def _get_source(self, resource, pymodule):
278
if resource is not None:
279
return resource.read()
280
else:
281
return pymodule.source_code
282
283
def _get_occurrence_pattern(self, name):
284
occurrence_pattern = _TextualFinder.any('occurrence',
285
['\\b' + name + '\\b'])
286
pattern = re.compile(occurrence_pattern + '|' + self.comment_pattern +
287
'|' + self.string_pattern)
288
return pattern
289
290
@staticmethod
291
def any(name, list_):
292
return '(?P<%s>' % name + '|'.join(list_) + ')'
293
294
295
class _OccurrenceToolsCreator(object):
296
297
def __init__(self, pycore, resource=None, pymodule=None, docs=False):
298
self.pycore = pycore
299
self.__resource = resource
300
self.__pymodule = pymodule
301
self.docs = docs
302
303
@property
304
@utils.saveit
305
def name_finder(self):
306
return evaluate.ScopeNameFinder(self.pymodule)
307
308
@property
309
@utils.saveit
310
def source_code(self):
311
if self.__resource is not None:
312
return self.resource.read()
313
else:
314
return self.pymodule.source_code
315
316
@property
317
@utils.saveit
318
def word_finder(self):
319
return worder.Worder(self.source_code, self.docs)
320
321
@property
322
@utils.saveit
323
def resource(self):
324
if self.__resource is not None:
325
return self.__resource
326
if self.__pymodule is not None:
327
return self.__pymodule.resource
328
329
@property
330
@utils.saveit
331
def pymodule(self):
332
if self.__pymodule is not None:
333
return self.__pymodule
334
return self.pycore.resource_to_pyobject(self.resource)
335
336