4 from rox
import g
, saving
9 from rox
import processes
11 rox
.croak('Sorry, this version of Archive requires ROX-Lib 1.9.3 or later')
15 class ChildError(Exception):
16 "Raised when the child process reports an error."
18 class ChildKilled(saving
.AbortSave
):
19 "Raised when child died due to calling the kill method."
21 saving
.AbortSave
.__init
__(self
, "Operation aborted at user's request")
24 """Return text with \ and ' escaped"""
25 return text
.replace("\\", "\\\\").replace("'", "\\'")
27 def Tmp(mode
= 'w+b'):
28 "Create a seekable, randomly named temp file (deleted automatically after use)."
31 name
= tempfile
.mktemp(`random
.randint(1, 1000000)`
+ '-archive')
33 fd
= os
.open(name
, os
.O_RDWR|os
.O_CREAT|os
.O_EXCL
, 0700)
34 tmp
= tempfile
.TemporaryFileWrapper(os
.fdopen(fd
, mode
), name
)
39 fcntl
.fcntl(fd
, fcntl
.F_SETFD
, 0)
41 class PipeThroughCommand(processes
.Process
):
42 def __init__(self
, command
, src
, dst
):
43 """Execute 'command' with src as stdin and writing to stream
44 dst. src must be a fileno() stream, but dst need not be.
45 Either stream may be None if input or output is not required.
46 Call the wait() method to wait for the command to finish."""
48 assert src
is None or hasattr(src
, 'fileno')
50 processes
.Process
.__init
__(self
)
52 self
.command
= command
55 self
.tmp_stream
= None
64 # Output to 'dst' directly if it's a fileno stream. Otherwise,
65 # send output to a temporary file.
66 assert self
.tmp_stream
is None
69 if hasattr(self
.dst
, 'fileno'):
71 self
.tmp_stream
= self
.dst
73 self
.tmp_stream
= Tmp()
75 def start_error(self
):
76 """Clean up effects of pre_fork()."""
77 self
.tmp_stream
= None
83 os
.dup2(src
.fileno(), 0)
86 os
.dup2(self
.tmp_stream
.fileno(), 1)
89 if os
.system(self
.command
) == 0:
90 os
._exit
(0) # No error code or signal
93 def parent_post_fork(self
):
94 if self
.dst
and self
.tmp_stream
is self
.dst
:
95 self
.tmp_stream
= None
97 def got_error_output(self
, data
):
100 def child_died(self
, status
):
101 errors
= self
.errors
.strip()
108 err
= ChildError("Errors from command '%s':\n%s" % (self
.command
, errors
))
110 err
= ChildError("Command '%s' returned an error code!" % self
.command
)
112 # If dst wasn't a fileno stream, copy from the temp file to it
113 if not err
and self
.tmp_stream
:
114 self
.tmp_stream
.seek(0)
115 self
.dst
.write(self
.tmp_stream
.read())
116 self
.tmp_stream
= None
121 """Run a recursive mainloop until the command terminates.
122 Raises an exception on error."""
124 def set_done(exception
):
125 done
.append(exception
)
127 self
.callback
= set_done
136 processes
.Process
.kill(self
)
139 "Check that this module works."
142 error
= sys
.exc_info()[1]
143 print "(error reported was '%s')" % error
145 def pipe_through_command(command
, src
, dst
): PipeThroughCommand(command
, src
, dst
).wait()
147 print "Test escape()..."
149 assert escape(''' a test ''') == ' a test '
150 assert escape(''' "a's test" ''') == ''' "a\\'s test" '''
151 assert escape(''' "a\\'s test" ''') == ''' "a\\\\\\'s test" '''
153 print "Test Tmp()..."
159 os
.write(file.fileno(), 'World')
162 assert file.read() == 'Hello World'
164 print "Test pipe_through_command():"
166 print "Try an invalid command..."
168 pipe_through_command('bad_command_1234', None, None)
175 print "Try a valid command..."
176 pipe_through_command('exit 0', None, None)
178 print "Writing to a non-fileno stream..."
179 from cStringIO
import StringIO
181 pipe_through_command('echo Hello', None, a
)
182 assert a
.getvalue() == 'Hello\n'
184 print "Reading from a stream to a StringIO..."
186 pipe_through_command('cat', file, a
)
187 assert a
.getvalue() == 'Hello\nello World'
189 print "Writing to a fileno stream..."
192 pipe_through_command('echo Foo', None, file)
194 assert file.read() == 'Foo\n'
196 print "Read and write fileno streams..."
202 pipe_through_command('cat', src
, file)
204 assert file.read() == '123'
206 print "Detect non-zero exit value..."
208 pipe_through_command('exit 1', None, None)
214 print "Detect writes to stderr..."
216 pipe_through_command('echo one >&2; sleep 2; echo two >&2', None, None)
222 print "Check tmp file is deleted..."
224 assert os
.path
.exists(name
)
226 assert not os
.path
.exists(name
)
228 print "Check we can kill a runaway proces..."
229 ptc
= PipeThroughCommand('sleep 100; exit 1', None, None)
232 g
.timeout_add(2000, stop
)
239 print "All tests passed!"
241 if __name__
== '__main__':