Skip to content

lineabuiltins

l_dict(*keys_and_values)

Build a dict from a number of key value pairs.

There is a special case for dictionary unpacking. In this case, the key will be an instance of _DictKwargsSentinel.

For example, if the user creates a dict like {1: 2, **d, 3: 4}, then it will create a call like:

l_dict((1, 2), (l_dict_kwargs_sentinel(), d), (3, 4))

We use a sentinel value instead of None, because None can be a valid dictionary key.

Source code in lineapy/utils/lineabuiltins.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
@register
def l_dict(
    *keys_and_values: Union[
        Tuple[K, V], Tuple[_DictKwargsSentinel, Mapping[K, V]]
    ]
) -> Dict[K, V]:
    """
    Build a dict from a number of key value pairs.

    There is a special case for dictionary unpacking. In this case, the
    key will be an instance of _DictKwargsSentinel.

    For example, if the user creates a dict like `{1: 2, **d, 3: 4}`,
    then it will create a call like:

    ```
    l_dict((1, 2), (l_dict_kwargs_sentinel(), d), (3, 4))
    ```

    We use a sentinel value instead of None, because None can be a valid
    dictionary key.
    """
    d: Dict[K, V] = {}
    for (key, value) in keys_and_values:
        if isinstance(key, _DictKwargsSentinel):
            d.update(value)  # type: ignore
        else:
            d[key] = value  # type: ignore
    return d

l_exec_expr(code)

Executes code expressions. These typically are ast nodes that inherit from ast.expr. Examples include ast.ListComp, ast.Lambda

Execute the code with input_locals set as locals, and returns a list of the output_locals pulled from the environment.

Returns:

Type Description
object

Result as well as the last argument.

Source code in lineapy/utils/lineabuiltins.py
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
@register
def l_exec_expr(code: str) -> object:
    """
    Executes code expressions. These typically are ast nodes that inherit from ast.expr.
    Examples include ast.ListComp, ast.Lambda

    Execute the `code` with `input_locals` set as locals,
    and returns a list of the `output_locals` pulled from the environment.

    Returns
    -------
    object
        Result as well as the last argument.
    """
    from lineapy.execution.context import get_context

    context = get_context()

    statement_code = f"{_EXEC_EXPRESSION_SAVED_NAME} = {code}"
    l_exec_statement(statement_code)

    res = context.global_variables[_EXEC_EXPRESSION_SAVED_NAME]
    del context.global_variables[_EXEC_EXPRESSION_SAVED_NAME]

    return res

l_exec_statement(code)

Executes code statements. These typically are ast nodes that inherit from ast.stmt. Examples include ast.ClassDef, ast.If, ast.For, ast.FunctionDef, ast.While, ast.Try, ast.With

Execute the code with input_locals set as locals, and returns a list of the output_locals pulled from the environment.

Returns:

Type Description
None

Since the code is a statement, it will not return anything.

Source code in lineapy/utils/lineabuiltins.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
@register
def l_exec_statement(code: str) -> None:
    """
    Executes code statements. These typically are ast nodes that inherit from ast.stmt.
    Examples include ast.ClassDef, ast.If, ast.For, ast.FunctionDef, ast.While, ast.Try, ast.With

    Execute the `code` with `input_locals` set as locals,
    and returns a list of the `output_locals` pulled from the environment.

    Returns
    -------
    None
        Since the code is a statement, it will not return anything.
    """
    # Move inside to avoid circular import with context using the lookups to trace
    from lineapy.execution.context import get_context

    context = get_context()
    source_location = context.node.source_location
    if source_location:
        location = source_location.source_code.location
        # Pad the code with extra lines, so that the line numbers match up
        code = (source_location.lineno - 1) * "\n" + code
        path = str(get_location_path(location))
    else:
        path = "<unknown>"
    bytecode = compile(code, path, "exec")
    trace_fn = exec_and_record_function_calls(
        bytecode, context.global_variables
    )
    # If we were able to understand all the opcode, then save the function calls, otherwise throw them away
    # and depend on the worst case assumptions
    if not trace_fn.not_implemented_ops:
        context.function_calls = trace_fn.function_calls

l_import(name, base_module=None)

Imports and returns a module. If the base_module is provided, the module will be a submodule of the base.

If a base_module is provided, the base_module will be flagged as 'mutated' by our annotations.

Source code in lineapy/utils/lineabuiltins.py
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
@register
def l_import(
    name: str, base_module: types.ModuleType = None
) -> types.ModuleType:
    """
    Imports and returns a module. If the base_module is provided, the module
    will be a submodule of the base.

    If a `base_module` is provided, the base_module will be flagged as 'mutated' by our annotations.
    """
    assert "." not in name
    full_name = base_module.__name__ + "." + name if base_module else name
    try:
        value = importlib.import_module(full_name)
    # wrap all exceptions happening during import into a single LImportError
    except Exception as e:
        raise LImportError from e

    return value

l_unpack_ex(xs, before, after)

Slits the iterable xs into three pieces and then joins them [*first, middle, *list] The first of length before, the last of length after, and the middle whatever is remaining.

Modeled after the UNPACK_EX bytecode to be used in unpacking.

Source code in lineapy/utils/lineabuiltins.py
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
@register
def l_unpack_ex(
    xs: Iterable[T], before: int, after: int
) -> List[Union[T, List[T]]]:
    """
    Slits the iterable `xs` into three pieces and then joins them `[*first, middle, *list]`
    The first of length `before`, the last of length `after`, and the middle whatever is remaining.

    Modeled after the UNPACK_EX bytecode to be used in unpacking.
    """
    try:
        xs_list = list(xs)
    except TypeError:
        raise TypeError(
            f"cannot unpack non-iterable {type(xs).__name__} object)"
        )
    xs_n = len(xs_list)
    min_values = before + after
    if xs_n < min_values:
        raise ValueError(
            f"not enough values to unpack (expected at least {min_values}, got {xs_n})"
        )
    before_list = xs_list[:before]
    if after != 0:
        after_list = xs_list[-after:]
        middle_list = xs_list[before:-after]
    else:
        after_list = []
        middle_list = xs_list[before:]
    return [*before_list, middle_list, *after_list]

l_unpack_sequence(xs, n)

Asserts the iterable xs is of length n and turns it into a list.

The same as l_list but asserts the length. This was modeled after the UNPACK_SEQUENCE bytecode to be used in unpacking

The result should be a view of the input.

Source code in lineapy/utils/lineabuiltins.py
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
@register
def l_unpack_sequence(xs: Iterable[T], n: int) -> List[T]:
    """
    Asserts the iterable `xs` is of length `n` and turns it into a list.

    The same as `l_list` but asserts the length. This was modeled after the UNPACK_SEQUENCE
    bytecode to be used in unpacking

    The result should be a view of the input.
    """
    try:
        res = list(xs)
    except TypeError:
        raise TypeError(
            f"cannot unpack non-iterable {type(xs).__name__} object)"
        )
    actual_n = len(res)
    if actual_n > n:
        raise ValueError(f"too many values to unpack (expected {n})")
    if actual_n < n:
        raise ValueError(
            f"not enough values to unpack (expected {n}, got {actual_n})"
        )
    return res

Was this helpful?

Help us improve docs with your feedback!