【发布时间】: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/1 和 handle_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