Skip to content

viat.providers.tracker

A convenience module that lists all ViatFileTracker providers.

GitFileTracker

Bases: TrackerBaseMixin, ViatFileTracker

The git file tracker.

Parameters:

Source code in src/viat/providers/tracker/_git/real.py
class GitFileTracker(TrackerBaseMixin, ViatFileTracker):
    """The git file tracker.

    Args:
        config: All configuration required for the tracker.
        resolver: A path resolver used to process incoming paths.
        static_config: Static configuration for the vault.
    """

    config: GitFileTrackerConfig
    """The configuration used to initialize the tracker."""

    resolver: ViatPathResolver | None
    """The resolver used to initialize the storage."""

    static_config: ViatVaultStaticConfig
    """The vault's static configuration."""

    _repo: pygit2.Repository

    def __init__(
        self,
        config: GitFileTrackerConfig,
        resolver: ViatPathResolver | None = None,
        static_config: ViatVaultStaticConfig | None = None,
    ) -> None:
        try:
            self._repo = pygit2.Repository(config.repo_root.as_posix())
        except pygit2.GitError as err:
            raise ViatFileTrackerError('The tracker root is not a git repository') from err

        self.config = config
        self.resolver = resolver
        self.static_config = static_config or ViatVaultStaticConfig()

    def _recurse_into_tree(self, tree: pygit2.Tree, base_path: pathlib.Path) -> Iterable[pathlib.Path]:
        for obj in tree:
            match obj:
                case pygit2.Blob():
                    if obj.name:
                        yield base_path / obj.name

                case pygit2.Tree():
                    if obj.name:
                        yield from self._recurse_into_tree(obj, base_path / obj.name)

    @override
    def iter_paths(self) -> Iterable[pathlib.Path]:
        try:
            ref = self._repo.revparse_single(self.config.revision)
        except KeyError:
            raise ViatFileTrackerError('No git HEAD pointer') from None

        if not isinstance(ref, pygit2.Commit):
            raise ViatFileTrackerError('git HEAD does not point to a commit')

        yield from self._recurse_into_tree(ref.tree, self.config.repo_root)

    @override
    def is_tracked(self, path: pathlib.Path | str) -> bool:
        rel_path = self._resolve_path(path)

        try:
            self._repo.blame(rel_path.as_posix())
        except (KeyError, ValueError):
            return False

        return True

config = config instance-attribute

The configuration used to initialize the tracker.

resolver = resolver instance-attribute

The resolver used to initialize the storage.

static_config = static_config or ViatVaultStaticConfig() instance-attribute

The vault's static configuration.

GitFileTrackerConfig dataclass

Configuration for the git file tracker.

Source code in src/viat/providers/tracker/_git/config.py
@dataclass
class GitFileTrackerConfig:
    """Configuration for the git file tracker."""

    repo_root: pathlib.Path
    """The root of the repository to track files from."""

    revision: str = 'HEAD'
    """The git revision to track files from."""

repo_root instance-attribute

The root of the repository to track files from.

revision = 'HEAD' class-attribute instance-attribute

The git revision to track files from.

GlobFileTracker

Bases: TrackerBaseMixin, ViatFileTracker

The default glob file tracker.

Due to inconsistencies between glob and fnmatch from the standard library, we use the more flexible wcmatch library.

Parameters:

Source code in src/viat/providers/tracker/glob.py
class GlobFileTracker(TrackerBaseMixin, ViatFileTracker):
    """The default glob file tracker.

    Due to inconsistencies between [glob][] and [fnmatch][] from the standard library,
    we use the more flexible [wcmatch] library.

    [wcmatch]: https://facelessuser.github.io/wcmatch

    Args:
        config: All configuration required for the tracker.
        resolver: A path resolver used to process incoming paths.
        static_config: Static configuration for the vault.
    """

    config: GlobFileTrackerConfig
    """The configuration used to initialize the tracker."""

    resolver: ViatPathResolver | None
    """The resolver used to initialize the storage."""

    static_config: ViatVaultStaticConfig
    """The vault's static configuration."""

    def __init__(
        self,
        config: GlobFileTrackerConfig,
        resolver: ViatPathResolver | None = None,
        static_config: ViatVaultStaticConfig | None = None,
    ) -> None:
        self.config = config
        self.resolver = resolver
        self.static_config = static_config or ViatVaultStaticConfig()

    def _glob_flags(self) -> int:
        return sum(getattr(glob, f) for f in self.config.flags)

    @override
    def iter_paths(self) -> Iterable[pathlib.Path]:
        for raw_path in glob.glob(self.config.patterns, root_dir=self.config.root, flags=self._glob_flags()):
            yield pathlib.Path(raw_path)

    @override
    def is_tracked(self, path: pathlib.Path | str) -> bool:
        rel_path = self._resolve_path(path)
        return glob.globmatch(rel_path, self.config.patterns, root_dir=self.config.root, flags=self._glob_flags() | glob.REALPATH)

config = config instance-attribute

The configuration used to initialize the tracker.

resolver = resolver instance-attribute

The resolver used to initialize the storage.

static_config = static_config or ViatVaultStaticConfig() instance-attribute

The vault's static configuration.

GlobFileTrackerConfig dataclass

Configuration for the glob file tracker.

Raises:

Source code in src/viat/providers/tracker/glob.py
@dataclass
class GlobFileTrackerConfig:
    """Configuration for the glob file tracker.

    Raises:
        viat.exceptions.ViatConfigError: If some flag is unrecognized.
    """

    root: pathlib.Path
    """The root patch for matching relative patterns.

    Relative paths are resolved with respect to the vault root."""

    patterns: Sequence[str]
    """A sequence of wcmatch-flavored glob patterns."""

    flags: Sequence[str] = field(default_factory=lambda: DEFAULT_GLOB_FLAGS)
    """A sequence of strings corresponding to glob options."""

    # ruff: ignore[undocumented-magic-method]
    def __post_init__(self) -> None:
        validate_wcmatch_flags(self.flags)

flags = field(default_factory=(lambda: DEFAULT_GLOB_FLAGS)) class-attribute instance-attribute

A sequence of strings corresponding to glob options.

patterns instance-attribute

A sequence of wcmatch-flavored glob patterns.

root instance-attribute

The root patch for matching relative patterns.

Relative paths are resolved with respect to the vault root.

validate_wcmatch_flags(flags)

Check the validity of wcmatch glob options.

Parameters:

  • flags (Sequence[str]) –

    A sequence of strings corresponding to glob options.

Raises:

Source code in src/viat/providers/tracker/glob.py
def validate_wcmatch_flags(flags: Sequence[str]) -> None:
    """Check the validity of wcmatch glob options.

    Args:
        flags: A sequence of strings corresponding to glob options.

    Raises:
        viat.exceptions.ViatConfigError: If some flag is unrecognized.
    """
    for f in flags:
        if not hasattr(glob, f):
            raise ViatConfigError(f'Unrecognized wcmatch glob flag {f}')