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

Unified Diff: tools/telemetry/third_party/rope/rope/base/pycore.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/base/pycore.py
diff --git a/tools/telemetry/third_party/rope/rope/base/pycore.py b/tools/telemetry/third_party/rope/rope/base/pycore.py
new file mode 100644
index 0000000000000000000000000000000000000000..c4c1195a4d7ef5ee471655a4e5d97bb2b5836d3d
--- /dev/null
+++ b/tools/telemetry/third_party/rope/rope/base/pycore.py
@@ -0,0 +1,346 @@
+import bisect
+import difflib
+import sys
+import warnings
+
+import rope.base.libutils
+import rope.base.resourceobserver
+import rope.base.resources
+import rope.base.oi.doa
+import rope.base.oi.objectinfo
+import rope.base.oi.soa
+from rope.base import builtins
+from rope.base import exceptions
+from rope.base import stdmods
+from rope.base import taskhandle
+from rope.base import utils
+from rope.base.exceptions import ModuleNotFoundError
+from rope.base.pyobjectsdef import PyModule, PyPackage
+
+
+class PyCore(object):
+
+ def __init__(self, project):
+ self.project = project
+ self._init_resource_observer()
+ self.cache_observers = []
+ self.module_cache = _ModuleCache(self)
+ self.extension_cache = _ExtensionCache(self)
+ self.object_info = rope.base.oi.objectinfo.ObjectInfoManager(project)
+ self._init_python_files()
+ self._init_automatic_soa()
+
+ def _init_python_files(self):
+ self.python_matcher = None
+ patterns = self.project.prefs.get('python_files', None)
+ if patterns is not None:
+ self.python_matcher = rope.base.resources._ResourceMatcher()
+ self.python_matcher.set_patterns(patterns)
+
+ def _init_resource_observer(self):
+ callback = self._invalidate_resource_cache
+ observer = rope.base.resourceobserver.ResourceObserver(
+ changed=callback, moved=callback, removed=callback)
+ self.observer = \
+ rope.base.resourceobserver.FilteredResourceObserver(observer)
+ self.project.add_observer(self.observer)
+
+ def _init_automatic_soa(self):
+ if not self.automatic_soa:
+ return
+ callback = self._file_changed_for_soa
+ observer = rope.base.resourceobserver.ResourceObserver(
+ changed=callback, moved=callback, removed=callback)
+ self.project.add_observer(observer)
+
+ @property
+ def automatic_soa(self):
+ auto_soa = self.project.prefs.get('automatic_soi', None)
+ return self.project.prefs.get('automatic_soa', auto_soa)
+
+ def _file_changed_for_soa(self, resource, new_resource=None):
+ old_contents = self.project.history.\
+ contents_before_current_change(resource)
+ if old_contents is not None:
+ perform_soa_on_changed_scopes(self.project, resource, old_contents)
+
+ def is_python_file(self, resource):
+ if resource.is_folder():
+ return False
+ if self.python_matcher is None:
+ return resource.name.endswith('.py')
+ return self.python_matcher.does_match(resource)
+
+ @utils.deprecated('Use `project.get_module` instead')
+ def get_module(self, name, folder=None):
+ """Returns a `PyObject` if the module was found."""
+ return self.project.get_module(name, folder)
+
+ def _builtin_submodules(self, modname):
+ result = {}
+ for extension in self.extension_modules:
+ if extension.startswith(modname + '.'):
+ name = extension[len(modname) + 1:]
+ if '.' not in name:
+ result[name] = self.builtin_module(extension)
+ return result
+
+ def builtin_module(self, name):
+ return self.extension_cache.get_pymodule(name)
+
+ @utils.deprecated('Use `project.get_relative_module` instead')
+ def get_relative_module(self, name, folder, level):
+ return self.project.get_relative_module(name, folder, level)
+
+ @utils.deprecated('Use `libutils.get_string_module` instead')
+ def get_string_module(self, code, resource=None, force_errors=False):
+ """Returns a `PyObject` object for the given code
+
+ If `force_errors` is `True`, `exceptions.ModuleSyntaxError` is
+ raised if module has syntax errors. This overrides
+ ``ignore_syntax_errors`` project config.
+
+ """
+ return PyModule(self, code, resource, force_errors=force_errors)
+
+ @utils.deprecated('Use `libutils.get_string_scope` instead')
+ def get_string_scope(self, code, resource=None):
+ """Returns a `Scope` object for the given code"""
+ return rope.base.libutils.get_string_scope(code, resource)
+
+ def _invalidate_resource_cache(self, resource, new_resource=None):
+ for observer in self.cache_observers:
+ observer(resource)
+
+ @utils.deprecated('Use `project.get_python_path_folders` instead')
+ def get_python_path_folders(self):
+ return self.project.get_python_path_folders()
+
+ @utils.deprecated('Use `project.find_module` instead')
+ def find_module(self, modname, folder=None):
+ """Returns a resource corresponding to the given module
+
+ returns None if it can not be found
+ """
+ return self.project.find_module(modname, folder)
+
+ @utils.deprecated('Use `project.find_relative_module` instead')
+ def find_relative_module(self, modname, folder, level):
+ return self.project.find_relative_module(modname, folder, level)
+
+ # INFO: It was decided not to cache source folders, since:
+ # - Does not take much time when the root folder contains
+ # packages, that is most of the time
+ # - We need a separate resource observer; `self.observer`
+ # does not get notified about module and folder creations
+ @utils.deprecated('Use `project.get_source_folders` instead')
+ def get_source_folders(self):
+ """Returns project source folders"""
+ return self.project.get_source_folders()
+
+ def resource_to_pyobject(self, resource, force_errors=False):
+ return self.module_cache.get_pymodule(resource, force_errors)
+
+ @utils.deprecated('Use `project.get_python_files` instead')
+ def get_python_files(self):
+ """Returns all python files available in the project"""
+ return self.project.get_python_files()
+
+ def _is_package(self, folder):
+ if folder.has_child('__init__.py') and \
+ not folder.get_child('__init__.py').is_folder():
+ return True
+ else:
+ return False
+
+ def _find_source_folders(self, folder):
+ for resource in folder.get_folders():
+ if self._is_package(resource):
+ return [folder]
+ result = []
+ for resource in folder.get_files():
+ if resource.name.endswith('.py'):
+ result.append(folder)
+ break
+ for resource in folder.get_folders():
+ result.extend(self._find_source_folders(resource))
+ return result
+
+ def run_module(self, resource, args=None, stdin=None, stdout=None):
+ """Run `resource` module
+
+ Returns a `rope.base.oi.doa.PythonFileRunner` object for
+ controlling the process.
+
+ """
+ perform_doa = self.project.prefs.get('perform_doi', True)
+ perform_doa = self.project.prefs.get('perform_doa', perform_doa)
+ receiver = self.object_info.doa_data_received
+ if not perform_doa:
+ receiver = None
+ runner = rope.base.oi.doa.PythonFileRunner(
+ self, resource, args, stdin, stdout, receiver)
+ runner.add_finishing_observer(self.module_cache.forget_all_data)
+ runner.run()
+ return runner
+
+ def analyze_module(self, resource, should_analyze=lambda py: True,
+ search_subscopes=lambda py: True, followed_calls=None):
+ """Analyze `resource` module for static object inference
+
+ This function forces rope to analyze this module to collect
+ information about function calls. `should_analyze` is a
+ function that is called with a `PyDefinedObject` argument. If
+ it returns `True` the element is analyzed. If it is `None` or
+ returns `False` the element is not analyzed.
+
+ `search_subscopes` is like `should_analyze`; The difference is
+ that if it returns `False` the sub-scopes are all ignored.
+ That is it is assumed that `should_analyze` returns `False`
+ for all of its subscopes.
+
+ `followed_calls` override the value of ``soa_followed_calls``
+ project config.
+ """
+ if followed_calls is None:
+ followed_calls = self.project.prefs.get('soa_followed_calls', 0)
+ pymodule = self.resource_to_pyobject(resource)
+ self.module_cache.forget_all_data()
+ rope.base.oi.soa.analyze_module(
+ self, pymodule, should_analyze, search_subscopes, followed_calls)
+
+ def get_classes(self, task_handle=taskhandle.NullTaskHandle()):
+ warnings.warn('`PyCore.get_classes()` is deprecated',
+ DeprecationWarning, stacklevel=2)
+ return []
+
+ def __str__(self):
+ return str(self.module_cache) + str(self.object_info)
+
+ @utils.deprecated('Use `libutils.modname` instead')
+ def modname(self, resource):
+ return rope.base.libutils.modname(resource)
+
+ @property
+ @utils.cacheit
+ def extension_modules(self):
+ result = set(self.project.prefs.get('extension_modules', []))
+ if self.project.prefs.get('import_dynload_stdmods', False):
+ result.update(stdmods.dynload_modules())
+ return result
+
+
+class _ModuleCache(object):
+
+ def __init__(self, pycore):
+ self.pycore = pycore
+ self.module_map = {}
+ self.pycore.cache_observers.append(self._invalidate_resource)
+ self.observer = self.pycore.observer
+
+ def _invalidate_resource(self, resource):
+ if resource in self.module_map:
+ self.forget_all_data()
+ self.observer.remove_resource(resource)
+ del self.module_map[resource]
+
+ def get_pymodule(self, resource, force_errors=False):
+ if resource in self.module_map:
+ return self.module_map[resource]
+ if resource.is_folder():
+ result = PyPackage(self.pycore, resource,
+ force_errors=force_errors)
+ else:
+ result = PyModule(self.pycore, resource=resource,
+ force_errors=force_errors)
+ if result.has_errors:
+ return result
+ self.module_map[resource] = result
+ self.observer.add_resource(resource)
+ return result
+
+ def forget_all_data(self):
+ for pymodule in self.module_map.values():
+ pymodule._forget_concluded_data()
+
+ def __str__(self):
+ return 'PyCore caches %d PyModules\n' % len(self.module_map)
+
+
+class _ExtensionCache(object):
+
+ def __init__(self, pycore):
+ self.pycore = pycore
+ self.extensions = {}
+
+ def get_pymodule(self, name):
+ if name == '__builtin__':
+ return builtins.builtins
+ allowed = self.pycore.extension_modules
+ if name not in self.extensions and name in allowed:
+ self.extensions[name] = builtins.BuiltinModule(name, self.pycore)
+ return self.extensions.get(name)
+
+
+def perform_soa_on_changed_scopes(project, resource, old_contents):
+ pycore = project.pycore
+ if resource.exists() and pycore.is_python_file(resource):
+ try:
+ new_contents = resource.read()
+ # detecting changes in new_contents relative to old_contents
+ detector = _TextChangeDetector(new_contents, old_contents)
+
+ def search_subscopes(pydefined):
+ scope = pydefined.get_scope()
+ return detector.is_changed(scope.get_start(), scope.get_end())
+
+ def should_analyze(pydefined):
+ scope = pydefined.get_scope()
+ start = scope.get_start()
+ end = scope.get_end()
+ return detector.consume_changes(start, end)
+ pycore.analyze_module(resource, should_analyze, search_subscopes)
+ except exceptions.ModuleSyntaxError:
+ pass
+
+
+class _TextChangeDetector(object):
+
+ def __init__(self, old, new):
+ self.old = old
+ self.new = new
+ self._set_diffs()
+
+ def _set_diffs(self):
+ differ = difflib.Differ()
+ self.lines = []
+ lineno = 0
+ for line in differ.compare(self.old.splitlines(True),
+ self.new.splitlines(True)):
+ if line.startswith(' '):
+ lineno += 1
+ elif line.startswith('-'):
+ lineno += 1
+ self.lines.append(lineno)
+
+ def is_changed(self, start, end):
+ """Tell whether any of start till end lines have changed
+
+ The end points are inclusive and indices start from 1.
+ """
+ left, right = self._get_changed(start, end)
+ if left < right:
+ return True
+ return False
+
+ def consume_changes(self, start, end):
+ """Clear the changed status of lines from start till end"""
+ left, right = self._get_changed(start, end)
+ if left < right:
+ del self.lines[left:right]
+ return left < right
+
+ def _get_changed(self, start, end):
+ left = bisect.bisect_left(self.lines, start)
+ right = bisect.bisect_right(self.lines, end)
+ return left, right
« no previous file with comments | « tools/telemetry/third_party/rope/rope/base/project.py ('k') | tools/telemetry/third_party/rope/rope/base/pynames.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698