Skip to content

Formula Validation

validate_formula(expr, allowed_symbols)

Validate a mathematical formula expression for safety and correctness.

This function parses the expression using Python's AST and validates that: - Only allowed operators and functions are used - All variable names are in the allowed symbols set - The expression is syntactically valid

Parameters:

Name Type Description Default
expr str

The mathematical expression to validate

required
allowed_symbols Set[str]

Set of allowed variable names in the expression

required

Raises:

Type Description
ValueError

If the expression contains disallowed operators, functions, or symbols

SyntaxError

If the expression is not syntactically valid Python

Source code in trace/utilities/formula_validation.py
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
def validate_formula(expr: str, allowed_symbols: Set[str]) -> None:
    """Validate a mathematical formula expression for safety and correctness.

    This function parses the expression using Python's AST and validates that:
    - Only allowed operators and functions are used
    - All variable names are in the allowed symbols set
    - The expression is syntactically valid

    Parameters
    ----------
    expr : str
        The mathematical expression to validate
    allowed_symbols : Set[str]
        Set of allowed variable names in the expression

    Raises
    ------
    ValueError
        If the expression contains disallowed operators, functions, or symbols
    SyntaxError
        If the expression is not syntactically valid Python
    """
    tree = ast.parse(expr, mode="eval")

    for node in ast.walk(tree):
        if not isinstance(node, _ALLOWED_NODES):
            raise ValueError(f'Operator "{type(node).__name__}" not allowed')

        if isinstance(node, ast.Call):
            fn = node.func.id if isinstance(node.func, ast.Name) else None
            if fn not in _ALLOWED_FUNC_NAMES:
                raise ValueError(f'Function "{fn}" not permitted')

        if isinstance(node, ast.Name):
            if node.id not in allowed_symbols and node.id not in _ALLOWED_FUNC_NAMES:
                raise ValueError(f'Unknown symbol "{node.id}"')

sanitize_for_validation(expr)

Convert formula expression with variable placeholders to valid Python expression.

This function replaces variable placeholders in the format {VAR_NAME} with temporary identifiers (v0, v1, etc.) that can be parsed by Python's AST. This allows validation of formulas that reference curve variables.

Parameters:

Name Type Description Default
expr str

The formula expression containing variable placeholders like {PV1}

required

Returns:

Type Description
tuple[str, set[str]]

A tuple containing: - python_expr : str Expression that the Python AST can parse with temporary identifiers - allowed_syms : set[str] The generated temporary identifiers, ready to pass to validate_formula

Source code in trace/utilities/formula_validation.py
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
def sanitize_for_validation(expr: str) -> tuple[str, set[str]]:
    """Convert formula expression with variable placeholders to valid Python expression.

    This function replaces variable placeholders in the format {VAR_NAME} with
    temporary identifiers (v0, v1, etc.) that can be parsed by Python's AST.
    This allows validation of formulas that reference curve variables.

    Parameters
    ----------
    expr : str
        The formula expression containing variable placeholders like {PV1}

    Returns
    -------
    tuple[str, set[str]]
        A tuple containing:
        - python_expr : str
            Expression that the Python AST can parse with temporary identifiers
        - allowed_syms : set[str]
            The generated temporary identifiers, ready to pass to `validate_formula`
    """
    mapping = {}

    def _repl(match):
        var = match.group(1)
        if var not in mapping:
            mapping[var] = f"v{len(mapping)}"
        return mapping[var]

    python_expr = re.sub(r"{([^}]+)}", _repl, expr)
    return python_expr, set(mapping.values())