Source code for aiida.orm.nodes.data.folder

###########################################################################
# Copyright (c), The AiiDA team. All rights reserved.                     #
# This file is part of the AiiDA code.                                    #
#                                                                         #
# The code is hosted on GitHub at https://github.com/aiidateam/aiida-core #
# For further information on the license, see the LICENSE.txt file        #
# For further information please visit http://www.aiida.net               #
###########################################################################
"""`Data` sub class to represent a folder on a file system."""

from __future__ import annotations

import contextlib
import io
import pathlib
import typing as t

from aiida.repository import File

from .data import Data

__all__ = ('FolderData',)

FilePath = t.Union[str, pathlib.PurePosixPath]


[docs] class FolderData(Data): """`Data` sub class to represent a folder on a file system."""
[docs] def __init__(self, **kwargs): """Construct a new `FolderData` to which any files and folders can be added. Use the `tree` keyword to simply wrap a directory: folder = FolderData(tree='/absolute/path/to/directory') Alternatively, one can construct the node first and then use the various repository methods to add objects: folder = FolderData() folder.put_object_from_tree('/absolute/path/to/directory') folder.put_object_from_filepath('/absolute/path/to/file.txt') folder.put_object_from_filelike(filelike_object) :param tree: absolute path to a folder to wrap :type tree: str """ tree = kwargs.pop('tree', None) super().__init__(**kwargs) if tree: self.base.repository.put_object_from_tree(tree)
[docs] def list_objects(self, path: str | None = None) -> list[File]: """Return a list of the objects contained in this repository sorted by name, optionally in given sub directory. :param path: optional relative path inside the repository whose objects to list. :return: a list of `File` named tuples representing the objects present in directory with the given key. :raises TypeError: if the path is not a string and relative path. :raises FileNotFoundError: if no object exists for the given path. :raises NotADirectoryError: if the object at the given path is not a directory. """ return self.base.repository.list_objects(path)
[docs] def list_object_names(self, path: str | None = None) -> list[str]: """Return a sorted list of the object names contained in this repository, optionally in the given sub directory. :param path: optional relative path inside the repository whose objects to list. :return: a list of `File` named tuples representing the objects present in directory with the given key. :raises TypeError: if the path is not a string and relative path. :raises FileNotFoundError: if no object exists for the given path. :raises NotADirectoryError: if the object at the given path is not a directory. """ return self.base.repository.list_object_names(path)
@t.overload @contextlib.contextmanager def open(self, path: FilePath, mode: t.Literal['r']) -> t.Iterator[t.TextIO]: ... @t.overload @contextlib.contextmanager def open(self, path: FilePath, mode: t.Literal['rb']) -> t.Iterator[t.BinaryIO]: ...
[docs] @contextlib.contextmanager def open(self, path: FilePath, mode: t.Literal['r', 'rb'] = 'r') -> t.Iterator[t.BinaryIO] | t.Iterator[t.TextIO]: """Open a file handle to an object stored under the given key. .. note:: this should only be used to open a handle to read an existing file. To write a new file use the method ``put_object_from_filelike`` instead. :param path: the relative path of the object within the repository. :return: yield a byte stream object. :raises TypeError: if the path is not a string and relative path. :raises FileNotFoundError: if the file does not exist. :raises IsADirectoryError: if the object is a directory and not a file. :raises OSError: if the file could not be opened. """ with self.base.repository.open(path, mode) as handle: yield handle
[docs] @contextlib.contextmanager def as_path(self, path: FilePath | None = None) -> t.Iterator[pathlib.Path]: """Make the contents of the repository available as a normal filepath on the local file system. :param path: optional relative path of the object within the repository. :return: the filepath of the content of the repository or object if ``path`` is specified. :raises TypeError: if the path is not a string or ``Path``, or is an absolute path. :raises FileNotFoundError: if no object exists for the given path. """ with self.base.repository.as_path(path) as filepath: yield filepath
[docs] def get_object(self, path: FilePath | None = None) -> File: """Return the object at the given path. :param path: the relative path of the object within the repository. :return: the `File` representing the object located at the given relative path. :raises TypeError: if the path is not a string or ``Path``, or is an absolute path. :raises FileNotFoundError: if no object exists for the given path. """ return self.base.repository.get_object(path)
@t.overload def get_object_content(self, path: str, mode: t.Literal['r']) -> str: ... @t.overload def get_object_content(self, path: str, mode: t.Literal['rb']) -> bytes: ...
[docs] def get_object_content(self, path: str, mode: t.Literal['r', 'rb'] = 'r') -> str | bytes: """Return the content of a object identified by key. :param path: the relative path of the object within the repository. :raises TypeError: if the path is not a string and relative path. :raises FileNotFoundError: if the file does not exist. :raises IsADirectoryError: if the object is a directory and not a file. :raises OSError: if the file could not be opened. """ return self.base.repository.get_object_content(path, mode)
[docs] def put_object_from_bytes(self, content: bytes, path: str) -> None: """Store the given content in the repository at the given path. :param path: the relative path where to store the object in the repository. :param content: the content to store. :raises TypeError: if the path is not a string and relative path. :raises FileExistsError: if an object already exists at the given path. """ return self.base.repository.put_object_from_bytes(content, path)
[docs] def put_object_from_filelike(self, handle: io.BufferedReader, path: str) -> None: """Store the byte contents of a file in the repository. :param handle: filelike object with the byte content to be stored. :param path: the relative path where to store the object in the repository. :raises TypeError: if the path is not a string and relative path. :raises `~aiida.common.exceptions.ModificationNotAllowed`: when the node is stored and therefore immutable. """ return self.base.repository.put_object_from_filelike(handle, path)
[docs] def put_object_from_file(self, filepath: str, path: str) -> None: """Store a new object under `path` with contents of the file located at `filepath` on the local file system. :param filepath: absolute path of file whose contents to copy to the repository :param path: the relative path where to store the object in the repository. :raises TypeError: if the path is not a string and relative path, or the handle is not a byte stream. :raises `~aiida.common.exceptions.ModificationNotAllowed`: when the node is stored and therefore immutable. """ return self.base.repository.put_object_from_file(filepath, path)
[docs] def put_object_from_tree(self, filepath: str, path: str | None = None) -> None: """Store the entire contents of `filepath` on the local file system in the repository with under given `path`. :param filepath: absolute path of the directory whose contents to copy to the repository. :param path: the relative path where to store the objects in the repository. :raises TypeError: if the path is not a string and relative path. :raises `~aiida.common.exceptions.ModificationNotAllowed`: when the node is stored and therefore immutable. """ return self.base.repository.put_object_from_tree(filepath, path)
[docs] def walk(self, path: FilePath | None = None) -> t.Iterable[tuple[pathlib.PurePosixPath, list[str], list[str]]]: """Walk over the directories and files contained within this repository. .. note:: the order of the dirname and filename lists that are returned is not necessarily sorted. This is in line with the ``os.walk`` implementation where the order depends on the underlying file system used. :param path: the relative path of the directory within the repository whose contents to walk. :return: tuples of root, dirnames and filenames just like ``os.walk``, with the exception that the root path is always relative with respect to the repository root, instead of an absolute path and it is an instance of ``pathlib.PurePosixPath`` instead of a normal string """ yield from self.base.repository.walk(path)
[docs] def glob(self) -> t.Iterable[pathlib.PurePosixPath]: """Yield a recursive list of all paths (files and directories).""" yield from self.base.repository.glob()
[docs] def copy_tree(self, target: str | pathlib.Path, path: FilePath | None = None) -> None: """Copy the contents of the entire node repository to another location on the local file system. :param target: absolute path of the directory where to copy the contents to. :param path: optional relative path whose contents to copy. """ self.base.repository.copy_tree(target, path)
[docs] def delete_object(self, path: str) -> None: """Delete the object from the repository. :param key: fully qualified identifier for the object within the repository. :raises TypeError: if the path is not a string and relative path. :raises FileNotFoundError: if the file does not exist. :raises IsADirectoryError: if the object is a directory and not a file. :raises OSError: if the file could not be deleted. :raises `~aiida.common.exceptions.ModificationNotAllowed`: when the node is stored and therefore immutable. """ self.base.repository.delete_object(path)
[docs] def erase(self) -> None: """Delete all objects from the repository. :raises `~aiida.common.exceptions.ModificationNotAllowed`: when the node is stored and therefore immutable. """ self.base.repository.erase()