Skip to content

Instantly share code, notes, and snippets.

@lmmx
Created January 18, 2026 23:34
Show Gist options
  • Select an option

  • Save lmmx/f5d1b07d266f160f9a431c1f6bdc8a17 to your computer and use it in GitHub Desktop.

Select an option

Save lmmx/f5d1b07d266f160f9a431c1f6bdc8a17 to your computer and use it in GitHub Desktop.
Deferred templated pathlib Path-like type with t-strings
from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
from string.templatelib import Interpolation, Template
from typing import Any
# ============================================================
# Layer 1: Parameters (late binding slots)
# ============================================================
@dataclass(frozen=True)
class Param:
"""A named slot to be filled at resolution time."""
name: str
def __truediv__(self, other: PathExpr | str) -> PathExpr:
return ParamExpr(self) / other
def __rtruediv__(self, other: str) -> PathExpr:
return LiteralExpr(other) / ParamExpr(self)
def __repr__(self):
return repr(f"${self.name}")
# ============================================================
# Layer 2: Path Expressions (composable, deferred)
# ============================================================
class PathExpr:
"""Base class for path expressions. Everything is deferred."""
def __truediv__(self, other: PathExpr | str) -> PathExpr:
return JoinExpr(self, other)
def __rtruediv__(self, other: str) -> PathExpr:
return JoinExpr(LiteralExpr(other), self)
@property
def parent(self) -> PathExpr:
return ParentExpr(self)
def with_name(self, name: str) -> PathExpr:
return SiblingExpr(self, name)
def with_suffix(self, suffix: str) -> PathExpr:
return WithSuffixExpr(self, suffix)
def resolve(self, bindings: dict[str, Any] = {}) -> Path:
raise NotImplementedError
@dataclass(frozen=True)
class LiteralExpr(PathExpr):
"""A concrete string/path segment."""
value: str | Path
def resolve(self, bindings: dict[str, Any] = {}) -> Path:
return Path(self.value)
@dataclass(frozen=True)
class ParamExpr(PathExpr):
"""A parameter reference—resolved from bindings."""
param: Param
def resolve(self, bindings: dict[str, Any] = {}) -> Path:
if self.param.name not in bindings:
raise ValueError(f"Unbound parameter: {self.param.name}")
return Path(bindings[self.param.name])
@dataclass(frozen=True)
class TemplateExpr(PathExpr):
template: Template
def resolve(self, bindings: dict[str, Any] = {}) -> Path:
parts = []
for item in self.template:
if isinstance(item, str):
parts.append(item)
else:
value = item.value
if isinstance(value, Param):
if value.name not in bindings:
raise ValueError(f"Unbound parameter: {value.name}")
value = bindings[value.name]
parts.append(format(value, item.format_spec))
return Path("".join(parts))
@property
def parent(self) -> PathExpr:
# Find last '/' in the static structure
strings = self.template.strings
interps = self.template.interpolations
# Walk backwards through strings to find last separator
cumulative = []
for i, s in enumerate(strings):
cumulative.append(("s", i, s))
if i < len(interps):
cumulative.append(("i", i, interps[i]))
# Find the last '/' and truncate there
new_parts = []
last_sep_idx = None
for j, (kind, i, val) in enumerate(cumulative):
if kind == "s" and "/" in val:
last_sep_idx = j
last_sep_pos = val.rfind("/")
if last_sep_idx is None:
return ParentExpr(self) # No separator found, fall back
# Rebuild template up to (but not including) last segment
new_parts = []
for j, (kind, i, val) in enumerate(cumulative):
if j < last_sep_idx:
new_parts.append(val if kind == "s" else val)
elif j == last_sep_idx:
new_parts.append(val[:last_sep_pos]) # Truncate at last '/'
break
return TemplateExpr(Template(*new_parts))
@dataclass(frozen=True)
class JoinExpr(PathExpr):
"""Path join: left / right"""
left: PathExpr
right: PathExpr | str
def __post_init__(self):
# Normalize string to LiteralExpr
if isinstance(self.right, str):
object.__setattr__(self, "right", LiteralExpr(self.right))
def resolve(self, bindings: dict[str, Any] = {}) -> Path:
return self.left.resolve(bindings) / self.right.resolve(bindings)
@dataclass(frozen=True)
class ParentExpr(PathExpr):
child: PathExpr
def resolve(self, bindings: dict[str, Any] = {}) -> Path:
# Structural: parent(a / b) = a
if isinstance(self.child, JoinExpr):
return self.child.left.resolve(bindings)
return self.child.resolve(bindings).parent
@dataclass(frozen=True)
class SiblingExpr(PathExpr):
base: PathExpr
name: str
def resolve(self, bindings: dict[str, Any] = {}) -> Path:
# Expression-level parent, then resolve
return self.base.parent.resolve(bindings) / self.name
@dataclass(frozen=True)
class WithSuffixExpr(PathExpr):
"""Change suffix of a path."""
base: PathExpr
suffix: str
def resolve(self, bindings: dict[str, Any] = {}) -> Path:
return self.base.resolve(bindings).with_suffix(self.suffix)
# ============================================================
# Layer 3: Convenience constructors
# ============================================================
def P(name: str) -> PathExpr:
"""Shorthand for a parameter expression."""
return ParamExpr(Param(name))
def T(template: Template) -> PathExpr:
"""Wrap a t-string as a PathExpr."""
return TemplateExpr(template)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment