Example #1
0
    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
Example #2
0
    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)
Example #3
0
    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
Example #4
0
    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
Example #5
0
    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
Example #6
0
    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
Example #7
0
    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,
        ))
Example #8
0
    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
Example #9
0
        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: