当使用ErlPort和GenServer强制转换消息时,handle_info/2不会获取Python发送的消息

2024-05-29 04:10:43 发布

您现在位置:Python中文网/ 问答频道 /正文

我正试图使用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使erport工作,并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以照亮代码片段。是的,我知道目前没有使用sys,Python中的catchException{}块不是最好的方法,它只是暂时的

如果我在iexiex -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的通信不是成功的,我做错了什么


Tags: messagesessiondefcountokcallpiddo
1条回答
网友
1楼 · 发布于 2024-05-29 04:10:43

好吧,@Everett在问题中报告说我在^{中错误地关闭了原子

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 ^{}中发现了错误,它是Atom类的一部分。因此,错误出现在消息的第一部分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是一个文本字符串

相关问题 更多 >

    热门问题