Source code for scitex_todo._paths

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Task-store path resolution following the SciTeX local-state convention.

Resolution order (highest priority first):

    1. an explicit path argument (CLI ``--tasks`` / function arg)
    2. ``$SCITEX_TODO_TASKS`` environment variable
    3. project scope:  ``<git-root>/.scitex/todo/tasks.yaml``
    4. user scope:     ``$SCITEX_DIR/todo/tasks.yaml`` (default ``~/.scitex/todo``)
    5. bundled generic example:  ``scitex_todo/examples/tasks.yaml``

The personal data lives under scopes 3 and 4 — never in the package. The
bundled example (scope 5) is generic and exists only so a fresh install can
demo end-to-end. ``$SCITEX_DIR`` relocates the user-scope root per the
ecosystem convention.
"""

from __future__ import annotations

import os
from pathlib import Path

#: package short name (``scitex-todo`` with the ``scitex-`` prefix stripped).
PKG_SHORT = "todo"

#: env var that overrides the resolved task-store path entirely.
ENV_TASKS = "SCITEX_TODO_TASKS"


def _user_root() -> Path:
    """User-scope ``.scitex/todo`` root, honouring ``$SCITEX_DIR``."""
    base = os.environ.get("SCITEX_DIR")
    root = Path(base).expanduser() if base else Path.home() / ".scitex"
    return root / PKG_SHORT


def _find_git_root(start: Path) -> Path | None:
    """Walk up from ``start`` looking for a ``.git`` directory."""
    cur = start.resolve()
    for parent in (cur, *cur.parents):
        if (parent / ".git").exists():
            return parent
    return None


[docs] def bundled_example() -> Path: """Path to the generic example task store shipped inside the wheel.""" return Path(__file__).resolve().parent / "examples" / "tasks.yaml"
[docs] def resolve_tasks_path(explicit: str | Path | None = None) -> Path: """Resolve which task store to use, following the precedence chain. Parameters ---------- explicit : str or pathlib.Path or None An explicit path (e.g. a CLI ``--tasks`` flag). When given and it exists, it wins outright. Returns ------- pathlib.Path The first existing task store in precedence order. Falls back to the bundled generic example if no personal store is found. Examples -------- >>> p = resolve_tasks_path() # doctest: +SKIP >>> p.name # doctest: +SKIP 'tasks.yaml' """ if explicit is not None: cand = Path(explicit).expanduser() if cand.exists(): return cand # An explicit-but-missing path is a user error — surface it as-is so # the loader raises a clear FileNotFoundError on that path. return cand env_val = os.environ.get(ENV_TASKS) if env_val: return Path(env_val).expanduser() git_root = _find_git_root(Path.cwd()) if git_root is not None: project = git_root / ".scitex" / PKG_SHORT / "tasks.yaml" if project.exists(): return project user = _user_root() / "tasks.yaml" if user.exists(): return user return bundled_example()
# EOF