py_context (erlang_python v2.3.1)

View Source

Python context process.

A py_context process owns a Python context (subinterpreter or worker). Each process has exclusive access to its context, eliminating mutex contention and enabling true N-way parallelism.

The context is created when the process starts and destroyed when it stops. All Python operations are serialized through message passing.

Callback Handling

When Python code calls erlang.call()`, the NIF returns a `{suspended, ...}` tuple instead of blocking. The context process handles the callback inline using a recursive receive pattern, enabling arbitrarily deep callback nesting. This approach is inspired by PyO3s suspension mechanism and avoids the deadlock issues that occur with separate callback handler processes.

Summary

Functions

Call a Python function with empty kwargs.

Call a Python function.

Call a Python function with timeout.

Call a Python function with a process-local environment.

Call a method on a Python object reference.

Create a process-local Python environment for this context.

Alias for stop/1 for API consistency.

Evaluate a Python expression with empty locals.

Evaluate a Python expression.

Evaluate a Python expression with timeout.

Evaluate a Python expression with a process-local environment.

Execute Python statements.

Execute Python statements with a process-local environment.

Get the interpreter ID for this context.

Get the NIF context reference from a context process. This is useful for calling low-level py_nif functions directly.

Check if this context is a subinterpreter.

Create a new context with options map.

Start a new py_context process.

Stop a py_context process.

Convert a Python object reference to an Erlang term.

Types

context/0

-type context() :: pid().

context_mode/0

-type context_mode() :: worker | subinterp | owngil.

Functions

call(Ctx, Module, Func, Args)

-spec call(context(), atom() | binary(), atom() | binary(), list()) -> {ok, term()} | {error, term()}.

Call a Python function with empty kwargs.

This is a convenience wrapper for call/5 that defaults Kwargs to #{}.

call(Ctx, Module, Func, Args, Kwargs)

-spec call(context(), atom() | binary(), atom() | binary(), list(), map()) ->
              {ok, term()} | {error, term()}.

Call a Python function.

call(Ctx, Module, Func, Args, Kwargs, Timeout)

-spec call(context(), atom() | binary(), atom() | binary(), list(), map(), timeout()) ->
              {ok, term()} | {error, term()}.

Call a Python function with timeout.

call(Ctx, Module, Func, Args, Kwargs, Timeout, EnvRef)

-spec call(context(), atom() | binary(), atom() | binary(), list(), map(), timeout(), reference()) ->
              {ok, term()} | {error, term()}.

Call a Python function with a process-local environment.

call_method(Ctx, Ref, Method, Args)

-spec call_method(context(), reference(), atom() | binary(), list()) -> {ok, term()} | {error, term()}.

Call a method on a Python object reference.

create_local_env(Ctx)

-spec create_local_env(context()) -> {ok, reference()} | {error, term()}.

Create a process-local Python environment for this context.

The environment is created inside the context's interpreter to ensure the correct memory allocator is used. This is critical for subinterpreters where each interpreter has its own memory allocator.

The returned EnvRef should be stored in the calling process's dictionary, keyed by interpreter ID.

destroy(Ctx)

-spec destroy(context()) -> ok.

Alias for stop/1 for API consistency.

eval(Ctx, Code)

-spec eval(context(), binary() | string()) -> {ok, term()} | {error, term()}.

Evaluate a Python expression with empty locals.

This is a convenience wrapper for eval/3 that defaults Locals to #{}.

eval(Ctx, Code, Locals)

-spec eval(context(), binary() | string(), map()) -> {ok, term()} | {error, term()}.

Evaluate a Python expression.

eval(Ctx, Code, Locals, Timeout)

-spec eval(context(), binary() | string(), map(), timeout()) -> {ok, term()} | {error, term()}.

Evaluate a Python expression with timeout.

eval(Ctx, Code, Locals, Timeout, EnvRef)

-spec eval(context(), binary() | string(), map(), timeout(), reference()) ->
              {ok, term()} | {error, term()}.

Evaluate a Python expression with a process-local environment.

exec(Ctx, Code)

-spec exec(context(), binary() | string()) -> ok | {error, term()}.

Execute Python statements.

exec(Ctx, Code, EnvRef)

-spec exec(context(), binary() | string(), reference()) -> ok | {error, term()}.

Execute Python statements with a process-local environment.

get_interp_id(Ctx)

-spec get_interp_id(context()) -> {ok, non_neg_integer()} | {error, term()}.

Get the interpreter ID for this context.

get_nif_ref(Ctx)

-spec get_nif_ref(context()) -> reference().

Get the NIF context reference from a context process. This is useful for calling low-level py_nif functions directly.

is_subinterp(Ctx)

-spec is_subinterp(context()) -> boolean().

Check if this context is a subinterpreter.

Returns true for subinterpreter mode, false for worker mode. In worker mode, process-local environments are used. In subinterpreter mode, each context has its own isolated namespace.

new(Opts)

-spec new(map()) -> {ok, context()} | {error, term()}.

Create a new context with options map.

Options: - mode - Context mode (worker | subinterp | owngil), default: worker

start_link(Id, Mode)

-spec start_link(pos_integer(), context_mode()) -> {ok, pid()} | {error, term()}.

Start a new py_context process.

The process creates a Python context based on the mode: - worker - Create a thread-state worker (main interpreter namespace) - subinterp - Create a sub-interpreter with shared GIL (Python 3.12+) - owngil - Create a sub-interpreter with its own GIL (Python 3.14+)

The owngil mode creates a dedicated pthread for each context, allowing true parallel Python execution. Requires Python 3.14+.

stop(Ctx)

-spec stop(context()) -> ok.

Stop a py_context process.

to_term(Ref)

-spec to_term(reference()) -> {ok, term()} | {error, term()}.

Convert a Python object reference to an Erlang term.