pyab_experiment.codegen.python.python_generator#

Module that translates an Abstract Syntax Tree (AST) into executable Python code.

This module contains the PythonCodeGen class which traverses an ExperimentAST and generates corresponding Python code for experiment variant selection. The generated code includes conditional logic, group assignments, and deterministic choice functions.

Module Contents#

Classes#

PythonCodeGen

Generates Python code from an ExperimentAST representation.

class pyab_experiment.codegen.python.python_generator.PythonCodeGen(experiment_ast: pyab_experiment.language.grammar.ExperimentAST, indentation_char: str = '\t', expose_experiment_variant_function: bool = True)[source]#

Generates Python code from an ExperimentAST representation.

This class maintains state during AST traversal (like indentation level and variable tracking)and provides methods to generate formatted Python code for experiment variant selection.

Parameters:
  • experiment_ast (ExperimentAST) – The AST to translate into Python code

  • indentation_char (str, optional) – Character used for indentation.

  • expose_experiment_variant_function (Defaults to " ") –

  • level. (Whether to expose the variant selection function at the root) –

  • True (Defaults to) –

_local_vars#

Tracks local variables used in the generated code

Type:

set

_conditional_ids#

Tracks conditional variables referenced in predicates

Type:

set

_indent_depth#

Current indentation level during code generation

Type:

int

property conditional_ids[source]#
property local_vars[source]#
_generate_conditionals(condition: pyab_experiment.language.grammar.ExperimentConditional | list[pyab_experiment.language.grammar.ExperimentGroup]) str[source]#

Generates Python code for conditional statements and group return functions.

This method traverses the experiment’s conditional structure and generates the corresponding Python code. It handles: 1. Conditional statements (if/elif/else) with their predicates and branches 2. Terminal group definitions that return partial functions for variant selection

Parameters:

condition – Either an ExperimentConditional for if/elif/else logic, or a list[ExperimentGroup] for terminal group definitions. - ExperimentConditional contains predicate and branch information - list[ExperimentGroup] contains group definitions and weights

Returns:

Generated Python code as a string, including:
  • For conditionals: if/elif/else statements with their predicates

  • For groups: partial function definitions for variant selection

Return type:

str

Raises:

RuntimeError – If an unsupported condition type is provided

Side Effects:
  • Adds conditional ids to self._conditional_ids

Example generated code:

# For conditionals: if (age >= 18):

return partial(deterministic_choice, population=[‘A’, ‘B’], weights=[1, 2])

else:

return partial(deterministic_choice, population=[‘C’, ‘D’], weights=[1, 1])

# For direct group definitions: return partial(deterministic_choice, population=[‘A’, ‘B’], weights=[1, 1])

_generate_exception() str[source]#
_generate_group_return_statement(group_statement: list[pyab_experiment.language.grammar.ExperimentGroup]) str[source]#

unwrap the experiment group, into a partial function call that applies the splitter logic

_generate_op(op: pyab_experiment.language.grammar.LogicalOperatorEnum | pyab_experiment.language.grammar.BooleanOperatorEnum) str[source]#

renders legal python operations

_generate_predicate(predicate: pyab_experiment.language.grammar.TerminalPredicate | pyab_experiment.language.grammar.RecursivePredicate | None) str[source]#

Generates Python code for predicate expressions in conditional statements.

This method handles three types of predicates: 1. Terminal predicates: Simple comparisons between two terms (e.g., “age >= 18”) 2. Recursive predicates: Complex boolean expressions combining multiple predicates 3. None: Returns an empty string (used in ELSE conditions)

Parameters:

predicate – The predicate to convert to Python code. Can be: - TerminalPredicate: For simple comparisons - RecursivePredicate: For complex boolean expressions - None: For else conditions

Returns:

A Python expression string representing the predicate logic.

Return type:

str

Raises:

RuntimeError – If an unsupported predicate type is provided.

Side Effects:
  • Adds conditional ids to self._conditional_ids as it encounters them

_generate_term(term: float | int | str | tuple | pyab_experiment.language.grammar.Identifier) str[source]#

renders a term

generate() str[source]#

main method. Does a DFS on the AST rendering python code as it traverses the nodes

generate_key_definition() str[source]#

Generate the composite hash key used for deterministic experiment group assignment.

This method combines the experiment’s salt (if provided) with the string representation of splitting fields to create a composite key. This key is used by the deterministic_choice function to consistently assign users to experiment groups.

The composite key is constructed as follows: 1. If a salt exists, it’s used as a prefix 2. If splitting fields exist, their string representations are concatenated 3. If neither exists, returns “None”

Example outputs:
  • With salt=”exp1” and fields=[user_id, country]: “‘exp1’+’’.join(map(str, [user_id, country]))”

  • With only fields=[device_id]: “’’’’.join(map(str, [device_id]))”

  • With no salt or fields: “None”

Returns:

A Python expression that evaluates to the composite key

used for group assignment.

Return type:

str

Side Effects:
  • Adds any splitting field variables to self._local_vars

indent() str[source]#

helper function that renders scope sensitive indentation using an internal state to keep track the position in the AST traversal

render_topline() str[source]#

renders the topline of our python code i.e. imports, and top line comment