def remove_source_file(self, real_path): try: fake_paths = self.get_fake_paths(real_path) except PathNotFound: log_debug(u'remove_source_file: PathNotFound: %s', real_path) return for fake_path in fake_paths: log_debug( u'remove_source_file: removing %r, %r', fake_path, real_path, ) try: self.path_store.remove(fake_path, real_path) except PathNotFound: log_traceback() self._cache_prune_branch_to(fake_path) try: self.monitor.remove_source_file(self.source_tree.encode(real_path)) except NoSuchWatchError, e: log_error('failed to remove source file %s: %s', real_path, e) return
def _get_directory_time_attr(self, fake_path, attr_name): entries = self.get_entries(fake_path) entry_paths = [os.path.join(fake_path, e) for e in entries] # FIXME: This could be more efficient. subdirs = [ entry_path for entry_path in entry_paths if self.is_dir(entry_path) ] files = [ entry_path for entry_path in entry_paths if self.is_file(entry_path) ] subdir_times = [] for subdir in subdirs: try: time_attr = self._get_directory_time_attr(subdir, attr_name) except OSError, e: log_error( (u'_get_directory_time_attr: ' u'caught %r getting %r for subdir %r'), e, attr_name, subdir, ) else: subdir_times.append(time_attr)
def add_source_dir(self, real_path): ''' Recursively add source directory ``real_path`` to the source tree representation. Fail silently if: * Path corresponds with a file that is not a directory. * Directory does not exist. If a directory with the same real path already exists in the source tree representation, it will be silently removed from the representation before the add operation is executed. ''' # We only remove the existing entry if it is a directory. An existing # entry that is a file likely indicates a more serious problem. That # should give us a traceback. try: self.remove_source_dir(real_path) except PathNotFound: pass try: self.monitor.add_source_dir(self.source_tree.encode(real_path)) except (PathNotFound, NotADirectory, WatchExistsError), e: log_error('failed to add source directory %s: %s', real_path, e) return
def _get_subdirectories(self, fake_path): entries = self.get_entries(fake_path) subdirs = [] for entry in entries: entry_path = os.path.join(fake_path, entry) try: if self.is_dir(entry_path): subdirs.append(entry_path) except PathNotFound: log_traceback() log_error( u'PathNotFound calling is_dir on previously known entry') pass return subdirs
def remove_source_dir(self, real_path): ''' Recursively remove source directory ``real_path`` from the source tree representation. Do nothing if: * ``real_path`` was never added to the source tree representation. ''' # We remove source files only because any subdirectories should already # have been removed. for real_subpath in self.path_store.get_real_subpaths(real_path): self.remove_source_file(real_subpath) try: self.monitor.remove_source_dir(self.source_tree.encode(real_path)) except NoSuchWatchError, e: log_error('failed to remove source directory %s: %s', real_path, e) return
def add_directory(self, fake_path): self.validate_fake_path(fake_path) parts = split_path(fake_path) len_parts = len(parts) if len_parts >= len(self.substitution_patterns): log_error( 'add_directory: too many directories: %r', fake_path, ) raise InvalidArgument splitters = self._create_splitters(fake_path, {}) splitter = splitters[-1] try: splitter.split(parts[-1]) except PatternError, e: log_info(u'add_directory: %s', e) raise InvalidArgument
def _getattr(self, fake_path): try: real_path = self.get_real_path(fake_path) except (IsADirectory, PathNotFound): pass else: return self.source_tree.lstat(real_path) # Files should've been handled above. Now we're just dealing with # directories and non-existent paths. # If fake_path doesn't exist, the exception will be raised here and # caught be our caller. subdirs = self._get_subdirectories(fake_path) source_root_statinfo = self.source_tree.lstat(self.source_tree.root) if not isinstance(source_root_statinfo, os.stat_result): # FIXME: I don't think this is being handled correctly. Should we # be negating the value before returning it? Identify when and # where this actually happens and write a test for it. I think I # only saw this once or twice when testing on OSX. It may be a bug # in os.lstat. # source_root_statinfo is actually an integer indicating an error. log_error( u'SourceTreeRepresentation._getattr: source_root_statinfo = %r', source_root_statinfo, ) return source_root_statinfo st_mode = stat.S_IFDIR | (stat.S_IMODE(source_root_statinfo.st_mode)) st_ino = 0 st_dev = 0 st_nlink = 2 + len(subdirs) st_uid = source_root_statinfo.st_uid st_gid = source_root_statinfo.st_gid st_size = 0 st_atime = self._get_directory_atime(fake_path) st_mtime = self._get_directory_mtime(fake_path) st_ctime = self._get_directory_ctime(fake_path) if st_atime == 0: st_atime = source_root_statinfo.st_atime if st_mtime == 0: st_mtime = source_root_statinfo.st_mtime if st_ctime == 0: st_ctime = source_root_statinfo.st_ctime return os.stat_result(( st_mode, st_ino, st_dev, st_nlink, st_uid, st_gid, st_size, st_atime, st_mtime, st_ctime, ))
def rename_path(self, old_fake_path, new_fake_path): self.validate_fake_path(old_fake_path) self.validate_fake_path(new_fake_path) log_info( u'SourceTreeRepresentation.rename_path: renaming %s to %s', old_fake_path, new_fake_path, ) old_path_parts = split_path(old_fake_path) new_path_parts = split_path(new_fake_path) if len(old_path_parts) != len(new_path_parts): log_error( (u'rename_path: old path and new path have ' u'differing directory depths: %s, %s'), old_fake_path, new_fake_path, ) raise InvalidArgument # Find the index of the path segment that changed: for index, (old_path_part, new_path_part) in enumerate( zip(old_path_parts, new_path_parts)): if old_path_part != new_path_part: break old_node_path = join_path_abs(old_path_parts[:index + 1]) # Here's our approach: # 1. Separate the affected end points into files and directories. # 2. For each directory end point, remove the old directory and add the # new one. # 3. For each file end point: # a. We know which path segment changed, so we can use that # information to get old tag values for that segment and new tag # values for that segment. These values represent the total tag # change for that particular end point. # b. Group the values by real path and then combine each group. # c. For each affected real path, use Values.diff3 to calculate a # final values delta and apply it to the real path. end_points = self.path_store.get_end_points(old_fake_path) file_end_points = [] directory_end_points = [] for end_point in end_points: if self.is_file(end_point): file_end_points.append(end_point) else: directory_end_points.append(end_point) del end_points # Handle directory end points: for end_point in directory_end_points: self.remove_directory(end_point) end_point_parts = split_path(end_point) end_point_parts[index] = new_path_parts[index] self.add_directory_with_parents(join_path_abs(end_point_parts)) del directory_end_points # Get old values, new values for each file end point: old_values_by_end_point = {} new_values_by_end_point = {} for end_point in file_end_points: meta_data = self.path_store.get_meta_data(end_point) end_point_splitters = meta_data['splitters'] splitter = end_point_splitters[index] old_values = Values.from_flat_dict(splitter.split(old_path_part)) try: new_values = Values.from_flat_dict( splitter.split(new_path_part)) except PatternError, e: log_error(u'rename_path: %s', e) raise InvalidArgument old_values_by_end_point[end_point] = old_values new_values_by_end_point[end_point] = new_values
for fake_path, splitters in zip(fake_paths, splitter_groups): if not self.filter_path( self.source_tree.get_relative_path(real_path), fake_path): log_debug( u'Explicitly excluded fake path: %s; real path: %s', fake_path, real_path, ) return try: self.monitor.add_source_file( self.source_tree.encode(real_path)) except (PathNotFound, NotADirectory, WatchExistsError), e: log_error('failed to add source file %s: %s', real_path, e) return except SourceTreeMonitorError: # Log the error and ignore the file. If we can't monitor it # properly, we shouldn't present it at all. That would violate # the user's assumptions about the exposed files. log_traceback() return log_debug(u'add_source_file: adding %r, %r', fake_path, real_path) self.path_store.add_file(fake_path, real_path) self._set_splitters(fake_path, splitters) self._cache_prune_branch_to(fake_path) def remove_source_file(self, real_path): try: