2
0
Fork 0
textout/textoutpc/tags.py

100 lines
3.0 KiB
Python

#!/usr/bin/env python
# *****************************************************************************
# Copyright (C) 2018-2023 Thomas Touhey <thomas@touhey.fr>
# This file is part of the textoutpc project, which is MIT-licensed.
# *****************************************************************************
"""Tags definitions for textoutpc."""
from __future__ import annotations
from abc import ABC, abstractmethod
from collections.abc import Sequence
from typing import Iterator
from docutils.nodes import Node, TextElement
__all__ = ["RawTag", "Tag"]
class Tag(ABC):
"""A tag for textoutpc's BBCode.
Note that the provided name may be surrounded by brackets if the tag is
a normal tag, or not if it is a special tag such as "`".
:param name: The name of the tag.
:param value: The value of the content.
"""
__slots__ = ("name", "value")
@staticmethod
def get_text_from_raw_children(children: Sequence[Node], /) -> str:
"""Get text from children.
This is a function to use with raw tags only, as they are guaranteed
to be called with text elements or nodes only.
"""
if not children:
return ""
if len(children) > 1:
raise AssertionError(
"More than one children for a raw tag, this is a bug!",
)
child = children[0]
if isinstance(child, TextElement) and len(child.children) == 1:
return str(child.children[0])
elif isinstance(child, str):
return str(child)
raise AssertionError(f"Unsupported child for text: {child!r}")
def __init__(self, *, name: str, value: str | None = None):
self.name = name
self.value = value
self.validate()
def validate(self) -> None:
"""Validate the name and value for this tag.
:raises TagValidationError: The name and value combination is invalid.
"""
def is_raw(self) -> bool:
"""Return whether the content of this tag should be read as raw or not.
This will be called after the tag is initialied, but before the tag
is used to populate a node, in order to read if what follows the tag
is interpreted or not and whether we should look for an end tag or not.
This may take into account both the name and the value of the tag.
"""
return False
@abstractmethod
def process(self, *, children: Sequence[Node]) -> Iterator[Node]:
"""Process the tag with children to build document nodes.
:param children: The children to process.
:return: The produced nodes.
"""
class RawTag(Tag):
"""A tag for textoutpc's BBCode, except always raw.
This means that the content for such tags must systematically be
not interpreted, whatever the name and values are.
"""
__slots__ = ()
def is_raw(self) -> bool:
"""Return whether the content of this tag should be read as raw or not.
Since the tag is a raw tag, this will always be true.
"""
return True