from dataclasses import dataclass from collections.abc import Iterable, Iterator, Sequence from typing import Optional, overload, List, Callable, TypeVar, Self import requests T = TypeVar('T') def parse_listfile(contents: str, ctor: Callable[[str, Optional[str]], T]) -> List[T]: acc = [] comment_acc = [] for line in contents.splitlines(): if not len(line): continue elif line.startswith("##"): comment_acc.append(line[2:].strip()) elif line.startswith("#"): continue else: acc.append(ctor(line, " ".join(comment_acc) if len(comment_acc) else None)) comment_acc = [] return acc @dataclass(frozen=True) class ListFileEntry: entry: str comment: Optional[str] L = TypeVar('L', bound=ListFileEntry) class BaseListFile(Sequence[L]): def __init__(self, contents: str): self._contents: str = contents self._entries: List[L] = [] @overload def __getitem__(self, key: int) -> L: pass @overload def __getitem__(self, key: slice) -> Sequence[L]: pass def __getitem__(self, key): return self._entries[key] def __len__(self) -> int: return self._entries.__len__() @classmethod def from_url(cls, url: str) -> Optional[Self]: req = requests.get(url, stream = False) if not req.ok: return None return cls(req.text) class ListFile(BaseListFile[ListFileEntry]): def __init__(self, contents: str): super().__init__(contents) self._entries = parse_listfile(contents, ListFileEntry) __all__ = ["parse_listfile", "ListFileEntry", "BaseListFile", "ListFIle"]