Skip to content

Commit

Permalink
Merge pull request #507 from takluyver/prompt-manager
Browse files Browse the repository at this point in the history
Prompt manager refactoring: use a new `PromptManager` class responsible for handling everything to do with the prompts. The critical part is its `render` method, which assembles the necessary information, then uses the string formatting introduced in Python 2.6 to fill in the prompt template.

I've expanded the definition of 'prompts' to include the auto_rewrite prompt (`"------> "` by default). So there are now four prompts: input, continuation, output, and rewrite.

This definition of prompts does not include input/output separators. For now, I've left those as attributes of the main InteractiveShell object.
  • Loading branch information
fperez committed Nov 30, 2011
2 parents 206d352 + e9e1f20 commit 272152c
Show file tree
Hide file tree
Showing 13 changed files with 293 additions and 412 deletions.
8 changes: 4 additions & 4 deletions IPython/config/profile/pysh/ipython_config.py
Expand Up @@ -5,11 +5,11 @@
# and merge it into the current one.
load_subconfig('ipython_config.py', profile='default')

c.InteractiveShell.prompt_in1 = r'\C_LightGreen\u@\h\C_LightBlue[\C_LightCyan\Y1\C_LightBlue]\C_Green|\#> '
c.InteractiveShell.prompt_in2 = r'\C_Green|\C_LightGreen\D\C_Green> '
c.InteractiveShell.prompt_out = r'<\#> '
c.PromptManager.in_template = r'{color.LightGreen}\u@\h{color.LightBlue}[{color.LightCyan}\Y1{color.LightBlue}]{color.Green}|\#> '
c.PromptManager.in2_template = r'{color.Green}|{color.LightGreen}\D{color.Green}> '
c.PromptManager.out_template = r'<\#> '

c.InteractiveShell.prompts_pad_left = True
c.PromptManager.justify = True

c.InteractiveShell.separate_in = ''
c.InteractiveShell.separate_out = ''
Expand Down
86 changes: 11 additions & 75 deletions IPython/core/displayhook.py
Expand Up @@ -25,7 +25,6 @@
import __builtin__

from IPython.config.configurable import Configurable
from IPython.core import prompts
from IPython.utils import io
from IPython.utils.traitlets import Instance, List
from IPython.utils.warn import warn
Expand All @@ -34,32 +33,20 @@
# Main displayhook class
#-----------------------------------------------------------------------------

# TODO: The DisplayHook class should be split into two classes, one that
# manages the prompts and their synchronization and another that just does the
# displayhook logic and calls into the prompt manager.

# TODO: Move the various attributes (cache_size, colors, input_sep,
# output_sep, output_sep2, ps1, ps2, ps_out, pad_left). Some of these are also
# attributes of InteractiveShell. They should be on ONE object only and the
# other objects should ask that one object for their values.
# TODO: Move the various attributes (cache_size, [others now moved]). Some
# of these are also attributes of InteractiveShell. They should be on ONE object
# only and the other objects should ask that one object for their values.

class DisplayHook(Configurable):
"""The custom IPython displayhook to replace sys.displayhook.
This class does many things, but the basic idea is that it is a callable
that gets called anytime user code returns a value.
Currently this class does more than just the displayhook logic and that
extra logic should eventually be moved out of here.
"""

shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')

def __init__(self, shell=None, cache_size=1000,
colors='NoColor', input_sep='\n',
output_sep='\n', output_sep2='',
ps1 = None, ps2 = None, ps_out = None, pad_left=True,
config=None):
def __init__(self, shell=None, cache_size=1000, config=None):
super(DisplayHook, self).__init__(shell=shell, config=config)

cache_size_min = 3
Expand All @@ -75,36 +62,10 @@ def __init__(self, shell=None, cache_size=1000,
self.do_full_cache = 1

self.cache_size = cache_size
self.input_sep = input_sep

# we need a reference to the user-level namespace
self.shell = shell

# Set input prompt strings and colors
if cache_size == 0:
if ps1.find('%n') > -1 or ps1.find(r'\#') > -1 \
or ps1.find(r'\N') > -1:
ps1 = '>>> '
if ps2.find('%n') > -1 or ps2.find(r'\#') > -1 \
or ps2.find(r'\N') > -1:
ps2 = '... '
self.ps1_str = self._set_prompt_str(ps1,'In [\\#]: ','>>> ')
self.ps2_str = self._set_prompt_str(ps2,' .\\D.: ','... ')
self.ps_out_str = self._set_prompt_str(ps_out,'Out[\\#]: ','')

self.color_table = prompts.PromptColors
self.prompt1 = prompts.Prompt1(self,sep=input_sep,prompt=self.ps1_str,
pad_left=pad_left)
self.prompt2 = prompts.Prompt2(self,prompt=self.ps2_str,pad_left=pad_left)
self.prompt_out = prompts.PromptOut(self,sep='',prompt=self.ps_out_str,
pad_left=pad_left)
self.set_colors(colors)

# Store the last prompt string each time, we need it for aligning
# continuation and auto-rewrite prompts
self.last_prompt = ''
self.output_sep = output_sep
self.output_sep2 = output_sep2

self._,self.__,self.___ = '','',''

# these are deliberately global:
Expand All @@ -115,32 +76,6 @@ def __init__(self, shell=None, cache_size=1000,
def prompt_count(self):
return self.shell.execution_count

def _set_prompt_str(self,p_str,cache_def,no_cache_def):
if p_str is None:
if self.do_full_cache:
return cache_def
else:
return no_cache_def
else:
return p_str

def set_colors(self, colors):
"""Set the active color scheme and configure colors for the three
prompt subsystems."""

# FIXME: This modifying of the global prompts.prompt_specials needs
# to be fixed. We need to refactor all of the prompts stuff to use
# proper configuration and traits notifications.
if colors.lower()=='nocolor':
prompts.prompt_specials = prompts.prompt_specials_nocolor
else:
prompts.prompt_specials = prompts.prompt_specials_color

self.color_table.set_active_scheme(colors)
self.prompt1.set_colors()
self.prompt2.set_colors()
self.prompt_out.set_colors()

#-------------------------------------------------------------------------
# Methods used in __call__. Override these methods to modify the behavior
# of the displayhook.
Expand Down Expand Up @@ -180,8 +115,8 @@ def write_output_prompt(self):
``io.stdout``.
"""
# Use write, not print which adds an extra space.
io.stdout.write(self.output_sep)
outprompt = str(self.prompt_out)
io.stdout.write(self.shell.separate_out)
outprompt = self.shell.prompt_manager.render('out')
if self.do_full_cache:
io.stdout.write(outprompt)

Expand Down Expand Up @@ -235,11 +170,12 @@ def write_format_data(self, format_dict):
# So that multi-line strings line up with the left column of
# the screen, instead of having the output prompt mess up
# their first line.
# We use the ps_out_str template instead of the expanded prompt
# We use the prompt template instead of the expanded prompt
# because the expansion may add ANSI escapes that will interfere
# with our ability to determine whether or not we should add
# a newline.
if self.ps_out_str and not self.ps_out_str.endswith('\n'):
prompt_template = self.shell.prompt_manager.out_template
if prompt_template and not prompt_template.endswith('\n'):
# But avoid extraneous empty lines.
result_repr = '\n' + result_repr

Expand Down Expand Up @@ -286,7 +222,7 @@ def log_output(self, format_dict):

def finish_displayhook(self):
"""Finish up all displayhook activities."""
io.stdout.write(self.output_sep2)
io.stdout.write(self.shell.separate_out2)
io.stdout.flush()

def __call__(self, result=None):
Expand Down
9 changes: 1 addition & 8 deletions IPython/core/hooks.py
Expand Up @@ -51,7 +51,7 @@ def calljed(self,filename, linenum):

__all__ = ['editor', 'fix_error_editor', 'synchronize_with_editor',
'input_prefilter', 'shutdown_hook', 'late_startup_hook',
'generate_prompt', 'show_in_pager','pre_prompt_hook',
'show_in_pager','pre_prompt_hook',
'pre_run_code_hook', 'clipboard_get']

def editor(self,filename, linenum=None):
Expand Down Expand Up @@ -187,13 +187,6 @@ def late_startup_hook(self):
#print "default startup hook ok" # dbg


def generate_prompt(self, is_continuation):
""" calculate and return a string with the prompt to display """
if is_continuation:
return str(self.displayhook.prompt2)
return str(self.displayhook.prompt1)


def show_in_pager(self,s):
""" Run a string through pager """
# raising TryNext here will use the default paging functionality
Expand Down
16 changes: 4 additions & 12 deletions IPython/core/interactiveshell.py
Expand Up @@ -63,6 +63,7 @@
from IPython.core.prefilter import PrefilterManager, ESC_MAGIC
from IPython.core.profiledir import ProfileDir
from IPython.core.pylabtools import pylab_activate
from IPython.core.prompts import PromptManager
from IPython.external.Itpl import ItplNS
from IPython.utils import PyColorize
from IPython.utils import io
Expand Down Expand Up @@ -594,10 +595,8 @@ def init_io(self):
io.stderr = io.IOStream(sys.stderr)

def init_prompts(self):
# TODO: This is a pass for now because the prompts are managed inside
# the DisplayHook. Once there is a separate prompt manager, this
# will initialize that object and all prompt related information.
pass
self.prompt_manager = PromptManager(shell=self, config=self.config)
self.configurables.append(self.prompt_manager)

def init_display_formatter(self):
self.display_formatter = DisplayFormatter(config=self.config)
Expand All @@ -613,13 +612,6 @@ def init_displayhook(self):
config=self.config,
shell=self,
cache_size=self.cache_size,
input_sep = self.separate_in,
output_sep = self.separate_out,
output_sep2 = self.separate_out2,
ps1 = self.prompt_in1,
ps2 = self.prompt_in2,
ps_out = self.prompt_out,
pad_left = self.prompts_pad_left
)
self.configurables.append(self.displayhook)
# This is a context manager that installs/revmoes the displayhook at
Expand Down Expand Up @@ -2149,7 +2141,7 @@ def auto_rewrite_input(self, cmd):
after the user's input prompt. This helps the user understand that the
input line was transformed automatically by IPython.
"""
rw = self.displayhook.prompt1.auto_rewrite() + cmd
rw = self.prompt_manager.render('rewrite') + cmd

try:
# plain ascii works better w/ pyreadline, on some machines, so
Expand Down
38 changes: 18 additions & 20 deletions IPython/core/magic.py
Expand Up @@ -2554,12 +2554,12 @@ def color_switch_err(name):

# Set prompt colors
try:
shell.displayhook.set_colors(new_scheme)
shell.prompt_manager.color_scheme = new_scheme
except:
color_switch_err('prompt')
else:
shell.colors = \
shell.displayhook.color_table.active_scheme_name
shell.prompt_manager.color_scheme_table.active_scheme_name
# Set exception colors
try:
shell.InteractiveTB.set_colors(scheme = new_scheme)
Expand Down Expand Up @@ -3237,7 +3237,7 @@ def magic_doctest_mode(self,parameter_s=''):

# Shorthands
shell = self.shell
oc = shell.displayhook
pm = shell.prompt_manager
meta = shell.meta
disp_formatter = self.shell.display_formatter
ptformatter = disp_formatter.formatters['text/plain']
Expand All @@ -3252,41 +3252,38 @@ def magic_doctest_mode(self,parameter_s=''):
save_dstore('xmode',shell.InteractiveTB.mode)
save_dstore('rc_separate_out',shell.separate_out)
save_dstore('rc_separate_out2',shell.separate_out2)
save_dstore('rc_prompts_pad_left',shell.prompts_pad_left)
save_dstore('rc_prompts_pad_left',pm.justify)
save_dstore('rc_separate_in',shell.separate_in)
save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
save_dstore('prompt_templates',(pm.in_template, pm.in2_template, pm.out_template))

if mode == False:
# turn on
oc.prompt1.p_template = '>>> '
oc.prompt2.p_template = '... '
oc.prompt_out.p_template = ''
pm.in_template = '>>> '
pm.in2_template = '... '
pm.out_template = ''

# Prompt separators like plain python
oc.input_sep = oc.prompt1.sep = ''
oc.output_sep = ''
oc.output_sep2 = ''
shell.separate_in = ''
shell.separate_out = ''
shell.separate_out2 = ''

oc.prompt1.pad_left = oc.prompt2.pad_left = \
oc.prompt_out.pad_left = False
pm.justify = False

ptformatter.pprint = False
disp_formatter.plain_text_only = True

shell.magic_xmode('Plain')
else:
# turn off
oc.prompt1.p_template = shell.prompt_in1
oc.prompt2.p_template = shell.prompt_in2
oc.prompt_out.p_template = shell.prompt_out
pm.in_template, pm.in2_template, pm.out_template = dstore.prompt_templates

oc.input_sep = oc.prompt1.sep = dstore.rc_separate_in
shell.separate_in = dstore.rc_separate_in

oc.output_sep = dstore.rc_separate_out
oc.output_sep2 = dstore.rc_separate_out2
shell.separate_out = dstore.rc_separate_out
shell.separate_out2 = dstore.rc_separate_out2

oc.prompt1.pad_left = oc.prompt2.pad_left = \
oc.prompt_out.pad_left = dstore.rc_prompts_pad_left
pm.justify = dstore.rc_prompts_pad_left

ptformatter.pprint = dstore.rc_pprint
disp_formatter.plain_text_only = dstore.rc_plain_text_only
Expand Down Expand Up @@ -3603,6 +3600,7 @@ def magic_config(self, s):
PrefilterManager
AliasManager
IPCompleter
PromptManager
DisplayFormatter
To view what is configurable on a given class, just pass the class name::
Expand Down

0 comments on commit 272152c

Please sign in to comment.