Я использую пакет ArgumentParser для синтаксического анализа командной строки и хочу использовать его с async
API параллелизма Swift:
struct Foo: ParsableCommand {
@Argument(
help: "File to be parsed. If not present, parses stdin.",
transform: URL.init(fileURLWithPath:)
)
var file: URL?
mutating func run() async throws {
let handle: FileHandle
if let file {
handle = try .init(forReadingFrom: file)
} else {
handle = .standardInput
}
for try await line in handle.bytes.lines {
// do something with each line
}
try handle.close()
}
}
Но когда я это делаю, я всегда вижу текст «ИСПОЛЬЗОВАНИЕ»:
USAGE: foo [<file>]
ARGUMENTS:
<file> File to be parsed. If not present, parses stdin.
OPTIONS:
-h, --help Show help information.
Я не получаю ошибок компиляции, но я всегда вижу текст «USAGE», независимо от того, указываю ли я параметр или нет.
Проблема заключается в использовании ParsableCommand
в сочетании с run() async throws
.
Вместо этого используйте AsyncParsableCommand
. Как говорится в его документации:
Чтобы использовать код
async
/await
в реализациях методовrun()
ваших команд, выполните следующие действия:
- Для корневой команды в вашем инструменте командной строки объявите о соответствии
AsyncParsableCommand
, независимо от того, использует ли эта команда асинхронный код.- Примените атрибут
@main
к корневой команде. (Примечание. Если ваша корневая команда находится в файлеmain.Swift
, переименуйте файл в имя команды.)- Для любой команды, которая должна использовать асинхронный код, объявите о соответствии
AsyncParsableCommand
и пометьтеrun()
метод какasync
. Никаких изменений не требуется для подкоманд, которые не используют асинхронный код.
К сожалению, в то время как в более широкой документации время от времени упоминается поддержка async
, вам нужно копаться в примерах кода (в частности, количество строк) или просматривать всю библиотеку классов, чтобы наткнуться на AsyncParsableCommand
.
Итак, используйте AsyncParsableCommand
с async
исполнением run
:
import ArgumentParser
@main
struct Foo: AsyncParsableCommand {
@Argument(
help: "File to be parsed. If not present, parses stdin.",
transform: URL.init(fileURLWithPath:)
)
var file: URL?
mutating func run() async throws {
let handle: FileHandle
if let file {
handle = try .init(forReadingFrom: file)
} else {
handle = .standardInput
}
for try await line in handle.bytes.lines {
// do something with each line
}
try handle.close()
}
}
Но, к сожалению, если вы случайно используете async
версию run
с ParsableCommand
, она компилируется без ошибок, но просто выдает текст «ИСПОЛЬЗОВАНИЕ» всякий раз, когда вы ее запускаете, без какой-либо диагностической информации о том, почему она не работает.
Короче говоря, ParsableCommand
требует не-async
исполнения run
. Для исполнения async
требуется AsyncParsableCommand
.
FWIW, я опубликовал проблему в репозитории
ArgumentParser
, предлагая упростить обнаружениеAsyncParsableCommand
и добавить сообщение об ошибке, если у вас есть несоответствие междуParsableCommand
/AsyncParsableCommand
и связаннымrun
.