Packages

JSON-RPC 2.0 bridge for calling Python functions from Elixir over stdin/stdout Ports.

Current section

Files

Jump to
py_bridge lib py_bridge.ex
Raw

lib/py_bridge.ex

defmodule PyBridge do
@moduledoc """
JSON-RPC 2.0 bridge for calling Python functions from Elixir over stdin/stdout Ports.
## Quick Start
# Add a Python worker to your supervision tree
children = [
{PyBridge.Worker, name: :ml_model, python: "python3", script: "workers/model.py"}
]
Supervisor.start_link(children, strategy: :one_for_one)
# Synchronous call with default 30s timeout
{:ok, %{"prediction" => 0.85}} = PyBridge.call(:ml_model, "predict", %{features: [1, 2, 3]})
# Call with custom timeout
{:ok, result} = PyBridge.call(:ml_model, "train", %{epochs: 100}, timeout: 60_000)
# Async call — result delivered as a message
ref = PyBridge.async_call(:ml_model, "predict", %{features: [1, 2, 3]})
receive do
{:py_bridge_result, ^ref, result} -> result
end
"""
@default_timeout 30_000
@doc """
Call a Python function synchronously.
## Parameters
* `worker` - registered name or pid of the `PyBridge.Worker`
* `method` - Python function name (must be registered with `@worker.register`)
* `params` - map or list of parameters to pass
* `opts` - keyword list with optional `:timeout` (default #{@default_timeout}ms)
## Returns
* `{:ok, result}` on success
* `{:error, reason}` on failure
"""
@spec call(GenServer.server(), String.t(), map() | list(), keyword()) ::
{:ok, any()} | {:error, any()}
def call(worker, method, params \\ %{}, opts \\ []) do
timeout = Keyword.get(opts, :timeout, @default_timeout)
GenServer.call(worker, {:call, method, params, timeout}, timeout + 1_000)
end
@doc """
Call a Python function asynchronously.
Returns a reference. The result will be sent to the calling process as:
{:py_bridge_result, ref, {:ok, result} | {:error, reason}}
"""
@spec async_call(GenServer.server(), String.t(), map() | list(), keyword()) :: reference()
def async_call(worker, method, params \\ %{}, opts \\ []) do
ref = make_ref()
timeout = Keyword.get(opts, :timeout, @default_timeout)
GenServer.cast(worker, {:async_call, ref, self(), method, params, timeout})
ref
end
@doc """
Send a batch of calls and collect results.
Each call is a tuple `{method, params}` or `{method, params, timeout}`.
Returns a list of `{:ok, result}` or `{:error, reason}` in the same order.
"""
@spec batch_call(GenServer.server(), list(), keyword()) :: list({:ok, any()} | {:error, any()})
def batch_call(worker, calls, opts \\ []) do
timeout = Keyword.get(opts, :timeout, @default_timeout)
GenServer.call(worker, {:batch_call, calls, timeout}, timeout + 5_000)
end
end