Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1041)

Unified Diff: tools/telemetry/third_party/rope/rope/refactor/occurrences.py

Issue 1132103009: Example of refactoring using rope library. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tools/telemetry/third_party/rope/rope/refactor/occurrences.py
diff --git a/tools/telemetry/third_party/rope/rope/refactor/occurrences.py b/tools/telemetry/third_party/rope/rope/refactor/occurrences.py
new file mode 100644
index 0000000000000000000000000000000000000000..14a2d7deda8be202f2c30277f9325d670fe19e53
--- /dev/null
+++ b/tools/telemetry/third_party/rope/rope/refactor/occurrences.py
@@ -0,0 +1,384 @@
+"""Find occurrences of a name in a project.
+
+This module consists of a `Finder` that finds all occurrences of a name
+in a project. The `Finder.find_occurrences()` method is a generator that
+yields `Occurrence` instances for each occurrence of the name. To create
+a `Finder` object, use the `create_finder()` function:
+
+ finder = occurrences.create_finder(project, 'foo', pyname)
+ for occurrence in finder.find_occurrences():
+ pass
+
+It's possible to filter the occurrences. They can be specified when
+calling the `create_finder()` function.
+
+ * `only_calls`: If True, return only those instances where the name is
+ a function that's being called.
+
+ * `imports`: If False, don't return instances that are in import
+ statements.
+
+ * `unsure`: If a prediate function, return instances where we don't
+ know what the name references. It also filters based on the
+ predicate function.
+
+ * `docs`: If True, it will search for occurrences in regions normally
+ ignored. E.g., strings and comments.
+
+ * `in_hierarchy`: If True, it will find occurrences if the name is in
+ the class's hierarchy.
+
+ * `instance`: Used only when you want implicit interfaces to be
+ considered.
+"""
+
+import re
+
+from rope.base import codeanalyze
+from rope.base import evaluate
+from rope.base import exceptions
+from rope.base import pynames
+from rope.base import pyobjects
+from rope.base import utils
+from rope.base import worder
+
+
+class Finder(object):
+ """For finding occurrences of a name
+
+ The constructor takes a `filters` argument. It should be a list
+ of functions that take a single argument. For each possible
+ occurrence, these functions are called in order with the an
+ instance of `Occurrence`:
+
+ * If it returns `None` other filters are tried.
+ * If it returns `True`, the occurrence will be a match.
+ * If it returns `False`, the occurrence will be skipped.
+ * If all of the filters return `None`, it is skipped also.
+
+ """
+
+ def __init__(self, project, name, filters=[lambda o: True], docs=False):
+ self.project = project
+ self.name = name
+ self.docs = docs
+ self.filters = filters
+ self._textual_finder = _TextualFinder(name, docs=docs)
+
+ def find_occurrences(self, resource=None, pymodule=None):
+ """Generate `Occurrence` instances"""
+ tools = _OccurrenceToolsCreator(self.project, resource=resource,
+ pymodule=pymodule, docs=self.docs)
+ for offset in self._textual_finder.find_offsets(tools.source_code):
+ occurrence = Occurrence(tools, offset)
+ for filter in self.filters:
+ result = filter(occurrence)
+ if result is None:
+ continue
+ if result:
+ yield occurrence
+ break
+
+
+def create_finder(project, name, pyname, only_calls=False, imports=True,
+ unsure=None, docs=False, instance=None, in_hierarchy=False):
+ """A factory for `Finder`
+
+ Based on the arguments it creates a list of filters. `instance`
+ argument is needed only when you want implicit interfaces to be
+ considered.
+
+ """
+ pynames_ = set([pyname])
+ filters = []
+ if only_calls:
+ filters.append(CallsFilter())
+ if not imports:
+ filters.append(NoImportsFilter())
+ if isinstance(instance, pynames.ParameterName):
+ for pyobject in instance.get_objects():
+ try:
+ pynames_.add(pyobject[name])
+ except exceptions.AttributeNotFoundError:
+ pass
+ for pyname in pynames_:
+ filters.append(PyNameFilter(pyname))
+ if in_hierarchy:
+ filters.append(InHierarchyFilter(pyname))
+ if unsure:
+ filters.append(UnsureFilter(unsure))
+ return Finder(project, name, filters=filters, docs=docs)
+
+
+class Occurrence(object):
+
+ def __init__(self, tools, offset):
+ self.tools = tools
+ self.offset = offset
+ self.resource = tools.resource
+
+ @utils.saveit
+ def get_word_range(self):
+ return self.tools.word_finder.get_word_range(self.offset)
+
+ @utils.saveit
+ def get_primary_range(self):
+ return self.tools.word_finder.get_primary_range(self.offset)
+
+ @utils.saveit
+ def get_pyname(self):
+ try:
+ return self.tools.name_finder.get_pyname_at(self.offset)
+ except exceptions.BadIdentifierError:
+ pass
+
+ @utils.saveit
+ def get_primary_and_pyname(self):
+ try:
+ return self.tools.name_finder.get_primary_and_pyname_at(
+ self.offset)
+ except exceptions.BadIdentifierError:
+ pass
+
+ @utils.saveit
+ def is_in_import_statement(self):
+ return (self.tools.word_finder.is_from_statement(self.offset) or
+ self.tools.word_finder.is_import_statement(self.offset))
+
+ def is_called(self):
+ return self.tools.word_finder.is_a_function_being_called(self.offset)
+
+ def is_defined(self):
+ return self.tools.word_finder.is_a_class_or_function_name_in_header(
+ self.offset)
+
+ def is_a_fixed_primary(self):
+ return self.tools.word_finder.is_a_class_or_function_name_in_header(
+ self.offset) or \
+ self.tools.word_finder.is_a_name_after_from_import(self.offset)
+
+ def is_written(self):
+ return self.tools.word_finder.is_assigned_here(self.offset)
+
+ def is_unsure(self):
+ return unsure_pyname(self.get_pyname())
+
+ @property
+ @utils.saveit
+ def lineno(self):
+ offset = self.get_word_range()[0]
+ return self.tools.pymodule.lines.get_line_number(offset)
+
+
+def same_pyname(expected, pyname):
+ """Check whether `expected` and `pyname` are the same"""
+ if expected is None or pyname is None:
+ return False
+ if expected == pyname:
+ return True
+ if type(expected) not in (pynames.ImportedModule, pynames.ImportedName) \
+ and type(pyname) not in \
+ (pynames.ImportedModule, pynames.ImportedName):
+ return False
+ return expected.get_definition_location() == \
+ pyname.get_definition_location() and \
+ expected.get_object() == pyname.get_object()
+
+
+def unsure_pyname(pyname, unbound=True):
+ """Return `True` if we don't know what this name references"""
+ if pyname is None:
+ return True
+ if unbound and not isinstance(pyname, pynames.UnboundName):
+ return False
+ if pyname.get_object() == pyobjects.get_unknown():
+ return True
+
+
+class PyNameFilter(object):
+ """For finding occurrences of a name."""
+
+ def __init__(self, pyname):
+ self.pyname = pyname
+
+ def __call__(self, occurrence):
+ if same_pyname(self.pyname, occurrence.get_pyname()):
+ return True
+
+
+class InHierarchyFilter(object):
+ """Finds the occurrence if the name is in the class's hierarchy."""
+
+ def __init__(self, pyname, implementations_only=False):
+ self.pyname = pyname
+ self.impl_only = implementations_only
+ self.pyclass = self._get_containing_class(pyname)
+ if self.pyclass is not None:
+ self.name = pyname.get_object().get_name()
+ self.roots = self._get_root_classes(self.pyclass, self.name)
+ else:
+ self.roots = None
+
+ def __call__(self, occurrence):
+ if self.roots is None:
+ return
+ pyclass = self._get_containing_class(occurrence.get_pyname())
+ if pyclass is not None:
+ roots = self._get_root_classes(pyclass, self.name)
+ if self.roots.intersection(roots):
+ return True
+
+ def _get_containing_class(self, pyname):
+ if isinstance(pyname, pynames.DefinedName):
+ scope = pyname.get_object().get_scope()
+ parent = scope.parent
+ if parent is not None and parent.get_kind() == 'Class':
+ return parent.pyobject
+
+ def _get_root_classes(self, pyclass, name):
+ if self.impl_only and pyclass == self.pyclass:
+ return set([pyclass])
+ result = set()
+ for superclass in pyclass.get_superclasses():
+ if name in superclass:
+ result.update(self._get_root_classes(superclass, name))
+ if not result:
+ return set([pyclass])
+ return result
+
+
+class UnsureFilter(object):
+ """Occurrences where we don't knoow what the name references."""
+
+ def __init__(self, unsure):
+ self.unsure = unsure
+
+ def __call__(self, occurrence):
+ if occurrence.is_unsure() and self.unsure(occurrence):
+ return True
+
+
+class NoImportsFilter(object):
+ """Don't include import statements as occurrences."""
+
+ def __call__(self, occurrence):
+ if occurrence.is_in_import_statement():
+ return False
+
+
+class CallsFilter(object):
+ """Filter out non-call occurrences."""
+
+ def __call__(self, occurrence):
+ if not occurrence.is_called():
+ return False
+
+
+class _TextualFinder(object):
+
+ def __init__(self, name, docs=False):
+ self.name = name
+ self.docs = docs
+ self.comment_pattern = _TextualFinder.any('comment', [r'#[^\n]*'])
+ self.string_pattern = _TextualFinder.any(
+ 'string', [codeanalyze.get_string_pattern()])
+ self.pattern = self._get_occurrence_pattern(self.name)
+
+ def find_offsets(self, source):
+ if not self._fast_file_query(source):
+ return
+ if self.docs:
+ searcher = self._normal_search
+ else:
+ searcher = self._re_search
+ for matched in searcher(source):
+ yield matched
+
+ def _re_search(self, source):
+ for match in self.pattern.finditer(source):
+ for key, value in match.groupdict().items():
+ if value and key == 'occurrence':
+ yield match.start(key)
+
+ def _normal_search(self, source):
+ current = 0
+ while True:
+ try:
+ found = source.index(self.name, current)
+ current = found + len(self.name)
+ if (found == 0 or
+ not self._is_id_char(source[found - 1])) and \
+ (current == len(source) or
+ not self._is_id_char(source[current])):
+ yield found
+ except ValueError:
+ break
+
+ def _is_id_char(self, c):
+ return c.isalnum() or c == '_'
+
+ def _fast_file_query(self, source):
+ try:
+ source.index(self.name)
+ return True
+ except ValueError:
+ return False
+
+ def _get_source(self, resource, pymodule):
+ if resource is not None:
+ return resource.read()
+ else:
+ return pymodule.source_code
+
+ def _get_occurrence_pattern(self, name):
+ occurrence_pattern = _TextualFinder.any('occurrence',
+ ['\\b' + name + '\\b'])
+ pattern = re.compile(occurrence_pattern + '|' + self.comment_pattern +
+ '|' + self.string_pattern)
+ return pattern
+
+ @staticmethod
+ def any(name, list_):
+ return '(?P<%s>' % name + '|'.join(list_) + ')'
+
+
+class _OccurrenceToolsCreator(object):
+
+ def __init__(self, project, resource=None, pymodule=None, docs=False):
+ self.project = project
+ self.__resource = resource
+ self.__pymodule = pymodule
+ self.docs = docs
+
+ @property
+ @utils.saveit
+ def name_finder(self):
+ return evaluate.ScopeNameFinder(self.pymodule)
+
+ @property
+ @utils.saveit
+ def source_code(self):
+ if self.__resource is not None:
+ return self.resource.read()
+ else:
+ return self.pymodule.source_code
+
+ @property
+ @utils.saveit
+ def word_finder(self):
+ return worder.Worder(self.source_code, self.docs)
+
+ @property
+ @utils.saveit
+ def resource(self):
+ if self.__resource is not None:
+ return self.__resource
+ if self.__pymodule is not None:
+ return self.__pymodule.resource
+
+ @property
+ @utils.saveit
+ def pymodule(self):
+ if self.__pymodule is not None:
+ return self.__pymodule
+ return self.project.get_pymodule(self.resource)

Powered by Google App Engine
This is Rietveld 408576698