##// END OF EJS Templates
More tests of the frontend. Improve the ease of testing.
More tests of the frontend. Improve the ease of testing.

File last commit:

r1458:4eaf1646
r1458:4eaf1646
Show More
linefrontendbase.py
238 lines | 8.1 KiB | text/x-python | PythonLexer
Gael Varoquaux
Split the frontend base class in different files and add a base class...
r1355 """
gvaroquaux
Clean up code, names, and docstrings.
r1455 Base front end class for all line-oriented frontends, rather than
block-oriented.
Gael Varoquaux
Split the frontend base class in different files and add a base class...
r1355
Currently this focuses on synchronous frontends.
"""
__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 re
import IPython
Gael Varoquaux
More tests of the frontend. Improve the ease of testing.
r1458 import sys
Gael Varoquaux
Split the frontend base class in different files and add a base class...
r1355
from frontendbase import FrontEndBase
from IPython.kernel.core.interpreter import Interpreter
Gael Varoquaux
Proper tab completion that actually completes.
r1379 def common_prefix(strings):
gvaroquaux
Clean up code, names, and docstrings.
r1455 """ Given a list of strings, return the common prefix between all
these strings.
"""
Gael Varoquaux
Proper tab completion that actually completes.
r1379 ref = strings[0]
prefix = ''
for size in range(len(ref)):
test_prefix = ref[:size+1]
for string in strings[1:]:
if not string.startswith(test_prefix):
return prefix
prefix = test_prefix
return prefix
Gael Varoquaux
Split the frontend base class in different files and add a base class...
r1355 #-------------------------------------------------------------------------------
# Base class for the line-oriented front ends
#-------------------------------------------------------------------------------
class LineFrontEndBase(FrontEndBase):
gvaroquaux
Clean up code, names, and docstrings.
r1455 """ Concrete implementation of the FrontEndBase class. This is meant
to be the base class behind all the frontend that are line-oriented,
rather than block-oriented.
"""
Gael Varoquaux
Split the frontend base class in different files and add a base class...
r1355
Gael Varoquaux
Traceback capture now working.
r1360 # We need to keep the prompt number, to be able to increment
# it when there is an exception.
prompt_number = 1
Gael Varoquaux
Improve tab-completion.
r1373 # To bootstrap
last_result = dict(number=0)
Gael Varoquaux
Traceback capture now working.
r1360
Gael Varoquaux
Split the frontend base class in different files and add a base class...
r1355 #--------------------------------------------------------------------------
gvaroquaux
Clean up code, names, and docstrings.
r1455 # FrontEndBase interface
Gael Varoquaux
Split the frontend base class in different files and add a base class...
r1355 #--------------------------------------------------------------------------
def __init__(self, shell=None, history=None):
if shell is None:
shell = Interpreter()
FrontEndBase.__init__(self, shell=shell, history=history)
Gael Varoquaux
More tests of the frontend. Improve the ease of testing.
r1458 self.new_prompt(self.input_prompt_template.substitute(number=1))
Gael Varoquaux
Split the frontend base class in different files and add a base class...
r1355
Gael Varoquaux
Proper tab completion that actually completes.
r1379 def complete(self, line):
"""Complete line in engine's user_ns
Gael Varoquaux
Split the frontend base class in different files and add a base class...
r1355
Parameters
----------
Gael Varoquaux
Proper tab completion that actually completes.
r1379 line : string
Gael Varoquaux
Split the frontend base class in different files and add a base class...
r1355
Result
------
Gael Varoquaux
Proper tab completion that actually completes.
r1379 The replacement for the line and the list of possible completions.
Gael Varoquaux
Split the frontend base class in different files and add a base class...
r1355 """
Gael Varoquaux
Proper tab completion that actually completes.
r1379 completions = self.shell.complete(line)
complete_sep = re.compile('[\s\{\}\[\]\(\)\=]')
if completions:
prefix = common_prefix(completions)
residual = complete_sep.split(line)[:-1]
line = line[:-len(residual)] + prefix
return line, completions
Gael Varoquaux
Split the frontend base class in different files and add a base class...
r1355
def render_result(self, result):
gvaroquaux
Clean up code, names, and docstrings.
r1455 """ Frontend-specific rendering of the result of a calculation
that has been sent to an engine.
"""
Gael Varoquaux
Split the frontend base class in different files and add a base class...
r1355 if 'stdout' in result and result['stdout']:
self.write('\n' + result['stdout'])
if 'display' in result and result['display']:
self.write("%s%s\n" % (
Gael Varoquaux
More tests of the frontend. Improve the ease of testing.
r1458 self.output_prompt_template.substitute(
number=result['number']),
Gael Varoquaux
Split the frontend base class in different files and add a base class...
r1355 result['display']['pprint']
) )
Gael Varoquaux
Traceback capture now working.
r1360
Gael Varoquaux
Split the frontend base class in different files and add a base class...
r1355 def render_error(self, failure):
gvaroquaux
Clean up code, names, and docstrings.
r1455 """ Frontend-specific rendering of error.
"""
Gael Varoquaux
More tests of the frontend. Improve the ease of testing.
r1458 self.write('\n\n'+str(failure)+'\n\n')
Gael Varoquaux
Split the frontend base class in different files and add a base class...
r1355 return failure
Gael Varoquaux
Traceback capture now working.
r1360
def is_complete(self, string):
gvaroquaux
Clean up code, names, and docstrings.
r1455 """ Check if a string forms a complete, executable set of
commands.
For the line-oriented frontend, multi-line code is not executed
as soon as it is complete: the users has to enter two line
returns.
"""
Gael Varoquaux
Improve tab-completion.
r1373 if string in ('', '\n'):
gvaroquaux
Clean up code, names, and docstrings.
r1455 # Prefiltering, eg through ipython0, may return an empty
# string although some operations have been accomplished. We
# thus want to consider an empty string as a complete
# statement.
Gael Varoquaux
Improve tab-completion.
r1373 return True
elif ( len(self.get_current_edit_buffer().split('\n'))>2
Gael Varoquaux
Correct small history bug. Add busy cursor.
r1371 and not re.findall(r"\n[\t ]*\n[\t ]*$", string)):
Gael Varoquaux
Traceback capture now working.
r1360 return False
else:
Gael Varoquaux
Improve tab-completion.
r1373 # Add line returns here, to make sure that the statement is
# complete.
return FrontEndBase.is_complete(self, string.rstrip() + '\n\n')
Gael Varoquaux
More tests of the frontend. Improve the ease of testing.
r1458
def get_current_edit_buffer(self):
""" Return the current buffer being entered.
"""
raise NotImplementedError
def write(self, string):
""" Write some characters to the display.
Subclass should overide this method.
"""
print >>sys.__stderr__, string
Gael Varoquaux
Split the frontend base class in different files and add a base class...
r1355
Gael Varoquaux
More tests of the frontend. Improve the ease of testing.
r1458 def add_to_edit_buffer(self, string):
""" Add the given string to the current edit buffer.
"""
raise NotImplementedError
def new_prompt(self, prompt):
""" Prints a prompt and starts a new editing buffer.
Subclasses should use this method to make sure that the
terminal is put in a state favorable for a new line
input.
"""
self.write(prompt)
Gael Varoquaux
Split the frontend base class in different files and add a base class...
r1355
Gael Varoquaux
Traceback capture now working.
r1360 def execute(self, python_string, raw_string=None):
gvaroquaux
Clean up code, names, and docstrings.
r1455 """ Stores the raw_string in the history, and sends the
python string to the interpreter.
Gael Varoquaux
Traceback capture now working.
r1360 """
if raw_string is None:
Gael Varoquaux
Nice background color for already entered code....
r1374 raw_string = python_string
Gael Varoquaux
Traceback capture now working.
r1360 # Create a false result, in case there is an exception
Gael Varoquaux
Make code execution more robust.
r1362 self.last_result = dict(number=self.prompt_number)
Gael Varoquaux
Traceback capture now working.
r1360 try:
Gael Varoquaux
Correct small history bug. Add busy cursor.
r1371 self.history.input_cache[-1] = raw_string.rstrip()
Gael Varoquaux
Traceback capture now working.
r1360 result = self.shell.execute(python_string)
Gael Varoquaux
Make code execution more robust.
r1362 self.last_result = result
Gael Varoquaux
Traceback capture now working.
r1360 self.render_result(result)
Gael Varoquaux
Make code execution more robust.
r1362 except:
Gael Varoquaux
Traceback capture now working.
r1360 self.show_traceback()
finally:
Gael Varoquaux
Make code execution more robust.
r1362 self.after_execute()
gvaroquaux
Clean up code, names, and docstrings.
r1455 #--------------------------------------------------------------------------
# LineFrontEndBase interface
#--------------------------------------------------------------------------
def prefilter_input(self, string):
""" Priflter the input to turn it in valid python.
"""
string = string.replace('\r\n', '\n')
string = string.replace('\t', 4*' ')
# Clean the trailing whitespace
string = '\n'.join(l.rstrip() for l in string.split('\n'))
return string
Gael Varoquaux
Make code execution more robust.
r1362
def after_execute(self):
""" All the operations required after an execution to put the
terminal back in a shape where it is usable.
"""
self.prompt_number += 1
Gael Varoquaux
More tests of the frontend. Improve the ease of testing.
r1458 self.new_prompt(self.input_prompt_template.substitute(
number=(self.last_result['number'] + 1)))
Gael Varoquaux
Make code execution more robust.
r1362 # Start a new empty history entry
self._add_history(None, '')
Gael Varoquaux
Correct small history bug. Add busy cursor.
r1371 self.history_cursor = len(self.history.input_cache) - 1
Gael Varoquaux
Traceback capture now working.
r1360
gvaroquaux
Clean up code, names, and docstrings.
r1455 #--------------------------------------------------------------------------
# Private API
#--------------------------------------------------------------------------
Gael Varoquaux
History is now working. + misc keybindings.
r1358 def _on_enter(self):
Gael Varoquaux
Split the frontend base class in different files and add a base class...
r1355 """ Called when the return key is pressed in a line editing
buffer.
"""
current_buffer = self.get_current_edit_buffer()
Gael Varoquaux
Traceback capture now working.
r1360 cleaned_buffer = self.prefilter_input(current_buffer)
Gael Varoquaux
Correct small history bug. Add busy cursor.
r1371 if self.is_complete(cleaned_buffer):
Gael Varoquaux
Traceback capture now working.
r1360 self.execute(cleaned_buffer, raw_string=current_buffer)
Gael Varoquaux
Split the frontend base class in different files and add a base class...
r1355 else:
Gael Varoquaux
More tests of the frontend. Improve the ease of testing.
r1458 self.add_to_edit_buffer(self._get_indent_string(
gvaroquaux
Make the wx frontend well-behaved under windows.
r1436 current_buffer[:-1]))
if current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
Gael Varoquaux
More tests of the frontend. Improve the ease of testing.
r1458 self.add_to_edit_buffer('\t')
Gael Varoquaux
Split the frontend base class in different files and add a base class...
r1355
def _get_indent_string(self, string):
gvaroquaux
Clean up code, names, and docstrings.
r1455 """ Return the string of whitespace that prefixes a line. Used to
add the right amount of indendation when creating a new line.
"""
Gael Varoquaux
Make code execution more robust.
r1362 string = string.replace('\t', ' '*4)
Gael Varoquaux
Split the frontend base class in different files and add a base class...
r1355 string = string.split('\n')[-1]
indent_chars = len(string) - len(string.lstrip())
indent_string = '\t'*(indent_chars // 4) + \
' '*(indent_chars % 4)
return indent_string