linefrontendbase.py
179 lines
| 5.9 KiB
| text/x-python
|
PythonLexer
Gael Varoquaux
|
r1355 | """ | ||
Base front end class for all line-oriented frontends. | ||||
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 | ||||
from frontendbase import FrontEndBase | ||||
from IPython.kernel.core.interpreter import Interpreter | ||||
Gael Varoquaux
|
r1379 | |||
def common_prefix(strings): | ||||
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
|
r1355 | #------------------------------------------------------------------------------- | ||
# Base class for the line-oriented front ends | ||||
#------------------------------------------------------------------------------- | ||||
class LineFrontEndBase(FrontEndBase): | ||||
Gael Varoquaux
|
r1360 | # We need to keep the prompt number, to be able to increment | ||
# it when there is an exception. | ||||
prompt_number = 1 | ||||
Gael Varoquaux
|
r1373 | # To bootstrap | ||
last_result = dict(number=0) | ||||
Gael Varoquaux
|
r1360 | |||
Gael Varoquaux
|
r1355 | #-------------------------------------------------------------------------- | ||
# Public API | ||||
#-------------------------------------------------------------------------- | ||||
def __init__(self, shell=None, history=None): | ||||
if shell is None: | ||||
shell = Interpreter() | ||||
FrontEndBase.__init__(self, shell=shell, history=history) | ||||
#FIXME: print banner. | ||||
banner = """IPython1 %s -- An enhanced Interactive Python.""" \ | ||||
% IPython.__version__ | ||||
Gael Varoquaux
|
r1379 | def complete(self, line): | ||
"""Complete line in engine's user_ns | ||||
Gael Varoquaux
|
r1355 | |||
Parameters | ||||
---------- | ||||
Gael Varoquaux
|
r1379 | line : string | ||
Gael Varoquaux
|
r1355 | |||
Result | ||||
------ | ||||
Gael Varoquaux
|
r1379 | The replacement for the line and the list of possible completions. | ||
Gael Varoquaux
|
r1355 | """ | ||
Gael Varoquaux
|
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
|
r1355 | |||
def render_result(self, result): | ||||
if 'stdout' in result and result['stdout']: | ||||
self.write('\n' + result['stdout']) | ||||
if 'display' in result and result['display']: | ||||
self.write("%s%s\n" % ( | ||||
self.output_prompt % result['number'], | ||||
result['display']['pprint'] | ||||
) ) | ||||
Gael Varoquaux
|
r1360 | |||
Gael Varoquaux
|
r1355 | def render_error(self, failure): | ||
self.insert_text('\n\n'+str(failure)+'\n\n') | ||||
return failure | ||||
Gael Varoquaux
|
r1360 | |||
def prefilter_input(self, string): | ||||
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 | ||||
def is_complete(self, string): | ||||
Gael Varoquaux
|
r1373 | if string in ('', '\n'): | ||
return True | ||||
elif ( len(self.get_current_edit_buffer().split('\n'))>2 | ||||
Gael Varoquaux
|
r1371 | and not re.findall(r"\n[\t ]*\n[\t ]*$", string)): | ||
Gael Varoquaux
|
r1360 | return False | ||
else: | ||||
Gael Varoquaux
|
r1373 | # Add line returns here, to make sure that the statement is | ||
# complete. | ||||
return FrontEndBase.is_complete(self, string.rstrip() + '\n\n') | ||||
Gael Varoquaux
|
r1355 | |||
Gael Varoquaux
|
r1360 | def execute(self, python_string, raw_string=None): | ||
""" Send the python_string to the interpreter, stores the | ||||
raw_string in the history and starts a new prompt. | ||||
""" | ||||
if raw_string is None: | ||||
Gael Varoquaux
|
r1374 | raw_string = python_string | ||
Gael Varoquaux
|
r1360 | # Create a false result, in case there is an exception | ||
Gael Varoquaux
|
r1362 | self.last_result = dict(number=self.prompt_number) | ||
Gael Varoquaux
|
r1360 | try: | ||
Gael Varoquaux
|
r1371 | self.history.input_cache[-1] = raw_string.rstrip() | ||
Gael Varoquaux
|
r1360 | result = self.shell.execute(python_string) | ||
Gael Varoquaux
|
r1362 | self.last_result = result | ||
Gael Varoquaux
|
r1360 | self.render_result(result) | ||
Gael Varoquaux
|
r1362 | except: | ||
Gael Varoquaux
|
r1360 | self.show_traceback() | ||
finally: | ||||
Gael Varoquaux
|
r1362 | self.after_execute() | ||
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 | ||||
self.new_prompt(self.prompt % (self.last_result['number'] + 1)) | ||||
# Start a new empty history entry | ||||
self._add_history(None, '') | ||||
Gael Varoquaux
|
r1371 | self.history_cursor = len(self.history.input_cache) - 1 | ||
Gael Varoquaux
|
r1360 | |||
Gael Varoquaux
|
r1358 | def _on_enter(self): | ||
Gael Varoquaux
|
r1355 | """ Called when the return key is pressed in a line editing | ||
buffer. | ||||
""" | ||||
current_buffer = self.get_current_edit_buffer() | ||||
Gael Varoquaux
|
r1360 | cleaned_buffer = self.prefilter_input(current_buffer) | ||
Gael Varoquaux
|
r1371 | if self.is_complete(cleaned_buffer): | ||
Gael Varoquaux
|
r1360 | self.execute(cleaned_buffer, raw_string=current_buffer) | ||
Gael Varoquaux
|
r1355 | else: | ||
Gael Varoquaux
|
r1371 | if len(current_buffer.split('\n'))>2: | ||
# We need to clean the trailing '\n' | ||||
self.write(self._get_indent_string(current_buffer[:-1])) | ||||
Gael Varoquaux
|
r1360 | else: | ||
Gael Varoquaux
|
r1362 | self.write('\t') | ||
Gael Varoquaux
|
r1355 | |||
#-------------------------------------------------------------------------- | ||||
# Private API | ||||
#-------------------------------------------------------------------------- | ||||
def _get_indent_string(self, string): | ||||
Gael Varoquaux
|
r1362 | string = string.replace('\t', ' '*4) | ||
Gael Varoquaux
|
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 | ||||