Skip to content

Datamodel

messageformat2.datamodel.Pattern = list[str | Expression | Markup]

messageformat2.datamodel.Declaration = InputDeclaration | LocalDeclaration | UnsupportedStatement

messageformat2.datamodel.Message = PatternMessage | SelectMessage

messageformat2.datamodel.Node

Base class for all nodes in the data model AST.

Source code in messageformat2/datamodel.py
class Node:
    """Base class for all nodes in the data model AST."""

    @property
    def fields(self) -> tuple[str, ...]:
        return ()

fields property

messageformat2.datamodel.VariableRef dataclass

Bases: Node

Source code in messageformat2/datamodel.py
@dataclass
class VariableRef(Node):
    name: str

    def __str__(self) -> str:
        return f"${self.name}"

name instance-attribute

messageformat2.datamodel.Literal dataclass

Bases: Node

Source code in messageformat2/datamodel.py
@dataclass
class Literal(Node):
    value: str

    def __str__(self) -> str:
        value = self.value.replace("|", r"\|").replace("\\", r"\\")
        return f"|{value}|"

value instance-attribute

messageformat2.datamodel.Attribute dataclass

Bases: Node

Source code in messageformat2/datamodel.py
@dataclass
class Attribute(Node):
    name: str
    value: Literal | VariableRef | None

    @property
    def fields(self) -> tuple[str, ...]:
        return ("value",)

    def __str__(self) -> str:
        if not self.value:
            return f"@{self.name}"
        return f"@{self.name}={self.value}"

name instance-attribute

value instance-attribute

fields property

messageformat2.datamodel.Option dataclass

Bases: Node

Source code in messageformat2/datamodel.py
@dataclass
class Option(Node):
    name: str
    value: Literal | VariableRef

    @property
    def fields(self) -> tuple[str, ...]:
        return ("value",)

    def __str__(self) -> str:
        return f"{self.name}={self.value}"

name instance-attribute

value instance-attribute

fields property

messageformat2.datamodel.FunctionAnnotation dataclass

Bases: Node

Source code in messageformat2/datamodel.py
@dataclass
class FunctionAnnotation(Node):
    name: str
    options: list[Option]

    @property
    def fields(self) -> tuple[str, ...]:
        return ("options",)

    def __str__(self) -> str:
        options = " ".join([str(opt) for opt in self.options])
        if options:
            options = f" {options}"
        return f":{self.name}{options}"

name instance-attribute

options instance-attribute

fields property

messageformat2.datamodel.UnsupportedAnnotation dataclass

Bases: Node

Source code in messageformat2/datamodel.py
@dataclass
class UnsupportedAnnotation(Node):
    source: str

    def __str__(self) -> str:
        return self.source

source instance-attribute

messageformat2.datamodel.Expression

Bases: Node

Source code in messageformat2/datamodel.py
class Expression(Node):
    pass

messageformat2.datamodel.VariableExpression dataclass

Bases: Expression

Source code in messageformat2/datamodel.py
@dataclass
class VariableExpression(Expression):
    arg: VariableRef
    annotation: FunctionAnnotation | UnsupportedAnnotation | None
    attributes: list[Attribute]

    @property
    def fields(self) -> tuple[str, ...]:
        return ("arg", "annotation", "attributes")

    def __str__(self) -> str:
        annotation = f" {self.annotation}" if self.annotation else ""
        attributes = " ".join([str(attr) for attr in self.attributes])
        if attributes:
            attributes = f" {attributes}"
        return f"{{{self.arg}{annotation}{attributes}}}"

arg instance-attribute

annotation instance-attribute

attributes instance-attribute

fields property

messageformat2.datamodel.LiteralExpression dataclass

Bases: Expression

Source code in messageformat2/datamodel.py
@dataclass
class LiteralExpression(Expression):
    arg: Literal
    annotation: FunctionAnnotation | UnsupportedAnnotation | None
    attributes: list[Attribute]

    @property
    def fields(self) -> tuple[str, ...]:
        return ("arg", "annotation", "attributes")

    def __str__(self) -> str:
        annotation = f" {self.annotation}" if self.annotation else ""
        attributes = " ".join([str(attr) for attr in self.attributes])
        if attributes:
            attributes = f" {attributes}"
        return f"{{{self.arg}{annotation}{attributes}}}"

arg instance-attribute

annotation instance-attribute

attributes instance-attribute

fields property

messageformat2.datamodel.FunctionExpression dataclass

Bases: Expression

Source code in messageformat2/datamodel.py
@dataclass
class FunctionExpression(Expression):
    annotation: FunctionAnnotation
    attributes: list[Attribute]

    @property
    def fields(self) -> tuple[str, ...]:
        return ("annotation", "attributes")

    def __str__(self) -> str:
        attributes = " ".join([str(attr) for attr in self.attributes])
        if attributes:
            attributes = f" {attributes}"
        return f"{{{self.annotation}{attributes}}}"

annotation instance-attribute

attributes instance-attribute

fields property

messageformat2.datamodel.UnsupportedExpression dataclass

Bases: Expression

Source code in messageformat2/datamodel.py
@dataclass
class UnsupportedExpression(Expression):
    annotation: UnsupportedAnnotation
    attributes: list[Attribute]

    @property
    def fields(self) -> tuple[str, ...]:
        return ("annotation", "attributes")

    def __str__(self) -> str:
        attributes = " ".join([str(attr) for attr in self.attributes])
        if attributes:
            attributes = f" {attributes}"
        return f"{{{self.annotation}{attributes}}}"

annotation instance-attribute

attributes instance-attribute

fields property

messageformat2.datamodel.Markup dataclass

Bases: Node

Source code in messageformat2/datamodel.py
@dataclass
class Markup(Node):
    kind: TypingLiteral["open", "standalone", "close"]
    name: str
    options: list[Option]
    attributes: list[Attribute]

    @property
    def fields(self) -> tuple[str, ...]:
        return ("options", "attributes")

    def __str__(self) -> str:
        options = " ".join([str(opt) for opt in self.options])
        attributes = " ".join([str(attr) for attr in self.attributes])
        if options:
            options = f" {options}"
        if attributes:
            attributes = f" {attributes}"
        if self.kind == "open":
            return f"{{#{self.name}{options}{attributes}}}"
        if self.kind == "standalone":
            return f"{{#{self.name}{options}{attributes}/}}"
        return f"{{/{self.name}{options}{attributes}}}"

kind instance-attribute

name instance-attribute

options instance-attribute

attributes instance-attribute

fields property

messageformat2.datamodel.CatchallKey dataclass

Bases: Node

Source code in messageformat2/datamodel.py
@dataclass
class CatchallKey(Node):
    value: str | None

    def __str__(self) -> str:
        return "*"

value instance-attribute

messageformat2.datamodel.Variant dataclass

Bases: Node

Source code in messageformat2/datamodel.py
@dataclass
class Variant(Node):
    keys: list[Literal | CatchallKey]
    value: Pattern

    @property
    def fields(self) -> tuple[str, ...]:
        return ("keys", "value")

    def __str__(self) -> str:
        keys = " ".join([str(key) for key in self.keys])
        pattern = "".join([str(part) for part in self.value])
        return f"{keys} {{{{{pattern}}}}}"

keys instance-attribute

value instance-attribute

fields property

messageformat2.datamodel.InputDeclaration dataclass

Bases: Node

Source code in messageformat2/datamodel.py
@dataclass
class InputDeclaration(Node):
    name: str
    value: VariableExpression

    @property
    def fields(self) -> tuple[str, ...]:
        return ("value",)

    def __str__(self) -> str:
        return f".input {self.value}"

name instance-attribute

value instance-attribute

fields property

messageformat2.datamodel.LocalDeclaration dataclass

Bases: Node

Source code in messageformat2/datamodel.py
@dataclass
class LocalDeclaration(Node):
    name: str
    value: Expression

    @property
    def fields(self) -> tuple[str, ...]:
        return ("value",)

    def __str__(self) -> str:
        return f".local {self.name} = {self.value}"

name instance-attribute

value instance-attribute

fields property

messageformat2.datamodel.UnsupportedStatement dataclass

Bases: Node

Source code in messageformat2/datamodel.py
@dataclass
class UnsupportedStatement(Node):
    keyword: str
    body: str | None
    expressions: list[Expression]

    @property
    def fields(self) -> tuple[str, ...]:
        return ("expressions",)

    def __str__(self) -> str:
        body = self.body or ""
        expressions = "\n".join([str(expr) for expr in self.expressions])
        return f".{self.keyword}{body}\n{expressions}"

keyword instance-attribute

body instance-attribute

expressions instance-attribute

fields property

messageformat2.datamodel.PatternMessage dataclass

Bases: Node

Source code in messageformat2/datamodel.py
@dataclass
class PatternMessage(Node):
    declarations: list[Declaration]
    pattern: Pattern

    @property
    def fields(self) -> tuple[str, ...]:
        return ("declarations", "pattern")

    def __str__(self) -> str:
        declarations = "\n".join([str(decl) for decl in self.declarations])
        pattern = "".join([str(part) for part in self.pattern])
        if not declarations:
            return pattern
        return f"{declarations}\n{{{pattern}}}"

declarations instance-attribute

pattern instance-attribute

fields property

messageformat2.datamodel.SelectMessage dataclass

Bases: Node

Source code in messageformat2/datamodel.py
@dataclass
class SelectMessage(Node):
    declarations: list[Declaration]
    selectors: list[Expression]
    variants: list[Variant]

    @property
    def fields(self) -> tuple[str, ...]:
        return ("declarations", "selectors", "variants")

    def __str__(self) -> str:
        declarations = "\n".join([str(decl) for decl in self.declarations])
        selectors = " ".join([str(sel) for sel in self.selectors])
        variants = "\n".join([str(var) for var in self.variants])
        if not declarations:
            return f".match {selectors}\n{variants}"
        return f"{declarations}\n.match {selectors}\n{variants}"

declarations instance-attribute

selectors instance-attribute

variants instance-attribute

fields property

messageformat2.datamodel.DataModelVisitor

Data model AST visitor.

Works the same way as the ast.NodeVisitor from the Python stdlib.

Examples:

>>> from messageformat2 import Message
>>> from messageformat2.datamodel import DataModelVisitor
>>> class VariableVisitor(DataModelVisitor):
...     def visit_VariableRef(self, node):
...         print(f"${node.name}")
...         self.generic_visit(node)
...
>>> message = Message("Hello, {$name}!")
>>> visitor = VariableVisitor()
>>> visitor.visit(message.datamodel)
$name
Source code in messageformat2/datamodel.py
class DataModelVisitor:
    """Data model AST visitor.

    Works the same way as the ast.NodeVisitor from the Python stdlib.

    Examples:
        >>> from messageformat2 import Message
        >>> from messageformat2.datamodel import DataModelVisitor
        >>> class VariableVisitor(DataModelVisitor):
        ...     def visit_VariableRef(self, node):
        ...         print(f"${node.name}")
        ...         self.generic_visit(node)
        ...
        >>> message = Message("Hello, {$name}!")
        >>> visitor = VariableVisitor()
        >>> visitor.visit(message.datamodel)
        $name
    """

    def visit(self, node: Node) -> Any:
        method_name = f"visit_{type(node).__name__}"
        visitor = getattr(self, method_name, self.generic_visit)
        return visitor(node)

    def generic_visit(self, node: Node) -> Any:
        for name in node.fields:
            if (value := getattr(node, name)) is None:
                continue
            match value:
                case [*items]:
                    for item in items:
                        if isinstance(item, Node):
                            self.visit(item)
                case _:
                    self.visit(value)

visit(node)

Source code in messageformat2/datamodel.py
def visit(self, node: Node) -> Any:
    method_name = f"visit_{type(node).__name__}"
    visitor = getattr(self, method_name, self.generic_visit)
    return visitor(node)

generic_visit(node)

Source code in messageformat2/datamodel.py
def generic_visit(self, node: Node) -> Any:
    for name in node.fields:
        if (value := getattr(node, name)) is None:
            continue
        match value:
            case [*items]:
                for item in items:
                    if isinstance(item, Node):
                        self.visit(item)
            case _:
                self.visit(value)

messageformat2.datamodel.DataModelTransformer

Bases: DataModelVisitor

Data model AST transformer.

Works the same way as the ast.NodeTransformer from the Python stdlib.

Examples:

>>> from messageformat2 import Message
>>> from messageformat2.datamodel import Literal, DataModelTransformer
>>> class LiteralTransformer(DataModelTransformer):
...     def visit_Literal(self, node):
...         return Literal(node.value.upper())
...
>>> message = Message("{|foo|}")
>>> ast = LiteralTransformer().visit(message.datamodel)
>>> str(ast)
'{|FOO|}'
Source code in messageformat2/datamodel.py
class DataModelTransformer(DataModelVisitor):
    """Data model AST transformer.

    Works the same way as the ast.NodeTransformer from the Python stdlib.

    Examples:
        >>> from messageformat2 import Message
        >>> from messageformat2.datamodel import Literal, DataModelTransformer
        >>> class LiteralTransformer(DataModelTransformer):
        ...     def visit_Literal(self, node):
        ...         return Literal(node.value.upper())
        ...
        >>> message = Message("{|foo|}")
        >>> ast = LiteralTransformer().visit(message.datamodel)
        >>> str(ast)
        '{|FOO|}'
    """

    def generic_visit[T: Node](self, node: T) -> T:
        for name in node.fields:
            old_value = getattr(node, name)
            if old_value is None:
                continue
            match old_value:
                case [*items]:
                    new_value = []
                    for item in items:
                        if not isinstance(item, Node):
                            new_value.append(item)
                            continue
                        value = self.visit(item)
                        if isinstance(value, list):
                            new_value.extend(value)
                        elif value is not None:
                            new_value.append(value)
                    setattr(node, name, new_value)
                case _:
                    new_value = self.visit(old_value)
                    if new_value is None:
                        delattr(node, name)
                    else:
                        setattr(node, name, new_value)
        return node

generic_visit(node)

Source code in messageformat2/datamodel.py
def generic_visit[T: Node](self, node: T) -> T:
    for name in node.fields:
        old_value = getattr(node, name)
        if old_value is None:
            continue
        match old_value:
            case [*items]:
                new_value = []
                for item in items:
                    if not isinstance(item, Node):
                        new_value.append(item)
                        continue
                    value = self.visit(item)
                    if isinstance(value, list):
                        new_value.extend(value)
                    elif value is not None:
                        new_value.append(value)
                setattr(node, name, new_value)
            case _:
                new_value = self.visit(old_value)
                if new_value is None:
                    delattr(node, name)
                else:
                    setattr(node, name, new_value)
    return node

messageformat2.datamodel.dump(node)

Return a string representation of the node and its children.

Parameters:

Name Type Description Default
node Node

The node to dump.

required

Examples:

>>> from messageformat2.message import Message
>>> msg = Message("Hello, {$name}!")
>>> dump(msg.datamodel)
'Hello, {$name}!'
Source code in messageformat2/datamodel.py
def dump(node: Node) -> str:
    """Return a string representation of the node and its children.

    Args:
        node: The node to dump.

    Examples:
        >>> from messageformat2.message import Message
        >>> msg = Message("Hello, {$name}!")
        >>> dump(msg.datamodel)
        'Hello, {$name}!'
    """
    return str(node)