puppygirl-py/puppygirl/elements/shadow_root.py

193 lines
4.6 KiB
Python

from copy import copy
from dataclasses import dataclass
from enum import StrEnum
from typing import TYPE_CHECKING, Iterable, Optional, Self
from bs4 import BeautifulSoup, Tag
from ..clonable import Clonable
if TYPE_CHECKING:
from ..puppytype import Parsable
TemplateName = "template"
class ShadowRootMode(StrEnum):
Open = "open"
Closed = "closed"
class Node(Clonable):
@property
def value(self): pass
def from_path(path: str) -> Self:
with open(path, "r") as f:
soup = BeautifulSoup(f.read(), 'html.parser')
return Node.from_tag(soup)
def from_tag(tag: Tag | Iterable[Tag] | BeautifulSoup) -> Self:
if isinstance(tag, Tag):
return SingleNode(tag)
match len(tag):
case 0: return EmptyNode()
case 1: return SingleNode(tag[0])
case _: return NodeCollection(tag)
def attach_shadow(self, shadow_root_mode: ShadowRootMode, clonable: Optional[bool] = None, delegates_focus: Optional[bool] = None, serializable: Optional[bool] = None):
return ShadowRoot(
host=self.value,
mode=shadow_root_mode,
clonable=clonable,
delegates_focus=delegates_focus,
serializable=serializable
)
def find(self, *args, **kwargs):
return self.value.find(*args, **kwargs)
def find_all(self, *args, **kwargs):
return self.value.find_all(*args, **kwargs)
def append(self, *args, **kwargs):
return self.value.append(*args, **kwargs)
def insert(self, *args, **kwargs):
return self.value.insert(*args, **kwargs)
def get_attr(self, attr: str) -> Optional[str]:
return self.value.get(attr)
def has_attr(self, attr: str) -> bool:
return self.value.has_attr(attr)
def replace(self, node: "Node | Iterable[Node]"):
self.value.insert_before(node.value)
self.value.decompose()
def prettify(self) -> str:
return self.value.prettify()
class EmptyNode(Node):
@property
def value(self):
return None
@dataclass
class NodeCollection(Node):
nodes: Iterable[Tag]
def __iter__(self):
return self.nodes
@property
def value(self):
return self.nodes
def clone(self) -> Self:
return NodeCollection(copy(self.value))
@dataclass
class SingleNode(Node):
_value: Tag
@property
def value(self):
return self._value
@property
def parent(self):
return self._value.parent
def attach_shadow(self, shadow_root_mode: ShadowRootMode, clonable: Optional[bool] = None, delegates_focus: Optional[bool] = None, serializable: Optional[bool] = None):
return ShadowRoot(
host=self,
mode=shadow_root_mode,
clonable=clonable,
delegates_focus=delegates_focus,
serializable=serializable
)
def __getitem__(self, index):
return self._value[index]
def clone(self) -> Self:
return SingleNode(copy(self.value))
@dataclass
class Template(Node):
_value: Node
@property
def value(self):
return self._value
@property
def id(self):
return self._value["id"]
@property
def shadow_root_mode(self) -> ShadowRootMode | None:
return self._value.get("shadowrootmode")
@shadow_root_mode.setter
def shadow_root_mode(self, mode: ShadowRootMode):
self._value["shadowrootmode"] = mode.value
@property
def delegates_focus(self) -> Optional[bool]:
return self._value.get("delegatesfocus")
@delegates_focus.setter
def delegates_focus(self, value: bool):
self._value["delegatesfocus"] = value
@property
def clonable(self) -> Optional[bool]:
return self._value.get("clonable")
@clonable.setter
def clonable(self, value: bool):
self._value["clonable"] = value
@property
def serializable(self) -> Optional[bool]:
return self._value.get("serializable")
@serializable.setter
def serializable(self, value: bool):
self._value["serializable"] = value
def from_path(path: str) -> Self:
with open(path, "r") as f:
return Template(BeautifulSoup(f.read(), 'html.parser').find())
def from_element(element: "Parsable") -> Self:
if isinstance(element, Template):
return element
return Template(element)
def clone(self) -> Self:
return Template(copy(self._value))
@dataclass
class ShadowRoot(Clonable):
host: Node
mode: ShadowRootMode
clonable: Optional[bool] = None
delegates_focus: Optional[bool] = None
serializable: Optional[bool] = None
def append_child(self, template: Template):
template.shadow_root_mode = self.mode
if self.clonable is not None:
template.clonable = self.clonable
if self.delegates_focus is not None:
template.delegates_focus = self.delegates_focus
if self.serializable is not None:
template.serializable = self.serializable
self.host.value.insert(0, template.value)