【问题标题】:handle_info/2 doesn't get the message sent by Python when cast a message using ErlPort and GenServer使用 ErlPort 和 GenServer 投射消息时,handle_info/2 没有收到 Python 发送的消息
【发布时间】:2021-02-28 02:40:54
【问题描述】:

我正在尝试使用ErlPort 将 Elixir 与 Python 混合使用,因此我决定阅读一些关于它的教程以及与所涉及的所有内容相关的文档。我了解逻辑的工作原理以及每个功能的作用。但是,我在投射消息和接收 Python 响应时遇到问题。

根据我的阅读和所做的,我了解到,当我使用cast_count/1 发送一条消息时,这是由handle_cast/2 处理的,然后是由Python 函数handle_message() 处理的,然后这个是带有函数cast_message() 和从erlport.erlang 导入的cast() 的消息。最后,Elixir 应该使用handle_info/2 处理从 Python 接收到的消息。我认为这个函数没有被执行,但我不知道原因,虽然我在不同的来源和 GenServer 和 ErlPort 的文档中调查了很多这些东西。

在我的例子中,我有下一个结构:lib/python_helper.ex 使 ErlPort 工作,lib/server.ex 调用和转换 Python 函数。

lib/python_helper.ex

defmodule WikiElixirTest.PythonHelper do
  def start_instance do
    path =
      [:code.priv_dir(:wiki_elixir_test), "python"]
      |> Path.join()
      |> to_charlist()

    {:ok, pid} = :python.start([{:python_path, path}])
    pid
  end

  def call(pid, module, function, arguments \\ []) do
    pid
    |> :python.call(module, function, arguments)
  end

  def cast(pid, message) do
    pid
    |> :python.cast(message)
  end

  def stop_instance(pid) do
    pid
    |> :python.stop()
  end
end

lib/server.ex

defmodule WikiElixirTest.Server do
  use GenServer
  alias WikiElixirTest.PythonHelper

  def start_link() do
    GenServer.start_link(__MODULE__, [])
  end

  def init(_args) do
    session = PythonHelper.start_instance()
    PythonHelper.call(session, :counter, :register_handler, [self()])

    {:ok, session}
  end

  def cast_count(count) do
    {:ok, pid} = start_link()
    GenServer.cast(pid, {:count, count})
  end

  def call_count(count) do
    {:ok, pid} = start_link()
    GenServer.call(pid, {:count, count}, :infinity)
  end

  def handle_call({:count, count}, _from, session) do
    result = PythonHelper.call(session, :counter, :counter, [count])
    {:reply, result, session}
  end

  def handle_cast({:count, count}, session) do
    PythonHelper.cast(session, count)
    {:noreply, session}
  end

  def handle_info({:python, message}, session) do
    IO.puts("Received message from Python: #{inspect(message)}")
    {:stop, :normal, session}
  end

  def terminate(_reason, session) do
    PythonHelper.stop_instance(session)
    :ok
  end
end

priv/python/counter.py

import time
import sys
from erlport.erlang import set_message_handler, cast
from erlport.erlterms import Atom

message_handler = None


def cast_message(pid, message):
    cast(pid, (Atom('python', message)))


def register_handler(pid):
    global message_handler
    message_handler = pid


def handle_message(count):
    try:
        print('Received message from Elixir')
        print(f'Count: {count}')
        result = counter(count)
        if message_handler:
            cast_message(message_handler, result)

    except Exception as e:
        print(e)
        pass


def counter(count=100):
    i = 0
    data = []
    while i < count:
        time.sleep(1)
        data.append(i+1)
        i = i + 1

    return data


set_message_handler(handle_message)

注意:我删除了@doc 以点亮代码sn-ps。是的,我知道 sys 目前没有被使用,并且在 Python try 块中捕获 Exception 不是最好的方法,它只是暂时的。

如果我在iex (iex -S mix) 中测试它,我会得到下一个:

iex(1)>  WikiElixirTest.Server.cast_count(19)
Received message from Elixir
Count: 19
:ok

我想指出 call_count/1handle_call/1 工作正常:

iex(3)>  WikiElixirTest.Server.call_count(10)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

我在发送消息时Elixir与Python的通信成功但Python与Elixir的通信不是我做错了什么?

【问题讨论】:

  • 也许 cast(pid, (Atom('python', message))) 应该是 cast(pid, (Atom('python'), message)) ?但这只是猜测……我从未听说过 ErlPort。
  • 非常感谢@Everett 指出这个错误。在我多次尝试修复它之后,我一定是打字不好。但是,现在我也收到了bytes object expected 的消息,所以我收到了回复。查看其存储库中 ErlPort 的代码,我在erlterms.py 中发现了此错误消息,我发现了错误。我要回答我自己的问题。再次,非常感谢你,埃弗雷特。

标签: python elixir gen-server


【解决方案1】:

好吧,@Everett 在问题中报告说我在counter.py 中关闭了错误的原子。

def cast_message(pid, message):
    cast(pid, (Atom('python', message)))

这必须是:

def cast_message(pid, message):
    cast(pid, (Atom('python'), message))

虽然这并不能解决问题,但它可以帮助我找到解决方案。修复第一部分 ((Atom('python'), message)) 后,当我执行 cast_count/1 时,我收到来自 Python 的消息:

iex(1)> WikiElixirTest.Server.cast_count(2)
Received message from Elixir
Count: 2
:ok
bytes object expected 

虽然它第一次让我有点困惑,因为我在等待类似“收到来自 Python 的消息:预期的字节对象”之类的东西,由于 handle_info/2。但是,我决定检查 ErlPort 的代码,并在the line 66 of erlterms.py 中发现了错误,它是Atom 类的一部分。因此错误出现在消息的第一部分,即原子,因此我将其指定为二进制:

def cast_message(pid, message):
    cast(pid, (Atom(b'python', message)))

然后我检查了它:

iex(2)> WikiElixirTest.Server.cast_count(2)
:ok
Received message from Elixir        
Count: 2        
Received message from Python: [[1, 2]]

而且效果很好!所以那里的问题,即使考虑到我对消息元组的错误输入,Atom() 需要一个二进制文件。

可能这种误解可能是由于我遵循的第一个 ErlPort tutorial 使用 Python 2,其中 str 是一个字节字符串的版本,而在 Python 3 中需要将字符串转换为字节,因为 str是一串文本。

【讨论】:

    猜你喜欢
    • 2012-02-07
    • 2020-01-13
    • 1970-01-01
    • 1970-01-01
    • 2020-07-16
    • 1970-01-01
    • 2017-09-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多