Source code for repoman.source_parse
"""Pure helpers for parsing APT source text blocks. No GTK — fully unit-testable."""
from __future__ import annotations
import logging
import re
from urllib.parse import urlparse
from debian.deb822 import Deb822
_log = logging.getLogger(__name__)
# One-line format: deb[-src] [opts] URI suite [components...]
_ONELINE_RE = re.compile(
r"^(deb(?:-src)?)\s+(?:\[([^\]]*)\]\s+)?(\S+)\s+(\S+)(?:\s+(.+))?$",
re.IGNORECASE,
)
[docs]
def parse_source_block(text: str) -> dict | None:
"""Parse a deb one-liner or DEB822 block into a field dict. Returns None on failure."""
text = text.strip()
if not text:
return None
# Try DEB822 first (has colons in field names)
if ":" in text:
try:
stanza = Deb822(text)
uris = stanza.get("URIs", "").split() or stanza.get("URI", "").split()
types = stanza.get("Types", "deb").split()
suites = stanza.get("Suites", "").split() or stanza.get("Suite", "").split()
components = stanza.get("Components", "").split() or stanza.get("Component", "").split()
enabled_str = stanza.get("Enabled", "yes").lower()
signed_by = stanza.get("Signed-By", "").strip() or None
description = stanza.get("X-Repolib-Name") or stanza.get("Description") or None
if uris:
return {
"types": types,
"uris": uris,
"suites": suites,
"components": components,
"enabled": enabled_str not in ("no", "false", "0"),
"signed_by": signed_by,
"description": description,
}
except Exception:
_log.debug("DEB822 parse failed", exc_info=True)
# Try one-line format
m = _ONELINE_RE.match(text)
if m:
type_str, options_str, uri, suite, components_str = m.groups()
types = [type_str.lower()]
components = components_str.split() if components_str else []
signed_by = None
if options_str:
for opt in options_str.split():
if opt.startswith("signed-by="):
signed_by = opt[len("signed-by=") :]
return {
"types": types,
"uris": [uri],
"suites": [suite],
"components": components,
"enabled": True,
"signed_by": signed_by,
"description": None,
}
return None
[docs]
def uri_to_source_filename(uri: str) -> str:
"""Derive a .sources filename from a URI's hostname."""
host = urlparse(uri).netloc or uri
sanitized = re.sub(r"[^a-z0-9]+", "-", host.lower()).strip("-")
return f"{sanitized}.sources"
[docs]
def uri_to_key_filename(uri: str) -> str:
"""Derive a .gpg keyring filename from a URI's hostname."""
host = urlparse(uri).netloc or uri
sanitized = re.sub(r"[^a-z0-9]+", "-", host.lower()).strip("-")
return f"{sanitized}.gpg"