Skip to content

Creating your own contexts, reporters, and watchers

You can extend Capsula by creating your own contexts, reporters, and watchers. You only need to create a class that inherits from the base class of the corresponding type.

Contexts

To create a context, you need to

  • Inherit from the capsula.ContextBase class. This will register the class to the Capsula context registry.
  • Implement the encapsulate method that captures the context.
  • Implement the default_key method that returns the default key used to store the context in the capsule.

Here's an example of a custom context that captures the current time:

import capsula
from zoneinfo import ZoneInfo
from datetime import datetime

class TimeContext(capsula.ContextBase):
    def __init__(self, timezone: str = "UTC"):
        self.timezone = ZoneInfo(timezone)

    def encapsulate(self) -> dict[str, datetime]:
        return {"time": datetime.now(self.timezone)}

    def default_key(self) -> str:
        return "time"

Note

The above example uses the zoneinfo module, which is available in Python 3.9 and later.

Optionally, you can implement the builder method that returns a function that creates the context. This is useful when you need to access the runtime information (capsula.CapsuleParams) when creating the context.

Here's an example of a custom context that captures the project root path:

import capsula

class ProjectRootContext(capsula.ContextBase):
    def __init__(self, path: str | Path):
        self.path = str(path)

    def encapsulate(self) -> dict[str, str]:
        return {"project_root": self.path}

    def default_key(self) -> str:
        return "project_root"

    @classmethod
    def builder(cls):
        def build(params: capsula.CapsuleParams) -> "ProjectRootContext":
            return ProjectRootContext(params.project_root)
        return build

Watchers

To create a watcher, you need to

  • Inherit from the capsula.WatcherBase class. This will register the class to the Capsula watcher registry.
  • Implement the watch method that behaves as a context manager that watches the command/function execution.
  • Implement the encapsulate method that returns the watcher's output.
  • Implement the default_key method that returns the default key used to store the watcher's output in the capsule.

Here's an example of a custom watcher that watches the execution time of a command/function (from the implementation of the TimeWatcher class):

import capsula
import time
from datetime import timedelta
from collections.abc import Iterator
from contextlib import contextmanager

class TimeWatcher(capsula.WatcherBase):
    def __init__(
        self,
        name: str = "execution_time",
    ) -> None:
        self._name = name
        self._duration: timedelta | None = None

    def encapsulate(self) -> timedelta | None:
        return self._duration

    @contextmanager
    def watch(self) -> Iterator[None]:
        start = time.perf_counter()
        try:
            yield
        finally:
            end = time.perf_counter()
            self._duration = timedelta(seconds=end - start)

    def default_key(self) -> tuple[str, str]:
        return ("time", self._name)

Like contexts, you can implement the builder method to create the watcher using the runtime information.

Reporters

To create a reporter, you need to

  • Inherit from the capsula.ReporterBase class. This will register the class to the Capsula reporter registry.
  • Implement the report method that reports the capsule.

Here's an example of a custom reporter that dumps the capsule to a pickle file:

import capsula
import pickle
from pathlib import Path

class PickleDumpReporter(capsula.ReporterBase):
    def __init__(self, path: str | Path):
        self.path = Path(path)

    def report(self, capsule: capsula.Capsule) -> None:
        with open(self.path, "wb") as file:
            pickle.dump(capsule, file)

    @classmethod
    def builder(cls):
        def build(params: capsula.CapsuleParams) -> "PickleDumpReporter":
            return PickleDumpReporter(params.run_dir / "capsule.pkl")
        return build

As shown in the example, you can implement the builder method to create the reporter using the runtime information.