运行 并作为持久子进程调用 Python 程序

Running and calling into a Python program as a persistent subprocess

我正在 Haskell 中编写一个微服务,看来我们需要调用一个 Python 库。我知道如何创建和配置一个进程来从 Haskell 执行此操作,但我的 Python 生锈了。这是我要实现的逻辑:

  1. Haskell 应用程序通过创建持久子进程(子进程的生命周期 = 父进程的生命周期)来初始化 运行 为 Python 库服务的最小化应用程序。
  2. Haskell 应用程序接收到网络请求并通过 stdin 恰好发送 1 个数据块(即字节串或文本)到 Python 子进程;它等待——阻塞——正好从子进程接收到 1 个数据块stdout,收集结果并将其returns作为响应。

我环顾四周,找到了最接近的解决方案:

  1. Running a Python program from Go
  2. 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 块,因为它期待行。