prefilterfrontend.py
223 lines
| 8.3 KiB
| text/x-python
|
PythonLexer
Gael Varoquaux
|
r1385 | """ | ||
Frontend class that uses IPython0 to prefilter the inputs. | ||||
Using the IPython0 mechanism gives us access to the magics. | ||||
gvaroquaux
|
r1455 | |||
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. | ||||
Gael Varoquaux
|
r1385 | """ | ||
__docformat__ = "restructuredtext en" | ||||
#------------------------------------------------------------------------------- | ||||
# Copyright (C) 2008 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 | ||||
from linefrontendbase import LineFrontEndBase, common_prefix | ||||
from IPython.ipmaker import make_IPython | ||||
from IPython.ipapi import IPApi | ||||
Gael Varoquaux
|
r1423 | from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap | ||
Gael Varoquaux
|
r1385 | |||
Gael Varoquaux
|
r1437 | from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap | ||
Gael Varoquaux
|
r1385 | from IPython.genutils import Term | ||
Gael Varoquaux
|
r1391 | import pydoc | ||
gvaroquaux
|
r1455 | import os | ||
Gael Varoquaux
|
r1385 | |||
Gael Varoquaux
|
r1437 | 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. | ||||
Gael Varoquaux
|
r1424 | """ | ||
def my_system_call(args): | ||||
Gael Varoquaux
|
r1437 | system_call_function("%s %s" % (command, args)) | ||
Gael Varoquaux
|
r1424 | return my_system_call | ||
Gael Varoquaux
|
r1385 | #------------------------------------------------------------------------------- | ||
# Frontend class using ipython0 to do the prefiltering. | ||||
#------------------------------------------------------------------------------- | ||||
class PrefilterFrontEnd(LineFrontEndBase): | ||||
gvaroquaux
|
r1455 | """ 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... | ||||
""" | ||||
Gael Varoquaux
|
r1385 | |||
Gael Varoquaux
|
r1504 | def __init__(self, ipython0=None, *args, **kwargs): | ||
""" Parameters: | ||||
----------- | ||||
ipython0: an optional ipython0 instance to use for command | ||||
prefiltering and completion. | ||||
""" | ||||
Gael Varoquaux
|
r1472 | self.save_output_hooks() | ||
Gael Varoquaux
|
r1504 | if ipython0 is None: | ||
# Instanciate an IPython0 interpreter to be able to use the | ||||
# prefiltering. | ||||
Gael Varoquaux
|
r1505 | # XXX: argv=[] is a bit bold. | ||
ipython0 = make_IPython(argv=[]) | ||||
Gael Varoquaux
|
r1504 | self.ipython0 = ipython0 | ||
Gael Varoquaux
|
r1385 | # Set the pager: | ||
self.ipython0.set_hook('show_in_pager', | ||||
Gael Varoquaux
|
r1504 | lambda s, string: self.write("\n" + string)) | ||
Gael Varoquaux
|
r1385 | self.ipython0.write = self.write | ||
self._ip = _ip = IPApi(self.ipython0) | ||||
# Make sure the raw system call doesn't get called, as we don't | ||||
# have a stdin accessible. | ||||
Gael Varoquaux
|
r1437 | self._ip.system = self.system_call | ||
Gael Varoquaux
|
r1424 | # XXX: Muck around with magics so that they work better | ||
# in our environment | ||||
Gael Varoquaux
|
r1437 | self.ipython0.magic_ls = mk_system_call(self.system_call, | ||
'ls -CF') | ||||
Gael Varoquaux
|
r1472 | # And now clean up the mess created by ipython0 | ||
self.release_output() | ||||
Gael Varoquaux
|
r1505 | if not 'banner' in kwargs and self.banner is None: | ||
Gael Varoquaux
|
r1495 | kwargs['banner'] = self.ipython0.BANNER + """ | ||
This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code.""" | ||||
LineFrontEndBase.__init__(self, *args, **kwargs) | ||||
# XXX: Hack: mix the two namespaces | ||||
gvaroquaux
|
r1531 | self.shell.user_ns.update(self.ipython0.user_ns) | ||
self.ipython0.user_ns = self.shell.user_ns | ||||
self.shell.user_global_ns.update(self.ipython0.user_global_ns) | ||||
self.ipython0.user_global_ns = self.shell.user_global_ns | ||||
Gael Varoquaux
|
r1495 | |||
Gael Varoquaux
|
r1423 | self.shell.output_trap = RedirectorOutputTrap( | ||
out_callback=self.write, | ||||
err_callback=self.write, | ||||
Gael Varoquaux
|
r1407 | ) | ||
Gael Varoquaux
|
r1437 | self.shell.traceback_trap = SyncTracebackTrap( | ||
gvaroquaux
|
r1455 | formatters=self.shell.traceback_trap.formatters, | ||
Gael Varoquaux
|
r1437 | ) | ||
Gael Varoquaux
|
r1385 | |||
gvaroquaux
|
r1455 | #-------------------------------------------------------------------------- | ||
# FrontEndBase interface | ||||
#-------------------------------------------------------------------------- | ||||
Gael Varoquaux
|
r1385 | |||
def show_traceback(self): | ||||
gvaroquaux
|
r1455 | """ Use ipython0 to capture the last traceback and display it. | ||
""" | ||||
Gael Varoquaux
|
r1385 | self.capture_output() | ||
self.ipython0.showtraceback() | ||||
self.release_output() | ||||
Gael Varoquaux
|
r1391 | def execute(self, python_string, raw_string=None): | ||
gvaroquaux
|
r1502 | if self.debug: | ||
print 'Executing Python code:', repr(python_string) | ||||
Gael Varoquaux
|
r1391 | self.capture_output() | ||
LineFrontEndBase.execute(self, python_string, | ||||
raw_string=raw_string) | ||||
self.release_output() | ||||
Gael Varoquaux
|
r1472 | 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 | ||||
Gael Varoquaux
|
r1385 | def capture_output(self): | ||
Gael Varoquaux
|
r1391 | """ Capture all the output mechanisms we can think of. | ||
Gael Varoquaux
|
r1385 | """ | ||
Gael Varoquaux
|
r1472 | self.save_output_hooks() | ||
Gael Varoquaux
|
r1385 | Term.cout.write = self.write | ||
Term.cerr.write = self.write | ||||
sys.stdout = Term.cout | ||||
sys.stderr = Term.cerr | ||||
Gael Varoquaux
|
r1391 | pydoc.help.output = self.shell.output_trap.out | ||
Gael Varoquaux
|
r1390 | |||
Gael Varoquaux
|
r1385 | |||
def release_output(self): | ||||
Gael Varoquaux
|
r1391 | """ Release all the different captures we have made. | ||
Gael Varoquaux
|
r1385 | """ | ||
Term.cout.write = self.__old_cout_write | ||||
Gael Varoquaux
|
r1472 | Term.cerr.write = self.__old_cerr_write | ||
Gael Varoquaux
|
r1385 | sys.stdout = self.__old_stdout | ||
sys.stderr = self.__old_stderr | ||||
Gael Varoquaux
|
r1391 | pydoc.help.output = self.__old_help_output | ||
Gael Varoquaux
|
r1472 | sys.displayhook = self.__old_display_hook | ||
Gael Varoquaux
|
r1385 | |||
def complete(self, line): | ||||
word = line.split('\n')[-1].split(' ')[-1] | ||||
completions = self.ipython0.complete(word) | ||||
Gael Varoquaux
|
r1407 | # FIXME: The proper sort should be done in the complete method. | ||
Gael Varoquaux
|
r1385 | key = lambda x: x.replace('_', '') | ||
completions.sort(key=key) | ||||
if completions: | ||||
prefix = common_prefix(completions) | ||||
line = line[:-len(word)] + prefix | ||||
return line, completions | ||||
gvaroquaux
|
r1455 | |||
#-------------------------------------------------------------------------- | ||||
# 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: | ||||
for line in input_string.split('\n'): | ||||
gvaroquaux
|
r1502 | filtered_lines.append( | ||
self.ipython0.prefilter(line, False).rstrip()) | ||||
gvaroquaux
|
r1455 | except: | ||
# XXX: probably not the right thing to do. | ||||
self.ipython0.showsyntaxerror() | ||||
self.after_execute() | ||||
finally: | ||||
self.release_output() | ||||
gvaroquaux
|
r1502 | # Clean up the trailing whitespace, to avoid indentation errors | ||
gvaroquaux
|
r1455 | filtered_string = '\n'.join(filtered_lines) | ||
return filtered_string | ||||
#-------------------------------------------------------------------------- | ||||
Gael Varoquaux
|
r1458 | # PrefilterFrontEnd interface | ||
gvaroquaux
|
r1455 | #-------------------------------------------------------------------------- | ||
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) | ||||
Gael Varoquaux
|
r1385 | |||
Gael Varoquaux
|
r1391 | def do_exit(self): | ||
""" Exit the shell, cleanup and save the history. | ||||
""" | ||||
self.ipython0.atexit_operations() | ||||