File Coverage

blib/lib/AnyEvent/Open3/Simple.pm
Criterion Covered Total %
statement 113 143 79.0
branch 38 58 65.5
condition 9 13 69.2
subroutine 19 21 90.4
pod 1 2 50.0
total 180 237 75.9


line stmt bran cond sub pod time code
1             package AnyEvent::Open3::Simple;
2              
3 15     15   1223728 use strict;
  15         165  
  15         456  
4 15     15   91 use warnings;
  15         33  
  15         361  
5 15     15   338 use 5.006;
  15         63  
6 15     15   90 use warnings::register;
  15         28  
  15         2275  
7 15     15   6980 use IPC::Open3 qw( open3 );
  15         55092  
  15         908  
8 15     15   113 use Scalar::Util qw( reftype );
  15         30  
  15         693  
9 15     15   563 use Symbol qw( gensym );
  15         852  
  15         597  
10 15     15   6551 use AnyEvent::Open3::Simple::Process;
  15         41  
  15         500  
11 15     15   91 use Carp qw( croak );
  15         29  
  15         658  
12 15     15   9844 use File::Temp ();
  15         260841  
  15         511  
13 15     15   112 use constant _is_native_win32 => $^O eq 'MSWin32';
  15         33  
  15         1088  
14 15     15   95 use constant _detect => _is_native_win32() ? 'idle' : 'child';
  15         32  
  15         25333  
15              
16             # ABSTRACT: Interface to open3 under AnyEvent
17             our $VERSION = '0.89'; # VERSION
18              
19              
20             sub new
21             {
22 19     20 0 45924 my $default_handler = sub { };
        19      
23 19         63 my $class = shift;
24 19 100 100     260 my $args = (reftype($_[0]) || '') eq 'HASH' ? shift : { @_ };
25 19         49 my %self;
26 19 100       404 croak "stdin passed into AnyEvent::Open3::Simple->new no longer supported" if $args->{stdin};
27 18 50       66 croak "raw passed into AnyEvent::Open::Simple->new no longer supported" if $args->{raw};
28 18   66     379 $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     165 || _detect();
32 18 50       192 croak "unknown implementation $self{impl}" unless $self{impl} =~ /^(idle|child|mojo)$/;
33             $self{impl} = _detect()
34 18 0 33     74 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 20018 croak "run method requires at least one argument"
42             unless @_ >= 2;
43              
44 18 100   14   122 my $proc_user = (ref $_[-1] eq 'CODE' ? pop : sub {});
45              
46 18         41 my $stdin;
47 18 100       62 $stdin = pop if ref $_[-1];
48              
49 18         75 my($self, $program, @arguments) = @_;
50              
51 18         40 my($child_stdin, $child_stdout, $child_stderr);
52 18         85 $child_stderr = gensym;
53              
54 18         421 local *TEMP;
55 18 100       66 if(defined $stdin)
56             {
57 2         18 my $file = File::Temp->new;
58 2         1235 $file->autoflush(1);
59             $file->print(
60             ref($stdin) eq 'ARRAY'
61 2 100       165 ? join("\n", @{ $stdin })
  1         10  
62             : $$stdin
63             );
64 2         124 $file->seek(0,0);
65 2         46 open TEMP, '<&=', $file; ## no critic
66 2         81 $child_stdin = '<&TEMP';
67             }
68              
69 18 50       773 if($self->{impl} =~ /^(child|idle)$/)
    0          
70             {
71 18         1159 require AnyEvent;
72 18         5842 AnyEvent::detect();
73 18 50       1560 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         42 my $pid = eval { open3 $child_stdin, $child_stdout, $child_stderr, $program, @arguments };
  18         86  
83              
84 18 100       71363 if(my $error = $@)
85             {
86 2         33 $self->{on_error}->($error, $program, @arguments);
87 2         264 return;
88             }
89              
90 16         565 my $proc = AnyEvent::Open3::Simple::Process->new($pid, $child_stdin);
91 16         252 $proc_user->($proc);
92              
93 16         298 $self->{on_start}->($proc, $program, @arguments);
94              
95 16         146 my $watcher_stdout;
96             my $watcher_stderr;
97              
98             my $stdout_callback = sub {
99 7     7   44543 my $input = <$child_stdout>;
100 7 50       87 return unless defined $input;
101 0         0 $input =~ s/(\015?\012|\015)$//;
102 0         0 my $ref = $self->{on_stdout};
103 0 0       0 ref($ref) eq 'ARRAY' ? push @$ref, $input : $ref->($proc, $input);
104 16         489 };
105              
106             my $stderr_callback = sub {
107 7     7   1509 my $input = <$child_stderr>;
108 7 50       104 return unless defined $input;
109 0         0 $input =~ s/(\015?\012|\015)$//;
110 0         0 my $ref = $self->{on_stderr};
111 0 0       0 ref($ref) eq 'ARRAY' ? push @$ref, $input : $ref->($proc, $input);
112 16         263 };
113              
114 16 50       337 if(!_is_native_win32() && $self->{impl} =~ /^(idle|child)$/)
115             {
116 16         514 $watcher_stdout = AnyEvent->io(
117             fh => $child_stdout,
118             poll => 'r',
119             cb => $stdout_callback,
120             ) unless _is_native_win32();
121              
122 16         801 $watcher_stderr = AnyEvent->io(
123             fh => $child_stderr,
124             poll => 'r',
125             cb => $stderr_callback,
126             ) unless _is_native_win32();
127             }
128              
129 16         241 my $watcher_child;
130              
131             my $end_cb = sub {
132             #my($pid, $status) = @_;
133 13     13   9909 my $status = $_[1];
134 13         97 my($exit_value, $signal) = ($status >> 8, $status & 127);
135              
136 13         123 $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       103 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         226 while(!eof $child_stdout)
150             {
151 4         30 my $input = <$child_stdout>;
152 4 50       15 last unless defined $input;
153 4         37 $input =~ s/(\015?\012|\015)$//;
154 4         15 my $ref = $self->{on_stdout};
155 4 100       34 ref($ref) eq 'ARRAY' ? push @$ref, $input : $ref->($proc, $input);
156             }
157              
158 13         249 while(!eof $child_stderr)
159             {
160 2         16 my $input = <$child_stderr>;
161 2 50       9 last unless defined $input;
162 2         20 $input =~ s/(\015?\012|\015)$//;
163 2         8 my $ref = $self->{on_stderr};
164 2 100       18 ref($ref) eq 'ARRAY' ? push @$ref, $input : $ref->($proc, $input);
165             }
166             }
167              
168 13         133 $self->{on_exit}->($proc, $exit_value, $signal);
169 13 100       556 $self->{on_signal}->($proc, $signal) if $signal > 0;
170 13 100       84 $self->{on_fail}->($proc, $exit_value) if $exit_value > 0;
171 13 100 100     167 $self->{on_success}->($proc) if $signal == 0 && $exit_value == 0;
172 13         212 undef $watcher_stdout;
173 13         96 undef $watcher_stderr;
174 13         37 undef $watcher_child;
175 13         746 undef $proc;
176 16         304 };
177              
178 16 50       173 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         227 $watcher_child = AnyEvent->child(
232             pid => $pid,
233             cb => $end_cb,
234             );
235             }
236              
237 16         884 $self;
238             }
239              
240             1;
241              
242             __END__