Swift: буферизует очень длинный аргумент `Process` с помощью pipe - PullRequest
0 голосов
/ 05 февраля 2019

Проблема

Я пытаюсь вызвать функцию Python из моего исполняемого файла Swift.

Это работало идеально, пока аргументы не стали слишком длинными, и я не начал получатьошибка:

необработанное исключение NSInternalInconsistencyException
причина: «Не удалось posix_spawn: ошибка 7»

, что по сути означает, что данные, которые я отправляюкод Python для обработки (мой аргумент) слишком длинный.

Последняя альтернатива

Наиболее очевидным решением является запись данных в файл и отправка скриптупуть к этому файлу, содержащему данные.Это сопровождается очевидными ограничениями производительности.

Потенциально лучшая альтернатива

Это то, что мне нужно помочь: я думал, что это может быть возможно буфер эти данные вместо того, чтобы отправлять все это одним куском.

Я пытался исследовать это, используя множество различных комбинаций ключевых слов, и за последний день читал много статей в Интернете, но ни одна не ответила на мой вопрос: так ли это?можно буферизовать данные, чтобы обойти ограничение на ядро ​​ARG_MAX около 260000 байтов, используя Process es и Pipe s?

Код

Поскольку всегда можно увидеть код, который я сейчас использую для вызова функций Python, у меня есть следующее:

@discardableResult
public static func shell(_ args: String...) -> String {

    let task = Process()
    task.launchPath = "/usr/bin/env"
    task.arguments = args
    let pipe = Pipe()
    task.standardOutput = pipe
    task.launch()
    task.waitUntilExit()

    // return task.terminationStatus
    let data = pipe.fileHandleForReading.readDataToEndOfFile()
    let output: String = String(data: data, encoding: String.Encoding.utf8)!

    return output
}   
//  public static func shell(_ args: String...) -> String {}

И чуть позже:

//
//  Call classifier on data
//
let pyStcriptPath = "\(Bundle.main.bundlePath)/Classification/****Classify.py"

//
// The fonction I'm calling here, 'shell', is the one defined just above in my question
//
let pyResult = shell("python", pyStcriptPath, "\(featureVector)", "\(signalType)")

1 Ответ

0 голосов
/ 06 февраля 2019

Вы можете отправить большую полезную нагрузку, используя трубу , вместо стандартного ввода скрипта .Если, конечно, ваша проблема вызывает только один единственный аргумент *1005* (т. Е. Все остальное, что вы можете продолжать отправлять, используя обычные аргументы командной строки).

Например:

func shell(stdin input: String, _ args: String...) -> String {
    let task = Process()
    task.launchPath = "/usr/bin/env"
    task.arguments = args

    let inPipe = Pipe()
    task.standardInput = inPipe
    inPipe.fileHandleForWriting.write(input.data(using: .utf8)!)
    inPipe.fileHandleForWriting.closeFile()

    let outPipe = Pipe()
    task.standardOutput = outPipe

    task.launch()
    task.waitUntilExit()

    let data = outPipe.fileHandleForReading.readDataToEndOfFile()
    let output = String(data: data, encoding: .utf8)!

    return output
}

Быстрый тест:

func bigString() -> String {
    var str = ""
    for i in 0..<50_000 {
        str.append("\(i % 10)")
    }
    return str
}

let script = "/PATH/TO/SCRIPT/echo.py"
let input = bigString()
let result = shell(stdin: input, "python", script, "some arg")
print(result)

отпечатки:

ARG: ABC
STDIN: 012345678901234567890123 ...

гдескрипт echo.py просто:

import sys
print "ARG:", sys.argv[1]
print "STDIN:", sys.stdin.read()
...