File Coverage

blib/lib/AnyEvent/Open3/Simple.pm
Criterion Covered Total %
statement 119 143 83.2
branch 41 58 70.6
condition 9 13 69.2
subroutine 19 21 90.4
pod 1 2 50.0
total 189 237 79.7


line stmt bran cond sub pod time code
1             package AnyEvent::Open3::Simple;
2              
3 15     15   1220750 use strict;
  15         191  
  15         484  
4 15     15   83 use warnings;
  15         29  
  15         378  
5 15     15   366 use 5.006;
  15         56  
6 15     15   98 use warnings::register;
  15         30  
  15         2225  
7 15     15   6881 use IPC::Open3 qw( open3 );
  15         54887  
  15         900  
8 15     15   114 use Scalar::Util qw( reftype );
  15         31  
  15         1024  
9 15     15   556 use Symbol qw( gensym );
  15         807  
  15         582  
10 15     15   6395 use AnyEvent::Open3::Simple::Process;
  15         39  
  15         506  
11 15     15   181 use Carp qw( croak );
  15         32  
  15         646  
12 15     15   9514 use File::Temp ();
  15         261970  
  15         515  
13 15     15   115 use constant _is_native_win32 => $^O eq 'MSWin32';
  15         36  
  15         1053  
14 15     15   95 use constant _detect => _is_native_win32() ? 'idle' : 'child';
  15         32  
  15         26155  
15              
16             # ABSTRACT: Interface to open3 under AnyEvent
17             our $VERSION = '0.88'; # VERSION
18              
19              
20             sub new
21             {
22 19     20 0 44198 my $default_handler = sub { };
        19      
23 19         62 my $class = shift;
24 19 100 100     256 my $args = (reftype($_[0]) || '') eq 'HASH' ? shift : { @_ };
25 19         48 my %self;
26 19 100       436 croak "stdin passed into AnyEvent::Open3::Simple->new no longer supported" if $args->{stdin};
27 18 50       64 croak "raw passed into AnyEvent::Open::Simple->new no longer supported" if $args->{raw};
28 18   66     380 $self{$_} = $args->{$_} || $default_handler for qw( on_stdout on_stderr on_start on_exit on_signal on_fail on_error on_success );
29             $self{impl} = $args->{implementation}
30             || $ENV{ANYEVENT_OPEN3_SIMPLE}
31 18   50     171 || _detect();
32 18 50       181 croak "unknown implementation $self{impl}" unless $self{impl} =~ /^(idle|child|mojo)$/;
33             $self{impl} = _detect()
34 18 0 33     85 if $self{impl} eq 'mojo' && do { require Mojo::Reactor; Mojo::Reactor->detect eq 'Mojo::Reactor::EV' };
  0         0  
  0         0  
35 18         109 bless \%self, $class;
36             }
37              
38              
39             sub run
40             {
41 19 100   19 1 20437 croak "run method requires at least one argument"
42             unless @_ >= 2;
43              
44 18 100   14   112 my $proc_user = (ref $_[-1] eq 'CODE' ? pop : sub {});
45              
46 18         39 my $stdin;
47 18 100       62 $stdin = pop if ref $_[-1];
48              
49 18         74 my($self, $program, @arguments) = @_;
50              
51 18         56 my($child_stdin, $child_stdout, $child_stderr);
52 18         96 $child_stderr = gensym;
53              
54 18         397 local *TEMP;
55 18 100       68 if(defined $stdin)
56             {
57 2         22 my $file = File::Temp->new;
58 2         1069 $file->autoflush(1);
59             $file->print(
60             ref($stdin) eq 'ARRAY'
61 2 100       147 ? join("\n", @{ $stdin })
  1         9  
62             : $$stdin
63             );
64 2         104 $file->seek(0,0);
65 2         39 open TEMP, '<&=', $file; ## no critic
66 2         69 $child_stdin = '<&TEMP';
67             }
68              
69 18 50       683 if($self->{impl} =~ /^(child|idle)$/)
    0          
70             {
71 18         1106 require AnyEvent;
72 18         5536 AnyEvent::detect();
73 18 50       1493 require AnyEvent::Open3::Simple::Idle if $self->{impl} eq 'idle';
74             }
75             elsif($self->{impl} eq 'mojo')
76             {
77 0         0 require Mojo::Reactor;
78 0         0 require Mojo::IOLoop;
79 0         0 require AnyEvent::Open3::Simple::Mojo;
80             }
81              
82 18         41 my $pid = eval { open3 $child_stdin, $child_stdout, $child_stderr, $program, @arguments };
  18         85  
83              
84 18 100       75058 if(my $error = $@)
85             {
86 2         67 $self->{on_error}->($error, $program, @arguments);
87 2         400 return;
88             }
89              
90 16         562 my $proc = AnyEvent::Open3::Simple::Process->new($pid, $child_stdin);
91 16         211 $proc_user->($proc);
92              
93 16         299 $self->{on_start}->($proc, $program, @arguments);
94              
95 16         148 my $watcher_stdout;
96             my $watcher_stderr;
97              
98             my $stdout_callback = sub {
99 9     9   44150 my $input = <$child_stdout>;
100 9 100       111 return unless defined $input;
101 1         12 $input =~ s/(\015?\012|\015)$//;
102 1         3 my $ref = $self->{on_stdout};
103 1 50       8 ref($ref) eq 'ARRAY' ? push @$ref, $input : $ref->($proc, $input);
104 16         495 };
105              
106             my $stderr_callback = sub {
107 9     9   2896 my $input = <$child_stderr>;
108 9 100       89 return unless defined $input;
109 1         14 $input =~ s/(\015?\012|\015)$//;
110 1         7 my $ref = $self->{on_stderr};
111 1 50       16 ref($ref) eq 'ARRAY' ? push @$ref, $input : $ref->($proc, $input);
112 16         262 };
113              
114 16 50       358 if(!_is_native_win32() && $self->{impl} =~ /^(idle|child)$/)
115             {
116 16         523 $watcher_stdout = AnyEvent->io(
117             fh => $child_stdout,
118             poll => 'r',
119             cb => $stdout_callback,
120             ) unless _is_native_win32();
121              
122 16         779 $watcher_stderr = AnyEvent->io(
123             fh => $child_stderr,
124             poll => 'r',
125             cb => $stderr_callback,
126             ) unless _is_native_win32();
127             }
128              
129 16         242 my $watcher_child;
130              
131             my $end_cb = sub {
132             #my($pid, $status) = @_;
133 13     13   6252 my $status = $_[1];
134 13         80 my($exit_value, $signal) = ($status >> 8, $status & 127);
135              
136 13         91 $proc->close;
137              
138             # make sure we consume any stdout and stderr which hasn't
139             # been consumed yet. This seems to make on_out.t work on
140             # cygwin
141 13 50       85 if($self->{raw})
142             {
143 0         0 local $/;
144 0         0 $self->{on_stdout}->($proc, scalar <$child_stdout>);
145 0         0 $self->{on_stderr}->($proc, scalar <$child_stderr>);
146             }
147             else
148             {
149 13         199 while(!eof $child_stdout)
150             {
151 3         18 my $input = <$child_stdout>;
152 3 50       13 last unless defined $input;
153 3         33 $input =~ s/(\015?\012|\015)$//;
154 3         18 my $ref = $self->{on_stdout};
155 3 100       26 ref($ref) eq 'ARRAY' ? push @$ref, $input : $ref->($proc, $input);
156             }
157              
158 13         249 while(!eof $child_stderr)
159             {
160 1         4 my $input = <$child_stderr>;
161 1 50       4 last unless defined $input;
162 1         11 $input =~ s/(\015?\012|\015)$//;
163 1         10 my $ref = $self->{on_stderr};
164 1 50       13 ref($ref) eq 'ARRAY' ? push @$ref, $input : $ref->($proc, $input);
165             }
166             }
167              
168 13         117 $self->{on_exit}->($proc, $exit_value, $signal);
169 13 100       536 $self->{on_signal}->($proc, $signal) if $signal > 0;
170 13 100       71 $self->{on_fail}->($proc, $exit_value) if $exit_value > 0;
171 13 100 100     198 $self->{on_success}->($proc) if $signal == 0 && $exit_value == 0;
172 13         218 undef $watcher_stdout;
173 13         90 undef $watcher_stderr;
174 13         32 undef $watcher_child;
175 13         702 undef $proc;
176 16         303 };
177              
178 16 50       174 if($self->{impl} eq 'mojo')
    50          
179             {
180 0         0 my($selout, $selerr);
181              
182 0         0 if(_is_native_win32())
183             {
184             require IO::Select;
185             $selout = IO::Select->new($child_stdout);
186             $selerr = IO::Select->new($child_stderr);
187             }
188              
189 0         0 my $reactor = Mojo::IOLoop->singleton->reactor;
190 0         0 my $id;
191             $id = Mojo::IOLoop->recurring(0.25 => sub {
192             AnyEvent::Open3::Simple::Mojo::_watcher($pid, sub {
193 0         0 $end_cb->(@_);
194 0         0 Mojo::IOLoop->remove($id);
195 0         0 if(_is_native_win32())
196             {
197             $stdout_callback->() if $selout->can_read(0);
198             $stderr_callback->() if $selerr->can_read(0);
199             }
200             else
201             {
202 0         0 $reactor->remove($child_stdout);
203 0         0 $reactor->remove($child_stderr);
204             }
205 0     0   0 });
206 0         0 });
207              
208             }
209             elsif($self->{impl} eq 'idle')
210             {
211 0         0 my($selout, $selerr);
212              
213 0         0 if(_is_native_win32())
214             {
215             require IO::Select;
216             $selout = IO::Select->new($child_stdout);
217             $selerr = IO::Select->new($child_stderr);
218             }
219              
220             $watcher_child = AnyEvent->idle(cb => sub {
221 0     0   0 if(_is_native_win32())
222             {
223             $stdout_callback->() if $selout->can_read(0);
224             $stderr_callback->() if $selerr->can_read(0);
225             }
226 0         0 AnyEvent::Open3::Simple::Idle::_watcher($pid, $end_cb);
227 0         0 });
228             }
229             else
230             {
231 16         242 $watcher_child = AnyEvent->child(
232             pid => $pid,
233             cb => $end_cb,
234             );
235             }
236              
237 16         850 $self;
238             }
239              
240             1;
241              
242             __END__