4 from rox
import g
, saving
8 from rox
import processes
10 rox
.croak('Sorry, this version of Archive requires ROX-Lib 1.9.3 or later')
14 class ChildError(Exception):
15 "Raised when the child process reports an error."
17 class ChildKilled(saving
.AbortSave
):
18 "Raised when child died due to calling the kill method."
20 saving
.AbortSave
.__init
__(self
, "Operation aborted at user's request")
23 """Return text with \ and ' escaped"""
24 return text
.replace("\\", "\\\\").replace("'", "\\'")
26 def Tmp(mode
= 'w+b'):
27 "Create a seekable, randomly named temp file (deleted automatically after use)."
30 name
= tempfile
.mktemp(`random
.randint(1, 1000000)`
+ '-archive')
32 fd
= os
.open(name
, os
.O_RDWR|os
.O_CREAT|os
.O_EXCL
, 0700)
33 tmp
= tempfile
.TemporaryFileWrapper(os
.fdopen(fd
, mode
), name
)
37 class PipeThroughCommand(processes
.Process
):
38 def __init__(self
, command
, src
, dst
):
39 """Execute 'command' with src as stdin and writing to stream
40 dst. src must be a fileno() stream, but dst need not be.
41 Either stream may be None if input or output is not required.
42 Call the wait() method to wait for the command to finish."""
44 assert src
is None or hasattr(src
, 'fileno')
46 processes
.Process
.__init
__(self
)
48 self
.command
= command
51 self
.tmp_stream
= None
60 # Output to 'dst' directly if it's a fileno stream. Otherwise,
61 # send output to a temporary file.
62 assert self
.tmp_stream
is None
65 if hasattr(self
.dst
, 'fileno'):
67 self
.tmp_stream
= self
.dst
69 self
.tmp_stream
= Tmp()
71 def start_error(self
):
72 """Clean up effects of pre_fork()."""
73 self
.tmp_stream
= None
79 os
.dup2(src
.fileno(), 0)
81 os
.dup2(self
.tmp_stream
.fileno(), 1)
83 if os
.system(self
.command
) == 0:
84 os
._exit
(0) # No error code or signal
87 def parent_post_fork(self
):
88 if self
.dst
and self
.tmp_stream
is self
.dst
:
89 self
.tmp_stream
= None
91 def got_error_output(self
, data
):
94 def child_died(self
, status
):
95 errors
= self
.errors
.strip()
102 err
= ChildError("Errors from command '%s':\n%s" % (self
.command
, errors
))
104 err
= ChildError("Command '%s' returned an error code!" % self
.command
)
106 # If dst wasn't a fileno stream, copy from the temp file to it
107 if not err
and self
.tmp_stream
:
108 self
.tmp_stream
.seek(0)
109 self
.dst
.write(self
.tmp_stream
.read())
110 self
.tmp_stream
= None
115 """Run a recursive mainloop until the command terminates.
116 Raises an exception on error."""
118 def set_done(exception
):
119 done
.append(exception
)
121 self
.callback
= set_done
130 processes
.Process
.kill(self
)
133 "Check that this module works."
136 error
= sys
.exc_info()[1]
137 print "(error reported was '%s')" % error
139 def pipe_through_command(command
, src
, dst
): PipeThroughCommand(command
, src
, dst
).wait()
141 print "Test escape()..."
143 assert escape(''' a test ''') == ' a test '
144 assert escape(''' "a's test" ''') == ''' "a\\'s test" '''
145 assert escape(''' "a\\'s test" ''') == ''' "a\\\\\\'s test" '''
147 print "Test Tmp()..."
153 os
.write(file.fileno(), 'World')
156 assert file.read() == 'Hello World'
158 print "Test pipe_through_command():"
160 print "Try an invalid command..."
162 pipe_through_command('bad_command_1234', None, None)
169 print "Try a valid command..."
170 pipe_through_command('exit 0', None, None)
172 print "Writing to a non-fileno stream..."
173 from cStringIO
import StringIO
175 pipe_through_command('echo Hello', None, a
)
176 assert a
.getvalue() == 'Hello\n'
178 print "Reading from a stream to a StringIO..."
180 pipe_through_command('cat', file, a
)
181 assert a
.getvalue() == 'Hello\nello World'
183 print "Writing to a fileno stream..."
186 pipe_through_command('echo Foo', None, file)
188 assert file.read() == 'Foo\n'
190 print "Read and write fileno streams..."
196 pipe_through_command('cat', src
, file)
198 assert file.read() == '123'
200 print "Detect non-zero exit value..."
202 pipe_through_command('exit 1', None, None)
208 print "Detect writes to stderr..."
210 pipe_through_command('echo one >&2; sleep 2; echo two >&2', None, None)
216 print "Check tmp file is deleted..."
218 assert os
.path
.exists(name
)
220 assert not os
.path
.exists(name
)
222 print "Check we can kill a runaway proces..."
223 ptc
= PipeThroughCommand('sleep 100; exit 1', None, None)
226 g
.timeout_add(2000, stop
)
233 print "All tests passed!"
235 if __name__
== '__main__':