运行 并作为持久子进程调用 Python 程序
Running and calling into a Python program as a persistent subprocess
我正在 Haskell 中编写一个微服务,看来我们需要调用一个 Python 库。我知道如何创建和配置一个进程来从 Haskell 执行此操作,但我的 Python 生锈了。这是我要实现的逻辑:
- Haskell 应用程序通过创建持久子进程(子进程的生命周期 = 父进程的生命周期)来初始化 运行 为 Python 库服务的最小化应用程序。
- Haskell 应用程序接收到网络请求并通过 stdin 恰好发送 1 个数据块(即字节串或文本)到 Python 子进程;它等待——阻塞——正好从子进程接收到 1 个数据块stdout,收集结果并将其returns作为响应。
我环顾四周,找到了最接近的解决方案:
- Running a Python program from Go 和
- Persistent python subprocess
两者都只处理我知道如何处理的部分(即调用 Python 子进程),而不处理来自子进程的 Python 代码 运行 的细节 - - 因此这个问题。
显而易见的替代方法是简单地创建、运行 并在 Haskell 应用程序需要时停止子进程,但开销令人不快。
我试过一些最小化版本的东西:
-- From the Haskell parent process
{-# LANGUAGE OverloadedStrings #-}
import System.IO
import System.Process.Typed
configProc :: ProcessConfig Handle Handle ()
configProc =
setStdin createPipe $
setStdout createPipe $
setStderr closed $
setWorkingDir "/working/directory" $
shell "python3 my_program.py"
startPyProc :: IO (Process Handle Handle ())
startPyProc = do
p <- startProcess configProc
hSetBuffering (getStdin p) NoBuffering
hSetBuffering (getStdout p) NoBuffering
pure p
main :: IO ()
main = do
p <- startPyProc
let stdin = getStdin p
stdout = getStdout p
hSetBuffering stdin NoBuffering
hSetBuffering stdout NoBuffering
-- hGetLine won't get anything before I call hClose
-- making it impossible to stream over both stdin and stout
hPutStrLn stdin "foo" >> hClose stdin >> hGetLine stdout >>= print
# From the Python child process
import sys
if '__name__' == '__main__':
for line in sys.stdin:
# do some work and finally...
print(result)
此代码的一个问题是,如果不先关闭 stdin 句柄,我无法发送到 sdin 并从 stdout 接收,这使得实现无法执行我想要的操作(将 1 个块发送到 stdin,阻塞, 读取 stout 的结果,冲洗并重复)。另一个潜在的问题是 Python 代码可能根本不适合我要满足的规范。
只需将 print(...)
替换为 print(..., flush=True)
即可解决问题。似乎在 Python stdin/stdout 中默认为块缓冲,这使我调用了 hGetLine
块,因为它期待行。
我正在 Haskell 中编写一个微服务,看来我们需要调用一个 Python 库。我知道如何创建和配置一个进程来从 Haskell 执行此操作,但我的 Python 生锈了。这是我要实现的逻辑:
- Haskell 应用程序通过创建持久子进程(子进程的生命周期 = 父进程的生命周期)来初始化 运行 为 Python 库服务的最小化应用程序。
- Haskell 应用程序接收到网络请求并通过 stdin 恰好发送 1 个数据块(即字节串或文本)到 Python 子进程;它等待——阻塞——正好从子进程接收到 1 个数据块stdout,收集结果并将其returns作为响应。
我环顾四周,找到了最接近的解决方案:
- Running a Python program from Go 和
- Persistent python subprocess
两者都只处理我知道如何处理的部分(即调用 Python 子进程),而不处理来自子进程的 Python 代码 运行 的细节 - - 因此这个问题。
显而易见的替代方法是简单地创建、运行 并在 Haskell 应用程序需要时停止子进程,但开销令人不快。
我试过一些最小化版本的东西:
-- From the Haskell parent process
{-# LANGUAGE OverloadedStrings #-}
import System.IO
import System.Process.Typed
configProc :: ProcessConfig Handle Handle ()
configProc =
setStdin createPipe $
setStdout createPipe $
setStderr closed $
setWorkingDir "/working/directory" $
shell "python3 my_program.py"
startPyProc :: IO (Process Handle Handle ())
startPyProc = do
p <- startProcess configProc
hSetBuffering (getStdin p) NoBuffering
hSetBuffering (getStdout p) NoBuffering
pure p
main :: IO ()
main = do
p <- startPyProc
let stdin = getStdin p
stdout = getStdout p
hSetBuffering stdin NoBuffering
hSetBuffering stdout NoBuffering
-- hGetLine won't get anything before I call hClose
-- making it impossible to stream over both stdin and stout
hPutStrLn stdin "foo" >> hClose stdin >> hGetLine stdout >>= print
# From the Python child process
import sys
if '__name__' == '__main__':
for line in sys.stdin:
# do some work and finally...
print(result)
此代码的一个问题是,如果不先关闭 stdin 句柄,我无法发送到 sdin 并从 stdout 接收,这使得实现无法执行我想要的操作(将 1 个块发送到 stdin,阻塞, 读取 stout 的结果,冲洗并重复)。另一个潜在的问题是 Python 代码可能根本不适合我要满足的规范。
只需将 print(...)
替换为 print(..., flush=True)
即可解决问题。似乎在 Python stdin/stdout 中默认为块缓冲,这使我调用了 hGetLine
块,因为它期待行。