##// END OF EJS Templates
Turn little test into proper doctest. Cleanup and document.
Turn little test into proper doctest. Cleanup and document.

File last commit:

r1315:8cbb9f2f
r1417:e6bda754
Show More
frontendbase.py
407 lines | 12.0 KiB | text/x-python | PythonLexer
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 # encoding: utf-8
Barry Wark
pep8 compliance, first pass
r1291 # -*- test-case-name: IPython.frontend.tests.test_frontendbase -*-
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 """
Barry Wark
pep8 compliance, first pass
r1291 frontendbase provides an interface and base class for GUI frontends for
IPython.kernel/IPython.kernel.core.
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
Barry Wark
for code review
r1277 Frontend implementations will likely want to subclass FrontEndBase.
Author: Barry Wark
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 """
__docformat__ = "restructuredtext en"
#-------------------------------------------------------------------------------
Barry Wark
for code review
r1277 # Copyright (C) 2008 The IPython Development Team
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 #
# Distributed under the terms of the BSD License. The full license is in
# the file COPYING, distributed as part of this software.
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# Imports
#-------------------------------------------------------------------------------
import string
import uuid
Barry Wark
history partway there. render_error fixes for cocoa frontend
r1279 import _ast
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
Barry Wark
stubbed zope.interface and Twisted
r1313 try:
from zope.interface import Interface, Attribute, implements, classProvides
except ImportError:
#zope.interface is not available
Interface = object
def Attribute(name, doc): pass
def implements(interface): pass
def classProvides(interface): pass
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
from IPython.kernel.core.history import FrontEndHistory
from IPython.kernel.core.util import Bunch
from IPython.kernel.engineservice import IEngineCore
Barry Wark
stubbed zope.interface and Twisted
r1313 try:
from twisted.python.failure import Failure
except ImportError:
#Twisted not available
Failure = Exception
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
##############################################################################
# TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
# not
rc = Bunch()
rc.prompt_in1 = r'In [$number]: '
rc.prompt_in2 = r'...'
rc.prompt_out = r'Out [$number]: '
##############################################################################
Barry Wark
stubbed zope.interface and Twisted
r1313 class IFrontEndFactory(Interface):
Barry Wark
for code review
r1277 """Factory interface for frontends."""
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
Barry Wark
for code review
r1277 def __call__(engine=None, history=None):
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 """
Parameters:
interpreter : IPython.kernel.engineservice.IEngineCore
"""
Barry Wark
for code review
r1277
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 pass
Barry Wark
for code review
r1277
Barry Wark
stubbed zope.interface and Twisted
r1313 class IFrontEnd(Interface):
Barry Wark
for code review
r1277 """Interface for frontends. All methods return t.i.d.Deferred"""
Barry Wark
stubbed zope.interface and Twisted
r1313 Attribute("input_prompt_template", "string.Template instance\
Barry Wark
pep8 compliance, first pass
r1291 substituteable with execute result.")
Barry Wark
stubbed zope.interface and Twisted
r1313 Attribute("output_prompt_template", "string.Template instance\
Barry Wark
pep8 compliance, first pass
r1291 substituteable with execute result.")
Barry Wark
stubbed zope.interface and Twisted
r1313 Attribute("continuation_prompt_template", "string.Template instance\
Barry Wark
pep8 compliance, first pass
r1291 substituteable with execute result.")
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
Barry Wark
updates for frontendbase API. Cocoa plugin functional in Objective-C app
r1303 def update_cell_prompt(result, blockID=None):
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 """Subclass may override to update the input prompt for a block.
Barry Wark
pep8 compliance, first pass
r1291 Since this method will be called as a
Barry Wark
updates for frontendbase API. Cocoa plugin functional in Objective-C app
r1303 twisted.internet.defer.Deferred's callback/errback,
Barry Wark
pep8, third pass
r1293 implementations should return result when finished.
Barry Wark
updates for frontendbase API. Cocoa plugin functional in Objective-C app
r1303 Result is a result dict in case of success, and a
twisted.python.util.failure.Failure in case of an error
Barry Wark
pep8, third pass
r1293 """
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
Barry Wark
for code review
r1277 pass
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
Barry Wark
updates for frontendbase API. Cocoa plugin functional in Objective-C app
r1303
def render_result(result):
Barry Wark
pep8 compliance, first pass
r1291 """Render the result of an execute call. Implementors may choose the
method of rendering.
For example, a notebook-style frontend might render a Chaco plot
inline.
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
Parameters:
result : dict (result of IEngineBase.execute )
Barry Wark
updates for frontendbase API. Cocoa plugin functional in Objective-C app
r1303 blockID = result['blockID']
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
Result:
Output of frontend rendering
"""
Barry Wark
for code review
r1277 pass
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
Barry Wark
updates for frontendbase API. Cocoa plugin functional in Objective-C app
r1303 def render_error(failure):
Barry Wark
pep8 compliance, first pass
r1291 """Subclasses must override to render the failure. Since this method
Barry Wark
updates for frontendbase API. Cocoa plugin functional in Objective-C app
r1303 will be called as a twisted.internet.defer.Deferred's callback,
Barry Wark
pep8 compliance, first pass
r1291 implementations should return result when finished.
Barry Wark
updates for frontendbase API. Cocoa plugin functional in Objective-C app
r1303
blockID = failure.blockID
Barry Wark
pep8 compliance, first pass
r1291 """
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
Barry Wark
for code review
r1277 pass
Barry Wark
improved error rendering for cocoa_frontend
r1304 def input_prompt(number=''):
Barry Wark
pep8 compliance, first pass
r1291 """Returns the input prompt by subsituting into
self.input_prompt_template
"""
Barry Wark
for code review
r1277 pass
Barry Wark
improved error rendering for cocoa_frontend
r1304 def output_prompt(number=''):
Barry Wark
pep8 compliance, first pass
r1291 """Returns the output prompt by subsituting into
self.output_prompt_template
"""
Barry Wark
for code review
r1277
pass
Barry Wark
CammelCase fixed
r1282 def continuation_prompt():
Barry Wark
pep8 compliance, first pass
r1291 """Returns the continuation prompt by subsituting into
self.continuation_prompt_template
"""
Barry Wark
for code review
r1277
pass
def is_complete(block):
"""Returns True if block is complete, False otherwise."""
pass
def compile_ast(block):
"""Compiles block to an _ast.AST"""
pass
Barry Wark
history partway there. render_error fixes for cocoa frontend
r1279 def get_history_previous(currentBlock):
Barry Wark
fixes and tests for history
r1281 """Returns the block previous in the history. Saves currentBlock if
the history_cursor is currently at the end of the input history"""
Barry Wark
for code review
r1277 pass
Barry Wark
fixes and tests for history
r1281 def get_history_next():
Barry Wark
for code review
r1277 """Returns the next block in the history."""
pass
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
class FrontEndBase(object):
"""
FrontEndBase manages the state tasks for a CLI frontend:
- Input and output history management
- Input/continuation and output prompt generation
Some issues (due to possibly unavailable engine):
- How do we get the current cell number for the engine?
- How do we handle completions?
"""
history_cursor = 0
current_indent_level = 0
input_prompt_template = string.Template(rc.prompt_in1)
output_prompt_template = string.Template(rc.prompt_out)
continuation_prompt_template = string.Template(rc.prompt_in2)
Barry Wark
FrontEndBase is now synchronous.
r1306 def __init__(self, shell=None, history=None):
self.shell = shell
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 if history is None:
self.history = FrontEndHistory(input_cache=[''])
else:
self.history = history
Barry Wark
improved error rendering for cocoa_frontend
r1304 def input_prompt(self, number=''):
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 """Returns the current input prompt
It would be great to use ipython1.core.prompts.Prompt1 here
"""
Barry Wark
updates for frontendbase API. Cocoa plugin functional in Objective-C app
r1303 return self.input_prompt_template.safe_substitute({'number':number})
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
Barry Wark
CammelCase fixed
r1282 def continuation_prompt(self):
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 """Returns the current continuation prompt"""
return self.continuation_prompt_template.safe_substitute()
Barry Wark
improved error rendering for cocoa_frontend
r1304 def output_prompt(self, number=''):
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 """Returns the output prompt for result"""
Barry Wark
updates for frontendbase API. Cocoa plugin functional in Objective-C app
r1303 return self.output_prompt_template.safe_substitute({'number':number})
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
def is_complete(self, block):
"""Determine if block is complete.
Parameters
block : string
Result
True if block can be sent to the engine without compile errors.
False otherwise.
"""
try:
Barry Wark
fixed frontendbase.FrontEndBase.is_complete
r1278 ast = self.compile_ast(block)
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 except:
return False
Barry Wark
fixed frontendbase.FrontEndBase.is_complete
r1278
lines = block.split('\n')
return (len(lines)==1 or str(lines[-1])=='')
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
def compile_ast(self, block):
"""Compile block to an AST
Parameters:
block : str
Result:
AST
Throws:
Exception if block cannot be compiled
"""
return compile(block, "<string>", "exec", _ast.PyCF_ONLY_AST)
def execute(self, block, blockID=None):
Barry Wark
FrontEndBase is now synchronous.
r1306 """Execute the block and return the result.
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
Parameters:
block : {str, AST}
blockID : any
Barry Wark
pep8 compliance, first pass
r1291 Caller may provide an ID to identify this block.
result['blockID'] := blockID
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
Result:
Deferred result of self.interpreter.execute
"""
Barry Wark
history partway there. render_error fixes for cocoa frontend
r1279
Barry Wark
FrontEndBase is now synchronous.
r1306 if(not self.is_complete(block)):
raise Exception("Block is not compilable")
if(blockID == None):
blockID = uuid.uuid4() #random UUID
try:
result = self.shell.execute(block)
except Exception,e:
e = self._add_block_id_for_failure(e, blockID=blockID)
e = self.update_cell_prompt(e, blockID=blockID)
e = self.render_error(e)
else:
result = self._add_block_id_for_result(result, blockID=blockID)
result = self.update_cell_prompt(result, blockID=blockID)
result = self.render_result(result)
return result
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
Barry Wark
history partway there. render_error fixes for cocoa frontend
r1279
Barry Wark
updates for frontendbase API. Cocoa plugin functional in Objective-C app
r1303 def _add_block_id_for_result(self, result, blockID):
Barry Wark
pep8 compliance, first pass
r1291 """Add the blockID to result or failure. Unfortunatley, we have to
treat failures differently than result dicts.
Barry Wark
added blockID for failures (special case)
r1283 """
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
Barry Wark
updates for frontendbase API. Cocoa plugin functional in Objective-C app
r1303 result['blockID'] = blockID
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
return result
Barry Wark
updates for frontendbase API. Cocoa plugin functional in Objective-C app
r1303 def _add_block_id_for_failure(self, failure, blockID):
"""_add_block_id_for_failure"""
failure.blockID = blockID
return failure
Barry Wark
history partway there. render_error fixes for cocoa frontend
r1279 def _add_history(self, result, block=None):
"""Add block to the history"""
assert(block != None)
self.history.add_items([block])
self.history_cursor += 1
return result
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
Barry Wark
history partway there. render_error fixes for cocoa frontend
r1279 def get_history_previous(self, currentBlock):
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 """ Returns previous history string and decrement history cursor.
"""
command = self.history.get_history_item(self.history_cursor - 1)
Barry Wark
fixes and tests for history
r1281
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 if command is not None:
Barry Wark
fixes and tests for history
r1281 if(self.history_cursor == len(self.history.input_cache)):
self.history.input_cache[self.history_cursor] = currentBlock
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 self.history_cursor -= 1
return command
Barry Wark
fixes and tests for history
r1281 def get_history_next(self):
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 """ Returns next history string and increment history cursor.
"""
Barry Wark
fixes and tests for history
r1281 command = self.history.get_history_item(self.history_cursor+1)
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 if command is not None:
self.history_cursor += 1
return command
###
# Subclasses probably want to override these methods...
###
Barry Wark
updates for frontendbase API. Cocoa plugin functional in Objective-C app
r1303 def update_cell_prompt(self, result, blockID=None):
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263 """Subclass may override to update the input prompt for a block.
Barry Wark
pep8 compliance, first pass
r1291 Since this method will be called as a
twisted.internet.defer.Deferred's callback, implementations should
return result when finished.
Barry Wark
added blockID for failures (special case)
r1283 """
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
return result
def render_result(self, result):
Barry Wark
pep8 compliance, first pass
r1291 """Subclasses must override to render result. Since this method will
be called as a twisted.internet.defer.Deferred's callback,
implementations should return result when finished.
"""
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
return result
def render_error(self, failure):
Barry Wark
pep8 compliance, first pass
r1291 """Subclasses must override to render the failure. Since this method
will be called as a twisted.internet.defer.Deferred's callback,
Barry Wark
updates for frontendbase API. Cocoa plugin functional in Objective-C app
r1303 implementations should return result when finished.
"""
Barry Wark
moved frontend from ipython1-dev. Got engineservice.ThreadedEngineService running, but does nto correctly propagate errors during execute()
r1263
return failure
Barry Wark <barrywarkatgmaildotcom>
renamed AsynchronousFrontEndBase to AsyncFrontEndBase
r1315 class AsyncFrontEndBase(FrontEndBase):
Barry Wark
refactored FrontEndBase to base and AsynchronousFrontEndBase
r1305 """
Overrides FrontEndBase to wrap execute in a deferred result.
All callbacks are made as callbacks on the deferred result.
"""
Barry Wark
stubbed zope.interface and Twisted
r1313 implements(IFrontEnd)
classProvides(IFrontEndFactory)
Barry Wark
refactored FrontEndBase to base and AsynchronousFrontEndBase
r1305
Barry Wark
FrontEndBase is now synchronous.
r1306 def __init__(self, engine=None, history=None):
assert(engine==None or IEngineCore.providedBy(engine))
self.engine = IEngineCore(engine)
if history is None:
self.history = FrontEndHistory(input_cache=[''])
else:
self.history = history
Barry Wark
refactored FrontEndBase to base and AsynchronousFrontEndBase
r1305 def execute(self, block, blockID=None):
"""Execute the block and return the deferred result.
Parameters:
block : {str, AST}
blockID : any
Caller may provide an ID to identify this block.
result['blockID'] := blockID
Result:
Deferred result of self.interpreter.execute
"""
if(not self.is_complete(block)):
return Failure(Exception("Block is not compilable"))
if(blockID == None):
blockID = uuid.uuid4() #random UUID
d = self.engine.execute(block)
d.addCallback(self._add_history, block=block)
d.addCallbacks(self._add_block_id_for_result,
errback=self._add_block_id_for_failure,
callbackArgs=(blockID,),
errbackArgs=(blockID,))
d.addBoth(self.update_cell_prompt, blockID=blockID)
d.addCallbacks(self.render_result,
errback=self.render_error)
return d