##// END OF EJS Templates
Merge pull request #507 from takluyver/prompt-manager...
Fernando Perez -
r5526:272152cc merge
parent child Browse files
Show More
@@ -5,11 +5,11 b' app = c.InteractiveShellApp'
5 5 # and merge it into the current one.
6 6 load_subconfig('ipython_config.py', profile='default')
7 7
8 c.InteractiveShell.prompt_in1 = r'\C_LightGreen\u@\h\C_LightBlue[\C_LightCyan\Y1\C_LightBlue]\C_Green|\#> '
9 c.InteractiveShell.prompt_in2 = r'\C_Green|\C_LightGreen\D\C_Green> '
10 c.InteractiveShell.prompt_out = r'<\#> '
8 c.PromptManager.in_template = r'{color.LightGreen}\u@\h{color.LightBlue}[{color.LightCyan}\Y1{color.LightBlue}]{color.Green}|\#> '
9 c.PromptManager.in2_template = r'{color.Green}|{color.LightGreen}\D{color.Green}> '
10 c.PromptManager.out_template = r'<\#> '
11 11
12 c.InteractiveShell.prompts_pad_left = True
12 c.PromptManager.justify = True
13 13
14 14 c.InteractiveShell.separate_in = ''
15 15 c.InteractiveShell.separate_out = ''
@@ -25,7 +25,6 b' Authors:'
25 25 import __builtin__
26 26
27 27 from IPython.config.configurable import Configurable
28 from IPython.core import prompts
29 28 from IPython.utils import io
30 29 from IPython.utils.traitlets import Instance, List
31 30 from IPython.utils.warn import warn
@@ -34,32 +33,20 b' from IPython.utils.warn import warn'
34 33 # Main displayhook class
35 34 #-----------------------------------------------------------------------------
36 35
37 # TODO: The DisplayHook class should be split into two classes, one that
38 # manages the prompts and their synchronization and another that just does the
39 # displayhook logic and calls into the prompt manager.
40
41 # TODO: Move the various attributes (cache_size, colors, input_sep,
42 # output_sep, output_sep2, ps1, ps2, ps_out, pad_left). Some of these are also
43 # attributes of InteractiveShell. They should be on ONE object only and the
44 # other objects should ask that one object for their values.
36 # TODO: Move the various attributes (cache_size, [others now moved]). Some
37 # of these are also attributes of InteractiveShell. They should be on ONE object
38 # only and the other objects should ask that one object for their values.
45 39
46 40 class DisplayHook(Configurable):
47 41 """The custom IPython displayhook to replace sys.displayhook.
48 42
49 43 This class does many things, but the basic idea is that it is a callable
50 44 that gets called anytime user code returns a value.
51
52 Currently this class does more than just the displayhook logic and that
53 extra logic should eventually be moved out of here.
54 45 """
55 46
56 47 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
57 48
58 def __init__(self, shell=None, cache_size=1000,
59 colors='NoColor', input_sep='\n',
60 output_sep='\n', output_sep2='',
61 ps1 = None, ps2 = None, ps_out = None, pad_left=True,
62 config=None):
49 def __init__(self, shell=None, cache_size=1000, config=None):
63 50 super(DisplayHook, self).__init__(shell=shell, config=config)
64 51
65 52 cache_size_min = 3
@@ -75,36 +62,10 b' class DisplayHook(Configurable):'
75 62 self.do_full_cache = 1
76 63
77 64 self.cache_size = cache_size
78 self.input_sep = input_sep
79 65
80 66 # we need a reference to the user-level namespace
81 67 self.shell = shell
82
83 # Set input prompt strings and colors
84 if cache_size == 0:
85 if ps1.find('%n') > -1 or ps1.find(r'\#') > -1 \
86 or ps1.find(r'\N') > -1:
87 ps1 = '>>> '
88 if ps2.find('%n') > -1 or ps2.find(r'\#') > -1 \
89 or ps2.find(r'\N') > -1:
90 ps2 = '... '
91 self.ps1_str = self._set_prompt_str(ps1,'In [\\#]: ','>>> ')
92 self.ps2_str = self._set_prompt_str(ps2,' .\\D.: ','... ')
93 self.ps_out_str = self._set_prompt_str(ps_out,'Out[\\#]: ','')
94
95 self.color_table = prompts.PromptColors
96 self.prompt1 = prompts.Prompt1(self,sep=input_sep,prompt=self.ps1_str,
97 pad_left=pad_left)
98 self.prompt2 = prompts.Prompt2(self,prompt=self.ps2_str,pad_left=pad_left)
99 self.prompt_out = prompts.PromptOut(self,sep='',prompt=self.ps_out_str,
100 pad_left=pad_left)
101 self.set_colors(colors)
102
103 # Store the last prompt string each time, we need it for aligning
104 # continuation and auto-rewrite prompts
105 self.last_prompt = ''
106 self.output_sep = output_sep
107 self.output_sep2 = output_sep2
68
108 69 self._,self.__,self.___ = '','',''
109 70
110 71 # these are deliberately global:
@@ -115,32 +76,6 b' class DisplayHook(Configurable):'
115 76 def prompt_count(self):
116 77 return self.shell.execution_count
117 78
118 def _set_prompt_str(self,p_str,cache_def,no_cache_def):
119 if p_str is None:
120 if self.do_full_cache:
121 return cache_def
122 else:
123 return no_cache_def
124 else:
125 return p_str
126
127 def set_colors(self, colors):
128 """Set the active color scheme and configure colors for the three
129 prompt subsystems."""
130
131 # FIXME: This modifying of the global prompts.prompt_specials needs
132 # to be fixed. We need to refactor all of the prompts stuff to use
133 # proper configuration and traits notifications.
134 if colors.lower()=='nocolor':
135 prompts.prompt_specials = prompts.prompt_specials_nocolor
136 else:
137 prompts.prompt_specials = prompts.prompt_specials_color
138
139 self.color_table.set_active_scheme(colors)
140 self.prompt1.set_colors()
141 self.prompt2.set_colors()
142 self.prompt_out.set_colors()
143
144 79 #-------------------------------------------------------------------------
145 80 # Methods used in __call__. Override these methods to modify the behavior
146 81 # of the displayhook.
@@ -180,8 +115,8 b' class DisplayHook(Configurable):'
180 115 ``io.stdout``.
181 116 """
182 117 # Use write, not print which adds an extra space.
183 io.stdout.write(self.output_sep)
184 outprompt = str(self.prompt_out)
118 io.stdout.write(self.shell.separate_out)
119 outprompt = self.shell.prompt_manager.render('out')
185 120 if self.do_full_cache:
186 121 io.stdout.write(outprompt)
187 122
@@ -235,11 +170,12 b' class DisplayHook(Configurable):'
235 170 # So that multi-line strings line up with the left column of
236 171 # the screen, instead of having the output prompt mess up
237 172 # their first line.
238 # We use the ps_out_str template instead of the expanded prompt
173 # We use the prompt template instead of the expanded prompt
239 174 # because the expansion may add ANSI escapes that will interfere
240 175 # with our ability to determine whether or not we should add
241 176 # a newline.
242 if self.ps_out_str and not self.ps_out_str.endswith('\n'):
177 prompt_template = self.shell.prompt_manager.out_template
178 if prompt_template and not prompt_template.endswith('\n'):
243 179 # But avoid extraneous empty lines.
244 180 result_repr = '\n' + result_repr
245 181
@@ -286,7 +222,7 b' class DisplayHook(Configurable):'
286 222
287 223 def finish_displayhook(self):
288 224 """Finish up all displayhook activities."""
289 io.stdout.write(self.output_sep2)
225 io.stdout.write(self.shell.separate_out2)
290 226 io.stdout.flush()
291 227
292 228 def __call__(self, result=None):
@@ -51,7 +51,7 b' from IPython.core.error import TryNext'
51 51
52 52 __all__ = ['editor', 'fix_error_editor', 'synchronize_with_editor',
53 53 'input_prefilter', 'shutdown_hook', 'late_startup_hook',
54 'generate_prompt', 'show_in_pager','pre_prompt_hook',
54 'show_in_pager','pre_prompt_hook',
55 55 'pre_run_code_hook', 'clipboard_get']
56 56
57 57 def editor(self,filename, linenum=None):
@@ -187,13 +187,6 b' def late_startup_hook(self):'
187 187 #print "default startup hook ok" # dbg
188 188
189 189
190 def generate_prompt(self, is_continuation):
191 """ calculate and return a string with the prompt to display """
192 if is_continuation:
193 return str(self.displayhook.prompt2)
194 return str(self.displayhook.prompt1)
195
196
197 190 def show_in_pager(self,s):
198 191 """ Run a string through pager """
199 192 # raising TryNext here will use the default paging functionality
@@ -63,6 +63,7 b' from IPython.core.plugin import PluginManager'
63 63 from IPython.core.prefilter import PrefilterManager, ESC_MAGIC
64 64 from IPython.core.profiledir import ProfileDir
65 65 from IPython.core.pylabtools import pylab_activate
66 from IPython.core.prompts import PromptManager
66 67 from IPython.external.Itpl import ItplNS
67 68 from IPython.utils import PyColorize
68 69 from IPython.utils import io
@@ -594,10 +595,8 b' class InteractiveShell(SingletonConfigurable, Magic):'
594 595 io.stderr = io.IOStream(sys.stderr)
595 596
596 597 def init_prompts(self):
597 # TODO: This is a pass for now because the prompts are managed inside
598 # the DisplayHook. Once there is a separate prompt manager, this
599 # will initialize that object and all prompt related information.
600 pass
598 self.prompt_manager = PromptManager(shell=self, config=self.config)
599 self.configurables.append(self.prompt_manager)
601 600
602 601 def init_display_formatter(self):
603 602 self.display_formatter = DisplayFormatter(config=self.config)
@@ -613,13 +612,6 b' class InteractiveShell(SingletonConfigurable, Magic):'
613 612 config=self.config,
614 613 shell=self,
615 614 cache_size=self.cache_size,
616 input_sep = self.separate_in,
617 output_sep = self.separate_out,
618 output_sep2 = self.separate_out2,
619 ps1 = self.prompt_in1,
620 ps2 = self.prompt_in2,
621 ps_out = self.prompt_out,
622 pad_left = self.prompts_pad_left
623 615 )
624 616 self.configurables.append(self.displayhook)
625 617 # This is a context manager that installs/revmoes the displayhook at
@@ -2149,7 +2141,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
2149 2141 after the user's input prompt. This helps the user understand that the
2150 2142 input line was transformed automatically by IPython.
2151 2143 """
2152 rw = self.displayhook.prompt1.auto_rewrite() + cmd
2144 rw = self.prompt_manager.render('rewrite') + cmd
2153 2145
2154 2146 try:
2155 2147 # plain ascii works better w/ pyreadline, on some machines, so
@@ -2554,12 +2554,12 b' Defaulting color scheme to \'NoColor\'"""'
2554 2554
2555 2555 # Set prompt colors
2556 2556 try:
2557 shell.displayhook.set_colors(new_scheme)
2557 shell.prompt_manager.color_scheme = new_scheme
2558 2558 except:
2559 2559 color_switch_err('prompt')
2560 2560 else:
2561 2561 shell.colors = \
2562 shell.displayhook.color_table.active_scheme_name
2562 shell.prompt_manager.color_scheme_table.active_scheme_name
2563 2563 # Set exception colors
2564 2564 try:
2565 2565 shell.InteractiveTB.set_colors(scheme = new_scheme)
@@ -3237,7 +3237,7 b' Defaulting color scheme to \'NoColor\'"""'
3237 3237
3238 3238 # Shorthands
3239 3239 shell = self.shell
3240 oc = shell.displayhook
3240 pm = shell.prompt_manager
3241 3241 meta = shell.meta
3242 3242 disp_formatter = self.shell.display_formatter
3243 3243 ptformatter = disp_formatter.formatters['text/plain']
@@ -3252,23 +3252,23 b' Defaulting color scheme to \'NoColor\'"""'
3252 3252 save_dstore('xmode',shell.InteractiveTB.mode)
3253 3253 save_dstore('rc_separate_out',shell.separate_out)
3254 3254 save_dstore('rc_separate_out2',shell.separate_out2)
3255 save_dstore('rc_prompts_pad_left',shell.prompts_pad_left)
3255 save_dstore('rc_prompts_pad_left',pm.justify)
3256 3256 save_dstore('rc_separate_in',shell.separate_in)
3257 3257 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
3258 save_dstore('prompt_templates',(pm.in_template, pm.in2_template, pm.out_template))
3258 3259
3259 3260 if mode == False:
3260 3261 # turn on
3261 oc.prompt1.p_template = '>>> '
3262 oc.prompt2.p_template = '... '
3263 oc.prompt_out.p_template = ''
3262 pm.in_template = '>>> '
3263 pm.in2_template = '... '
3264 pm.out_template = ''
3264 3265
3265 3266 # Prompt separators like plain python
3266 oc.input_sep = oc.prompt1.sep = ''
3267 oc.output_sep = ''
3268 oc.output_sep2 = ''
3267 shell.separate_in = ''
3268 shell.separate_out = ''
3269 shell.separate_out2 = ''
3269 3270
3270 oc.prompt1.pad_left = oc.prompt2.pad_left = \
3271 oc.prompt_out.pad_left = False
3271 pm.justify = False
3272 3272
3273 3273 ptformatter.pprint = False
3274 3274 disp_formatter.plain_text_only = True
@@ -3276,17 +3276,14 b' Defaulting color scheme to \'NoColor\'"""'
3276 3276 shell.magic_xmode('Plain')
3277 3277 else:
3278 3278 # turn off
3279 oc.prompt1.p_template = shell.prompt_in1
3280 oc.prompt2.p_template = shell.prompt_in2
3281 oc.prompt_out.p_template = shell.prompt_out
3279 pm.in_template, pm.in2_template, pm.out_template = dstore.prompt_templates
3282 3280
3283 oc.input_sep = oc.prompt1.sep = dstore.rc_separate_in
3281 shell.separate_in = dstore.rc_separate_in
3284 3282
3285 oc.output_sep = dstore.rc_separate_out
3286 oc.output_sep2 = dstore.rc_separate_out2
3283 shell.separate_out = dstore.rc_separate_out
3284 shell.separate_out2 = dstore.rc_separate_out2
3287 3285
3288 oc.prompt1.pad_left = oc.prompt2.pad_left = \
3289 oc.prompt_out.pad_left = dstore.rc_prompts_pad_left
3286 pm.justify = dstore.rc_prompts_pad_left
3290 3287
3291 3288 ptformatter.pprint = dstore.rc_pprint
3292 3289 disp_formatter.plain_text_only = dstore.rc_plain_text_only
@@ -3603,6 +3600,7 b' Defaulting color scheme to \'NoColor\'"""'
3603 3600 PrefilterManager
3604 3601 AliasManager
3605 3602 IPCompleter
3603 PromptManager
3606 3604 DisplayFormatter
3607 3605
3608 3606 To view what is configurable on a given class, just pass the class name::
@@ -5,6 +5,7 b' Authors:'
5 5
6 6 * Fernando Perez
7 7 * Brian Granger
8 * Thomas Kluyver
8 9 """
9 10
10 11 #-----------------------------------------------------------------------------
@@ -23,20 +24,23 b' import os'
23 24 import re
24 25 import socket
25 26 import sys
27 import time
26 28
29 from IPython.config.configurable import Configurable
27 30 from IPython.core import release
28 from IPython.external.Itpl import ItplNS
29 31 from IPython.utils import coloransi
32 from IPython.utils.traitlets import (Unicode, Instance, Dict, Bool, Int)
30 33
31 34 #-----------------------------------------------------------------------------
32 35 # Color schemes for prompts
33 36 #-----------------------------------------------------------------------------
34 37
35 PromptColors = coloransi.ColorSchemeTable()
36 38 InputColors = coloransi.InputTermColors # just a shorthand
37 39 Colors = coloransi.TermColors # just a shorthand
38 40
39 PromptColors.add_scheme(coloransi.ColorScheme(
41 color_lists = dict(normal=Colors(), inp=InputColors(), nocolor=coloransi.NoColors())
42
43 PColNoColors = coloransi.ColorScheme(
40 44 'NoColor',
41 45 in_prompt = InputColors.NoColor, # Input prompt
42 46 in_number = InputColors.NoColor, # Input prompt number
@@ -47,10 +51,10 b' PromptColors.add_scheme(coloransi.ColorScheme('
47 51 out_number = Colors.NoColor, # Output prompt number
48 52
49 53 normal = Colors.NoColor # color off (usu. Colors.Normal)
50 ))
54 )
51 55
52 56 # make some schemes as instances so we can copy them for modification easily:
53 __PColLinux = coloransi.ColorScheme(
57 PColLinux = coloransi.ColorScheme(
54 58 'Linux',
55 59 in_prompt = InputColors.Green,
56 60 in_number = InputColors.LightGreen,
@@ -62,25 +66,35 b' __PColLinux = coloransi.ColorScheme('
62 66
63 67 normal = Colors.Normal
64 68 )
65 # Don't forget to enter it into the table!
66 PromptColors.add_scheme(__PColLinux)
67 69
68 70 # Slightly modified Linux for light backgrounds
69 __PColLightBG = __PColLinux.copy('LightBG')
71 PColLightBG = PColLinux.copy('LightBG')
70 72
71 __PColLightBG.colors.update(
73 PColLightBG.colors.update(
72 74 in_prompt = InputColors.Blue,
73 75 in_number = InputColors.LightBlue,
74 76 in_prompt2 = InputColors.Blue
75 77 )
76 PromptColors.add_scheme(__PColLightBG)
77
78 del Colors,InputColors
79 78
80 79 #-----------------------------------------------------------------------------
81 80 # Utilities
82 81 #-----------------------------------------------------------------------------
83 82
83 class LazyEvaluate(object):
84 """This is used for formatting strings with values that need to be updated
85 at that time, such as the current time or working directory."""
86 def __init__(self, func, *args, **kwargs):
87 self.func = func
88 self.args = args
89 self.kwargs = kwargs
90
91 def __call__(self, **kwargs):
92 self.kwargs.update(kwargs)
93 return self.func(*self.args, **self.kwargs)
94
95 def __str__(self):
96 return str(self())
97
84 98 def multiple_replace(dict, text):
85 99 """ Replace in 'text' all occurences of any key in the given
86 100 dictionary by its corresponding value. Returns the new string."""
@@ -121,51 +135,43 b' HOME = os.environ.get("HOME","//////:::::ZZZZZ,,,~~~")'
121 135 USER = os.environ.get("USER")
122 136 HOSTNAME = socket.gethostname()
123 137 HOSTNAME_SHORT = HOSTNAME.split(".")[0]
124 ROOT_SYMBOL = "$#"[os.name=='nt' or os.getuid()==0]
138 ROOT_SYMBOL = "#" if (os.name=='nt' or os.getuid()==0) else "$"
125 139
126 prompt_specials_color = {
140 prompt_abbreviations = {
127 141 # Prompt/history count
128 '%n' : '${self.col_num}' '${self.cache.prompt_count}' '${self.col_p}',
129 r'\#': '${self.col_num}' '${self.cache.prompt_count}' '${self.col_p}',
142 '%n' : '{color.number}' '{count}' '{color.prompt}',
143 r'\#': '{color.number}' '{count}' '{color.prompt}',
130 144 # Just the prompt counter number, WITHOUT any coloring wrappers, so users
131 145 # can get numbers displayed in whatever color they want.
132 r'\N': '${self.cache.prompt_count}',
146 r'\N': '{count}',
133 147
134 148 # Prompt/history count, with the actual digits replaced by dots. Used
135 149 # mainly in continuation prompts (prompt_in2)
136 #r'\D': '${"."*len(str(self.cache.prompt_count))}',
137
138 # More robust form of the above expression, that uses the __builtin__
139 # module. Note that we can NOT use __builtins__ (note the 's'), because
140 # that can either be a dict or a module, and can even mutate at runtime,
141 # depending on the context (Python makes no guarantees on it). In
142 # contrast, __builtin__ is always a module object, though it must be
143 # explicitly imported.
144 r'\D': '${"."*__builtin__.len(__builtin__.str(self.cache.prompt_count))}',
150 r'\D': '{dots}',
145 151
146 # Current working directory
147 r'\w': '${os.getcwd()}',
148 152 # Current time
149 r'\t' : '${time.strftime("%H:%M:%S")}',
153 r'\T' : '{time}',
154 # Current working directory
155 r'\w': '{cwd}',
150 156 # Basename of current working directory.
151 157 # (use os.sep to make this portable across OSes)
152 r'\W' : '${os.getcwd().split("%s")[-1]}' % os.sep,
158 r'\W' : '{cwd_last}',
153 159 # These X<N> are an extension to the normal bash prompts. They return
154 160 # N terms of the path, after replacing $HOME with '~'
155 r'\X0': '${os.getcwd().replace("%s","~")}' % HOME,
156 r'\X1': '${self.cwd_filt(1)}',
157 r'\X2': '${self.cwd_filt(2)}',
158 r'\X3': '${self.cwd_filt(3)}',
159 r'\X4': '${self.cwd_filt(4)}',
160 r'\X5': '${self.cwd_filt(5)}',
161 r'\X0': '{cwd_x[0]}',
162 r'\X1': '{cwd_x[1]}',
163 r'\X2': '{cwd_x[2]}',
164 r'\X3': '{cwd_x[3]}',
165 r'\X4': '{cwd_x[4]}',
166 r'\X5': '{cwd_x[5]}',
161 167 # Y<N> are similar to X<N>, but they show '~' if it's the directory
162 168 # N+1 in the list. Somewhat like %cN in tcsh.
163 r'\Y0': '${self.cwd_filt2(0)}',
164 r'\Y1': '${self.cwd_filt2(1)}',
165 r'\Y2': '${self.cwd_filt2(2)}',
166 r'\Y3': '${self.cwd_filt2(3)}',
167 r'\Y4': '${self.cwd_filt2(4)}',
168 r'\Y5': '${self.cwd_filt2(5)}',
169 r'\Y0': '{cwd_y[0]}',
170 r'\Y1': '{cwd_y[1]}',
171 r'\Y2': '{cwd_y[2]}',
172 r'\Y3': '{cwd_y[3]}',
173 r'\Y4': '{cwd_y[4]}',
174 r'\Y5': '{cwd_y[5]}',
169 175 # Hostname up to first .
170 176 r'\h': HOSTNAME_SHORT,
171 177 # Full hostname
@@ -184,253 +190,189 b' prompt_specials_color = {'
184 190 r'\$': ROOT_SYMBOL,
185 191 }
186 192
187 # A copy of the prompt_specials dictionary but with all color escapes removed,
188 # so we can correctly compute the prompt length for the auto_rewrite method.
189 prompt_specials_nocolor = prompt_specials_color.copy()
190 prompt_specials_nocolor['%n'] = '${self.cache.prompt_count}'
191 prompt_specials_nocolor[r'\#'] = '${self.cache.prompt_count}'
192
193 # Add in all the InputTermColors color escapes as valid prompt characters.
194 # They all get added as \\C_COLORNAME, so that we don't have any conflicts
195 # with a color name which may begin with a letter used by any other of the
196 # allowed specials. This of course means that \\C will never be allowed for
197 # anything else.
198 input_colors = coloransi.InputTermColors
199 for _color in dir(input_colors):
200 if _color[0] != '_':
201 c_name = r'\C_'+_color
202 prompt_specials_color[c_name] = getattr(input_colors,_color)
203 prompt_specials_nocolor[c_name] = ''
204
205 # we default to no color for safety. Note that prompt_specials is a global
206 # variable used by all prompt objects.
207 prompt_specials = prompt_specials_nocolor
208
209 193 #-----------------------------------------------------------------------------
210 194 # More utilities
211 195 #-----------------------------------------------------------------------------
212 196
213 def str_safe(arg):
214 """Convert to a string, without ever raising an exception.
215
216 If str(arg) fails, <ERROR: ... > is returned, where ... is the exception
217 error message."""
218
219 try:
220 out = str(arg)
221 except UnicodeError:
222 try:
223 out = arg.encode('utf_8','replace')
224 except Exception,msg:
225 # let's keep this little duplication here, so that the most common
226 # case doesn't suffer from a double try wrapping.
227 out = '<ERROR: %s>' % msg
228 except Exception,msg:
229 out = '<ERROR: %s>' % msg
230 #raise # dbg
231 return out
197 def cwd_filt(depth):
198 """Return the last depth elements of the current working directory.
232 199
233 #-----------------------------------------------------------------------------
234 # Prompt classes
235 #-----------------------------------------------------------------------------
200 $HOME is always replaced with '~'.
201 If depth==0, the full path is returned."""
236 202
237 class BasePrompt(object):
238 """Interactive prompt similar to Mathematica's."""
239
240 def _get_p_template(self):
241 return self._p_template
242
243 def _set_p_template(self,val):
244 self._p_template = val
245 self.set_p_str()
246
247 p_template = property(_get_p_template,_set_p_template,
248 doc='Template for prompt string creation')
249
250 def __init__(self, cache, sep, prompt, pad_left=False):
251
252 # Hack: we access information about the primary prompt through the
253 # cache argument. We need this, because we want the secondary prompt
254 # to be aligned with the primary one. Color table info is also shared
255 # by all prompt classes through the cache. Nice OO spaghetti code!
256 self.cache = cache
257 self.sep = sep
258
259 # regexp to count the number of spaces at the end of a prompt
260 # expression, useful for prompt auto-rewriting
261 self.rspace = re.compile(r'(\s*)$')
262 # Flag to left-pad prompt strings to match the length of the primary
263 # prompt
264 self.pad_left = pad_left
265
266 # Set template to create each actual prompt (where numbers change).
267 # Use a property
268 self.p_template = prompt
269 self.set_p_str()
270
271 def set_p_str(self):
272 """ Set the interpolating prompt strings.
273
274 This must be called every time the color settings change, because the
275 prompt_specials global may have changed."""
276
277 import os,time # needed in locals for prompt string handling
278 loc = locals()
279 try:
280 self.p_str = ItplNS('%s%s%s' %
281 ('${self.sep}${self.col_p}',
282 multiple_replace(prompt_specials, self.p_template),
283 '${self.col_norm}'),self.cache.shell.user_ns,loc)
284
285 self.p_str_nocolor = ItplNS(multiple_replace(prompt_specials_nocolor,
286 self.p_template),
287 self.cache.shell.user_ns,loc)
288 except:
289 print "Illegal prompt template (check $ usage!):",self.p_template
290 self.p_str = self.p_template
291 self.p_str_nocolor = self.p_template
292
293 def write(self, msg):
294 sys.stdout.write(msg)
295 return ''
203 cwd = os.getcwd().replace(HOME,"~")
204 out = os.sep.join(cwd.split(os.sep)[-depth:])
205 return out or os.sep
296 206
297 def __str__(self):
298 """Return a string form of the prompt.
299
300 This for is useful for continuation and output prompts, since it is
301 left-padded to match lengths with the primary one (if the
302 self.pad_left attribute is set)."""
303
304 out_str = str_safe(self.p_str)
305 if self.pad_left:
306 # We must find the amount of padding required to match lengths,
307 # taking the color escapes (which are invisible on-screen) into
308 # account.
309 esc_pad = len(out_str) - len(str_safe(self.p_str_nocolor))
310 format = '%%%ss' % (len(str(self.cache.last_prompt))+esc_pad)
311 return format % out_str
312 else:
313 return out_str
207 def cwd_filt2(depth):
208 """Return the last depth elements of the current working directory.
314 209
315 # these path filters are put in as methods so that we can control the
316 # namespace where the prompt strings get evaluated
317 def cwd_filt(self, depth):
318 """Return the last depth elements of the current working directory.
210 $HOME is always replaced with '~'.
211 If depth==0, the full path is returned."""
319 212
320 $HOME is always replaced with '~'.
321 If depth==0, the full path is returned."""
213 full_cwd = os.getcwd()
214 cwd = full_cwd.replace(HOME,"~").split(os.sep)
215 if '~' in cwd and len(cwd) == depth+1:
216 depth += 1
217 drivepart = ''
218 if sys.platform == 'win32' and len(cwd) > depth:
219 drivepart = os.path.splitdrive(full_cwd)[0]
220 out = drivepart + '/'.join(cwd[-depth:])
322 221
323 cwd = os.getcwd().replace(HOME,"~")
324 out = os.sep.join(cwd.split(os.sep)[-depth:])
325 if out:
326 return out
327 else:
328 return os.sep
222 return out or os.sep
329 223
330 def cwd_filt2(self, depth):
331 """Return the last depth elements of the current working directory.
332
333 $HOME is always replaced with '~'.
334 If depth==0, the full path is returned."""
335
336 full_cwd = os.getcwd()
337 cwd = full_cwd.replace(HOME,"~").split(os.sep)
338 if '~' in cwd and len(cwd) == depth+1:
339 depth += 1
340 drivepart = ''
341 if sys.platform == 'win32' and len(cwd) > depth:
342 drivepart = os.path.splitdrive(full_cwd)[0]
343 out = drivepart + '/'.join(cwd[-depth:])
224 #-----------------------------------------------------------------------------
225 # Prompt classes
226 #-----------------------------------------------------------------------------
344 227
345 if out:
346 return out
228 lazily_evaluate = {'time': LazyEvaluate(time.strftime, "%H:%M:%S"),
229 'cwd': LazyEvaluate(os.getcwd),
230 'cwd_last': LazyEvaluate(lambda: os.getcwd().split(os.sep)[-1]),
231 'cwd_x': [LazyEvaluate(lambda: os.getcwd().replace("%s","~"))] +\
232 [LazyEvaluate(cwd_filt, x) for x in range(1,6)],
233 'cwd_y': [LazyEvaluate(cwd_filt2, x) for x in range(6)]
234 }
235
236
237 class PromptManager(Configurable):
238 """This is the primary interface for producing IPython's prompts."""
239 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
240
241 color_scheme_table = Instance(coloransi.ColorSchemeTable)
242 color_scheme = Unicode('Linux', config=True)
243 def _color_scheme_changed(self, name, new_value):
244 self.color_scheme_table.set_active_scheme(new_value)
245 for pname in ['in', 'in2', 'out', 'rewrite']:
246 # We need to recalculate the number of invisible characters
247 self.update_prompt(pname)
248
249 lazy_evaluate_fields = Dict(help="""
250 This maps field names used in the prompt templates to functions which
251 will be called when the prompt is rendered. This allows us to include
252 things like the current time in the prompts. Functions are only called
253 if they are used in the prompt.
254 """)
255 def _lazy_evaluate_fields_default(self): return lazily_evaluate.copy()
256
257 in_template = Unicode('In [\\#]: ', config=True)
258 in2_template = Unicode(' .\\D.: ', config=True)
259 out_template = Unicode('Out[\\#]: ', config=True)
260 rewrite_template = Unicode("------> ", config=True)
261
262 justify = Bool(True, config=True, help="""
263 If True (default), each prompt will be right-aligned with the
264 preceding one.
265 """)
266
267 # We actually store the expanded templates here:
268 templates = Dict()
269
270 # The number of characters in the last prompt rendered, not including
271 # colour characters.
272 width = Int()
273
274 # The number of characters in each prompt which don't contribute to width
275 invisible_chars = Dict()
276 def _invisible_chars_default(self):
277 return {'in': 0, 'in2': 0, 'out': 0, 'rewrite': 0}
278
279 def __init__(self, shell, config=None):
280 super(PromptManager, self).__init__(shell=shell, config=config)
281
282 # Prepare colour scheme table
283 self.color_scheme_table = coloransi.ColorSchemeTable([PColNoColors,
284 PColLinux, PColLightBG], self.color_scheme)
285
286 # Prepare templates
287 self.update_prompt('in', self.in_template)
288 self.update_prompt('in2', self.in2_template)
289 self.update_prompt('out', self.out_template)
290 self.update_prompt('rewrite', self.rewrite_template)
291 self.on_trait_change(self._update_prompt_trait, ['in_template',
292 'in2_template', 'out_template', 'rewrite_template'])
293
294 def update_prompt(self, name, new_template=None):
295 """This is called when a prompt template is updated. It processes
296 abbreviations used in the prompt template (like \#) and calculates how
297 many invisible characters (ANSI colour escapes) the resulting prompt
298 contains.
299
300 It is also called for each prompt on changing the colour scheme. In both
301 cases, traitlets should take care of calling this automatically.
302 """
303 if new_template is not None:
304 self.templates[name] = multiple_replace(prompt_abbreviations, new_template)
305 invis_chars = len(self.render(name, color=True, just=False)) - \
306 len(self.render(name, color=False, just=False))
307 self.invisible_chars[name] = invis_chars
308
309 def _update_prompt_trait(self, traitname, new_template):
310 name = traitname[:-9] # Cut off '_template'
311 self.update_prompt(name, new_template)
312
313 def render(self, name, color=True, just=None, **kwargs):
314 """
315 Render the selected prompt.
316
317 Parameters
318 ----------
319 name : str
320 Which prompt to render. One of 'in', 'in2', 'out', 'rewrite'
321 color : bool
322 If True (default), include ANSI escape sequences for a coloured prompt.
323 just : bool
324 If True, justify the prompt to the width of the last prompt. The
325 default is stored in self.justify.
326 **kwargs :
327 Additional arguments will be passed to the string formatting operation,
328 so they can override the values that would otherwise fill in the
329 template.
330
331 Returns
332 -------
333 A string containing the rendered prompt.
334 """
335 if color:
336 scheme = self.color_scheme_table.active_colors
337 if name=='out':
338 colors = color_lists['normal']
339 colors.number, colors.prompt, colors.normal = \
340 scheme.out_number, scheme.out_prompt, scheme.normal
341 elif name=='rewrite':
342 colors = color_lists['normal']
343 # We need a non-input version of these escapes
344 colors.number = scheme.in_number.replace("\001","").replace("\002","")
345 colors.prompt = scheme.in_prompt.replace("\001","").replace("\002","")
346 colors.normal = scheme.normal
347 else:
348 colors = color_lists['inp']
349 colors.number, colors.prompt, colors.normal = \
350 scheme.in_number, scheme.in_prompt, scheme.in_normal
351 if name=='in2':
352 colors.prompt = scheme.in_prompt2
347 353 else:
348 return os.sep
349
350 def __nonzero__(self):
351 """Implement boolean behavior.
352
353 Checks whether the p_str attribute is non-empty"""
354
355 return bool(self.p_template)
356
357
358 class Prompt1(BasePrompt):
359 """Input interactive prompt similar to Mathematica's."""
360
361 def __init__(self, cache, sep='\n', prompt='In [\\#]: ', pad_left=True):
362 BasePrompt.__init__(self, cache, sep, prompt, pad_left)
363
364 def set_colors(self):
365 self.set_p_str()
366 Colors = self.cache.color_table.active_colors # shorthand
367 self.col_p = Colors.in_prompt
368 self.col_num = Colors.in_number
369 self.col_norm = Colors.in_normal
370 # We need a non-input version of these escapes for the '--->'
371 # auto-call prompts used in the auto_rewrite() method.
372 self.col_p_ni = self.col_p.replace('\001','').replace('\002','')
373 self.col_norm_ni = Colors.normal
374
375 def __str__(self):
376 self.cache.last_prompt = str_safe(self.p_str_nocolor).split('\n')[-1]
377 return str_safe(self.p_str)
378
379 def auto_rewrite(self):
380 """Return a string of the form '--->' which lines up with the previous
381 input string. Useful for systems which re-write the user input when
382 handling automatically special syntaxes."""
383
384 curr = str(self.cache.last_prompt)
385 nrspaces = len(self.rspace.search(curr).group())
386 return '%s%s>%s%s' % (self.col_p_ni,'-'*(len(curr)-nrspaces-1),
387 ' '*nrspaces,self.col_norm_ni)
388
389
390 class PromptOut(BasePrompt):
391 """Output interactive prompt similar to Mathematica's."""
392
393 def __init__(self, cache, sep='', prompt='Out[\\#]: ', pad_left=True):
394 BasePrompt.__init__(self, cache, sep, prompt, pad_left)
395 if not self.p_template:
396 self.__str__ = lambda: ''
397
398 def set_colors(self):
399 self.set_p_str()
400 Colors = self.cache.color_table.active_colors # shorthand
401 self.col_p = Colors.out_prompt
402 self.col_num = Colors.out_number
403 self.col_norm = Colors.normal
404
405
406 class Prompt2(BasePrompt):
407 """Interactive continuation prompt."""
408
409 def __init__(self, cache, prompt=' .\\D.: ', pad_left=True):
410 self.cache = cache
411 self.p_template = prompt
412 self.pad_left = pad_left
413 self.set_p_str()
414
415 def set_p_str(self):
416 import os,time # needed in locals for prompt string handling
417 loc = locals()
418 self.p_str = ItplNS('%s%s%s' %
419 ('${self.col_p2}',
420 multiple_replace(prompt_specials, self.p_template),
421 '$self.col_norm'),
422 self.cache.shell.user_ns,loc)
423 self.p_str_nocolor = ItplNS(multiple_replace(prompt_specials_nocolor,
424 self.p_template),
425 self.cache.shell.user_ns,loc)
426
427 def set_colors(self):
428 self.set_p_str()
429 Colors = self.cache.color_table.active_colors
430 self.col_p2 = Colors.in_prompt2
431 self.col_norm = Colors.in_normal
432 # FIXME (2004-06-16) HACK: prevent crashes for users who haven't
433 # updated their prompt_in2 definitions. Remove eventually.
434 self.col_p = Colors.out_prompt
435 self.col_num = Colors.out_number
354 # No color
355 colors = color_lists['nocolor']
356 colors.number, colors.prompt, colors.normal = '', '', ''
357
358 count = self.shell.execution_count # Shorthand
359 # Build the dictionary to be passed to string formatting
360 fmtargs = dict(color=colors, count=count,
361 dots="."*len(str(count)) )
362 fmtargs.update(self.lazy_evaluate_fields)
363 fmtargs.update(kwargs)
364
365 # Prepare the prompt
366 prompt = colors.prompt + self.templates[name] + colors.normal
367
368 # Fill in required fields
369 res = prompt.format(**fmtargs)
370
371 # Handle justification of prompt
372 invis_chars = self.invisible_chars[name] if color else 0
373 just = self.justify if (just is None) else just
374 if just:
375 res = res.rjust(self.width + invis_chars)
376 self.width = len(res) - invis_chars
377 return res
436 378
@@ -148,7 +148,7 b' class TestMagicRunPass(tt.TempFileMixin):'
148 148 """Test that prompts correctly generate after %run"""
149 149 self.run_tmpfile()
150 150 _ip = get_ipython()
151 p2 = str(_ip.displayhook.prompt2).strip()
151 p2 = _ip.prompt_manager.render('in2').strip()
152 152 nt.assert_equals(p2[:3], '...')
153 153
154 154 def test_run_profile( self ):
@@ -356,7 +356,7 b' class TerminalInteractiveShell(InteractiveShell):'
356 356 self.hooks.pre_prompt_hook()
357 357 if more:
358 358 try:
359 prompt = self.hooks.generate_prompt(True)
359 prompt = self.prompt_manager.render('in2')
360 360 except:
361 361 self.showtraceback()
362 362 if self.autoindent:
@@ -364,7 +364,7 b' class TerminalInteractiveShell(InteractiveShell):'
364 364
365 365 else:
366 366 try:
367 prompt = self.hooks.generate_prompt(False)
367 prompt = self.separate_in + self.prompt_manager.render('in')
368 368 except:
369 369 self.showtraceback()
370 370 try:
@@ -15,12 +15,7 b' import os'
15 15
16 16 from IPython.utils.ipstruct import Struct
17 17
18 def make_color_table(in_class):
19 """Build a set of color attributes in a class.
20
21 Helper function for building the *TermColors classes."""
22
23 color_templates = (
18 color_templates = (
24 19 # Dark colors
25 20 ("Black" , "0;30"),
26 21 ("Red" , "0;31"),
@@ -50,6 +45,11 b' def make_color_table(in_class):'
50 45 ("BlinkLightGray", "5;37"),
51 46 )
52 47
48 def make_color_table(in_class):
49 """Build a set of color attributes in a class.
50
51 Helper function for building the *TermColors classes."""
52
53 53 for name,value in color_templates:
54 54 setattr(in_class,name,in_class._base % value)
55 55
@@ -98,6 +98,14 b' class InputTermColors:'
98 98 # Build the actual color table as a set of class attributes:
99 99 make_color_table(InputTermColors)
100 100
101 class NoColors:
102 """This defines all the same names as the colour classes, but maps them to
103 empty strings, so it can easily be substituted to turn off colours."""
104 NoColor = ''
105
106 for name, value in color_templates:
107 setattr(NoColors, name, '')
108
101 109 class ColorScheme:
102 110 """Generic color scheme class. Just a name and a Struct."""
103 111 def __init__(self,__scheme_name_,colordict=None,**colormap):
@@ -75,7 +75,6 b' def eval_formatter_slicing_check(f):'
75 75 nt.assert_equals(s, ns['stuff'][::2])
76 76
77 77 nt.assert_raises(SyntaxError, f.format, "{n:x}", **ns)
78
79 78
80 79 def eval_formatter_no_slicing_check(f):
81 80 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
@@ -83,6 +82,9 b' def eval_formatter_no_slicing_check(f):'
83 82 s = f.format('{n:x} {pi**2:+f}', **ns)
84 83 nt.assert_equals(s, "c +9.869604")
85 84
85 s = f.format('{stuff[slice(1,4)]}', **ns)
86 nt.assert_equals(s, 'ell')
87
86 88 nt.assert_raises(SyntaxError, f.format, "{a[:]}")
87 89
88 90 def test_eval_formatter():
@@ -133,7 +133,7 b' class ZMQInteractiveShell(InteractiveShell):'
133 133 FIXME: this payload is currently not correctly processed by the
134 134 frontend.
135 135 """
136 new = self.displayhook.prompt1.auto_rewrite() + cmd
136 new = self.prompt_manager.render('rewrite') + cmd
137 137 payload = dict(
138 138 source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
139 139 transformed_input=new,
@@ -124,11 +124,12 b' attributes::'
124 124 c.InteractiveShell.confirm_exit = False
125 125 c.InteractiveShell.deep_reload = True
126 126 c.InteractiveShell.editor = 'nano'
127 c.InteractiveShell.prompt_in1 = 'In [\#]: '
128 c.InteractiveShell.prompt_in2 = ' .\D.: '
129 c.InteractiveShell.prompt_out = 'Out[\#]: '
130 c.InteractiveShell.prompts_pad_left = True
131 127 c.InteractiveShell.xmode = 'Context'
128
129 c.PromptManager.in_template = 'In [\#]: '
130 c.PromptManager.in2_template = ' .\D.: '
131 c.PromptManager.out_template = 'Out[\#]: '
132 c.PromptManager.justify = True
132 133
133 134 c.PrefilterManager.multi_line_specials = True
134 135
@@ -117,6 +117,15 b' Backwards incompatible changes'
117 117 traits, rather than several ip/port pair ``_addr`` traits. This better matches the
118 118 rest of the code, where the ip cannot not be set separately for each channel.
119 119
120 * Custom prompts are now configured using a new class,
121 :class:`~IPython.core.prompts.PromptManager`, which has traits for :attr:`in_template`,
122 :attr:`in2_template` (the ``...:`` continuation prompt), :attr:`out_template`
123 and :attr:`rewrite_template`. This uses Python's string formatting system, so
124 you can use ``{time}`` and ``{cwd}``, although we have preserved the abbreviations
125 from previous versions, e.g. ``\#`` (prompt number) and ``\w`` (working
126 directory). For the list of available fields, refer to the source of
127 :file:`IPython/core/prompts.py`.
128
120 129 * The class inheritance of the Launchers in :mod:`IPython.parallel.apps.launcher`
121 130 used by ipcluster has changed, so that trait names are more consistent across
122 131 batch systems. This may require a few renames in your config files, if you
General Comments 0
You need to be logged in to leave comments. Login now