##// END OF EJS Templates
Make the OS-level stdout/stderr capture work in the frontends.
Gael Varoquaux -
Show More
@@ -1,158 +1,149 b''
1 """
1 """
2 Frontend class that uses IPython0 to prefilter the inputs.
2 Frontend class that uses IPython0 to prefilter the inputs.
3
3
4 Using the IPython0 mechanism gives us access to the magics.
4 Using the IPython0 mechanism gives us access to the magics.
5 """
5 """
6 __docformat__ = "restructuredtext en"
6 __docformat__ = "restructuredtext en"
7
7
8 #-------------------------------------------------------------------------------
8 #-------------------------------------------------------------------------------
9 # Copyright (C) 2008 The IPython Development Team
9 # Copyright (C) 2008 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14
14
15 #-------------------------------------------------------------------------------
15 #-------------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-------------------------------------------------------------------------------
17 #-------------------------------------------------------------------------------
18 import sys
18 import sys
19
19
20 from linefrontendbase import LineFrontEndBase, common_prefix
20 from linefrontendbase import LineFrontEndBase, common_prefix
21
21
22 from IPython.ipmaker import make_IPython
22 from IPython.ipmaker import make_IPython
23 from IPython.ipapi import IPApi
23 from IPython.ipapi import IPApi
24 from IPython.kernel.core.file_like import FileLike
24 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
25 from IPython.kernel.core.output_trap import OutputTrap
26
25
27 from IPython.genutils import Term
26 from IPython.genutils import Term
28 import pydoc
27 import pydoc
29
28
30 #-------------------------------------------------------------------------------
29 #-------------------------------------------------------------------------------
31 # Utility functions (temporary, should be moved out of here)
30 # Utility functions (temporary, should be moved out of here)
32 #-------------------------------------------------------------------------------
31 #-------------------------------------------------------------------------------
33 import os
32 import os
34 def xterm_system(command):
33 def xterm_system(command):
35 """ Run a command in a separate console window.
34 """ Run a command in a separate console window.
36 """
35 """
37 os.system(("""xterm -title "%s" -e \'/bin/sh -c "%s ; """
36 os.system(("""xterm -title "%s" -e \'/bin/sh -c "%s ; """
38 """echo; echo press enter to close ; """
37 """echo; echo press enter to close ; """
39 # """echo \\"\x1b]0;%s (finished -- press enter to close)\x07\\" ;
38 # """echo \\"\x1b]0;%s (finished -- press enter to close)\x07\\" ;
40 """read foo;"\' """) % (command, command) )
39 """read foo;"\' """) % (command, command) )
41
40
42 #-------------------------------------------------------------------------------
41 #-------------------------------------------------------------------------------
43 # Frontend class using ipython0 to do the prefiltering.
42 # Frontend class using ipython0 to do the prefiltering.
44 #-------------------------------------------------------------------------------
43 #-------------------------------------------------------------------------------
45 class PrefilterFrontEnd(LineFrontEndBase):
44 class PrefilterFrontEnd(LineFrontEndBase):
46
45
47 def __init__(self, *args, **kwargs):
46 def __init__(self, *args, **kwargs):
48 LineFrontEndBase.__init__(self, *args, **kwargs)
47 LineFrontEndBase.__init__(self, *args, **kwargs)
49 # Instanciate an IPython0 interpreter to be able to use the
48 # Instanciate an IPython0 interpreter to be able to use the
50 # prefiltering.
49 # prefiltering.
51 self.ipython0 = make_IPython()
50 self.ipython0 = make_IPython()
52 # Set the pager:
51 # Set the pager:
53 self.ipython0.set_hook('show_in_pager',
52 self.ipython0.set_hook('show_in_pager',
54 lambda s, string: self.write("\n"+string))
53 lambda s, string: self.write("\n"+string))
55 self.ipython0.write = self.write
54 self.ipython0.write = self.write
56 self._ip = _ip = IPApi(self.ipython0)
55 self._ip = _ip = IPApi(self.ipython0)
57 # XXX: Hack: mix the two namespaces
56 # XXX: Hack: mix the two namespaces
58 self.shell.user_ns = self.ipython0.user_ns
57 self.shell.user_ns = self.ipython0.user_ns
59 self.shell.user_global_ns = self.ipython0.user_global_ns
58 self.shell.user_global_ns = self.ipython0.user_global_ns
60 # Make sure the raw system call doesn't get called, as we don't
59 # Make sure the raw system call doesn't get called, as we don't
61 # have a stdin accessible.
60 # have a stdin accessible.
62 self._ip.system = xterm_system
61 self._ip.system = xterm_system
63 # Redefine a serie of magics to avoid os.system:
62 self.shell.output_trap = RedirectorOutputTrap(
64 # FIXME: I am redefining way too much magics.
63 out_callback=self.write,
65 for alias_name, (_, alias_value) in \
64 err_callback=self.write,
66 _ip.IP.shell.alias_table.iteritems():
67 magic = lambda s : _ip.magic('sx %s %s' % (alias_value, s))
68 setattr(_ip.IP, 'magic_%s' % alias_name, magic)
69 # FIXME: I should create a real file-like object dedicated to this
70 # terminal
71 self.shell.output_trap = OutputTrap(
72 out=FileLike(write_callback=self.write),
73 err=FileLike(write_callback=self.write),
74 )
65 )
75 # Capture and release the outputs, to make sure all the
66 # Capture and release the outputs, to make sure all the
76 # shadow variables are set
67 # shadow variables are set
77 self.capture_output()
68 self.capture_output()
78 self.release_output()
69 self.release_output()
79
70
80
71
81 def prefilter_input(self, input_string):
72 def prefilter_input(self, input_string):
82 """ Using IPython0 to prefilter the commands.
73 """ Using IPython0 to prefilter the commands.
83 """
74 """
84 input_string = LineFrontEndBase.prefilter_input(self, input_string)
75 input_string = LineFrontEndBase.prefilter_input(self, input_string)
85 filtered_lines = []
76 filtered_lines = []
86 # The IPython0 prefilters sometime produce output. We need to
77 # The IPython0 prefilters sometime produce output. We need to
87 # capture it.
78 # capture it.
88 self.capture_output()
79 self.capture_output()
89 self.last_result = dict(number=self.prompt_number)
80 self.last_result = dict(number=self.prompt_number)
90 try:
81 try:
91 for line in input_string.split('\n'):
82 for line in input_string.split('\n'):
92 filtered_lines.append(self.ipython0.prefilter(line, False))
83 filtered_lines.append(self.ipython0.prefilter(line, False))
93 except:
84 except:
94 # XXX: probably not the right thing to do.
85 # XXX: probably not the right thing to do.
95 self.ipython0.showsyntaxerror()
86 self.ipython0.showsyntaxerror()
96 self.after_execute()
87 self.after_execute()
97 finally:
88 finally:
98 self.release_output()
89 self.release_output()
99
90
100 filtered_string = '\n'.join(filtered_lines)
91 filtered_string = '\n'.join(filtered_lines)
101 return filtered_string
92 return filtered_string
102
93
103
94
104 def show_traceback(self):
95 def show_traceback(self):
105 self.capture_output()
96 self.capture_output()
106 self.ipython0.showtraceback()
97 self.ipython0.showtraceback()
107 self.release_output()
98 self.release_output()
108
99
109
100
110 def execute(self, python_string, raw_string=None):
101 def execute(self, python_string, raw_string=None):
111 self.capture_output()
102 self.capture_output()
112 LineFrontEndBase.execute(self, python_string,
103 LineFrontEndBase.execute(self, python_string,
113 raw_string=raw_string)
104 raw_string=raw_string)
114 self.release_output()
105 self.release_output()
115
106
116
107
117 def capture_output(self):
108 def capture_output(self):
118 """ Capture all the output mechanisms we can think of.
109 """ Capture all the output mechanisms we can think of.
119 """
110 """
120 self.__old_cout_write = Term.cout.write
111 self.__old_cout_write = Term.cout.write
121 self.__old_err_write = Term.cerr.write
112 self.__old_err_write = Term.cerr.write
122 Term.cout.write = self.write
113 Term.cout.write = self.write
123 Term.cerr.write = self.write
114 Term.cerr.write = self.write
124 self.__old_stdout = sys.stdout
115 self.__old_stdout = sys.stdout
125 self.__old_stderr= sys.stderr
116 self.__old_stderr= sys.stderr
126 sys.stdout = Term.cout
117 sys.stdout = Term.cout
127 sys.stderr = Term.cerr
118 sys.stderr = Term.cerr
128 self.__old_help_output = pydoc.help.output
119 self.__old_help_output = pydoc.help.output
129 pydoc.help.output = self.shell.output_trap.out
120 pydoc.help.output = self.shell.output_trap.out
130
121
131
122
132 def release_output(self):
123 def release_output(self):
133 """ Release all the different captures we have made.
124 """ Release all the different captures we have made.
134 """
125 """
135 Term.cout.write = self.__old_cout_write
126 Term.cout.write = self.__old_cout_write
136 Term.cerr.write = self.__old_err_write
127 Term.cerr.write = self.__old_err_write
137 sys.stdout = self.__old_stdout
128 sys.stdout = self.__old_stdout
138 sys.stderr = self.__old_stderr
129 sys.stderr = self.__old_stderr
139 pydoc.help.output = self.__old_help_output
130 pydoc.help.output = self.__old_help_output
140
131
141
132
142 def complete(self, line):
133 def complete(self, line):
143 word = line.split('\n')[-1].split(' ')[-1]
134 word = line.split('\n')[-1].split(' ')[-1]
144 completions = self.ipython0.complete(word)
135 completions = self.ipython0.complete(word)
145 # FIXME: The proper sort should be done in the complete method.
136 # FIXME: The proper sort should be done in the complete method.
146 key = lambda x: x.replace('_', '')
137 key = lambda x: x.replace('_', '')
147 completions.sort(key=key)
138 completions.sort(key=key)
148 if completions:
139 if completions:
149 prefix = common_prefix(completions)
140 prefix = common_prefix(completions)
150 line = line[:-len(word)] + prefix
141 line = line[:-len(word)] + prefix
151 return line, completions
142 return line, completions
152
143
153
144
154 def do_exit(self):
145 def do_exit(self):
155 """ Exit the shell, cleanup and save the history.
146 """ Exit the shell, cleanup and save the history.
156 """
147 """
157 self.ipython0.atexit_operations()
148 self.ipython0.atexit_operations()
158
149
@@ -1,79 +1,93 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """
3 """
4 Trap stdout/stderr, including at the OS level. Calls a callback with
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.
5 the output each time Python tries to write to the stdout or stderr.
6 """
6 """
7
7
8 __docformat__ = "restructuredtext en"
8 __docformat__ = "restructuredtext en"
9
9
10 #-------------------------------------------------------------------------------
10 #-------------------------------------------------------------------------------
11 # Copyright (C) 2008 The IPython Development Team
11 # Copyright (C) 2008 The IPython Development Team
12 #
12 #
13 # Distributed under the terms of the BSD License. The full license is in
13 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
14 # the file COPYING, distributed as part of this software.
15 #-------------------------------------------------------------------------------
15 #-------------------------------------------------------------------------------
16
16
17 #-------------------------------------------------------------------------------
17 #-------------------------------------------------------------------------------
18 # Imports
18 # Imports
19 #-------------------------------------------------------------------------------
19 #-------------------------------------------------------------------------------
20
20
21 from fd_redirector import FDRedirector, STDOUT, STDERR
21 from fd_redirector import FDRedirector, STDOUT, STDERR
22
22
23 from IPython.kernel.core.file_like import FileLike
23 from IPython.kernel.core.file_like import FileLike
24 from IPython.kernel.core.output_trap import OutputTrap
24 from IPython.kernel.core.output_trap import OutputTrap
25
25
26 class RedirectorOutputTrap(OutputTrap):
26 class RedirectorOutputTrap(OutputTrap):
27 """ Object which can trap text sent to stdout and stderr.
27 """ Object which can trap text sent to stdout and stderr.
28 """
28 """
29
29
30 #------------------------------------------------------------------------
30 #------------------------------------------------------------------------
31 # OutputTrap interface.
31 # OutputTrap interface.
32 #------------------------------------------------------------------------
32 #------------------------------------------------------------------------
33 def __init__(self, out_callback, err_callback):
33 def __init__(self, out_callback, err_callback):
34 # Callback invoked on write to stdout and stderr
34 # Callback invoked on write to stdout and stderr
35 self.out_callback = out_callback
35 self.out_callback = out_callback
36 self.err_callback = err_callback
36 self.err_callback = err_callback
37
37
38 # File descriptor redirectors, to capture non-Python
38 # File descriptor redirectors, to capture non-Python
39 # output.
39 # output.
40 self.out_redirector = FDRedirector(STDOUT)
40 self.out_redirector = FDRedirector(STDOUT)
41 self.err_redirector = FDRedirector(STDERR)
41 self.err_redirector = FDRedirector(STDERR)
42
42
43 # Call the base class with file like objects that will trigger
43 # Call the base class with file like objects that will trigger
44 # our callbacks
44 # our callbacks
45 OutputTrap.__init__(self, out=FileLike(self.on_out_write),
45 OutputTrap.__init__(self, out=FileLike(self.on_out_write),
46 err=FileLike(self.on_err_write), )
46 err=FileLike(self.on_err_write), )
47
47
48
48
49 def set(self):
49 def set(self):
50 """ Set the hooks: set the redirectors and call the base class.
50 """ Set the hooks: set the redirectors and call the base class.
51 """
51 """
52 self.out_redirector.start()
52 self.out_redirector.start()
53 #self.err_redirector.start()
53 #self.err_redirector.start()
54 OutputTrap.set(self)
54 OutputTrap.set(self)
55
55
56
56
57 def unset(self):
57 def unset(self):
58 """ Remove the hooks: call the base class and stop the
58 """ Remove the hooks: call the base class and stop the
59 redirectors.
59 redirectors.
60 """
60 """
61 OutputTrap.unset(self)
61 OutputTrap.unset(self)
62 # Flush the redirectors before stopping them
63 self.on_out_write('')
62 self.err_redirector.stop()
64 self.err_redirector.stop()
65 self.on_err_write('')
63 self.out_redirector.stop()
66 self.out_redirector.stop()
64
67
65
68
66 #------------------------------------------------------------------------
69 #------------------------------------------------------------------------
67 # Callbacks for synchronous output
70 # Callbacks for synchronous output
68 #------------------------------------------------------------------------
71 #------------------------------------------------------------------------
69 def on_out_write(self, string):
72 def on_out_write(self, string):
70 """ Callback called when there is some Python output on stdout.
73 """ Callback called when there is some Python output on stdout.
71 """
74 """
72 self.out_callback(self.out_redirector.getvalue() + string)
75 try:
76 self.out_callback(self.out_redirector.getvalue() + string)
77 except:
78 # If tracebacks are happening and we can't see them, it is
79 # quasy impossible to debug
80 self.unset()
81 raise
73
82
74 def on_err_write(self, string):
83 def on_err_write(self, string):
75 """ Callback called when there is some Python output on stderr.
84 """ Callback called when there is some Python output on stderr.
76 """
85 """
77 self.err_callback(self.err_redirector.getvalue() + string)
86 try:
78
87 self.err_callback(self.err_redirector.getvalue() + string)
88 except:
89 # If tracebacks are happening and we can't see them, it is
90 # quasy impossible to debug
91 self.unset()
92 raise
79
93
General Comments 0
You need to be logged in to leave comments. Login now