示例#1
0
    def test_is_root(self):
        self.assertTrue(ProperPath('/', True).is_root)
        with self.assertRaises(InvalidPathError):
            resp = ProperPath('/', False).is_root

        self.assertFalse(ProperPath('/this/folder', True).is_root)
        self.assertFalse(ProperPath('/this/folder', False).is_root)
示例#2
0
    def test_input_isdir_determines_file(self):
        self.assertFalse(ProperPath('/this/is/folder', True).is_file)
        self.assertFalse(ProperPath('/this/is/folder/', True).is_file)

        self.assertTrue(ProperPath('/this/is/folder', False).is_file)
        with self.assertRaises(InvalidPathError):
            resp = ProperPath('/this/is/folder/', False).is_file
示例#3
0
 def test_double_slash_is_invalid(self):
     with self.assertRaises(InvalidPathError):
         resp = ProperPath('/hi//this', True)
     with self.assertRaises(InvalidPathError):
         resp = ProperPath('//hi/this', True)
     with self.assertRaises(InvalidPathError):
         resp = ProperPath('/hi/this//', True)
 def _get_item_by_path(self, path):
     for node in session.query(Node):
         if ProperPath(node.path, True) == path:
             return node
     for file_folder in session.query(File):
         file_path = ProperPath(file_folder.path, file_folder.is_folder)
         if file_path == path:
             return file_folder
     raise ItemNotInDB('item has path: {}'.format(path.full_path))
示例#5
0
 def test_cant_have_dotdot(self):
     with self.assertRaises(InvalidPathError):
         resp = ProperPath('..', True)
     with self.assertRaises(InvalidPathError):
         resp = ProperPath('/hi/../as', True)
     with self.assertRaises(InvalidPathError):
         resp = ProperPath('../', True)
     with self.assertRaises(InvalidPathError):
         resp = ProperPath('/..', True)
 def _event_is_for_components_file_folder(self, event):
     try:
         if ProperPath(event.src_path, True).name == 'Components':
             return True
         try:
             return ProperPath(event.dest_path, True).name == 'Components'
         except AttributeError:
             return False
     except Exception:
         pass  # Similar path-related error will occur and be caught later, better error message will be displayed then
 def _get_proper_path(self, item):
     if isinstance(item, ProperPath):
         return item
     elif isinstance(item, User):
         return ProperPath(item.osf_local_folder_path, True)
     elif isinstance(item, File) and item.is_file:
         return ProperPath(item.path, False)
     elif isinstance(item, Base):
         return ProperPath(item.path, True)
     elif isinstance(item, str):
         absolute = os.path.join(self.osf_path.full_path, item)
         return ProperPath(absolute, os.path.isdir(absolute))
     else:
         raise InvalidItemType('LocalDBSync._get_proper_path does '
                               'not handle items of type '
                               '{item_type}'.format(item_type=type(item)))
示例#8
0
    def run(self):
        try:
            old_folder_path = ProperPath(self.old_path, is_dir=True)
        except Exception:
            # TODO: Narrow down this exception and do client side warnings
            logging.exception(
                'Exception caught: Invalid origin path for renamed folder.')
            return
        try:
            new_folder_path = ProperPath(self.new_path, is_dir=True)
        except Exception:
            # TODO: Narrow down this exception and do client side warnings
            logging.exception(
                'Exception caught: Invalid target path for renamed folder.')
            return

        AlertHandler.info(new_folder_path.name, AlertHandler.MODIFYING)
        yield from _rename(old_folder_path, new_folder_path)
示例#9
0
 def run(self):
     file_to_delete = ProperPath(self.path, is_dir=False)
     AlertHandler.info(file_to_delete.name, AlertHandler.DELETING)
     try:
         os.remove(file_to_delete.full_path)
     except FileNotFoundError:
         logging.warning(
             'file not deleted because does not exist on local filesystem. inside delete_local_file_folder (2)'
         )
    def _get_children(self, item):
        if item is None:
            return []

        # local
        if isinstance(item, ProperPath):
            if item.is_file:
                return []
            else:
                children = []
                for child in os.listdir(item.full_path):
                    child_item_path = os.path.join(item.full_path, child)
                    is_dir = os.path.isdir(child_item_path)
                    child_item = ProperPath(child_item_path, is_dir)

                    # handle the components folder
                    if child_item.name == LocalDBSync.COMPONENTS_FOLDER_NAME:
                        for component in os.listdir(child_item_path):
                            component_path = os.path.join(
                                child_item_path, component)
                            component_is_dir = os.path.isdir(component_path)
                            # NOTE: making a concious decision here to ignore invalid file in components folder
                            # NOTE: this means the user is not getting an alert in this case
                            if component_is_dir:
                                children.append(
                                    ProperPath(component_path,
                                               component_is_dir))
                    else:
                        children.append(child_item)
                return children
        # db
        else:
            if isinstance(item, File):
                return item.files
            elif isinstance(item, Node):
                return item.child_nodes + item.top_level_file_folders
            elif isinstance(item, User):
                return item.top_level_nodes
            else:
                raise InvalidItemType(
                    'LocalDBSync._get_children does '
                    'not handle items of type '
                    '{item_type}'.format(item_type=type(item)))
示例#11
0
 def run(self):
     try:
         new_file_path = ProperPath(self.path, is_dir=False)
     except Exception:
         # TODO: Narrow down this exception and do client side warnings
         logging.exception(
             'Exception caught: Invalid target path for new file.')
         return
     AlertHandler.info(new_file_path.name, AlertHandler.DOWNLOAD)
     yield from _download_file(new_file_path, self.download_url,
                               self.osf_query)
    def __init__(self, absolute_osf_dir_path, observer, user):
        if not isinstance(observer, Observer):
            raise TypeError
        if not isinstance(user, User):
            raise TypeError
        if not os.path.isdir(absolute_osf_dir_path):
            raise FolderNotInFileSystem

        self.osf_path = ProperPath(absolute_osf_dir_path, True)
        self.observer = observer
        self.user = user
    def on_deleted(self, event):
        """Called when a file or directory is deleted.

        :param event:
            Event representing file/directory deletion.
        :type event:
            :class:`DirDeletedEvent` or :class:`FileDeletedEvent`
        """
        try:
            src_path = ProperPath(event.src_path, event.is_directory)
        except Exception:
            logging.exception('Exception caught: Invalid path')
            AlertHandler.warn(
                'invalid path specified. {} will not be synced'.format(
                    os.path.basename(event.src_path)))
        else:
            if not self._already_exists(src_path):
                return

            # get item
            try:
                item = self._get_item_by_path(src_path)
            except ItemNotInDB:
                logging.exception(
                    'Exception caught: Tried to delete item {}, but it was not found in DB'
                    .format(src_path.name))
            else:
                # put item in delete state after waiting a second and
                # checking to make sure the file was actually deleted
                yield from asyncio.sleep(1)
                if not os.path.exists(item.path):
                    item.locally_deleted = True
                    # nodes cannot be deleted online. THUS, delete it inside database. It will be recreated locally.
                    if isinstance(item, Node):
                        session.delete(item)
                        try:
                            save(session)
                        except SQLAlchemyError as e:
                            logging.exception(
                                'Exception caught: Error deleting node {} from database.'
                                .format(item.name))
                        return
                    try:
                        save(session, item)
                    except SQLAlchemyError as e:
                        logging.exception(
                            'Exception caught: Error deleting {} {} from database.'
                            .format('folder' if event.is_directory else 'file',
                                    item.name))
                    else:
                        logging.info('{} set to be deleted'.format(
                            src_path.full_path))
    def on_modified(self, event):
        """Called when a file or directory is modified.

        :param event:
            Event representing file/directory modification.
        :type event:
            :class:`DirModifiedEvent` or :class:`FileModifiedEvent`
        """

        if isinstance(event, DirModifiedEvent):
            return
        try:
            src_path = ProperPath(event.src_path, event.is_directory)
        except Exception:
            logging.exception('Exception caught: Invalid path')
            AlertHandler.warn(
                'invalid path specified. {} will not be synced'.format(
                    os.path.basename(event.src_path)))
        else:
            # get item
            try:
                item = self._get_item_by_path(src_path)
            except ItemNotInDB:
                # todo: create file folder
                logging.warning(
                    'file {} was modified but not already in db. create it in db.'
                    .format(src_path))
                return  # todo: remove this once above is implemented

            # update hash
            try:
                item.update_hash()
            except OSError:
                logging.exception('File inaccessible during update_hash')
                AlertHandler.warn(
                    'Error updating {}. {} inaccessible, will stop syncing.'.
                    format('Folder' if event.is_directory else 'File',
                           item.name))
                return

            # save
            try:
                save(session, item)
            except SQLAlchemyError:
                logging.exception(
                    'Exception caught: Could not save data for {}'.format(
                        item))
                AlertHandler.warn(
                    'Error updating {}. {} will stop syncing.'.format(
                        'folder' if event.is_directory else 'file', item.name))
示例#15
0
 def run(self):
     if not isinstance(self.osf_query, OSFQuery):
         logging.error('Update file query is not an instance of OSFQuery')
         return
     if not isinstance(self.download_url, str):
         logging.error('Update file download_url is not a str.')
         return
     try:
         updated_file_path = ProperPath(self.path, is_dir=False)
     except Exception:
         # TODO: Narrow down this exception and do client side warnings
         logging.exception(
             'Exception caught: Invalid target path for updated file.')
         return
     AlertHandler.info(updated_file_path.name, AlertHandler.MODIFYING)
     yield from _download_file(updated_file_path, self.download_url,
                               self.osf_query)
示例#16
0
 def run(self):
     # create local node folder on filesystem
     try:
         folder_to_create = ProperPath(self.path, is_dir=True)
     except Exception:
         # TODO: Narrow down this exception and do client side warnings
         logging.exception(
             'Exception caught: Invalid target path for folder.')
         return
     if not os.path.exists(folder_to_create.full_path):
         AlertHandler.info(folder_to_create.name, AlertHandler.DOWNLOAD)
         try:
             os.makedirs(folder_to_create.full_path)
         except Exception:
             # TODO: Narrow down this exception and do client side warnings
             logging.exception(
                 'Exception caught: Problem making a directory.')
             return
示例#17
0
    def run(self):
        try:
            folder_to_delete = ProperPath(self.path, is_dir=True)
        except Exception:
            # TODO: Narrow down this exception and do client side warnings
            logging.exception(
                'Exception caught: Invalid source path for deleted folder.')
            return

        AlertHandler.info(folder_to_delete.name, AlertHandler.DELETING)
        try:
            shutil.rmtree(
                folder_to_delete.full_path,
                onerror=lambda a, b, c: logging.warning(
                    'local node not deleted because it does not exist.'))
        except Exception:
            # TODO: Narrow down this exception and do client side warnings
            logging.exception('Exception caught: Problem removing the tree.')
            return
    def on_created(self, event):
        """Called when a file or directory is created.

        :param event:
            Event representing file/directory creation.
        :type event:
            :class:`DirCreatedEvent` or :class:`FileCreatedEvent`
        """
        try:
            src_path = ProperPath(event.src_path, event.is_directory)
        except Exception:
            logging.exception('Exception caught: Invalid path')
            AlertHandler.warn(
                'invalid path specified. {} will not be synced.'.format(
                    os.path.basename(event.src_path)))
        else:
            # create new model
            if self._already_exists(src_path):
                return

            yield from self._create_file_or_folder(event, src_path=src_path)
 def __init__(self, osf_folder, loop):
     super().__init__()
     self._loop = loop or asyncio.get_event_loop()
     self.osf_folder = ProperPath(osf_folder, True)
     self.user = session.query(User).filter(User.logged_in).one()
示例#20
0
 def test_should_expand_tilde(self):
     with self.assertRaises(InvalidPathError):
         resp = ProperPath('~/mydir/', True)
     with self.assertRaises(InvalidPathError):
         resp = ProperPath('~/mydir', False)
示例#21
0
 def test_file_cant_end_with_slash(self):
     with self.assertRaises(InvalidPathError):
         resp = ProperPath('/hi/this/is/file/', False)
示例#22
0
 def test_root_cant_be_file(self):
     with self.assertRaises(InvalidPathError):
         resp = ProperPath('/', False)
    def on_moved(self, event):
        """Called when a file or a directory is moved or renamed.

        :param event:
            Event representing file/directory movement.
        :type event:
            :class:`DirMovedEvent` or :class:`FileMovedEvent`
        """
        try:
            src_path = ProperPath(event.src_path, event.is_directory)
            dest_path = ProperPath(event.dest_path, event.is_directory)
        except Exception:
            logging.exception('Exception caught: Invalid path specified')
            AlertHandler.warn('Error moving {}. {} will not be synced.'.format(
                'folder' if event.is_directory else 'file',
                os.path.basename(event.src_path)))
        else:
            # determine and get what moved
            if not self._already_exists(src_path):
                try:
                    self._get_parent_item_from_path(src_path)
                except ItemNotInDB:
                    # This means it was put into a place on the hierarchy being watched but otherwise not attached to a
                    # node, so it needs to be added just like a new event rather than as a move.

                    new_event = DirCreatedEvent(
                        event.dest_path
                    ) if event.is_directory else FileCreatedEvent(
                        event.dest_path)
                    yield from self._create_file_or_folder(new_event,
                                                           src_path=dest_path)
                    return
                logging.warning(
                    'Tried to move item that does not exist: {}'.format(
                        src_path.name))
                return

            try:
                item = self._get_item_by_path(src_path)
            except ItemNotInDB:
                logging.exception(
                    'Exception caught: Tried to move or rename item {}, but it could not be found in DB'
                    .format(src_path.name))
                AlertHandler.warn(
                    'Could not find item to manipulate. {} will not be synced'.
                    format(src_path.name))
            else:
                if isinstance(item, Node):
                    AlertHandler.warn(
                        'Cannot manipulate components locally. {} will stop syncing'
                        .format(item.title))
                    return

                # File

                # rename
                if item.name != dest_path.name:
                    item.name = dest_path.name
                    item.locally_renamed = True
                    try:
                        save(session, item)
                    except SQLAlchemyError:
                        logging.exception(
                            'Exception caught: Could not save data for {}'.
                            format(item))
                        AlertHandler.warn(
                            'Error renaming file. {} will stop syncing.'.
                            format(item.name))
                    else:
                        logging.info("renamed a file {}".format(
                            dest_path.full_path))
                # move
                elif src_path != dest_path:
                    # check if file already exists in this moved location. If so, delete it from db.
                    try:
                        item_to_replace = self._get_item_by_path(dest_path)
                        session.delete(item_to_replace)
                        save(session)
                    except ItemNotInDB:
                        logging.info(
                            'file does not already exist in moved destination: {}'
                            .format(dest_path.full_path))
                    except SQLAlchemyError:
                        logging.exception(
                            'Exception caught: Could not save data for {}'.
                            format(item_to_replace))
                        AlertHandler.warn(
                            'Error moving file. {} will stop syncing.'.format(
                                dest_path.name))

                    try:
                        new_parent_item = self._get_parent_item_from_path(
                            dest_path)
                    except ItemNotInDB:
                        AlertHandler.warn(
                            '{} {} placed into invalid containing folder. It will not be synced.'
                            .format('Folder' if event.is_directory else 'File',
                                    dest_path.name))
                    else:
                        # move item

                        # set previous fields
                        item.previous_provider = item.provider
                        item.previous_node_osf_id = item.node.osf_id

                        # update parent and node fields
                        # NOTE: this line makes it so the file no longer exists in the database.
                        # NOTE: item at this point is stale. Unclear why it matters though.
                        # NOTE: fix is above: session.refresh(item)
                        item.parent = new_parent_item if isinstance(
                            new_parent_item, File) else None
                        item.node = new_parent_item if isinstance(
                            new_parent_item, Node) else new_parent_item.node

                        # basically always osfstorage. this is just meant to be extendible in the future to other providers
                        item.provider = new_parent_item.provider if isinstance(
                            new_parent_item, File) else File.DEFAULT_PROVIDER

                        # flags
                        item.locally_moved = True
                        try:
                            save(session, item)
                        except SQLAlchemyError:
                            logging.exception(
                                'Exception caught: Could not save data for {}'.
                                format(item))
                            AlertHandler.warn(
                                'Error moving file. {} will stop syncing.'.
                                format(item.name))
                        else:
                            logging.info('moved from {} to {}'.format(
                                src_path.full_path, dest_path.full_path))
示例#24
0
 def test_cant_be_empty(self):
     with self.assertRaises(InvalidPathError):
         resp = ProperPath('', True)
示例#25
0
 def test_name_file(self):
     path = ProperPath('/this/is/a/long/path', False)
     self.assertEquals(path.name, 'path')
示例#26
0
 def test_name_file_with_slash(self):
     with self.assertRaises(InvalidPathError):
         path = ProperPath('/this/is/a/long/path/', False)
示例#27
0
 def test_parent_folder_with_slash(self):
     path = ProperPath('/this/is/a/long/path/', True)
     self.assertEquals(path.parent.name, 'long')
     self.assertEquals(path.parent, ProperPath('/this/is/a/long', True))
示例#28
0
 def test_parent_file(self):
     path = ProperPath('/this/is/a/long/path', False)
     self.assertEquals(path.parent.name, 'long')
     self.assertEquals(path.parent, ProperPath('/this/is/a/long', True))
示例#29
0
 def test_name_folder_with_slash(self):
     path = ProperPath('/this/is/a/long/path/', True)
     self.assertEquals(path.name, 'path')