from __future__ import annotations
from collections.abc import Callable, MutableMapping
import dataclasses as dc
from typing import Any, Literal
import warnings
from markdown_it._compat import DATACLASS_KWARGS
def convert_attrs(value: Any) -> Any:
if not value:
return {}
if isinstance(value, list):
return dict(value)
return value
@dc.dataclass(**DATACLASS_KWARGS)
class Token:
type: str
tag: str
nesting: Literal[-1, 0, 1]
attrs: dict[str, str | int | float] = dc.field(default_factory=dict)
map: list[int] | None = None
level: int = 0
children: list[Token] | None = None
content: str = ""
markup: str = ""
info: str = ""
meta: dict[Any, Any] = dc.field(default_factory=dict)
block: bool = False
hidden: bool = False
def __post_init__(self) -> None:
self.attrs = convert_attrs(self.attrs)
def attrIndex(self, name: str) -> int:
warnings.warn( "Token.attrIndex should not be used, since Token.attrs is a dictionary",
UserWarning,
)
if name not in self.attrs:
return -1
return list(self.attrs.keys()).index(name)
def attrItems(self) -> list[tuple[str, str | int | float]]:
return list(self.attrs.items())
def attrPush(self, attrData: tuple[str, str | int | float]) -> None:
name, value = attrData
self.attrSet(name, value)
def attrSet(self, name: str, value: str | int | float) -> None:
self.attrs[name] = value
def attrGet(self, name: str) -> None | str | int | float:
return self.attrs.get(name, None)
def attrJoin(self, name: str, value: str) -> None:
if name in self.attrs:
current = self.attrs[name]
if not isinstance(current, str):
raise TypeError(
f"existing attr 'name' is not a str: {self.attrs[name]}"
)
self.attrs[name] = f"{current} {value}"
else:
self.attrs[name] = value
def copy(self, **changes: Any) -> Token:
return dc.replace(self, **changes)
def as_dict(
self,
*,
children: bool = True,
as_upstream: bool = True,
meta_serializer: Callable[[dict[Any, Any]], Any] | None = None,
filter: Callable[[str, Any], bool] | None = None,
dict_factory: Callable[..., MutableMapping[str, Any]] = dict,
) -> MutableMapping[str, Any]:
mapping = dict_factory((f.name, getattr(self, f.name)) for f in dc.fields(self))
if filter:
mapping = dict_factory((k, v) for k, v in mapping.items() if filter(k, v))
if as_upstream and "attrs" in mapping:
mapping["attrs"] = (
None
if not mapping["attrs"]
else [[k, v] for k, v in mapping["attrs"].items()]
)
if meta_serializer and "meta" in mapping:
mapping["meta"] = meta_serializer(mapping["meta"])
if children and mapping.get("children", None):
mapping["children"] = [
child.as_dict(
children=children,
filter=filter,
dict_factory=dict_factory,
as_upstream=as_upstream,
meta_serializer=meta_serializer,
)
for child in mapping["children"]
]
return mapping
@classmethod
def from_dict(cls, dct: MutableMapping[str, Any]) -> Token:
token = cls(**dct)
if token.children:
token.children = [cls.from_dict(c) for c in token.children] return token