AnyEvent-Open3-Simple

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN


0.74      2013-09-12 06:10:50 -0400
  - skip raw support testing on all platforms

0.73      2013-09-10 14:08:24 -0400
  - perl 5.8 support fixes

0.72      2013-09-09 08:20:35 -0400
  - marking raw support as experiemental
    https://github.com/plicease/AnyEvent-Open3-Simple/issues/6
  - undocumented (and experimental): on_stderr and on_stdout can be list ref

0.71      2013-09-03 15:19:59 -0400
  - fixed raw support

0.70      2013-09-03 12:39:38 -0400
  - promote to production

0.69_02   2013-09-01 14:25:50 -0400
  - add prereq EV on MSWin32

README  view on Meta::CPAN

         my $proc = shift;       # isa AnyEvent::Open3::Simple::Process
         my $program = shift;    # string
         my @args = @_;          # list of arguments
         say 'child PID: ', $proc->pid;
       },
       on_stdout => sub {
         my $proc = shift;       # isa AnyEvent::Open3::Simple::Process
         my $line = shift;       # string
         say 'out: ', $string;
       },
       on_stderr => sub {
         my $proc = shift;       # isa AnyEvent::Open3::Simple::Process
         my $line = shift;       # string
         say 'err: ', $line;
       },
       on_exit   => sub {
         my $proc = shift;       # isa AnyEvent::Open3::Simple::Process
         my $exit_value = shift; # integer
         my $signal = shift;     # integer
         say 'exit value: ', $exit_value;
         say 'signal:     ', $signal;

README  view on Meta::CPAN

         $done->send;
       },
     );
     
     $ipc->run('echo', 'hello there');
     $done->recv;

DESCRIPTION

    This module provides an interface to open3 while running under AnyEvent
    that delivers data from stdout and stderr as lines are written by the
    subprocess. The interface is reminiscent of IPC::Open3::Simple,
    although this module does provides a somewhat different API, so it
    cannot be used a drop in replacement for that module.

    There are already a number of interfaces for interacting with
    subprocesses in the context of AnyEvent, but this one is the most
    convenient for my usage. Note the modules listed in the SEE ALSO
    section below for other interfaces that may be more or less
    appropriate.

README  view on Meta::CPAN

      will raise an on_error event, on MSWin32 it will not trigger a
      on_error and instead cause a normal exit with a exit value of 1.

      In versions 0.77 and better, this event also gets the program name
      and arguments passed into the run method.

      * on_stdout ($proc, $line)

      Called on every line printed to stdout by the child process.

      * on_stderr ($proc, $line)

      Called on every line printed to stderr by the child process.

      * on_exit ($proc, $exit_value, $signal)

      Called when the processes completes, either because it called exit,
      or if it was killed by a signal.

      * on_success ($proc)

      Called when the process returns zero exit value and is not terminated
      by a signal.

author.yml  view on Meta::CPAN

---
pod_spelling_system:
  stopwords:
    - AnyEvent
    - stderr
    - API
    - MSWin32
    - MSWin
    - Scaffidi
    - prereq
    - OpenBSD
    - Wiersdorf
    - open3
    - suboptimal
    - mojo

lib/AnyEvent/Open3/Simple.pm  view on Meta::CPAN



sub new
{
  my $default_handler = sub { };
  my $class = shift;
  my $args = (reftype($_[0]) || '') eq 'HASH' ? shift : { @_ };
  my %self;
  croak "stdin passed into AnyEvent::Open3::Simple->new no longer supported" if $args->{stdin};
  croak "raw passed into AnyEvent::Open::Simple->new no longer supported" if $args->{raw};
  $self{$_} = $args->{$_} || $default_handler for qw( on_stdout on_stderr on_start on_exit on_signal on_fail on_error on_success );
  $self{impl} = $args->{implementation}
             || $ENV{ANYEVENT_OPEN3_SIMPLE}
             || _detect();
  croak "unknown implementation $self{impl}" unless $self{impl} =~ /^(idle|child|mojo)$/;
  $self{impl} = _detect()
    if $self{impl} eq 'mojo' && do { require Mojo::Reactor; Mojo::Reactor->detect eq 'Mojo::Reactor::EV' };
  bless \%self, $class;
}


lib/AnyEvent/Open3/Simple.pm  view on Meta::CPAN

  croak "run method requires at least one argument"
    unless @_ >= 2;

  my $proc_user = (ref $_[-1] eq 'CODE' ? pop : sub {});

  my $stdin;
  $stdin = pop if ref $_[-1];

  my($self, $program, @arguments) = @_;

  my($child_stdin, $child_stdout, $child_stderr);
  $child_stderr = gensym;

  local *TEMP;
  if(defined $stdin)
  {
    my $file = File::Temp->new;
    $file->autoflush(1);
    $file->print(
      ref($stdin) eq 'ARRAY'
      ? join("\n", @{ $stdin })
      : $$stdin

lib/AnyEvent/Open3/Simple.pm  view on Meta::CPAN

    AnyEvent::detect();
    require AnyEvent::Open3::Simple::Idle if $self->{impl} eq 'idle';
  }
  elsif($self->{impl} eq 'mojo')
  {
    require Mojo::Reactor;
    require Mojo::IOLoop;
    require AnyEvent::Open3::Simple::Mojo;
  }

  my $pid = eval { open3 $child_stdin, $child_stdout, $child_stderr, $program, @arguments };

  if(my $error = $@)
  {
    $self->{on_error}->($error, $program, @arguments);
    return;
  }

  my $proc = AnyEvent::Open3::Simple::Process->new($pid, $child_stdin);
  $proc_user->($proc);

  $self->{on_start}->($proc, $program, @arguments);

  my $watcher_stdout;
  my $watcher_stderr;

  my $stdout_callback = sub {
    my $input = <$child_stdout>;
    return unless defined $input;
    $input =~ s/(\015?\012|\015)$//;
    my $ref = $self->{on_stdout};
    ref($ref) eq 'ARRAY' ? push @$ref, $input : $ref->($proc, $input);
  };

  my $stderr_callback = sub {
    my $input = <$child_stderr>;
    return unless defined $input;
    $input =~ s/(\015?\012|\015)$//;
    my $ref = $self->{on_stderr};
    ref($ref) eq 'ARRAY' ? push @$ref, $input : $ref->($proc, $input);
  };

  if(!_is_native_win32() && $self->{impl} =~ /^(idle|child)$/)
  {
    $watcher_stdout = AnyEvent->io(
      fh   => $child_stdout,
      poll => 'r',
      cb   => $stdout_callback,
    ) unless _is_native_win32();

    $watcher_stderr = AnyEvent->io(
      fh   => $child_stderr,
      poll => 'r',
      cb   => $stderr_callback,
    ) unless _is_native_win32();
  }

  my $watcher_child;

  my $end_cb = sub {
    #my($pid, $status) = @_;
    my $status = $_[1];
    my($exit_value, $signal) = ($status >> 8, $status & 127);

    $proc->close;

    # make sure we consume any stdout and stderr which hasn't
    # been consumed yet.  This seems to make on_out.t work on
    # cygwin
    if($self->{raw})
    {
      local $/;
      $self->{on_stdout}->($proc, scalar <$child_stdout>);
      $self->{on_stderr}->($proc, scalar <$child_stderr>);
    }
    else
    {
      while(!eof $child_stdout)
      {
        my $input = <$child_stdout>;
        last unless defined $input;
        $input =~ s/(\015?\012|\015)$//;
        my $ref = $self->{on_stdout};
        ref($ref) eq 'ARRAY' ? push @$ref, $input : $ref->($proc, $input);
      }

      while(!eof $child_stderr)
      {
        my $input = <$child_stderr>;
        last unless defined $input;
        $input =~ s/(\015?\012|\015)$//;
        my $ref = $self->{on_stderr};
        ref($ref) eq 'ARRAY' ? push @$ref, $input : $ref->($proc, $input);
      }
    }

    $self->{on_exit}->($proc, $exit_value, $signal);
    $self->{on_signal}->($proc, $signal) if $signal > 0;
    $self->{on_fail}->($proc, $exit_value) if $exit_value > 0;
    $self->{on_success}->($proc) if $signal == 0 && $exit_value == 0;
    undef $watcher_stdout;
    undef $watcher_stderr;
    undef $watcher_child;
    undef $proc;
  };

  if($self->{impl} eq 'mojo')
  {
    my($selout, $selerr);

    if(_is_native_win32())
    {
      require IO::Select;
      $selout = IO::Select->new($child_stdout);
      $selerr = IO::Select->new($child_stderr);
    }

    my $reactor = Mojo::IOLoop->singleton->reactor;
    my $id;
    $id = Mojo::IOLoop->recurring(0.25 => sub {
      AnyEvent::Open3::Simple::Mojo::_watcher($pid, sub {
        $end_cb->(@_);
        Mojo::IOLoop->remove($id);
        if(_is_native_win32())
        {
          $stdout_callback->() if $selout->can_read(0);
          $stderr_callback->() if $selerr->can_read(0);
        }
        else
        {
          $reactor->remove($child_stdout);
          $reactor->remove($child_stderr);
        }
      });
    });

  }
  elsif($self->{impl} eq 'idle')
  {
    my($selout, $selerr);

    if(_is_native_win32())
    {
      require IO::Select;
      $selout = IO::Select->new($child_stdout);
      $selerr = IO::Select->new($child_stderr);
    }

    $watcher_child = AnyEvent->idle(cb => sub {
      if(_is_native_win32())
      {
        $stdout_callback->() if $selout->can_read(0);
        $stderr_callback->() if $selerr->can_read(0);
      }
      AnyEvent::Open3::Simple::Idle::_watcher($pid, $end_cb);
    });
  }
  else
  {
    $watcher_child = AnyEvent->child(
      pid => $pid,
      cb  => $end_cb,
    );

lib/AnyEvent/Open3/Simple.pm  view on Meta::CPAN

     my $proc = shift;       # isa AnyEvent::Open3::Simple::Process
     my $program = shift;    # string
     my @args = @_;          # list of arguments
     say 'child PID: ', $proc->pid;
   },
   on_stdout => sub {
     my $proc = shift;       # isa AnyEvent::Open3::Simple::Process
     my $line = shift;       # string
     say 'out: ', $string;
   },
   on_stderr => sub {
     my $proc = shift;       # isa AnyEvent::Open3::Simple::Process
     my $line = shift;       # string
     say 'err: ', $line;
   },
   on_exit   => sub {
     my $proc = shift;       # isa AnyEvent::Open3::Simple::Process
     my $exit_value = shift; # integer
     my $signal = shift;     # integer
     say 'exit value: ', $exit_value;
     say 'signal:     ', $signal;

lib/AnyEvent/Open3/Simple.pm  view on Meta::CPAN

     $done->send;
   },
 );
 
 $ipc->run('echo', 'hello there');
 $done->recv;

=head1 DESCRIPTION

This module provides an interface to open3 while running under AnyEvent
that delivers data from stdout and stderr as lines are written by the
subprocess.  The interface is reminiscent of L<IPC::Open3::Simple>,
although this module does provides a somewhat different API, so it
cannot be used a drop in replacement for that module.

There are already a number of interfaces for interacting with subprocesses
in the context of L<AnyEvent>, but this one is the most convenient for my
usage.  Note the modules listed in the SEE ALSO section below for other
interfaces that may be more or less appropriate.

=head1 CONSTRUCTOR

lib/AnyEvent/Open3/Simple.pm  view on Meta::CPAN

with a exit value of 1.

In versions 0.77 and better, this event also gets the program name
and arguments passed into the L<run|AnyEvent::Open3::Simple#run>
method.

=item * C<on_stdout> ($proc, $line)

Called on every line printed to stdout by the child process.

=item * C<on_stderr> ($proc, $line)

Called on every line printed to stderr by the child process.

=item * C<on_exit> ($proc, $exit_value, $signal)

Called when the processes completes, either because it called exit,
or if it was killed by a signal.

=item * C<on_success> ($proc)

Called when the process returns zero exit value and is not terminated by a signal.

t/anyevent_open3_simple__mojo.t  view on Meta::CPAN

  on_exit => sub {
    (undef, $exit_value, undef) = @_;
    Mojo::IOLoop->stop;
    pass 'called on_exit';
  },
  on_stdout => sub {
    my $line = pop;
    push @out, $line;
    note "[out] $line";
  },
  on_stderr => sub {
    my $line = pop;
    push @err, $line;
    note "[err] $line";
  },
);

my $program = do {
  my $fn = File::Spec->catfile( tempdir( CLEANUP => 1 ), 'mojo_test.pl' );
  open my $fh, '>', $fn;
  print $fh 'print "dragon\n"; print STDERR "lime\n"; exit 22';

t/anyevent_open3_simple__mojo.t  view on Meta::CPAN

  $fn;
};

$ipc->run($^X, $program);

Mojo::IOLoop->start;

ok $called_on_start, 'called on start';
is $exit_value, 22, 'exit = 22';
is_deeply \@out, ['dragon'], 'stdout';
is_deeply \@err, ['lime'], 'stderr';

is $ipc->{impl}, 'mojo', 'used mojo implementation';

ok !$INC{'AnyEvent.pm'}, 'did not load AnyEvent';
diag "AnyEvent.pm = $INC{'AnyEvent.pm'}" if $INC{'AnyEvent.pm'};

t/anyevent_open3_simple__on_out.t  view on Meta::CPAN

my $done = AnyEvent->condvar;

my @out;
my @err;
my $exit_value;
my $signal;
my $proc;

my $ipc = AnyEvent::Open3::Simple->new(
  on_stdout => sub { push @out, pop },
  on_stderr => sub { push @err, pop },
  on_exit   => sub {
    ($proc, $exit_value, $signal) = @_;
    $done->send;
  },
);

my $timeout = AnyEvent->timer (
  after => 5,
  cb    => sub { diag 'timeout!'; exit 2; },
);

t/anyevent_open3_simple__on_out_array.t  view on Meta::CPAN

my $done = AnyEvent->condvar;

my @out;
my @err;
my $exit_value;
my $signal;
my $proc;

my $ipc = AnyEvent::Open3::Simple->new(
  on_stdout => \@out,
  on_stderr => \@err,
  on_exit   => sub {
    ($proc, $exit_value, $signal) = @_;
    $done->send;
  },
);

my $timeout = AnyEvent->timer (
  after => 5,
  cb    => sub { diag 'timeout!'; exit 2; },
);

xt/author/pod_spelling_system.t  view on Meta::CPAN

  if -r $config_filename;

plan skip_all => 'disabled' if $config->{pod_spelling_system}->{skip};

chdir(File::Spec->catdir($FindBin::Bin, File::Spec->updir, File::Spec->updir));

add_stopwords($config->{pod_spelling_system}->{stopwords}->@*);
add_stopwords(qw(
Plicease
stdout
stderr
stdin
subref
loopback
username
os
Ollis
Mojolicious
plicease
CPAN
reinstall



( run in 0.608 second using v1.01-cache-2.11-cpan-49f99fa48dc )