Path: blob/master/elisp/emacs-for-python/rope-dist/rope/refactor/occurrences.py
1419 views
import re12import rope.base.pynames3from rope.base import pynames, pyobjects, codeanalyze, evaluate, exceptions, utils, worder456class Finder(object):7"""For finding occurrences of a name89The constructor takes a `filters` argument. It should be a list10of functions that take a single argument. For each possible11occurrence, these functions are called in order with the an12instance of `Occurrence`:1314* If it returns `None` other filters are tried.15* If it returns `True`, the occurrence will be a match.16* If it returns `False`, the occurrence will be skipped.17* If all of the filters return `None`, it is skipped also.1819"""2021def __init__(self, pycore, name, filters=[lambda o: True], docs=False):22self.pycore = pycore23self.name = name24self.docs = docs25self.filters = filters26self._textual_finder = _TextualFinder(name, docs=docs)2728def find_occurrences(self, resource=None, pymodule=None):29"""Generate `Occurrence` instances"""30tools = _OccurrenceToolsCreator(self.pycore, resource=resource,31pymodule=pymodule, docs=self.docs)32for offset in self._textual_finder.find_offsets(tools.source_code):33occurrence = Occurrence(tools, offset)34for filter in self.filters:35result = filter(occurrence)36if result is None:37continue38if result:39yield occurrence40break414243def create_finder(pycore, name, pyname, only_calls=False, imports=True,44unsure=None, docs=False, instance=None, in_hierarchy=False):45"""A factory for `Finder`4647Based on the arguments it creates a list of filters. `instance`48argument is needed only when you want implicit interfaces to be49considered.5051"""52pynames = set([pyname])53filters = []54if only_calls:55filters.append(CallsFilter())56if not imports:57filters.append(NoImportsFilter())58if isinstance(instance, rope.base.pynames.ParameterName):59for pyobject in instance.get_objects():60try:61pynames.add(pyobject[name])62except exceptions.AttributeNotFoundError:63pass64for pyname in pynames:65filters.append(PyNameFilter(pyname))66if in_hierarchy:67filters.append(InHierarchyFilter(pyname))68if unsure:69filters.append(UnsureFilter(unsure))70return Finder(pycore, name, filters=filters, docs=docs)717273class Occurrence(object):7475def __init__(self, tools, offset):76self.tools = tools77self.offset = offset78self.resource = tools.resource7980@utils.saveit81def get_word_range(self):82return self.tools.word_finder.get_word_range(self.offset)8384@utils.saveit85def get_primary_range(self):86return self.tools.word_finder.get_primary_range(self.offset)8788@utils.saveit89def get_pyname(self):90try:91return self.tools.name_finder.get_pyname_at(self.offset)92except exceptions.BadIdentifierError:93pass9495@utils.saveit96def get_primary_and_pyname(self):97try:98return self.tools.name_finder.get_primary_and_pyname_at(self.offset)99except exceptions.BadIdentifierError:100pass101102@utils.saveit103def is_in_import_statement(self):104return (self.tools.word_finder.is_from_statement(self.offset) or105self.tools.word_finder.is_import_statement(self.offset))106107def is_called(self):108return self.tools.word_finder.is_a_function_being_called(self.offset)109110def is_defined(self):111return self.tools.word_finder.is_a_class_or_function_name_in_header(self.offset)112113def is_a_fixed_primary(self):114return self.tools.word_finder.is_a_class_or_function_name_in_header(self.offset) or \115self.tools.word_finder.is_a_name_after_from_import(self.offset)116117def is_written(self):118return self.tools.word_finder.is_assigned_here(self.offset)119120def is_unsure(self):121return unsure_pyname(self.get_pyname())122123@property124@utils.saveit125def lineno(self):126offset = self.get_word_range()[0]127return self.tools.pymodule.lines.get_line_number(offset)128129130def same_pyname(expected, pyname):131"""Check whether `expected` and `pyname` are the same"""132if expected is None or pyname is None:133return False134if expected == pyname:135return True136if type(expected) not in (pynames.ImportedModule, pynames.ImportedName) and \137type(pyname) not in (pynames.ImportedModule, pynames.ImportedName):138return False139return expected.get_definition_location() == pyname.get_definition_location() and \140expected.get_object() == pyname.get_object()141142def unsure_pyname(pyname, unbound=True):143"""Return `True` if we don't know what this name references"""144if pyname is None:145return True146if unbound and not isinstance(pyname, pynames.UnboundName):147return False148if pyname.get_object() == pyobjects.get_unknown():149return True150151152class PyNameFilter(object):153"""For finding occurrences of a name"""154155def __init__(self, pyname):156self.pyname = pyname157158def __call__(self, occurrence):159if same_pyname(self.pyname, occurrence.get_pyname()):160return True161162163class InHierarchyFilter(object):164"""For finding occurrences of a name"""165166def __init__(self, pyname, implementations_only=False):167self.pyname = pyname168self.impl_only = implementations_only169self.pyclass = self._get_containing_class(pyname)170if self.pyclass is not None:171self.name = pyname.get_object().get_name()172self.roots = self._get_root_classes(self.pyclass, self.name)173else:174self.roots = None175176def __call__(self, occurrence):177if self.roots is None:178return179pyclass = self._get_containing_class(occurrence.get_pyname())180if pyclass is not None:181roots = self._get_root_classes(pyclass, self.name)182if self.roots.intersection(roots):183return True184185def _get_containing_class(self, pyname):186if isinstance(pyname, pynames.DefinedName):187scope = pyname.get_object().get_scope()188parent = scope.parent189if parent is not None and parent.get_kind() == 'Class':190return parent.pyobject191192def _get_root_classes(self, pyclass, name):193if self.impl_only and pyclass == self.pyclass:194return set([pyclass])195result = set()196for superclass in pyclass.get_superclasses():197if name in superclass:198result.update(self._get_root_classes(superclass, name))199if not result:200return set([pyclass])201return result202203204class UnsureFilter(object):205206def __init__(self, unsure):207self.unsure = unsure208209def __call__(self, occurrence):210if occurrence.is_unsure() and self.unsure(occurrence):211return True212213214class NoImportsFilter(object):215216def __call__(self, occurrence):217if occurrence.is_in_import_statement():218return False219220221class CallsFilter(object):222223def __call__(self, occurrence):224if not occurrence.is_called():225return False226227228class _TextualFinder(object):229230def __init__(self, name, docs=False):231self.name = name232self.docs = docs233self.comment_pattern = _TextualFinder.any('comment', [r'#[^\n]*'])234self.string_pattern = _TextualFinder.any(235'string', [codeanalyze.get_string_pattern()])236self.pattern = self._get_occurrence_pattern(self.name)237238def find_offsets(self, source):239if not self._fast_file_query(source):240return241if self.docs:242searcher = self._normal_search243else:244searcher = self._re_search245for matched in searcher(source):246yield matched247248def _re_search(self, source):249for match in self.pattern.finditer(source):250for key, value in match.groupdict().items():251if value and key == 'occurrence':252yield match.start(key)253254def _normal_search(self, source):255current = 0256while True:257try:258found = source.index(self.name, current)259current = found + len(self.name)260if (found == 0 or not self._is_id_char(source[found - 1])) and \261(current == len(source) or not self._is_id_char(source[current])):262yield found263except ValueError:264break265266def _is_id_char(self, c):267return c.isalnum() or c == '_'268269def _fast_file_query(self, source):270try:271source.index(self.name)272return True273except ValueError:274return False275276def _get_source(self, resource, pymodule):277if resource is not None:278return resource.read()279else:280return pymodule.source_code281282def _get_occurrence_pattern(self, name):283occurrence_pattern = _TextualFinder.any('occurrence',284['\\b' + name + '\\b'])285pattern = re.compile(occurrence_pattern + '|' + self.comment_pattern +286'|' + self.string_pattern)287return pattern288289@staticmethod290def any(name, list_):291return '(?P<%s>' % name + '|'.join(list_) + ')'292293294class _OccurrenceToolsCreator(object):295296def __init__(self, pycore, resource=None, pymodule=None, docs=False):297self.pycore = pycore298self.__resource = resource299self.__pymodule = pymodule300self.docs = docs301302@property303@utils.saveit304def name_finder(self):305return evaluate.ScopeNameFinder(self.pymodule)306307@property308@utils.saveit309def source_code(self):310if self.__resource is not None:311return self.resource.read()312else:313return self.pymodule.source_code314315@property316@utils.saveit317def word_finder(self):318return worder.Worder(self.source_code, self.docs)319320@property321@utils.saveit322def resource(self):323if self.__resource is not None:324return self.__resource325if self.__pymodule is not None:326return self.__pymodule.resource327328@property329@utils.saveit330def pymodule(self):331if self.__pymodule is not None:332return self.__pymodule333return self.pycore.resource_to_pyobject(self.resource)334335336