3 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 return tempfile
.NamedTemporaryFile(mode
, suffix
= '-archive')
32 # python2.2 doesn't have NamedTemporaryFile...
36 name
= tempfile
.mktemp(`random
.randint(1, 1000000)`
+ '-archive')
38 fd
= os
.open(name
, os
.O_RDWR|os
.O_CREAT|os
.O_EXCL
, 0700)
39 tmp
= tempfile
.TemporaryFileWrapper(os
.fdopen(fd
, mode
), name
)
44 fcntl
.fcntl(fd
, fcntl
.F_SETFD
, 0)
46 class PipeThroughCommand(processes
.Process
):
47 def __init__(self
, command
, src
, dst
):
48 """Execute 'command' with src as stdin and writing to stream
49 dst. src must be a fileno() stream, but dst need not be.
50 Either stream may be None if input or output is not required.
51 Call the wait() method to wait for the command to finish."""
53 assert src
is None or hasattr(src
, 'fileno')
55 processes
.Process
.__init
__(self
)
57 self
.command
= command
60 self
.tmp_stream
= None
69 # Output to 'dst' directly if it's a fileno stream. Otherwise,
70 # send output to a temporary file.
71 assert self
.tmp_stream
is None
74 if hasattr(self
.dst
, 'fileno'):
76 self
.tmp_stream
= self
.dst
78 self
.tmp_stream
= Tmp()
80 def start_error(self
):
81 """Clean up effects of pre_fork()."""
82 self
.tmp_stream
= None
88 os
.dup2(src
.fileno(), 0)
90 os
.lseek(0, 0, 0) # OpenBSD needs this, dunno why
92 os
.dup2(self
.tmp_stream
.fileno(), 1)
95 if os
.system(self
.command
) == 0:
96 os
._exit
(0) # No error code or signal
99 def parent_post_fork(self
):
100 if self
.dst
and self
.tmp_stream
is self
.dst
:
101 self
.tmp_stream
= None
103 def got_error_output(self
, data
):
106 def child_died(self
, status
):
107 errors
= self
.errors
.strip()
114 err
= ChildError(_("Errors from command '%s':\n%s") % (self
.command
, errors
))
116 err
= ChildError(_("Command '%s' returned an error code!") % self
.command
)
118 # If dst wasn't a fileno stream, copy from the temp file to it
119 if not err
and self
.tmp_stream
:
120 self
.tmp_stream
.seek(0)
121 self
.dst
.write(self
.tmp_stream
.read())
122 self
.tmp_stream
= None
127 """Run a recursive mainloop until the command terminates.
128 Raises an exception on error."""
130 def set_done(exception
):
131 done
.append(exception
)
133 self
.callback
= set_done
142 processes
.Process
.kill(self
)
145 "Check that this module works."
148 error
= sys
.exc_info()[1]
149 print "(error reported was '%s')" % error
151 def pipe_through_command(command
, src
, dst
): PipeThroughCommand(command
, src
, dst
).wait()
153 print "Test escape()..."
155 assert escape(''' a test ''') == ' a test '
156 assert escape(''' "a's test" ''') == ''' "a\\'s test" '''
157 assert escape(''' "a\\'s test" ''') == ''' "a\\\\\\'s test" '''
159 print "Test Tmp()..."
165 os
.write(file.fileno(), 'World')
168 assert file.read() == 'Hello World'
170 print "Test pipe_through_command():"
172 print "Try an invalid command..."
174 pipe_through_command('bad_command_1234', None, None)
181 print "Try a valid command..."
182 pipe_through_command('exit 0', None, None)
184 print "Writing to a non-fileno stream..."
185 from cStringIO
import StringIO
187 pipe_through_command('echo Hello', None, a
)
188 assert a
.getvalue() == 'Hello\n'
190 print "Reading from a stream to a StringIO..."
192 pipe_through_command('cat', file, a
)
193 assert a
.getvalue() == 'Hello\nello World'
195 print "Writing to a fileno stream..."
198 pipe_through_command('echo Foo', None, file)
200 assert file.read() == 'Foo\n'
202 print "Read and write fileno streams..."
208 pipe_through_command('cat', src
, file)
210 assert file.read() == '123'
212 print "Detect non-zero exit value..."
214 pipe_through_command('exit 1', None, None)
220 print "Detect writes to stderr..."
222 pipe_through_command('echo one >&2; sleep 2; echo two >&2', None, None)
228 print "Check tmp file is deleted..."
230 assert os
.path
.exists(name
)
232 assert not os
.path
.exists(name
)
234 print "Check we can kill a runaway proces..."
235 ptc
= PipeThroughCommand('sleep 100; exit 1', None, None)
238 g
.timeout_add(2000, stop
)
245 print "All tests passed!"
247 if __name__
== '__main__':