handle_info/2 在使用 ErlPort 和 GenServer 投射消息时未收到 Python 发送的消息
handle_info/2 doesn't get the message sent by Python when cast a message using ErlPort and GenServer
我正在尝试使用 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
以点亮代码片段。是的,我知道 sys
目前没有被使用, Python try
块中的 catch 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 的通信不成功,我做错了什么?
好吧,@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
class 的一部分。因此错误出现在消息的第一部分,即原子,所以我将其指定为二进制:
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我跟着用了Python2,版本中str
是一个字节串,在Python3中它将字符串转换为字节是必要的,因为 str
是一个文本字符串。
我正在尝试使用 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
以点亮代码片段。是的,我知道 sys
目前没有被使用, Python try
块中的 catch 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 的通信不成功,我做错了什么?
好吧,@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
class 的一部分。因此错误出现在消息的第一部分,即原子,所以我将其指定为二进制:
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我跟着用了Python2,版本中str
是一个字节串,在Python3中它将字符串转换为字节是必要的,因为 str
是一个文本字符串。