Callback into the Python Interpreter from within JIT’ed code

There are rare but real cases when a nopython-mode function needs to callback into the Python interpreter to invoke code that cannot be compiled by Numba. Such cases include:

  • logging progress for long running JIT’ed functions;

  • use data structures that are not currently supported by Numba;

  • debugging inside JIT’ed code using the Python debugger.

When Numba callbacks into the Python interpreter, the following has to happen:

  • acquire the GIL;

  • convert values in native representation back into Python objects;

  • call-back into the Python interpreter;

  • convert returned values from the Python-code into native representation;

  • release the GIL.

These steps can be expensive. Users should not rely on the feature described here on performance-critical paths.

The objmode context-manager

Warning

This feature can be easily mis-used. Users should first consider alternative approaches to achieve their intended goal before using this feature.

numba.objmode(*args, **kwargs)

Creates a contextmanager to be used inside jitted functions to enter object-mode for using interpreter features. The body of the with-context is lifted into a function that is compiled in object-mode. This transformation process is limited and cannot process all possible Python code. However, users can wrap complicated logic in another Python function, which will then be executed by the interpreter.

Use this as a function that takes keyword arguments only. The argument names must correspond to the output variables from the with-block. Their respective values can be:

  1. strings representing the expected types; i.e. "float32".

  2. compile-time bound global or nonlocal variables referring to the expected type. The variables are read at compile time.

When exiting the with-context, the output variables are converted to the expected nopython types according to the annotation. This process is the same as passing Python objects into arguments of a nopython function.

Example:

import numpy as np
from numba import njit, objmode, types

def bar(x):
    # This code is executed by the interpreter.
    return np.asarray(list(reversed(x.tolist())))

# Output type as global variable
out_ty = types.intp[:]

@njit
def foo():
    x = np.arange(5)
    y = np.zeros_like(x)
    with objmode(y='intp[:]', z=out_ty):  # annotate return type
        # this region is executed by object-mode.
        y += bar(x)
        z = y
    return y, z

Note

Known limitations:

  • with-block cannot use incoming list objects.

  • with-block cannot use incoming function objects.

  • with-block cannot yield, break, return or raise such that the execution will leave the with-block immediately.

  • with-block cannot contain with statements.

  • random number generator states do not synchronize; i.e. nopython-mode and object-mode uses different RNG states.

Note

When used outside of no-python mode, the context-manager has no effect.

Warning

This feature is experimental. The supported features may change with or without notice.