"""Files and folders in a project are represented as resource objects. Files and folders are access through `Resource` objects. `Resource` has two subclasses: `File` and `Folder`. What we care about is that refactorings and `rope.base.change.Change`s use resources. There are two options to create a `Resource` for a path in a project. Note that in these examples `path` is the path to a file or folder relative to the project's root. A project's root folder is represented by an empty string. 1) Use the `rope.base.Project.get_resource()` method. E.g.: myresource = myproject.get_resource(path) 2) Use the `rope.base.libutils` module. `libutils` has a function named `path_to_resource()`. It takes a project and a path: from rope.base import libutils myresource = libutils.path_to_resource(myproject, path) Once we have a `Resource`, we can retrieve information from it, like getting the path relative to the project's root (via `path`), reading from and writing to the resource, moving the resource, etc. """ import os import re import warnings from rope.base import change from rope.base import exceptions from rope.base import fscommands class Resource(object): """Represents files and folders in a project""" def __init__(self, project, path): self.project = project self._path = path def move(self, new_location): """Move resource to `new_location`""" self._perform_change( change.MoveResource(self, new_location), "Moving <%s> to <%s>" % (self.path, new_location), ) def remove(self): """Remove resource from the project""" self._perform_change(change.RemoveResource(self), "Removing <%s>" % self.path) def is_folder(self): """Return true if the resource is a folder""" def create(self): """Create this resource""" def exists(self): return os.path.exists(self.real_path) @property def parent(self): parent = "/".join(self.path.split("/")[0:-1]) return self.project.get_folder(parent) @property def path(self): """Return the path of this resource relative to the project root The path is the list of parent directories separated by '/' followed by the resource name. """ return self._path @property def name(self): """Return the name of this resource""" return self.path.split("/")[-1] @property def real_path(self): """Return the file system path of this resource""" return self.project._get_resource_path(self.path) def __eq__(self, obj): return self.__class__ == obj.__class__ and self.path == obj.path def __ne__(self, obj): return not self.__eq__(obj) def __hash__(self): return hash(self.path) def _perform_change(self, change_, description): changes = change.ChangeSet(description) changes.add_change(change_) self.project.do(changes) class File(Resource): """Represents a file""" def __init__(self, project, name): # from rope.base.project import Project # self.project = Project() super(File, self).__init__(project, name) def read(self): data = self.read_bytes() try: return fscommands.file_data_to_unicode(data) except UnicodeDecodeError as e: raise exceptions.ModuleDecodeError(self.path, e.reason) def read_bytes(self): if not hasattr(self.project.fscommands, "read"): warnings.warn( "FileSystemCommands should implement read() method", DeprecationWarning, stacklevel=2, ) with open(self.real_path, "rb") as handle: return handle.read() return self.project.fscommands.read(self.real_path) def write(self, contents): try: if contents == self.read(): return except IOError: pass self._perform_change( change.ChangeContents(self, contents), "Writing file <%s>" % self.path ) def is_folder(self): return False def create(self): self.parent.create_file(self.name) class Folder(Resource): """Represents a folder""" def __init__(self, project, name): super(Folder, self).__init__(project, name) def is_folder(self): return True def get_children(self): """Return the children of this folder""" try: children = os.listdir(self.real_path) except OSError: return [] result = [] for name in children: try: child = self.get_child(name) except exceptions.ResourceNotFoundError: continue if not self.project.is_ignored(child): result.append(self.get_child(name)) return result def create_file(self, file_name): self._perform_change( change.CreateFile(self, file_name), "Creating file <%s>" % self._get_child_path(file_name), ) return self.get_child(file_name) def create_folder(self, folder_name): self._perform_change( change.CreateFolder(self, folder_name), "Creating folder <%s>" % self._get_child_path(folder_name), ) return self.get_child(folder_name) def _get_child_path(self, name): if self.path: return self.path + "/" + name else: return name def get_child(self, name): return self.project.get_resource(self._get_child_path(name)) def has_child(self, name): try: self.get_child(name) return True except exceptions.ResourceNotFoundError: return False def get_files(self): return [ resource for resource in self.get_children() if not resource.is_folder() ] def get_folders(self): return [resource for resource in self.get_children() if resource.is_folder()] def contains(self, resource): if self == resource: return False return self.path == "" or resource.path.startswith(self.path + "/") def create(self): self.parent.create_folder(self.name) class _ResourceMatcher(object): def __init__(self): self.patterns = [] self._compiled_patterns = [] def set_patterns(self, patterns): """Specify which resources to match `patterns` is a `list` of `str` that can contain ``*`` and ``?`` signs for matching resource names. """ self._compiled_patterns = None self.patterns = patterns def _add_pattern(self, pattern): re_pattern = ( pattern.replace(".", "\\.") .replace("*", "[^/]*") .replace("?", "[^/]") .replace("//", "/(.*/)?") ) re_pattern = "^(.*/)?" + re_pattern + "(/.*)?$" self.compiled_patterns.append(re.compile(re_pattern)) def does_match(self, resource): for pattern in self.compiled_patterns: if pattern.match(resource.path): return True path = os.path.join(resource.project.address, *resource.path.split("/")) if os.path.islink(path): return True return False @property def compiled_patterns(self): if self._compiled_patterns is None: self._compiled_patterns = [] for pattern in self.patterns: self._add_pattern(pattern) return self._compiled_patterns