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