from dataclasses import dataclass @dataclass(frozen=True) class Tarball: url: str sha256: str strip_components: int = 1 patches: tuple[str, ...] = () @property def cache_key(self) -> str: return self.sha256 @dataclass(frozen=True) class Git: url: str commit: str patches: tuple[str, ...] = () @property def cache_key(self) -> str: return self.commit def tarball( *, url: str, sha256: str, strip_components: int = 1, patches: list[str] | tuple[str, ...] = (), ) -> Tarball: return Tarball( url=url, sha256=sha256, strip_components=strip_components, patches=tuple(patches), ) def git(*, url: str, commit: str, patches: list[str] | tuple[str, ...] = ()) -> Git: if not commit or commit == "?": raise ValueError("git source requires an explicit commit SHA") return Git(url=url, commit=commit, patches=tuple(patches)) @dataclass(frozen=True) class Subpackage: name: str files: tuple[str, ...] description: str = "" license: str = "" url: str = "" maintainer: str = "" def subpackage( name: str, *, description: str = "", license: str = "", url: str = "", maintainer: str = "", files: list[str] | tuple[str, ...] = (), ) -> Subpackage: for field_name, value in ( ("description", description), ("license", license), ("url", url), ("maintainer", maintainer), ): if not isinstance(value, str): raise TypeError(f"subpackage {name}: '{field_name}' must be a string") for pat in files: if pat.startswith("/") or ".." in pat.split("/"): raise ValueError( f"subpackage {name}: file pattern {pat!r} must be relative and free of '..'" ) return Subpackage( name=name, description=description, license=license, url=url, maintainer=maintainer, files=tuple(files), ) def patches_of(src) -> tuple[str, ...]: return src.patches