Source code for pjkm.core.templates.loader

"""Template loader: resolves built-in, git, and local template sources."""

from __future__ import annotations

from importlib import resources
from pathlib import Path

[docs] BUILTIN_TEMPLATES_ANCHOR = "pjkm.templates"
[docs] ARCHETYPE_TEMPLATES = { "single_package", "service", "poly_repo", "script_tool", }
[docs] class TemplateNotFoundError(Exception): """Raised when a template cannot be resolved."""
[docs] class TemplateLoader: """Resolves template names to local filesystem paths."""
[docs] def resolve(self, name: str) -> Path: """Resolve a template name to a local path. Supports: - Built-in names: "base", "single_package", "service", etc. - Local paths: "/path/to/template" or "./relative/path" - Fragment names: "fragments/infra_otel" """ # Local path local = Path(name) if local.is_absolute() and local.exists(): return local if local.exists() and (local / "copier.yml").exists(): return local.resolve() # Built-in template return self._resolve_builtin(name)
def _resolve_builtin(self, name: str) -> Path: """Resolve a built-in template by name.""" try: ref = resources.files(BUILTIN_TEMPLATES_ANCHOR).joinpath(name) path = Path(str(ref)) if path.is_dir(): return path except (TypeError, FileNotFoundError): pass msg = f"Template {name!r} not found" raise TemplateNotFoundError(msg)
[docs] def list_builtin(self) -> list[str]: """List available built-in template names.""" try: ref = resources.files(BUILTIN_TEMPLATES_ANCHOR) base = Path(str(ref)) if not base.is_dir(): return [] return sorted( d.name for d in base.iterdir() if d.is_dir() and not d.name.startswith("_") ) except (TypeError, FileNotFoundError): return []