from typing import Callable, Iterable, Self from bs4 import BeautifulSoup, Tag from .elements import IdAttr, Template, Node from .renderer import Renderable, Renderer from .puppytype import Parsable, ParsableIterable, Parsable, Templates class Puppygirl[R: Renderer]: renderer: R elements: list[Renderable] templates: Templates def __init__(self, renderer: R, elements: list[Callable[[Self], Renderable]] = [], templates: ParsableIterable = []): self.renderer = renderer self.templates = Puppygirl._create_template_dict(templates) self.elements = [self._instantiate(el) for el in elements] def _instantiate(self, value: Callable[[Self], Renderable] | Renderable) -> Renderable: if(isinstance(value, Callable)): return value(self) return value def _create_template_dict(templates: Iterable[Parsable]) -> Templates: templates = [Template.from_element(t) for t in templates] return {t.id: t for t in templates} def add_template(self, template: Parsable): template = Template.from_element(template) self._templates[template[IdAttr]] = template def _is_parsed(value: Parsable) -> bool: return isinstance(value, BeautifulSoup) or \ isinstance(value, Tag) or \ isinstance(value, Node) or \ isinstance(value, Template) def parse(self, value: Parsable) -> BeautifulSoup: if value is None: return BeautifulSoup(features='html.parser') if Puppygirl._is_parsed(value): return self.parse_tree(value) return self.parse_tree(BeautifulSoup(value, features='html.parser')) def parse_tree(self, tree: BeautifulSoup) -> BeautifulSoup: return self.renderer.render(self, tree)