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 return tempfile
.NamedTemporaryFile(mode
, suffix
= '-archive')
33 # python2.2 doesn't have NamedTemporaryFile...
37 name
= tempfile
.mktemp(`random
.randint(1, 1000000)`
+ '-archive')
39 fd
= os
.open(name
, os
.O_RDWR|os
.O_CREAT|os
.O_EXCL
, 0700)
40 tmp
= tempfile
.TemporaryFileWrapper(os
.fdopen(fd
, mode
), name
)
45 fcntl
.fcntl(fd
, fcntl
.F_SETFD
, 0)
47 class PipeThroughCommand(processes
.Process
):
48 def __init__(self
, command
, src
, dst
):
49 """Execute 'command' with src as stdin and writing to stream
50 dst. src must be a fileno() stream, but dst need not be.
51 Either stream may be None if input or output is not required.
52 Call the wait() method to wait for the command to finish."""
54 assert src
is None or hasattr(src
, 'fileno')
56 processes
.Process
.__init
__(self
)
58 self
.command
= command
61 self
.tmp_stream
= None
70 # Output to 'dst' directly if it's a fileno stream. Otherwise,
71 # send output to a temporary file.
72 assert self
.tmp_stream
is None
75 if hasattr(self
.dst
, 'fileno'):
77 self
.tmp_stream
= self
.dst
79 self
.tmp_stream
= Tmp()
81 def start_error(self
):
82 """Clean up effects of pre_fork()."""
83 self
.tmp_stream
= None
89 os
.dup2(src
.fileno(), 0)
91 os
.lseek(0, 0, 0) # OpenBSD needs this, dunno why
93 os
.dup2(self
.tmp_stream
.fileno(), 1)
96 if os
.system(self
.command
) == 0:
97 os
._exit
(0) # No error code or signal
100 def parent_post_fork(self
):
101 if self
.dst
and self
.tmp_stream
is self
.dst
:
102 self
.tmp_stream
= None
104 def got_error_output(self
, data
):
107 def child_died(self
, status
):
108 errors
= self
.errors
.strip()
115 err
= ChildError("Errors from command '%s':\n%s" % (self
.command
, errors
))
117 err
= ChildError("Command '%s' returned an error code!" % self
.command
)
119 # If dst wasn't a fileno stream, copy from the temp file to it
120 if not err
and self
.tmp_stream
:
121 self
.tmp_stream
.seek(0)
122 self
.dst
.write(self
.tmp_stream
.read())
123 self
.tmp_stream
= None
128 """Run a recursive mainloop until the command terminates.
129 Raises an exception on error."""
131 def set_done(exception
):
132 done
.append(exception
)
134 self
.callback
= set_done
143 processes
.Process
.kill(self
)
146 "Check that this module works."
149 error
= sys
.exc_info()[1]
150 print "(error reported was '%s')" % error
152 def pipe_through_command(command
, src
, dst
): PipeThroughCommand(command
, src
, dst
).wait()
154 print "Test escape()..."
156 assert escape(''' a test ''') == ' a test '
157 assert escape(''' "a's test" ''') == ''' "a\\'s test" '''
158 assert escape(''' "a\\'s test" ''') == ''' "a\\\\\\'s test" '''
160 print "Test Tmp()..."
166 os
.write(file.fileno(), 'World')
169 assert file.read() == 'Hello World'
171 print "Test pipe_through_command():"
173 print "Try an invalid command..."
175 pipe_through_command('bad_command_1234', None, None)
182 print "Try a valid command..."
183 pipe_through_command('exit 0', None, None)
185 print "Writing to a non-fileno stream..."
186 from cStringIO
import StringIO
188 pipe_through_command('echo Hello', None, a
)
189 assert a
.getvalue() == 'Hello\n'
191 print "Reading from a stream to a StringIO..."
193 pipe_through_command('cat', file, a
)
194 assert a
.getvalue() == 'Hello\nello World'
196 print "Writing to a fileno stream..."
199 pipe_through_command('echo Foo', None, file)
201 assert file.read() == 'Foo\n'
203 print "Read and write fileno streams..."
209 pipe_through_command('cat', src
, file)
211 assert file.read() == '123'
213 print "Detect non-zero exit value..."
215 pipe_through_command('exit 1', None, None)
221 print "Detect writes to stderr..."
223 pipe_through_command('echo one >&2; sleep 2; echo two >&2', None, None)
229 print "Check tmp file is deleted..."
231 assert os
.path
.exists(name
)
233 assert not os
.path
.exists(name
)
235 print "Check we can kill a runaway proces..."
236 ptc
= PipeThroughCommand('sleep 100; exit 1', None, None)
239 g
.timeout_add(2000, stop
)
246 print "All tests passed!"
248 if __name__
== '__main__':