##// END OF EJS Templates
Fix traceback on exit.
Fix traceback on exit.

File last commit:

r1484:0ff50065
r1489:46a76184
Show More
linefrontendbase.py
286 lines | 9.7 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
gvaroquaux
More tests....
r1460 # We keep a reference to the last result: it helps testing and
# programatic control of the frontend.
Gael Varoquaux
Improve tab-completion.
r1373 last_result = dict(number=0)
Gael Varoquaux
Traceback capture now working.
r1360
gvaroquaux
More code reuse between GUI-independant frontend and Wx frontend: getting...
r1462 # The input buffer being edited
input_buffer = ''
gvaroquaux
Abstract completion mechanism outside of wx-specific code.
r1463 # Set to true for debug output
debug = False
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 #--------------------------------------------------------------------------
gvaroquaux
Tweak the debug mode....
r1484 def __init__(self, shell=None, history=None, *args, **kwargs):
Gael Varoquaux
Split the frontend base class in different files and add a base class...
r1355 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
gvaroquaux
More code reuse between GUI-independant frontend and Wx frontend: getting...
r1462 elif ( len(self.input_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
gvaroquaux
Fix segfaults under windows.
r1479 def write(self, string, refresh=True):
Gael Varoquaux
More tests of the frontend. Improve the ease of testing.
r1458 """ Write some characters to the display.
Subclass should overide this method.
gvaroquaux
Fix segfaults under windows.
r1479
The refresh keyword argument is used in frontends with an
event loop, to choose whether the write should trigget an UI
refresh, and thus be syncrhonous, or not.
Gael Varoquaux
More tests of the frontend. Improve the ease of testing.
r1458 """
print >>sys.__stderr__, string
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
gvaroquaux
Abstract completion mechanism outside of wx-specific code.
r1463
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
Abstract completion mechanism outside of wx-specific code.
r1463 def complete_current_input(self):
""" Do code completion on current line.
"""
if self.debug:
print >>sys.__stdout__, "complete_current_input",
line = self.input_buffer
new_line, completions = self.complete(line)
if len(completions)>1:
self.write_completion(completions)
self.input_buffer = new_line
if self.debug:
print >>sys.__stdout__, completions
gvaroquaux
Test completion.
r1464 def get_line_width(self):
""" Return the width of the line in characters.
"""
return 80
gvaroquaux
Abstract completion mechanism outside of wx-specific code.
r1463 def write_completion(self, possibilities):
""" Write the list of possible completions.
"""
current_buffer = self.input_buffer
self.write('\n')
max_len = len(max(possibilities, key=len)) + 1
gvaroquaux
Test completion.
r1464 # Now we check how much symbol we can put on a line...
chars_per_line = self.get_line_width()
gvaroquaux
Abstract completion mechanism outside of wx-specific code.
r1463 symbols_per_line = max(1, chars_per_line/max_len)
pos = 1
buf = []
for symbol in possibilities:
if pos < symbols_per_line:
buf.append(symbol.ljust(max_len))
pos += 1
else:
buf.append(symbol.rstrip() + '\n')
pos = 1
self.write(''.join(buf))
self.new_prompt(self.input_prompt_template.substitute(
number=self.last_result['number'] + 1))
self.input_buffer = current_buffer
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.input_buffer = ''
self.write(prompt)
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.
"""
gvaroquaux
More code reuse between GUI-independant frontend and Wx frontend: getting...
r1462 current_buffer = self.input_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:
gvaroquaux
More code reuse between GUI-independant frontend and Wx frontend: getting...
r1462 self.input_buffer += self._get_indent_string(
current_buffer[:-1])
gvaroquaux
Make the wx frontend well-behaved under windows.
r1436 if current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
gvaroquaux
More code reuse between GUI-independant frontend and Wx frontend: getting...
r1462 self.input_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