##// END OF EJS Templates
Add OS-level output capture, using file-descriptor redirection.
Gael Varoquaux -
Show More
@@ -0,0 +1,67 b''
1 # encoding: utf-8
2
3 """
4 Stdout/stderr redirector, at the OS level, using file descriptors.
5 """
6
7 __docformat__ = "restructuredtext en"
8
9 #-------------------------------------------------------------------------------
10 # Copyright (C) 2008 The IPython Development Team
11 #
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
14 #-------------------------------------------------------------------------------
15
16
17 import os
18 import sys
19
20 STDOUT = 1
21 STDERR = 2
22
23 class FDRedirector(object):
24 """ Class to redirect output (stdout or stderr) at the OS level using
25 file descriptors.
26 """
27
28 def __init__(self, fd=STDOUT):
29 """ fd is the file descriptor of the outpout you want to capture.
30 It can be STDOUT or STERR.
31 """
32 self.fd = fd
33 self.started = False
34
35 def start(self):
36 if not self.started:
37 self.oldhandle = os.dup(self.fd)
38 self.piper, self.pipew = os.pipe()
39 os.dup2(self.pipew, self.fd)
40 os.close(self.pipew)
41
42 self.started = True
43
44 def flush(self):
45 if self.fd == STDOUT:
46 sys.stdout.flush()
47 elif self.fd == STDERR:
48 sys.stderr.flush()
49
50 def stop(self):
51 if self.started:
52 self.flush()
53 os.dup2(self.oldhandle, self.fd)
54 os.close(self.oldhandle)
55 f = os.fdopen(self.piper, 'r')
56 output = f.read()
57 f.close()
58
59 self.started = False
60 return output
61 else:
62 return ''
63
64 def getvalue(self):
65 output = self.stop()
66 self.start()
67 return output
@@ -0,0 +1,79 b''
1 # encoding: utf-8
2
3 """
4 Trap stdout/stderr, including at the OS level. Calls a callback with
5 the output each time Python tries to write to the stdout or stderr.
6 """
7
8 __docformat__ = "restructuredtext en"
9
10 #-------------------------------------------------------------------------------
11 # Copyright (C) 2008 The IPython Development Team
12 #
13 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
15 #-------------------------------------------------------------------------------
16
17 #-------------------------------------------------------------------------------
18 # Imports
19 #-------------------------------------------------------------------------------
20
21 from fd_redirector import FDRedirector, STDOUT, STDERR
22
23 from IPython.kernel.core.file_like import FileLike
24 from IPython.kernel.core.output_trap import OutputTrap
25
26 class RedirectorOutputTrap(OutputTrap):
27 """ Object which can trap text sent to stdout and stderr.
28 """
29
30 #------------------------------------------------------------------------
31 # OutputTrap interface.
32 #------------------------------------------------------------------------
33 def __init__(self, out_callback, err_callback):
34 # Callback invoked on write to stdout and stderr
35 self.out_callback = out_callback
36 self.err_callback = err_callback
37
38 # File descriptor redirectors, to capture non-Python
39 # output.
40 self.out_redirector = FDRedirector(STDOUT)
41 self.err_redirector = FDRedirector(STDERR)
42
43 # Call the base class with file like objects that will trigger
44 # our callbacks
45 OutputTrap.__init__(self, out=FileLike(self.on_out_write),
46 err=FileLike(self.on_err_write), )
47
48
49 def set(self):
50 """ Set the hooks: set the redirectors and call the base class.
51 """
52 self.out_redirector.start()
53 #self.err_redirector.start()
54 OutputTrap.set(self)
55
56
57 def unset(self):
58 """ Remove the hooks: call the base class and stop the
59 redirectors.
60 """
61 OutputTrap.unset(self)
62 self.err_redirector.stop()
63 self.out_redirector.stop()
64
65
66 #------------------------------------------------------------------------
67 # Callbacks for synchronous output
68 #------------------------------------------------------------------------
69 def on_out_write(self, string):
70 """ Callback called when there is some Python output on stdout.
71 """
72 self.out_callback(self.out_redirector.getvalue() + string)
73
74 def on_err_write(self, string):
75 """ Callback called when there is some Python output on stderr.
76 """
77 self.err_callback(self.err_redirector.getvalue() + string)
78
79
@@ -0,0 +1,50 b''
1 """
2 Test the output capture at the OS level, using file descriptors.
3 """
4
5 import os
6 from cStringIO import StringIO
7
8
9 def test_redirector():
10 """ Checks that the redirector can be used to do synchronous capture.
11 """
12 from IPython.kernel.core.fd_redirector import FDRedirector
13 r = FDRedirector()
14 out = StringIO()
15 try:
16 r.start()
17 for i in range(10):
18 os.system('echo %ic' % i)
19 print >>out, r.getvalue(),
20 print >>out, i
21 except:
22 r.stop()
23 raise
24 r.stop()
25 assert out.getvalue() == "".join("%ic\n%i\n" %(i, i) for i in range(10))
26
27
28 def test_redirector_output_trap():
29 """ This test check not only that the redirector_output_trap does
30 trap the output, but also that it does it in a gready way, that
31 is by calling the callabck ASAP.
32 """
33 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
34 out = StringIO()
35 trap = RedirectorOutputTrap(out.write, out.write)
36 try:
37 trap.set()
38 for i in range(10):
39 os.system('echo %ic' % i)
40 print "%ip" % i
41 print >>out, i
42 except:
43 trap.unset()
44 raise
45 trap.unset()
46 assert out.getvalue() == "".join("%ic\n%ip\n%i\n" %(i, i, i)
47 for i in range(10))
48
49
50
General Comments 0
You need to be logged in to leave comments. Login now