So I have this erlport connection module:
defmodule PheedThePi.PythonConnection do
@moduledoc """
Module responsible for the Python connection and IO.
"""
def start() do
path = [
:code.priv_dir(:pheed_the_pi), "python"
] |> Path.join() |> IO.inspect(label: "Python priv path")
{:ok, pid} = :python.start([{:python_path, to_charlist(path)}])
pid
end
def call(pid, module, function, arguments \\ []), do:
:python.call(pid, module, function, arguments)
def cast(pid, message), do:
:python.cast(pid, message)
def stop(pid), do:
:python.stop(pid)
end
And my GenServer for the connection:
defmodule PheedThePi.PythonServer do
@moduledoc """
GenServer which manages the Python erlang port.
"""
use GenServer
alias PheedThePi.PythonConnection, as: Python
def start_link(_), do:
GenServer.start_link(__MODULE__, nil, name: __MODULE__)
def init(_) do
# Get the pid of the python session.
python_session = Python.start()
# Start the connection to the python session.
Python.call(python_session, :api, :register_handler, [self()])
{:ok, python_session}
end
# Call a specific function given an atom as the function name
@spec cast_function(atom(), list(any())) :: :ok
def cast_function(function, arguments), do:
GenServer.cast(__MODULE__, {function, arguments})
# Send a general message to Python
@spec send_message(any) :: :ok
def send_message(message), do:
GenServer.cast(__MODULE__, {:python_message, message})
def handle_cast({:python_message, message}, python_session) do
Python.cast(python_session, message)
{:noreply, python_session}
end
def handle_cast({function, arguments}, python_session) do
Python.call(python_session, :api, function, arguments)
{:noreply, python_session}
end
def handle_info({:python, message}, python_session) do
IO.write "Got message from Python: #{message}\n"
{:noreply, python_session}
end
def terminate(_reason, python_session) do
Python.stop(python_session)
end
end
And my Python code:
from erlport.erlang import set_message_handler, cast
from erlport.erlterms import Atom
message_handler = None #reference to the elixir process to send result to
def message(message):
message = message.decode("utf-8")
print('Hey, python has gotten the message: ' + message)
return 'test'
def cast_message(pid, message):
cast(pid, (Atom(b'python'), message))
def register_handler(pid):
#save message handler pid
global message_handler
message_handler = pid
def handle_message(message):
message = message.decode("utf-8")
print("Received message from Elixir: " + message)
cast_message(message_handler, 'Here you go: ' + message)
set_message_handler(handle_message)
Now this works when you run expected code:
PheedThePi.PythonServer.send_message "this is a message"
:ok
iex(12)> Received message from Elixir: this is a message
Got message from Python: Here you go: this is a message
But if I were to do something stupid like:
iex(12)> PheedThePi.PythonServer.send_message 1+1
:ok
iex(13)> [error] GenServer #PID<0.420.0> terminating
** (stop) {:message_handler_error, {:python, :"builtins.AttributeError", '\'int\' object has no attribute \'decode\'', [' File "/home/zastrix/Documents/Personal/Phoenix/pheed_the_pi/_build/dev/lib/pheed_the_pi/priv/python/api.py", line 20, in handle_message\n message = message.decode("utf-8")\n', ' File "/home/zastrix/Documents/Personal/Phoenix/pheed_the_pi/_build/dev/lib/erlport/priv/python3/erlport/erlang.py", line 233, in _call_with_error_handler\n function(*args)\n']}}
Last message: {#Port<0.10>, {:data, <<131, 104, 2, 100, 0, 1, 101, 104, 4, 100, 0, 6, 112, 121, 116, 104, 111, 110, 100, 0, 23, 98, 117, 105, 108, 116, 105, 110, 115, 46, 65, 116, 116, 114, 105, 98, 117, 116, 101, 69, 114, 114, 111, 114, 107, 0, ...>>}}
State: {:state, :infinity, 0, #Port<0.10>, [], []}
I get an error message, and my erlport process dies, not my GenServer so it doesn’t restart. I couldn’t find a way but is it possible to perhaps add a Supervisor to my GenServer which supercises the erlport process?