Swift: буферизует очень длинный аргумент `Process` с помощью pipe
Эта проблема
Я пытаюсь вызвать функцию Python из моего исполняемого файла Swift.
Это работало отлично, пока аргументы не стали слишком длинными, и я начал получать ошибку:
неисследованное исключение
NSInternalInconsistencyException
причина: "Не удалось posix_spawn: ошибка 7"
что по сути означает, что данные, которые я отправляю для обработки в коде python (мой аргумент), слишком длинные.
Последняя альтернатива
Наиболее очевидным решением является запись данных в файл и отправка скрипту пути к этому файлу, содержащему данные. Это идет с очевидными ограничениями производительности.
Потенциально лучшая альтернатива
Это то, что мне нужно помочь: я подумал, что можно было бы буферизовать эти данные, а не отправлять их все одним куском.
Я попытался исследовать это, используя множество различных комбинаций ключевых слов, и за последний день читал много статей в Интернете, но ни одна из них не ответила на мой вопрос: можно ли буферизовать данные, чтобы обойти ядро? ARG_MAX
ограничение около 260000 байт с использованием Process
эс и 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 ответ
Вместо этого вы можете отправить свою большую полезную нагрузку, используя канал на стандартный ввод скрипта. Предполагая, конечно, только один аргумент вызывает вашу проблему (т. Е. Все остальное, что вы можете продолжать отправлять, используя обычные аргументы командной строки).
Например:
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()