##// END OF EJS Templates
Refactor prompt handling into new prompt manager.
Thomas Kluyver -
Show More
@@ -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):
@@ -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,7 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)
601 599
602 600 def init_display_formatter(self):
603 601 self.display_formatter = DisplayFormatter(config=self.config)
@@ -613,13 +611,6 b' class InteractiveShell(SingletonConfigurable, Magic):'
613 611 config=self.config,
614 612 shell=self,
615 613 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 614 )
624 615 self.configurables.append(self.displayhook)
625 616 # This is a context manager that installs/revmoes the displayhook at
@@ -2149,7 +2140,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
2149 2140 after the user's input prompt. This helps the user understand that the
2150 2141 input line was transformed automatically by IPython.
2151 2142 """
2152 rw = self.displayhook.prompt1.auto_rewrite() + cmd
2143 rw = self.prompt_manager.render('rewrite') + cmd
2153 2144
2154 2145 try:
2155 2146 # 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
@@ -23,20 +23,23 b' import os'
23 23 import re
24 24 import socket
25 25 import sys
26 import time
26 27
28 from IPython.config.configurable import Configurable
27 29 from IPython.core import release
28 from IPython.external.Itpl import ItplNS
29 30 from IPython.utils import coloransi
31 from IPython.utils.traitlets import (Unicode, Instance, Dict, Bool, Int)
30 32
31 33 #-----------------------------------------------------------------------------
32 34 # Color schemes for prompts
33 35 #-----------------------------------------------------------------------------
34 36
35 PromptColors = coloransi.ColorSchemeTable()
36 37 InputColors = coloransi.InputTermColors # just a shorthand
37 38 Colors = coloransi.TermColors # just a shorthand
38 39
39 PromptColors.add_scheme(coloransi.ColorScheme(
40 color_lists = dict(normal=Colors(), inp=InputColors(), nocolor=coloransi.NoColors())
41
42 PColNoColors = coloransi.ColorScheme(
40 43 'NoColor',
41 44 in_prompt = InputColors.NoColor, # Input prompt
42 45 in_number = InputColors.NoColor, # Input prompt number
@@ -47,10 +50,10 b' PromptColors.add_scheme(coloransi.ColorScheme('
47 50 out_number = Colors.NoColor, # Output prompt number
48 51
49 52 normal = Colors.NoColor # color off (usu. Colors.Normal)
50 ))
53 )
51 54
52 55 # make some schemes as instances so we can copy them for modification easily:
53 __PColLinux = coloransi.ColorScheme(
56 PColLinux = coloransi.ColorScheme(
54 57 'Linux',
55 58 in_prompt = InputColors.Green,
56 59 in_number = InputColors.LightGreen,
@@ -62,25 +65,35 b' __PColLinux = coloransi.ColorScheme('
62 65
63 66 normal = Colors.Normal
64 67 )
65 # Don't forget to enter it into the table!
66 PromptColors.add_scheme(__PColLinux)
67 68
68 69 # Slightly modified Linux for light backgrounds
69 __PColLightBG = __PColLinux.copy('LightBG')
70 PColLightBG = PColLinux.copy('LightBG')
70 71
71 __PColLightBG.colors.update(
72 PColLightBG.colors.update(
72 73 in_prompt = InputColors.Blue,
73 74 in_number = InputColors.LightBlue,
74 75 in_prompt2 = InputColors.Blue
75 76 )
76 PromptColors.add_scheme(__PColLightBG)
77
78 del Colors,InputColors
79 77
80 78 #-----------------------------------------------------------------------------
81 79 # Utilities
82 80 #-----------------------------------------------------------------------------
83 81
82 class LazyEvaluate(object):
83 """This is used for formatting strings with values that need to be updated
84 at that time, such as the current time or line number."""
85 def __init__(self, func, *args, **kwargs):
86 self.func = func
87 self.args = args
88 self.kwargs = kwargs
89
90 def __call__(self, **kwargs):
91 self.kwargs.update(kwargs)
92 return self.func(*self.args, **self.kwargs)
93
94 def __str__(self):
95 return str(self())
96
84 97 def multiple_replace(dict, text):
85 98 """ Replace in 'text' all occurences of any key in the given
86 99 dictionary by its corresponding value. Returns the new string."""
@@ -121,51 +134,43 b' HOME = os.environ.get("HOME","//////:::::ZZZZZ,,,~~~")'
121 134 USER = os.environ.get("USER")
122 135 HOSTNAME = socket.gethostname()
123 136 HOSTNAME_SHORT = HOSTNAME.split(".")[0]
124 ROOT_SYMBOL = "$#"[os.name=='nt' or os.getuid()==0]
137 ROOT_SYMBOL = "#" if (os.name=='nt' or os.getuid()==0) else "$"
125 138
126 prompt_specials_color = {
139 prompt_abbreviations = {
127 140 # 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}',
141 '%n' : '{color.number}' '{count}' '{color.prompt}',
142 r'\#': '{color.number}' '{count}' '{color.prompt}',
130 143 # Just the prompt counter number, WITHOUT any coloring wrappers, so users
131 144 # can get numbers displayed in whatever color they want.
132 r'\N': '${self.cache.prompt_count}',
145 r'\N': '{count}',
133 146
134 147 # Prompt/history count, with the actual digits replaced by dots. Used
135 148 # mainly in continuation prompts (prompt_in2)
136 #r'\D': '${"."*len(str(self.cache.prompt_count))}',
149 r'\D': '{dots}',
137 150
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))}',
145
146 # Current working directory
147 r'\w': '${os.getcwd()}',
148 151 # Current time
149 r'\t' : '${time.strftime("%H:%M:%S")}',
152 r'\t' : '{time}',
153 # Current working directory
154 r'\w': '{cwd}',
150 155 # Basename of current working directory.
151 156 # (use os.sep to make this portable across OSes)
152 r'\W' : '${os.getcwd().split("%s")[-1]}' % os.sep,
157 r'\W' : '{cwd_last}',
153 158 # These X<N> are an extension to the normal bash prompts. They return
154 159 # 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)}',
160 r'\X0': '{cwd_x[0])}',
161 r'\X1': '{cwd_x[1])}',
162 r'\X2': '{cwd_x[2])}',
163 r'\X3': '{cwd_x[3])}',
164 r'\X4': '{cwd_x[4])}',
165 r'\X5': '{cwd_x[5])}',
161 166 # Y<N> are similar to X<N>, but they show '~' if it's the directory
162 167 # 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)}',
168 r'\Y0': '{cwd_y[0])}',
169 r'\Y1': '{cwd_y[1])}',
170 r'\Y2': '{cwd_y[2])}',
171 r'\Y3': '{cwd_y[3])}',
172 r'\Y4': '{cwd_y[4])}',
173 r'\Y5': '{cwd_y[5])}',
169 174 # Hostname up to first .
170 175 r'\h': HOSTNAME_SHORT,
171 176 # Full hostname
@@ -184,28 +189,6 b' prompt_specials_color = {'
184 189 r'\$': ROOT_SYMBOL,
185 190 }
186 191
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 192 #-----------------------------------------------------------------------------
210 193 # More utilities
211 194 #-----------------------------------------------------------------------------
@@ -230,207 +213,166 b' def str_safe(arg):'
230 213 #raise # dbg
231 214 return out
232 215
233 #-----------------------------------------------------------------------------
234 # Prompt classes
235 #-----------------------------------------------------------------------------
236
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()
216 def cwd_filt(self, depth):
217 """Return the last depth elements of the current working directory.
246 218
247 p_template = property(_get_p_template,_set_p_template,
248 doc='Template for prompt string creation')
219 $HOME is always replaced with '~'.
220 If depth==0, the full path is returned."""
249 221
250 def __init__(self, cache, sep, prompt, pad_left=False):
222 cwd = os.getcwd().replace(HOME,"~")
223 out = os.sep.join(cwd.split(os.sep)[-depth:])
224 return out or os.sep
251 225
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
226 def cwd_filt2(self, depth):
227 """Return the last depth elements of the current working directory.
258 228
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
229 $HOME is always replaced with '~'.
230 If depth==0, the full path is returned."""
265 231
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()
232 full_cwd = os.getcwd()
233 cwd = full_cwd.replace(HOME,"~").split(os.sep)
234 if '~' in cwd and len(cwd) == depth+1:
235 depth += 1
236 drivepart = ''
237 if sys.platform == 'win32' and len(cwd) > depth:
238 drivepart = os.path.splitdrive(full_cwd)[0]
239 out = drivepart + '/'.join(cwd[-depth:])
270 240
271 def set_p_str(self):
272 """ Set the interpolating prompt strings.
241 return out or os.sep
273 242
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 ''
296
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
314
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.
319
320 $HOME is always replaced with '~'.
321 If depth==0, the full path is returned."""
322
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
329
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:])
243 #-----------------------------------------------------------------------------
244 # Prompt classes
245 #-----------------------------------------------------------------------------
344 246
345 if out:
346 return out
247 lazily_evaluate = {'time': LazyEvaluate(time.strftime, "%H:%M:%S"),
248 'cwd': LazyEvaluate(os.getcwd),
249 'cwd_last': LazyEvaluate(lambda: os.getcwd().split(os.sep)[-1]),
250 'cwd_x': [LazyEvaluate(lambda: os.getcwd().replace("%s","~"))] +\
251 [LazyEvaluate(cwd_filt, x) for x in range(1,6)],
252 'cwd_y': [LazyEvaluate(cwd_filt2, x) for x in range(6)]
253 }
254
255
256 class PromptManager(Configurable):
257 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
258
259 color_scheme_table = Instance(coloransi.ColorSchemeTable)
260 color_scheme = Unicode('Linux')
261 def _color_scheme_changed(self, name, new_value):
262 self.color_scheme_table.set_active_scheme(new_value)
263 for pname in ['in', 'in2', 'out', 'rewrite']:
264 # We need to recalculate the number of invisible characters
265 self.update_prompt(pname)
266
267 # These fields can be referenced in prompt templates, and are evaluated
268 # when the prompt is generated - for things like timestamps. They are only
269 # evaluated if a prompt uses them.
270 lazy_evaluate_fields = Dict()
271 def _lazy_evaluate_fields_default(self): return lazily_evaluate.copy()
272
273 in_template = Unicode('In [\\#]: ', config=True)
274 in2_template = Unicode(' .\\D.: ', config=True)
275 out_template = Unicode('Out[\\#]: ', config=True)
276 rewrite_template = Unicode("------> ", config=True)
277
278 # Justify prompts by default?
279 justify = Bool(True)
280
281 # We actually store the expanded templates here:
282 templates = Dict()
283
284 # The number of characters in the last prompt rendered, not including
285 # colour characters.
286 width = Int()
287
288 # The number of characters in each prompt which don't contribute to width
289 invisible_chars = Dict()
290 def _invisible_chars_default(self):
291 return {'in': 0, 'in2': 0, 'out': 0, 'rewrite': 0}
292
293 def __init__(self, shell, config=None):
294 super(PromptManager, self).__init__(shell=shell, config=config)
295
296 # Prepare colour scheme table
297 self.color_scheme_table = coloransi.ColorSchemeTable([PColNoColors,
298 PColLinux, PColLightBG], self.color_scheme)
299
300 # Prepare templates
301 self.update_prompt('in', self.in_template)
302 self.update_prompt('in2', self.in2_template)
303 self.update_prompt('out', self.out_template)
304 self.update_prompt('rewrite', self.rewrite_template)
305 self.on_trait_change(self._update_prompt_trait, ['in_template',
306 'in2_template', 'out_template', 'rewrite_template'])
307
308 def update_prompt(self, name, new_template=None):
309 if new_template is not None:
310 self.templates[name] = multiple_replace(prompt_abbreviations, new_template)
311 invis_chars = len(self.render(name, color=True, just=False)) - \
312 len(self.render(name, color=False, just=False))
313 self.invisible_chars[name] = invis_chars
314
315 def _update_prompt_trait(self, traitname, new_template):
316 name = traitname[:-9] # Cut off '_template'
317 self.update_prompt(name, new_template)
318
319 def render(self, name, color=True, just=None, **kwargs):
320 """
321 Render the selected prompt.
322
323 Parameters
324 ----------
325 name : str
326 Which prompt to render. One of 'in', 'in2', 'out', 'rewrite'
327 color : bool
328 If True (default), include ANSI escape sequences for a coloured prompt.
329 just : bool
330 If True, justify the prompt to the width of the last prompt. The
331 default is stored in self.justify.
332 **kwargs :
333 Additional arguments will be passed to the string formatting operation,
334 so they can override the values that would otherwise fill in the
335 template.
336
337 Returns
338 -------
339 A string containing the rendered prompt.
340 """
341 if color:
342 scheme = self.color_scheme_table.active_colors
343 if name=='out':
344 colors = color_lists['normal']
345 colors.number, colors.prompt, colors.normal = \
346 scheme.out_number, scheme.out_prompt, 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):
@@ -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,
General Comments 0
You need to be logged in to leave comments. Login now