Passing JSON to Elixir from Python w/Export(erlport)

defmodule Supervisor.PyOperatorManager do
  use Supervisor

  def start_link(_) do
    Supervisor.start_link(__MODULE__, [], name: __MODULE__)
  end

  @impl true
  def init(_) do
    Process.flag(:trap_exit, true)

    children = [
      :poolboy.child_spec(:py_pool,
        name: {:local, :py_pool},
        worker_module: Server.PyOperator,
        size: 10,
        max_overflow: 5
      )
    ]

    Supervisor.init(children, strategy: :one_for_one)
  end

  def launch(data \\ [], py_module, py_lambda) do
    :poolboy.transaction(:py_pool, fn pid ->
      GenServer.call(pid, {data, py_module, py_lambda}, 30_000)
    end)
  end
end
defmodule Server.PyOperator do
  use GenServer
  use Export.Python

  def start_link(_) do
    GenServer.start_link(__MODULE__, %{})
  end

  @impl true
  def init(state) do
    Process.flag(:trap_exit, true)

    priv_path = Path.join(:code.priv_dir(:arbit), "python")
    {:ok, py} = Python.start_link(python_path: priv_path)
    {:ok, Map.put(state, :py, py)}
  end

  @impl true
  def handle_call({data, py_module, py_lambda}, _from, %{py: py} = state) do
    result = Python.call(py, py_module, py_lambda, [data])

    {:reply, result, state}
    |>IO.inspect(label: "#{__MODULE__}  - line 24")

    {:reply, result, state}
  end

  @impl true
  def terminate(_reason, %{py: py}) do
    Python.stop(py)
    :ok
  end
end
#foo.py
import simplejson as json


def main(_):
    json.dumps({'message': 'Hello Elixir'})

iex(1)> Supervisor.PyOperatorManager.launch("foo", "main")
Elixir.Server.PyOperator  - line 24: {:reply, :undefined, %{py: #PID<0.1598.0>}}
:undefined

How is JSON passed to Elixir from Python w/ Export(erlport)?

Fix:

#foo.py
import simplejson as json


def main(_):
    return json.dumps({'message': 'Hello Elixir'})

Modification:

  • Added explicit return keyword. In contrast to Elixir, Python return values must be explicitly assigned to the return keyword or they will be discarded.

Note:

  • Python does not support hot code reloading. Because files are built as they are imported, restarting iex is required for code changes to take effect.