from __future__ import annotations
from functools import lru_cache
from typing import TYPE_CHECKING, Optional
if TYPE_CHECKING:
from ..interpreter import Context
__all__ = ("Block", "verb_required_block")
[docs]class Block:
"""
The base class for TagScript blocks.
Implementations must subclass this to create new blocks.
Attributes
----------
ACCEPTED_NAMES: Tuple[str, ...]
The accepted names for this block. This ideally should be set as a class attribute.
"""
ACCEPTED_NAMES = ()
def __repr__(self) -> str:
"""
String repr
"""
return f"<{type(self).__qualname__} at {hex(id(self))}>"
[docs] @classmethod
def will_accept(cls, ctx: Context) -> bool:
"""
Describes whether the block is valid for the given :class:`~bTagScript.interpreter.Context`.
Parameters
----------
ctx: Context
The context object containing the TagScript :class:`~bTagScript.verb.Verb`.
Returns
-------
bool
Whether the block should be processed for this :class:`~bTagScript.interpreter.Context`.
"""
dec = ctx.verb.declaration.lower()
return dec in cls.ACCEPTED_NAMES
[docs] def pre_process(self, ctx: Context) -> Optional[str]: # pylint: disable=unused-argument
"""
Any pre processing that needs to be done before the block is processed.
"""
return None
[docs] def process(self, ctx: Context) -> Optional[str]:
"""
Processes the block's actions for a given :class:`~bTagScript.interpreter.Context`.
Subclasses must implement this.
Parameters
----------
ctx: Context
The context object containing the TagScript :class:`~bTagScript.verb.Verb`.
Returns
-------
Optional[str]
The block's processed value.
Raises
------
NotImplementedError
The subclass did not implement this required method.
"""
raise NotImplementedError
[docs] def post_process(self, ctx: Context) -> Optional[str]: # pylint: disable=unused-argument
"""
Any post processing that needs to be done after the block is processed.
"""
return None
[docs]@lru_cache(maxsize=None)
def verb_required_block(
implicit: bool,
*,
parameter: bool = False,
payload: bool = False,
) -> Block:
"""
Get a Block subclass that requires a verb to implicitly or explicitly have a parameter or payload passed.
Parameters
----------
implicit: bool
Specifies whether the value is required to be passed implicitly or explicitly.
``{block()}`` would be allowed if implicit is False.
parameter: bool
Passing True will cause the block to require a parameter to be passed.
payload: bool
Passing True will cause the block to require the payload to be passed.
"""
check = (lambda x: x) if implicit else (lambda x: x is not None)
class RequireMeta(type):
"""
Require a verb to have a parameter or payload if added.
"""
def __repr__(cls) -> str:
"""
String repr
"""
return f"VerbRequiredBlock(implicit={implicit!r}, payload={payload!r}, parameter={parameter!r})"
class VerbRequiredBlock(Block, metaclass=RequireMeta): # pylint: disable=abstract-method
"""
The required block.
"""
def will_accept(self, ctx: Context) -> bool: # pylint: disable=arguments-differ
"""
Describes whether the block is valid for the given :class:`~bTagScript.interpreter.Context`.
"""
verb = ctx.verb
if payload and not check(verb.payload):
return False
if parameter and not check(verb.parameter):
return False
return super().will_accept(ctx)
return VerbRequiredBlock