How to send messages with Elixir porcelain to a python script?
<p>I'm trying to learn how to do interop from Elixir with the <a href="https://github.com/alco/porcelain" rel="nofollow" title="porcelain module">porcelain module</a>.</p>

<p>So I made this simple example:</p>

<p>I have an Elixir function that looks like this:</p>

<pre><code>defmodule PythonMessenger do
  alias Porcelain.Process, as: Proc
  alias Porcelain.Result

  def test_messages do
    proc = %Proc{pid: pid} =
      Porcelain.spawn_shell("python ./python_scripts/reply_to_elixir.py",
        in: :receive, out: {:send, self()})

    Proc.send_input(proc, "Greetings from Elixir\n")

    data = receive do
      {^pid, :data, :out, data} -&gt; data
    end

    IO.inspect data

    Proc.send_input(proc, "Elixir: I heard you said \"#{data}\"\n")

    data = receive do
      {^pid, :data, data} -&gt; data
    end

    IO.inspect data

    Proc.send_input(proc, "Please quit\n")

    data = receive do
      {^pid, :data, data} -&gt; data
    end

    IO.inspect data
  end
end
</code></pre>

<p>and a python script that looks like this:</p>

<pre><code>import sys

while 1:

    line = sys.stdin.readline()
    if "quit" in line:
        print("Quitting, bye for now")
        sys.exit()
    print(line)
</code></pre>

<p>but this does not work. The python script never exits.
If a read just one line like:</p>

<pre><code>  line = sys.stdin.readline()
</code></pre>

<p>it works just fine.</p>

<p>So whats the problem, any ideas?</p>

<p>You need to pass <code>-u</code> to disable buffering in <code>sys.stdin.readline()</code>. You won't see this when running the program interactively, but you will see it when the program is spawned without a TTY. Because of the default buffering, the Python process was not printing anything for a short message like <code>"Greetings from Elixir\n"</code>, and because of the <code>receive</code> expression, the Elixir code was blocking forever, waiting for the Python process to print something.</p>

<p>From <code>man python</code>:</p>

<pre><code>   -u     Force  stdin,  stdout  and stderr to be totally unbuffered.  On systems where it matters, also
          put stdin, stdout and stderr in binary mode.  Note that there is internal buffering in  xread-
          lines(),  readlines()  and file-object iterators ("for line in sys.stdin") which is not influ-
          enced by this option.  To work around this, you will want to use "sys.stdin.readline()" inside
          a "while 1:" loop.
</code></pre>

<p>You also had some mistakes in the 2nd and 3rd <code>receive</code> patterns. Here's the code that works for me:</p>

<pre><code>defmodule PythonMessenger do
  alias Porcelain.Process, as: Proc
  alias Porcelain.Result

  def test_messages do
    proc = %Proc{pid: pid} =
      Porcelain.spawn_shell("python -u ./a.py",
        in: :receive, out: {:send, self()})

    Proc.send_input(proc, "Greetings from Elixir\n")

    data = receive do
      {^pid, :data, :out, data} -&gt; data
    end

    IO.inspect data

    Proc.send_input(proc, "Elixir: I heard you said \"#{data}\"\n")

    data = receive do
      {^pid, :data, :out, data} -&gt; data
    end

    IO.inspect data

    Proc.send_input(proc, "Please quit\n")

    data = receive do
      {^pid, :data, :out, data} -&gt; data
    end

    IO.inspect data
  end
end

PythonMessenger.test_messages
</code></pre>

<p>Output:</p>

<pre><code>"Greetings from Elixir\n\n"
"Elixir: I heard you said \"Greetings from Elixir\n\n\n\n"
"\"\n\n"
</code></pre>

python elixir