Source code for pystructs.expressions

"""Expression system for validation and computation."""

from __future__ import annotations

import binascii
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
    from pystructs.struct import Struct

__all__ = (
    "Expression",
    "Len",
    "Value",
    "Const",
    "Checksum",
    "BinaryOp",
)


[docs] class Expression(ABC): """Abstract base class for expressions. Expressions are used in validators to compute expected values. """
[docs] @abstractmethod def evaluate(self, instance: Struct) -> Any: """Evaluate the expression. Args: instance: The struct instance to evaluate against Returns: The computed value """ pass
def __add__(self, other: Expression | Any) -> BinaryOp: """Add two expressions.""" return BinaryOp(self, "+", other) def __radd__(self, other: Any) -> BinaryOp: """Add with expression on the right.""" return BinaryOp(Const(other), "+", self) def __sub__(self, other: Expression | Any) -> BinaryOp: """Subtract two expressions.""" return BinaryOp(self, "-", other) def __rsub__(self, other: Any) -> BinaryOp: """Subtract with expression on the right.""" return BinaryOp(Const(other), "-", self) def __mul__(self, other: Expression | Any) -> BinaryOp: """Multiply two expressions.""" return BinaryOp(self, "*", other) def __rmul__(self, other: Any) -> BinaryOp: """Multiply with expression on the right.""" return BinaryOp(Const(other), "*", self) def __truediv__(self, other: Expression | Any) -> BinaryOp: """Divide two expressions.""" return BinaryOp(self, "/", other)
[docs] class Len(Expression): """Length of a field's value. Examples: >>> Consistency('payload_size', equals=Len('payload')) """ def __init__(self, field: str): """Initialize a Len expression. Args: field: Field name to get length of """ self.field = field
[docs] def evaluate(self, instance: Struct) -> int: """Get the length of the field value. Args: instance: The struct instance Returns: Length of the field value """ value = getattr(instance, self.field) return len(value)
def __repr__(self) -> str: return f"Len({self.field!r})"
[docs] class Value(Expression): """Value of a field. Examples: >>> Consistency('checksum', equals=Value('expected_checksum')) """ def __init__(self, field: str): """Initialize a Value expression. Args: field: Field name to get value of """ self.field = field
[docs] def evaluate(self, instance: Struct) -> Any: """Get the field value. Args: instance: The struct instance Returns: The field value """ return getattr(instance, self.field)
def __repr__(self) -> str: return f"Value({self.field!r})"
[docs] class Const(Expression): """Constant value. Examples: >>> Consistency('magic', equals=Const(0xDEADBEEF)) """ def __init__(self, value: Any): """Initialize a Const expression. Args: value: The constant value """ self.value = value
[docs] def evaluate(self, instance: Struct) -> Any: """Return the constant value. Args: instance: The struct instance (unused) Returns: The constant value """ return self.value
def __repr__(self) -> str: return f"Const({self.value!r})"
[docs] class Checksum(Expression): """Checksum of a field's value. Supported algorithms: 'crc32' Examples: >>> Consistency('checksum', equals=Checksum('payload', 'crc32')) """ def __init__(self, field: str, algorithm: str = "crc32"): """Initialize a Checksum expression. Args: field: Field name to compute checksum of algorithm: Checksum algorithm ('crc32') """ self.field = field self.algorithm = algorithm
[docs] def evaluate(self, instance: Struct) -> int: """Compute the checksum of the field value. Args: instance: The struct instance Returns: The checksum value Raises: ValueError: If algorithm is not supported """ data = getattr(instance, self.field) if isinstance(data, str): data = data.encode("utf-8") if self.algorithm == "crc32": return binascii.crc32(data) & 0xFFFFFFFF else: raise ValueError(f"Unknown checksum algorithm: {self.algorithm}")
def __repr__(self) -> str: return f"Checksum({self.field!r}, {self.algorithm!r})"
class BinaryOp(Expression): """Binary operation between two expressions. Examples: >>> Consistency('total_size', equals=Len('header') + Len('payload')) """ def __init__(self, left: Expression, op: str, right: Expression | Any): """Initialize a BinaryOp expression. Args: left: Left operand (Expression) op: Operator ('+', '-', '*', '/') right: Right operand (Expression or value) """ self.left = left self.op = op self.right = right if isinstance(right, Expression) else Const(right) def evaluate(self, instance: Struct) -> Any: """Evaluate the binary operation. Args: instance: The struct instance Returns: The result of the operation """ left_val = self.left.evaluate(instance) right_val = self.right.evaluate(instance) if self.op == "+": return left_val + right_val elif self.op == "-": return left_val - right_val elif self.op == "*": return left_val * right_val elif self.op == "/": return left_val / right_val else: raise ValueError(f"Unknown operator: {self.op}") def __repr__(self) -> str: return f"({self.left!r} {self.op} {self.right!r})"