Index: tools/telemetry/third_party/rope/rope/base/project.py |
diff --git a/tools/telemetry/third_party/rope/rope/base/project.py b/tools/telemetry/third_party/rope/rope/base/project.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..304398aaa8852b762f838008021c460e082c288e |
--- /dev/null |
+++ b/tools/telemetry/third_party/rope/rope/base/project.py |
@@ -0,0 +1,498 @@ |
+import os |
+import shutil |
+import sys |
+import warnings |
+ |
+import rope.base.fscommands |
+from rope.base import exceptions, taskhandle, prefs, history, pycore, utils |
+import rope.base.resourceobserver as resourceobserver |
+from rope.base.resources import File, Folder, _ResourceMatcher |
+from rope.base.exceptions import ModuleNotFoundError |
+ |
+try: |
+ import pickle |
+except ImportError: |
+ import cPickle as pickle |
+try: |
+ execfile |
+except NameError: |
+ def execfile(fn, global_vars, local_vars): |
+ with open(fn) as f: |
+ code = compile(f.read(), fn, 'exec') |
+ exec(code, global_vars, local_vars) |
+ |
+ |
+ |
+class _Project(object): |
+ |
+ def __init__(self, fscommands): |
+ self.observers = [] |
+ self.fscommands = fscommands |
+ self.prefs = prefs.Prefs() |
+ self.data_files = _DataFiles(self) |
+ self._custom_source_folders = [] |
+ |
+ def get_resource(self, resource_name): |
+ """Get a resource in a project. |
+ |
+ `resource_name` is the path of a resource in a project. It is |
+ the path of a resource relative to project root. Project root |
+ folder address is an empty string. If the resource does not |
+ exist a `exceptions.ResourceNotFound` exception would be |
+ raised. Use `get_file()` and `get_folder()` when you need to |
+ get nonexistent `Resource`\s. |
+ |
+ """ |
+ path = self._get_resource_path(resource_name) |
+ if not os.path.exists(path): |
+ raise exceptions.ResourceNotFoundError( |
+ 'Resource <%s> does not exist' % resource_name) |
+ elif os.path.isfile(path): |
+ return File(self, resource_name) |
+ elif os.path.isdir(path): |
+ return Folder(self, resource_name) |
+ else: |
+ raise exceptions.ResourceNotFoundError('Unknown resource ' |
+ + resource_name) |
+ |
+ def get_module(self, name, folder=None): |
+ """Returns a `PyObject` if the module was found.""" |
+ # check if this is a builtin module |
+ pymod = self.pycore.builtin_module(name) |
+ if pymod is not None: |
+ return pymod |
+ module = self.find_module(name, folder) |
+ if module is None: |
+ raise ModuleNotFoundError('Module %s not found' % name) |
+ return self.pycore.resource_to_pyobject(module) |
+ |
+ def get_python_path_folders(self): |
+ result = [] |
+ for src in self.prefs.get('python_path', []) + sys.path: |
+ try: |
+ src_folder = get_no_project().get_resource(src) |
+ result.append(src_folder) |
+ except exceptions.ResourceNotFoundError: |
+ pass |
+ return result |
+ |
+ # 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 |
+ def get_source_folders(self): |
+ """Returns project source folders""" |
+ if self.root is None: |
+ return [] |
+ result = list(self._custom_source_folders) |
+ result.extend(self.pycore._find_source_folders(self.root)) |
+ return result |
+ |
+ def validate(self, folder): |
+ """Validate files and folders contained in this folder |
+ |
+ It validates all of the files and folders contained in this |
+ folder if some observers are interested in them. |
+ |
+ """ |
+ for observer in list(self.observers): |
+ observer.validate(folder) |
+ |
+ def add_observer(self, observer): |
+ """Register a `ResourceObserver` |
+ |
+ See `FilteredResourceObserver`. |
+ """ |
+ self.observers.append(observer) |
+ |
+ def remove_observer(self, observer): |
+ """Remove a registered `ResourceObserver`""" |
+ if observer in self.observers: |
+ self.observers.remove(observer) |
+ |
+ def do(self, changes, task_handle=taskhandle.NullTaskHandle()): |
+ """Apply the changes in a `ChangeSet` |
+ |
+ Most of the time you call this function for committing the |
+ changes for a refactoring. |
+ """ |
+ self.history.do(changes, task_handle=task_handle) |
+ |
+ def get_pymodule(self, resource, force_errors=False): |
+ return self.pycore.resource_to_pyobject(resource, force_errors) |
+ |
+ def get_pycore(self): |
+ return self.pycore |
+ |
+ def get_file(self, path): |
+ """Get the file with `path` (it may not exist)""" |
+ return File(self, path) |
+ |
+ def get_folder(self, path): |
+ """Get the folder with `path` (it may not exist)""" |
+ return Folder(self, path) |
+ |
+ def get_prefs(self): |
+ return self.prefs |
+ |
+ def get_relative_module(self, name, folder, level): |
+ module = self.find_relative_module(name, folder, level) |
+ if module is None: |
+ raise ModuleNotFoundError('Module %s not found' % name) |
+ return self.pycore.resource_to_pyobject(module) |
+ |
+ def find_module(self, modname, folder=None): |
+ """Returns a resource corresponding to the given module |
+ |
+ returns None if it can not be found |
+ """ |
+ for src in self.get_source_folders(): |
+ module = _find_module_in_folder(src, modname) |
+ if module is not None: |
+ return module |
+ for src in self.get_python_path_folders(): |
+ module = _find_module_in_folder(src, modname) |
+ if module is not None: |
+ return module |
+ if folder is not None: |
+ module = _find_module_in_folder(folder, modname) |
+ if module is not None: |
+ return module |
+ return None |
+ |
+ def find_relative_module(self, modname, folder, level): |
+ for i in range(level - 1): |
+ folder = folder.parent |
+ if modname == '': |
+ return folder |
+ else: |
+ return _find_module_in_folder(folder, modname) |
+ |
+ def is_ignored(self, resource): |
+ return False |
+ |
+ def _get_resource_path(self, name): |
+ pass |
+ |
+ @property |
+ @utils.saveit |
+ def history(self): |
+ return history.History(self) |
+ |
+ @property |
+ @utils.saveit |
+ def pycore(self): |
+ return pycore.PyCore(self) |
+ |
+ def close(self): |
+ warnings.warn('Cannot close a NoProject', |
+ DeprecationWarning, stacklevel=2) |
+ |
+ ropefolder = None |
+ |
+ |
+class Project(_Project): |
+ """A Project containing files and folders""" |
+ |
+ def __init__(self, projectroot, fscommands=None, |
+ ropefolder='.ropeproject', **prefs): |
+ """A rope project |
+ |
+ :parameters: |
+ - `projectroot`: The address of the root folder of the project |
+ - `fscommands`: Implements the file system operations used |
+ by rope; have a look at `rope.base.fscommands` |
+ - `ropefolder`: The name of the folder in which rope stores |
+ project configurations and data. Pass `None` for not using |
+ such a folder at all. |
+ - `prefs`: Specify project preferences. These values |
+ overwrite config file preferences. |
+ |
+ """ |
+ if projectroot != '/': |
+ projectroot = _realpath(projectroot).rstrip('/\\') |
+ self._address = projectroot |
+ self._ropefolder_name = ropefolder |
+ if not os.path.exists(self._address): |
+ os.mkdir(self._address) |
+ elif not os.path.isdir(self._address): |
+ raise exceptions.RopeError('Project root exists and' |
+ ' is not a directory') |
+ if fscommands is None: |
+ fscommands = rope.base.fscommands.create_fscommands(self._address) |
+ super(Project, self).__init__(fscommands) |
+ self.ignored = _ResourceMatcher() |
+ self.file_list = _FileListCacher(self) |
+ self.prefs.add_callback('ignored_resources', self.ignored.set_patterns) |
+ if ropefolder is not None: |
+ self.prefs['ignored_resources'] = [ropefolder] |
+ self._init_prefs(prefs) |
+ self._init_source_folders() |
+ |
+ @utils.deprecated('Delete once deprecated functions are gone') |
+ def _init_source_folders(self): |
+ for path in self.prefs.get('source_folders', []): |
+ folder = self.get_resource(path) |
+ self._custom_source_folders.append(folder) |
+ |
+ def get_files(self): |
+ return self.file_list.get_files() |
+ |
+ def get_python_files(self): |
+ """Returns all python files available in the project""" |
+ return [resource for resource in self.get_files() |
+ if self.pycore.is_python_file(resource)] |
+ |
+ def _get_resource_path(self, name): |
+ return os.path.join(self._address, *name.split('/')) |
+ |
+ def _init_ropefolder(self): |
+ if self.ropefolder is not None: |
+ if not self.ropefolder.exists(): |
+ self._create_recursively(self.ropefolder) |
+ if not self.ropefolder.has_child('config.py'): |
+ config = self.ropefolder.create_file('config.py') |
+ config.write(self._default_config()) |
+ |
+ def _create_recursively(self, folder): |
+ if folder.parent != self.root and not folder.parent.exists(): |
+ self._create_recursively(folder.parent) |
+ folder.create() |
+ |
+ def _init_prefs(self, prefs): |
+ run_globals = {} |
+ if self.ropefolder is not None: |
+ config = self.get_file(self.ropefolder.path + '/config.py') |
+ run_globals.update({'__name__': '__main__', |
+ '__builtins__': __builtins__, |
+ '__file__': config.real_path}) |
+ if config.exists(): |
+ config = self.ropefolder.get_child('config.py') |
+ execfile(config.real_path, run_globals) |
+ else: |
+ exec(self._default_config(), run_globals) |
+ if 'set_prefs' in run_globals: |
+ run_globals['set_prefs'](self.prefs) |
+ for key, value in prefs.items(): |
+ self.prefs[key] = value |
+ self._init_other_parts() |
+ self._init_ropefolder() |
+ if 'project_opened' in run_globals: |
+ run_globals['project_opened'](self) |
+ |
+ def _default_config(self): |
+ import rope.base.default_config |
+ import inspect |
+ return inspect.getsource(rope.base.default_config) |
+ |
+ def _init_other_parts(self): |
+ # Forcing the creation of `self.pycore` to register observers |
+ self.pycore |
+ |
+ def is_ignored(self, resource): |
+ return self.ignored.does_match(resource) |
+ |
+ def sync(self): |
+ """Closes project open resources""" |
+ self.close() |
+ |
+ def close(self): |
+ """Closes project open resources""" |
+ self.data_files.write() |
+ |
+ def set(self, key, value): |
+ """Set the `key` preference to `value`""" |
+ self.prefs.set(key, value) |
+ |
+ @property |
+ def ropefolder(self): |
+ if self._ropefolder_name is not None: |
+ return self.get_folder(self._ropefolder_name) |
+ |
+ def validate(self, folder=None): |
+ if folder is None: |
+ folder = self.root |
+ super(Project, self).validate(folder) |
+ |
+ root = property(lambda self: self.get_resource('')) |
+ address = property(lambda self: self._address) |
+ |
+ |
+class NoProject(_Project): |
+ """A null object for holding out of project files. |
+ |
+ This class is singleton use `get_no_project` global function |
+ """ |
+ |
+ def __init__(self): |
+ fscommands = rope.base.fscommands.FileSystemCommands() |
+ super(NoProject, self).__init__(fscommands) |
+ |
+ def _get_resource_path(self, name): |
+ real_name = name.replace('/', os.path.sep) |
+ return _realpath(real_name) |
+ |
+ def get_resource(self, name): |
+ universal_name = _realpath(name).replace(os.path.sep, '/') |
+ return super(NoProject, self).get_resource(universal_name) |
+ |
+ def get_files(self): |
+ return [] |
+ |
+ def get_python_files(self): |
+ return [] |
+ |
+ _no_project = None |
+ |
+ |
+def get_no_project(): |
+ if NoProject._no_project is None: |
+ NoProject._no_project = NoProject() |
+ return NoProject._no_project |
+ |
+ |
+class _FileListCacher(object): |
+ |
+ def __init__(self, project): |
+ self.project = project |
+ self.files = None |
+ rawobserver = resourceobserver.ResourceObserver( |
+ self._changed, self._invalid, self._invalid, |
+ self._invalid, self._invalid) |
+ self.project.add_observer(rawobserver) |
+ |
+ def get_files(self): |
+ if self.files is None: |
+ self.files = set() |
+ self._add_files(self.project.root) |
+ return self.files |
+ |
+ def _add_files(self, folder): |
+ for child in folder.get_children(): |
+ if child.is_folder(): |
+ self._add_files(child) |
+ elif not self.project.is_ignored(child): |
+ self.files.add(child) |
+ |
+ def _changed(self, resource): |
+ if resource.is_folder(): |
+ self.files = None |
+ |
+ def _invalid(self, resource, new_resource=None): |
+ self.files = None |
+ |
+ |
+class _DataFiles(object): |
+ |
+ def __init__(self, project): |
+ self.project = project |
+ self.hooks = [] |
+ |
+ def read_data(self, name, compress=False, import_=False): |
+ if self.project.ropefolder is None: |
+ return None |
+ compress = compress and self._can_compress() |
+ opener = self._get_opener(compress) |
+ file = self._get_file(name, compress) |
+ if not compress and import_: |
+ self._import_old_files(name) |
+ if file.exists(): |
+ input = opener(file.real_path, 'rb') |
+ try: |
+ result = [] |
+ try: |
+ while True: |
+ result.append(pickle.load(input)) |
+ except EOFError: |
+ pass |
+ if len(result) == 1: |
+ return result[0] |
+ if len(result) > 1: |
+ return result |
+ finally: |
+ input.close() |
+ |
+ def write_data(self, name, data, compress=False): |
+ if self.project.ropefolder is not None: |
+ compress = compress and self._can_compress() |
+ file = self._get_file(name, compress) |
+ opener = self._get_opener(compress) |
+ output = opener(file.real_path, 'wb') |
+ try: |
+ pickle.dump(data, output, 2) |
+ finally: |
+ output.close() |
+ |
+ def add_write_hook(self, hook): |
+ self.hooks.append(hook) |
+ |
+ def write(self): |
+ for hook in self.hooks: |
+ hook() |
+ |
+ def _can_compress(self): |
+ try: |
+ import gzip # noqa |
+ return True |
+ except ImportError: |
+ return False |
+ |
+ def _import_old_files(self, name): |
+ old = self._get_file(name + '.pickle', False) |
+ new = self._get_file(name, False) |
+ if old.exists() and not new.exists(): |
+ shutil.move(old.real_path, new.real_path) |
+ |
+ def _get_opener(self, compress): |
+ if compress: |
+ try: |
+ import gzip |
+ return gzip.open |
+ except ImportError: |
+ pass |
+ return open |
+ |
+ def _get_file(self, name, compress): |
+ path = self.project.ropefolder.path + '/' + name |
+ if compress: |
+ path += '.gz' |
+ return self.project.get_file(path) |
+ |
+ |
+def _realpath(path): |
+ """Return the real path of `path` |
+ |
+ Is equivalent to ``realpath(abspath(expanduser(path)))``. |
+ |
+ Of the particular notice is the hack dealing with the unfortunate |
+ sitaution of running native-Windows python (os.name == 'nt') inside |
+ of Cygwin (abspath starts with '/'), which apparently normal |
+ os.path.realpath completely messes up. |
+ |
+ """ |
+ # there is a bug in cygwin for os.path.abspath() for abs paths |
+ if sys.platform == 'cygwin': |
+ if path[1:3] == ':\\': |
+ return path |
+ elif path[1:3] == ':/': |
+ path = "/cygdrive/" + path[0] + path[2:] |
+ return os.path.abspath(os.path.expanduser(path)) |
+ return os.path.realpath(os.path.abspath(os.path.expanduser(path))) |
+ |
+ |
+def _find_module_in_folder(folder, modname): |
+ module = folder |
+ packages = modname.split('.') |
+ for pkg in packages[:-1]: |
+ if module.is_folder() and module.has_child(pkg): |
+ module = module.get_child(pkg) |
+ else: |
+ return None |
+ if module.is_folder(): |
+ if module.has_child(packages[-1]) and \ |
+ module.get_child(packages[-1]).is_folder(): |
+ return module.get_child(packages[-1]) |
+ elif module.has_child(packages[-1] + '.py') and \ |
+ not module.get_child(packages[-1] + '.py').is_folder(): |
+ return module.get_child(packages[-1] + '.py') |