Skip to content

FileContext

The FileContext captures the file information. It can be created using the capsula.FileContext.builder method (recommended) or the capsula.FileContext.__init__ method.

capsula.FileContext.builder classmethod

builder(
    path: Path | str,
    *,
    compute_hash: bool = True,
    hash_algorithm: str | None = None,
    copy: bool = False,
    move: bool = False,
    ignore_missing: bool = False,
    path_relative_to_project_root: bool = False
) -> Callable[[CapsuleParams], FileContext]
PARAMETER DESCRIPTION
path

Path to the file

TYPE: Path | str

compute_hash

Whether to compute the hash of the file

TYPE: bool DEFAULT: True

hash_algorithm

Hash algorithm to use. This will be fed to hashlib.file_digest as the digest argument. If not provided, sha256 will be used.

TYPE: str | None DEFAULT: None

copy

Whether to copy the file to the run directory

TYPE: bool DEFAULT: False

move

Whether to move the file to the run directory

TYPE: bool DEFAULT: False

ignore_missing

Whether to ignore if the file does not exist

TYPE: bool DEFAULT: False

path_relative_to_project_root

Whether path is relative to the project root. Will be ignored if path is absolute. If True, it will be interpreted as relative to the project root. If False, path will be interpreted as relative to the current working directory. It is recommended to set this to True in the configuration file.

TYPE: bool DEFAULT: False

Source code in capsula/_context/_file.py
@classmethod
def builder(
    cls,
    path: Annotated[Path | str, Doc("Path to the file")],
    *,
    compute_hash: Annotated[bool, Doc("Whether to compute the hash of the file")] = True,
    hash_algorithm: Annotated[
        str | None,
        Doc(
            "Hash algorithm to use. This will be fed to `hashlib.file_digest` as the `digest` argument. "
            "If not provided, `sha256` will be used.",
        ),
    ] = None,
    copy: Annotated[bool, Doc("Whether to copy the file to the run directory")] = False,
    move: Annotated[bool, Doc("Whether to move the file to the run directory")] = False,
    ignore_missing: Annotated[bool, Doc("Whether to ignore if the file does not exist")] = False,
    path_relative_to_project_root: Annotated[
        bool,
        Doc(
            "Whether `path` is relative to the project root. Will be ignored if `path` is absolute. "
            "If True, it will be interpreted as relative to the project root. "
            "If False, `path` will be interpreted as relative to the current working directory. "
            "It is recommended to set this to True in the configuration file.",
        ),
    ] = False,
) -> Callable[[CapsuleParams], FileContext]:
    if copy and move:
        warnings.warn("Both copy and move are True. Only move will be performed.", UserWarning, stacklevel=2)
        move = True
        copy = False

    def build(params: CapsuleParams) -> FileContext:
        if path_relative_to_project_root and path is not None and not Path(path).is_absolute():
            file_path = params.project_root / path
        else:
            file_path = Path(path)

        return cls(
            path=file_path,
            compute_hash=compute_hash,
            hash_algorithm=hash_algorithm,
            copy_to=params.run_dir if copy else None,
            move_to=params.run_dir if move else None,
            ignore_missing=ignore_missing,
        )

    return build

capsula.FileContext.__init__

__init__(
    path: Path | str,
    *,
    compute_hash: bool = True,
    hash_algorithm: str | None = None,
    copy_to: (
        Iterable[Path | str] | Path | str | None
    ) = None,
    move_to: Path | str | None = None,
    ignore_missing: bool = False
)
PARAMETER DESCRIPTION
path

TYPE: Path | str

compute_hash

TYPE: bool DEFAULT: True

hash_algorithm

TYPE: str | None DEFAULT: None

copy_to

TYPE: Iterable[Path | str] | Path | str | None DEFAULT: None

move_to

TYPE: Path | str | None DEFAULT: None

ignore_missing

TYPE: bool DEFAULT: False

Source code in capsula/_context/_file.py
def __init__(
    self,
    path: Path | str,
    *,
    compute_hash: bool = True,
    hash_algorithm: str | None = None,
    copy_to: Iterable[Path | str] | Path | str | None = None,
    move_to: Path | str | None = None,
    ignore_missing: bool = False,
) -> None:
    self._path = Path(path)
    self._hash_algorithm = self._default_hash_algorithm if hash_algorithm is None else hash_algorithm
    self._compute_hash = compute_hash
    self._move_to = None if move_to is None else Path(move_to)
    self._ignore_missing = ignore_missing

    if copy_to is None:
        self._copy_to: tuple[Path, ...] = ()
    elif isinstance(copy_to, (str, Path)):
        self._copy_to = (Path(copy_to),)
    else:
        self._copy_to = tuple(Path(p) for p in copy_to)

Configuration example

Via capsula.toml

[pre-run]
contexts = [
  { type = "FileContext", path = "pyproject.toml", copy = true, path_relative_to_project_root = true },
]

Via @capsula.context decorator

@capsula.context decorator is useful to move the output file to the run directory after the function execution.

import capsula
PROJECT_ROOT = capsula.search_for_project_root(__file__)

@capsula.run()
@capsula.context(capsula.FileContext.builder(PROJECT_ROOT / "pyproject.toml", copy=True), mode="pre")
@capsula.context(capsula.FileContext.builder("output.txt", move=True), mode="post")
def func():
  with open("output.txt", "w") as output_file:
    output_file.write("Hello, world!")

Output example

The following is an example of the output of the FileContext, reported by the JsonDumpReporter:

"file": {
  "/home/nomura/ghq/github.com/shunichironomura/capsula/pyproject.toml": {
    "copied_to": [
      "/home/nomura/ghq/github.com/shunichironomura/capsula/vault/20240708_024409_coj0/pyproject.toml"
    ],
    "moved_to": null,
    "hash": {
      "algorithm": "sha256",
      "digest": "1ecab310035eea9c07fad2a8b22a16f999cd4d8c59fa1732c088f754af548ad9"
    }
  },
}