AsyncParsableCommand doesn't work?

I’m trying to build a command line tool that uses async/await. ArgumentParser supports this, by simply inheriting from AsyncParsableCommand and making the run method async.

Unfortunately, when I do that, it fails to parse the command line arguments, and displays the help text. If I change it to by synchronous, it parses things just fine.

Am I missing something (I don’t think I'm doing anything different from the sample code?

This works:

@main
struct
Raise3D : ParsableCommand
{
    static
    var
    configuration = CommandConfiguration(commandName: "raise3d",
                        abstract: "A utility to interact witih Raise3D printers.",
                        subcommands: [])
    
    @Option(help: "The printer’s local address.")
    var addr                        :   String

    @Option(help: "The printer’s password.")
    var password                    :   String

    mutating
    func
    run()
//      async
        throws
    {
        print("Monitoring printer at: \(self.addr)")
        
        let api = Raise3DAPI(host: self.addr, password: self.password)
        print("Signature: \(api.calculateSignature(time: Date()))")
    }
}
Monitoring printer at: 192.168.2.136:10800
Signature: password=foobar&timestamp=1713318296281

This does not (output below):

@main
struct
Raise3D : AsyncParsableCommand
{
    static
    var
    configuration = CommandConfiguration(commandName: "raise3d",
                        abstract: "A utility to interact witih Raise3D printers.",
                        subcommands: [])
    
    @Option(help: "The printer’s local address.")
    var addr                        :   String

    @Option(help: "The printer’s password.")
    var password                    :   String

    mutating
    func
    run()
        async
        throws
    {
        print("Monitoring printer at: \(self.addr)")
        
        let api = Raise3DAPI(host: self.addr, password: self.password)
        print("Signature: \(api.calculateSignature(time: Date()))")
    }
}
OVERVIEW: A utility to interact witih Raise3D printers.

USAGE: raise3d --addr <addr> --password <password>

OPTIONS:
  --addr <addr>           The printer’s local address.
  --password <password>   The printer’s password.
  -h, --help              Show help information.

The compiler is using the default (non-async) run overload/implementation. Script has a workaround and a comment on point:
https://github.com/GeorgeLyon/Shwift/blob/d7be04898d094ddce6140cc6a2e9a83fc994b66d/Sources/Script/Script.swift#L152

I imagine there may be a better solution, but Shwift is helpful anyway if you're adopting async/await in tools.

1 Like

After trying harder to understand why their example code worked, I took a shot in the dark and added @available(macOS 12, *) after @main, and that fixed it. This seems like a really horrible solution, as this tool is not macOS-specific.