Conditions/TransitionsΒΆ

Conditions represent choices to move between actions – these are read by the application builder when executing the graph. Note that these will always be specified in order – the first condition that evaluates to True will be the selected action.

class burr.core.action.Condition(
keys: List[str],
resolver: Callable[[State], bool],
name: str = None,
optional_keys: List[str] = None,
)ΒΆ
__init__(
keys: List[str],
resolver: Callable[[State], bool],
name: str = None,
optional_keys: List[str] = None,
)ΒΆ

Base condition class. Chooses keys to read from the state and a resolver function. If you want a condition that defaults to true, use Condition.default or just default.

Note that you can use a few fundamental operators to build more complex conditions:

  • ~ operator allows you to automatically invert the condition.

  • | operator allows you to OR two conditions together.

  • & operator allows you to AND two conditions together.

Parameters:
  • keys – Keys to read from the state

  • resolver – Function to resolve the condition to True or False

  • name – Name of the condition

__and__(other: Condition) ConditionΒΆ

Combines two conditions with an AND operator. This will return a new condition that is the AND of the two conditions.

To check if both foo is bar and baz is qux:

condition = Condition.when(foo="bar") & Condition.when(baz="qux")
# equivalent to
condition = Condition.when(foo="bar", baz="qux")
Parameters:

other – Other condition to AND with

Returns:

A new condition that is the AND of the two conditions

__or__(other: Condition) ConditionΒΆ

Combines two conditions with an OR operator. This will return a new condition that is the OR of the two conditions.

To check if either foo is bar or baz is qux:

condition = Condition.when(foo="bar") | Condition.when(baz="qux")
Parameters:

other – Other condition to OR with

Returns:

A new condition that is the OR of the two conditions

static expr(expr: str) ConditionΒΆ

Returns a condition that evaluates the given expression against state.

Do not accept expressions generated from user-inputted text, this has the potential to be unsafe. Internally this uses eval(), so the expression can execute arbitrary Python and should only be used with developer-authored strings. If you need to accept expressions from less-trusted sources (dashboards, YAML, user input), use Condition.safe_expr() which restricts evaluation to a small allowlisted AST grammar interpreted directly (no eval()).

You can also refer to this as from burr.core import expr in the API.

Warning

Condition.expr runs the supplied string under a full Python eval. Passing user-supplied or otherwise attacker-controllable strings to this function is equivalent to arbitrary code execution inside the application process. For example, an attacker who controls the expression string can execute __import__("os").system("...") or __import__("subprocess").check_output([...]) and reach anything the host process can reach.

The globals=None argument to eval below is not a sandbox: CPython auto-injects __builtins__ when globals is empty or None, which is in fact relied upon here so that expressions like len(x) work. No part of this function attempts to sandbox the evaluation.

If you need to accept untrusted expressions, do not use expr. Track apache/burr#817 for the opt-in safe-AST evaluator intended for that use case.

Parameters:

expr – Expression to evaluate. Must be a developer-authored Python expression over state variables and standard operators/builtins.

Returns:

A condition that evaluates the given expression

static lmda(
resolver: Callable[[State], bool],
state_keys: List[str],
) ConditionΒΆ

Returns a condition that evaluates the given function of State. Note that this is just a simple wrapper over the Condition object.

This does not (yet) support optional (default) arguments.

Parameters:
  • fn

  • state_keys

Returns:

property reads: list[str]ΒΆ

Returns the keys from the state that this function reads

Returns:

A list of keys

run(state: State, **run_kwargs) dictΒΆ

Runs the function on the given state and returns the result. The result is just a key/value dictionary.

Parameters:
  • state – State to run the function on

  • run_kwargs – Additional arguments to the function passed at runtime.

Returns:

Result of the function

static safe_expr(expr: str) ConditionΒΆ

Returns a condition that evaluates expr under a restricted, allowlisted AST grammar.

This is the opt-in safe sibling of Condition.expr(). Whereas expr() uses eval() and therefore accepts the full Python expression grammar (and is unsafe for untrusted input), safe_expr() parses the expression, validates every node against an allowlist at call time, and then interprets the validated tree directly. eval() is never invoked on the parsed tree.

Use safe_expr when the expression string comes from a less-trusted source – for example a dashboard rule editor, a YAML-driven graph definition, or any user input. Use expr when the expression is authored by a developer and checked in.

The allowed grammar is intentionally small:

  • Constants: int, float, str, bool, None.

  • Name lookups, resolved against the state via State.get_all().

  • Attribute access on names / other attributes. Any attribute whose name starts with __ (dunder) is rejected – this closes the standard ().__class__.__bases__[0].__subclasses__() sandbox-escape pattern.

  • Subscript (state["foo"], items[0], slices).

  • Compare: ==, !=, <, >, <=, >=, in, not in, is, is not.

  • BoolOp: and, or.

  • UnaryOp: not, unary -, unary +.

  • BinOp arithmetic: +, -, *, /, //, %, **.

  • Literal containers: tuple, list, set, dict.

  • Call only to a tight allowlist of safe builtins by name: len, abs, min, max, sum, all, any, str, int, float, bool. All other calls are rejected.

Everything else is rejected at safe_expr() call time (not at run time), including: lambdas, conditional expressions (a if b else c), comprehensions and generator expressions, the walrus operator, await, yield, imports, and any Call not on the builtin allowlist.

Parameters:

expr – Expression to evaluate

Returns:

A condition that evaluates the given expression

Raises:
  • ValueError – if the expression contains any disallowed construct (raised at call time – the condition is rejected before it ever runs).

  • SyntaxError – if the expression is not syntactically valid Python.

classmethod when(**kwargs)ΒΆ

Returns a condition that checks state values using optional operators.

You can also refer to this as from burr.core import when in the API.

Basic equality (unchanged from original):

when(foo="bar")            # state["foo"] == "bar"
when(foo="bar", baz="qux") # state["foo"] == "bar" AND state["baz"] == "qux"

Comparison operators via __ suffix:

when(age__gt=18)           # state["age"] > 18
when(age__gte=18)          # state["age"] >= 18
when(age__lt=18)           # state["age"] < 18
when(age__lte=18)          # state["age"] <= 18
when(age__ne=0)            # state["age"] != 0
when(age__eq=18)           # state["age"] == 18  (explicit)

Membership operators:

when(status__in=["a", "b"])     # state["status"] in ["a", "b"]
when(status__notin=["x", "y"])  # state["status"] not in ["x", "y"]
when(tags__contains="python")   # "python" in state["tags"]

Identity operators:

when(value__is=None)            # state["value"] is None
when(value__isnot=None)         # state["value"] is not None

Multiple conditions are ANDed together:

when(age__gte=18, status="active")  # age >= 18 AND status == "active"
Parameters:

kwargs – Keyword arguments with optional __operator suffixes

Returns:

A condition that checks all specified constraints (AND)