Esempio n. 1
0
    def _meta_updater(self, pathmeta, fetch=True):
        original = self.meta
        file_is_different, updated = self._update_meta(original, pathmeta)
        # FIXME missing checksum is one source of problems here
        must_fetch = file_is_different and self.is_file() and self.exists(
        ) and fetch

        if must_fetch:
            try:
                # FIXME performance, and pathmeta.checksum is None case
                if self.local.content_different(
                ) and self.local.meta.checksum != pathmeta.checksum:
                    raise exc.LocalChangesError(f'not fetching {self}')

            except exc.NoRemoteFileWithThatIdError as e:
                log.warning(
                    'cant fetch remote file there may be untracked local changes for\n{self}'
                )

            log.info(f'crumpling to preserve existing metadata\n{self}')
            trashed = self.crumple()

        try:
            self._meta_setter(updated)
            if must_fetch:
                self.fetch(size_limit_mb=None)

        except BaseException as e:
            log.error(e)
            if must_fetch:
                trashed.rename(self)
            raise e

        return file_is_different
Esempio n. 2
0
    def update_cache(self, cache=None, fetch=True):
        """ Update a cache object using the metadata attached to this remote.

            This is different form _cache_setter in that it runs update_meta
            by default, handles many more edge cases, and checks for consistency.
            _cache_setter is usually invoked internally by a CachePath method that
            wants to register itself with a remote as an implementaiton detail. """

        if cache is not None and self.cache is not None:
            # TODO see if there are any exceptions to this behavior
            raise TypeError(
                'cannot accept cache kwarg when self.cache not None')
        elif cache is None:
            cache = self.cache

        parent_changed = self._parent_changed(cache)

        if self.cache is None:
            # HACK test if cache is not None before it may have been reassigned
            if cache.name != self.name:
                msg = ('Cannot update the name and content of a file at the '
                       'same time.\nAre you sure you have passed the right '
                       f'cache object?\n{cache.name} != {self.name}')
                raise ValueError(msg)

            elif parent_changed:
                msg = ('Cannot update the parent and content of a file at the '
                       'same time.\nAre you sure you have passed the right '
                       f'cache object?\n{cache.parent.id} != {self.parent_id}')
                raise ValueError(msg)

        log.debug(f'maybe updating cache for {self.name}')
        file_is_different = cache._meta_updater(self.meta, fetch=fetch)
        # update the cache first  # FIXME this may be out of order ...
        # then move to the new name if relevant
        # prevents moving partial metadata onto existing files
        if cache.name != self.name or parent_changed:  # this is localy correct
            # the issue is that move is now smarter
            # and will detect if a parent path has changed
            try:
                cache.move(remote=self)
            except exc.WhyDidntThisGetMovedBeforeError as e:
                # AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
                # deal with the sadness that is non-unique filenames
                # I am 99.999999999999999% certain that users do not
                # expect this behavior ...
                log.error(e)
                self._on_cache_move_error(e, cache)

        return file_is_different
Esempio n. 3
0
    def stat(self):
        remote_cmd = self._stat_cmd()
        out = self._ssh(remote_cmd)
        try:
            return StatResult(out)
        except ValueError as e:
            if out.endswith(b'Permission denied'):
                raise PermissionError(out.decode())

            elif out.endswith(b'No such file or directory'):
                raise FileNotFoundError(out.decode())

            else:
                log.error(remote_cmd)
                raise ValueError(out) from e
Esempio n. 4
0
    def _parts_relative_to(self, remote, cache_parent=None):
        parent_names = [
        ]  # FIXME massive inefficient due to retreading subpaths :/
        # have a look at how pathlib implements parents
        parent = self.parent
        if parent != remote:
            parent_names.append(parent.name)
            # FIXME can this go stale? if so how?
            #log.debug(cache_parent)
            if cache_parent is not None and parent.id == cache_parent.id:
                for c_parent in cache_parent.parents:
                    if c_parent is None:
                        continue
                    elif c_parent.name == remote.name:  # FIXME trick to avoid calling id
                        parent_names.append(
                            c_parent.name
                        )  # since be compare one earlier we add here
                        break
                    else:
                        parent_names.append(c_parent.name)

            else:
                for parent in parent.parents:
                    if parent == remote:
                        break
                    elif parent is None:
                        continue  # value error incoming
                    else:
                        parent_names.append(parent.name)

                else:
                    self._errors += ['file-deleted']
                    msg = f'{remote} is not one of {self}\'s parents'
                    log.error(msg)
                    #raise ValueError()

            args = (*reversed(parent_names), self.name)
        elif self == parent:
            args = ('', )
        else:
            args = self.name,

        return args
Esempio n. 5
0
    def meta(self, pathmeta):
        if not self.exists():
            # if the path does not exist write even temporary to disk
            if self.is_symlink():
                meta = self.meta
                if meta == pathmeta:
                    log.debug(
                        f'Metadata unchanged for {meta.id}. Not updating.')
                    return

                if meta.id != pathmeta.id:
                    msg = ('Existing cache id does not match new id!\n'
                           f'{self!r}\n'
                           f'{meta.id} != {pathmeta.id}\n'
                           f'{meta.as_pretty()}\n'
                           f'{pathmeta.as_pretty()}')
                    log.critical(msg)
                    meta_newer = 'Meta newer. Not updating.'
                    pathmeta_newer = 'Other meta newer.'
                    msg = '{}'  # apparently I was out of my mind when I wrote this originally ...
                    if meta.updated is None and pathmeta.updated is None:
                        log.warning(
                            'no change since either has an updated value (wat)'
                        )
                        return  #FIXME

                    if meta.updated > pathmeta.updated:
                        log.info(msg.format(meta_newer))
                        return  # this is the right thing to do for a sane filesystem
                    elif meta.updated < pathmeta.updated:
                        log.info(msg.format(pathmeta_newer))
                        # THIS IS EXPLICITLY ALLOWED
                    else:  # they are equal
                        extra = 'Both updated at the same time '
                        if meta.created is not None and pathmeta.created is not None:
                            if meta.created > pathmeta.created:
                                log.info(msg.format(extra + meta_newer))
                                return
                            elif meta.created < pathmeta.created:
                                log.info(msg.format(extra + pathmeta_newer))
                                # THIS IS EXPLICITLY ALLOWED
                            else:  # same created
                                log.info(
                                    msg.format(
                                        'Identical timestamps. Not updating.'))
                                return
                        elif meta.created is not None:
                            log.info(
                                msg.format(
                                    extra +
                                    'Meta has datetime other does not. Not updating.'
                                ))
                            return
                        elif pathmeta.created is not None:
                            msg = msg.format(
                                extra + 'Meta has no datetime other does.')
                            log.info(msg)
                            raise exc.MetadataIdMismatchError(msg)
                        else:  # both none
                            log.info(
                                msg.format(extra + (
                                    'Identical update time both missing created time. '
                                    'Not updating.')))
                            return
                    # equality
                # id mismatch all cases above should return or raise except for other metadata newer

                if meta.size is not None and pathmeta.size is None:
                    log.error('new meta has no size so will not overwrite')
                    return

                # FIXME do the timestamp dance above here
                log.debug('Metadata exists, but ids match so will update')

                # trash old versions instead of just unlinking
                pc = self.local.cache
                trash = pc.trash
                self.rename(trash / f'{pc.parent.id}-{meta.id}-{self.name}')
                #self.unlink()

            # FIXME if an id starts with / then the local name is overwritten due to pathlib logic
            # we need to error if that happens
            #symlink = pathlib.PurePosixPath(self.local.name, pathmeta.as_symlink().as_posix().strip('/'))
            symlink = pathlib.PurePosixPath(
                self.local.name) / pathmeta.as_symlink()
            self.local.symlink_to(symlink)

        else:
            raise exc.PathExistsError(f'Path exists {self}')