perlpromisemojolicious

Using IPC::Run inside Mojo::IOLoop::Subprocess


I have this perl package:

package Mojo::Runner;

use Mojo::Base 'Mojo::EventEmitter';

use Mojo::IOLoop::Subprocess;
use Mojo::Promise;
use IPC::Run;

sub run_p {
    my ($self, $command, $stdin) = @_;
    my ($stdout, $stderr);

    my $subprocess = Mojo::IOLoop::Subprocess->new;

    return $subprocess->run_p(sub {
               my ($stdin, $stdout, $stderr);
               my $process = IPC::Run::run $command, \$stdin, \$stdout, \$stderr;

               my $e = $?;

               my $promise = Mojo::Promise->new;
               return $promise->reject($stderr)  if $e;
               return $stdout
               })
}

1

that is supposed to run some process with IPC::Run from within a Mojo::IOLoop::Subprocess so that I can I use it as follows:

use FindBin qw($Bin);
use lib "$Bin/./lib";
use Mojo::Runner;
use strict;

$\ = "\n"; $, = "\t";

my $r = Mojo::Runner->new;

my $subprocess = $r->run_p(["rclone", "lsl", "my_own:", sprintf "--drive-root-folder-id=%s", "some_id" ])
    ->then(sub {
           print $_ =~ s/\n$//msr for @_
       })
    ->catch(sub  {
        my $err = shift;
        print "Subprocess error: $err";
        });

$subprocess->ioloop->start unless $subprocess->ioloop->is_running;

Now, when I intentionally fail it, for example by using the wrong command syntax, I get an error:

Unhandled rejected promise: 2024/10/01 17:25:57 Failed to lsl: couldn't list directory: googleapi: Error 404: File not found: ., notFound
 at /Users/simone/perl5/perlbrew/perls/perl-5.36.0/lib/site_perl/5.36.0/Mojo/IOLoop/Subprocess.pm line 55.

If the IPC::Run process returns ok, everything works fine.

Why the unhandled promise? what do I need to change?


Solution

  • That "Unhandled rejected promise" is the one you create with

    return $promise->reject($stderr)  if $e;
    

    The error message is emitted just before the then()-handler in your code is executed.

    You could simply change that to

    die $stderr if $e;
    

    and your code should behave as you expect it to.

    I think what happens is that you create that rejected promise in the child process, but there is no handler for a rejected promise in the code executed in the child and you cannot transfer a rejected promise via JSON serialization from the child to the parent (where your catch handler exists).