ZastrixArundell

ZastrixArundell

How do I supervise an erlport GenServer?

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?

Marked As Solved

kokolegorille

kokolegorille

Nice You find a solution.

I would trap exit from this GenServer, and restart the python session upon receiving a DOWN message… avoiding the GenSever restart, but it’s just cosmetic.

BTW You could use the following line, as it is atomic.

python_session = Python.start_link()

instead of…

# Get the pid of the python session.
python_session = Python.start()

# Link the python pid to the GenServer
Process.link(python_session)

Also Liked

kokolegorille

kokolegorille

As it is just a process, You could monitor it, catch DOWN message… then take appropriate mesure.

BTW there is also a :python.start_link() so it is easy to link it to another process.

ZastrixArundell

ZastrixArundell

Thanks. I didn’t know that the difference between start and start_link is that start_link basically links the process to the caller (I mean, now it makes sense when I wrote it). I’ll give you a solution as it’s basically what I’ve done but better ahahah!

Where Next?

Popular in Questions Top

srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
logicmason
Hi there, I'm working through my first release with elixir/phoenix. I've built a release with distillery and found that it crashes when I...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
Mooodi
Given a string, how can I get access to its character by index? Enum.at("my_string", 2) doesn't work. Or rather, not char, but a substr...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New
jc00ke
Expanding on this topic: https://forum.elixirforum.net/t/map-typespec-question/19217 Let’s say I have a map with required and optional k...
New

Other popular topics Top

chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 30048 115
New
peerreynders
Manning 2016 Halloween weekend sale via Deal of the Day Friday, October 28 - Half off all MEAPs - code WM102816LT Saturday, October 29 ...
326 29600 154
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 49522 488
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New

We're in Beta

About us Mission Statement