File: prefilterfrontend.py

package info (click to toggle)
ipython 0.13.1-2%2Bdeb7u1
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 15,752 kB
  • sloc: python: 69,537; makefile: 355; lisp: 272; sh: 80; objc: 37
file content (256 lines) | stat: -rw-r--r-- 9,866 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
"""
Frontend class that uses IPython0 to prefilter the inputs.

Using the IPython0 mechanism gives us access to the magics.

This is a transitory class, used here to do the transition between
ipython0 and ipython1. This class is meant to be short-lived as more
functionnality is abstracted out of ipython0 in reusable functions and
is added on the interpreter. This class can be a used to guide this
refactoring.
"""

#-------------------------------------------------------------------------------
#  Copyright (C) 2008-2011  The IPython Development Team
#
#  Distributed under the terms of the BSD License.  The full license is in
#  the file COPYING, distributed as part of this software.
#-------------------------------------------------------------------------------

#-------------------------------------------------------------------------------
# Imports
#-------------------------------------------------------------------------------
import sys
import pydoc
import os
import re
import __builtin__

from IPython.core.iplib import InteractiveShell
from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap

from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap

import IPython.utils.io

from linefrontendbase import LineFrontEndBase, common_prefix

#-----------------------------------------------------------------------------
# Utility functions
#-----------------------------------------------------------------------------

def mk_system_call(system_call_function, command):
    """ given a os.system replacement, and a leading string command,
        returns a function that will execute the command with the given
        argument string.
    """
    def my_system_call(args):
        system_call_function("%s %s" % (command, args))

    my_system_call.__doc__ = "Calls %s" % command
    return my_system_call

#-----------------------------------------------------------------------------
# Frontend class using ipython0 to do the prefiltering.
#-----------------------------------------------------------------------------

class PrefilterFrontEnd(LineFrontEndBase):
    """ Class that uses ipython0 to do prefilter the input, do the
    completion and the magics.

    The core trick is to use an ipython0 instance to prefilter the
    input, and share the namespace between the interpreter instance used
    to execute the statements and the ipython0 used for code
    completion...
    """

    debug = False

    def __init__(self, ipython0=None, *args, **kwargs):
        """ Parameters
            ----------

            ipython0: an optional ipython0 instance to use for command
            prefiltering and completion.
        """
        LineFrontEndBase.__init__(self, *args, **kwargs)
        self.shell.output_trap = RedirectorOutputTrap(
                            out_callback=self.write,
                            err_callback=self.write,
                                            )
        self.shell.traceback_trap = SyncTracebackTrap(
                        formatters=self.shell.traceback_trap.formatters,
                            )

        # Start the ipython0 instance:
        self.save_output_hooks()
        if ipython0 is None:
            # Instanciate an IPython0 InteractiveShell to be able to use the
            # prefiltering.
            # Suppress all key input, to avoid waiting
            def my_rawinput(x=None):
                return '\n'
            old_rawinput = __builtin__.raw_input
            __builtin__.raw_input = my_rawinput
            ipython0 = InteractiveShell(
                parent=None, user_ns=self.shell.user_ns,
                user_global_ns=self.shell.user_global_ns
            )
            __builtin__.raw_input = old_rawinput
        self.ipython0 = ipython0
        # Set the pager:
        self.ipython0.set_hook('show_in_pager',
                    lambda s, string: self.write("\n" + string))
        self.ipython0.write = self.write
        self._ip = _ip = self.ipython0
        # Make sure the raw system call doesn't get called, as we don't
        # have a stdin accessible.
        self._ip.system = self.system_call
        # XXX: Muck around with magics so that they work better
        # in our environment
        if not sys.platform.startswith('win'):
            self.ipython0.magic_ls = mk_system_call(self.system_call,
                                                                'ls -CF')
        # And now clean up the mess created by ipython0
        self.release_output()


        if not 'banner' in kwargs and self.banner is None:
            self.banner = self.ipython0.banner

        # FIXME: __init__ and start should be two different steps
        self.start()

    #--------------------------------------------------------------------------
    # FrontEndBase interface
    #--------------------------------------------------------------------------

    def show_traceback(self):
        """ Use ipython0 to capture the last traceback and display it.
        """
        # Don't do the capture; the except_hook has already done some
        # modifications to the IO streams, if we store them, we'll be
        # storing the wrong ones.
        #self.capture_output()
        self.ipython0.showtraceback(tb_offset=-1)
        self.release_output()


    def execute(self, python_string, raw_string=None):
        if self.debug:
            print 'Executing Python code:', repr(python_string)
        self.capture_output()
        LineFrontEndBase.execute(self, python_string,
                                    raw_string=raw_string)
        self.release_output()


    def save_output_hooks(self):
        """ Store all the output hooks we can think of, to be able to
        restore them.

        We need to do this early, as starting the ipython0 instance will
        screw ouput hooks.
        """
        self.__old_cout_write = Term.cout.write
        self.__old_cerr_write = Term.cerr.write
        self.__old_stdout = sys.stdout
        self.__old_stderr= sys.stderr
        self.__old_help_output = pydoc.help.output
        self.__old_display_hook = sys.displayhook


    def capture_output(self):
        """ Capture all the output mechanisms we can think of.
        """
        self.save_output_hooks()
        Term.cout.write = self.write
        Term.cerr.write = self.write
        sys.stdout = Term.cout
        sys.stderr = Term.cerr
        pydoc.help.output = self.shell.output_trap.out


    def release_output(self):
        """ Release all the different captures we have made.
        """
        Term.cout.write = self.__old_cout_write
        Term.cerr.write = self.__old_cerr_write
        sys.stdout = self.__old_stdout
        sys.stderr = self.__old_stderr
        pydoc.help.output = self.__old_help_output
        sys.displayhook = self.__old_display_hook


    def complete(self, line):
        # FIXME: This should be factored out in the linefrontendbase
        # method.
        word = self._get_completion_text(line)
        completions = self.ipython0.complete(word)
        # FIXME: The proper sort should be done in the complete method.
        key = lambda x: x.replace('_', '')
        completions.sort(key=key)
        if completions:
            prefix = common_prefix(completions)
            line = line[:-len(word)] + prefix
        return line, completions

    #--------------------------------------------------------------------------
    # LineFrontEndBase interface
    #--------------------------------------------------------------------------

    def prefilter_input(self, input_string):
        """ Using IPython0 to prefilter the commands to turn them
        in executable statements that are valid Python strings.
        """
        input_string = LineFrontEndBase.prefilter_input(self, input_string)
        filtered_lines = []
        # The IPython0 prefilters sometime produce output. We need to
        # capture it.
        self.capture_output()
        self.last_result = dict(number=self.prompt_number)

        try:
            try:
                for line in input_string.split('\n'):
                    pf = self.ipython0.prefilter_manager.prefilter_lines
                    filtered_lines.append(pf(line, False).rstrip())
            except:
                # XXX: probably not the right thing to do.
                self.ipython0.showsyntaxerror()
                self.after_execute()
        finally:
            self.release_output()

        # Clean up the trailing whitespace, to avoid indentation errors
        filtered_string = '\n'.join(filtered_lines)
        return filtered_string

    #--------------------------------------------------------------------------
    # PrefilterFrontEnd interface
    #--------------------------------------------------------------------------

    def system_call(self, command_string):
        """ Allows for frontend to define their own system call, to be
            able capture output and redirect input.
        """
        return os.system(command_string)

    def do_exit(self):
        """ Exit the shell, cleanup and save the history.
        """
        self.ipython0.atexit_operations()

    def _get_completion_text(self, line):
        """ Returns the text to be completed by breaking the line at specified
        delimiters.
        """
        # Break at: spaces, '=', all parentheses (except if balanced).
        # FIXME2: In the future, we need to make the implementation similar to
        # that in the 'pyreadline' module (modes/basemode.py) where we break at
        # each delimiter and try to complete the residual line, until we get a
        # successful list of completions.
        expression = '\s|=|,|:|\((?!.*\))|\[(?!.*\])|\{(?!.*\})'
        complete_sep = re.compile(expression)
        text = complete_sep.split(line)[-1]
        return text