##// END OF EJS Templates
Use len and str from builtins in computing dynamic prompts....
Fernando Perez -
Show More
@@ -1,622 +1,622 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Classes for handling input/output prompts.
3 Classes for handling input/output prompts.
4 """
4 """
5
5
6 #*****************************************************************************
6 #*****************************************************************************
7 # Copyright (C) 2008-2009 The IPython Development Team
7 # Copyright (C) 2008-2009 The IPython Development Team
8 # Copyright (C) 2001-2007 Fernando Perez <fperez@colorado.edu>
8 # Copyright (C) 2001-2007 Fernando Perez <fperez@colorado.edu>
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #*****************************************************************************
12 #*****************************************************************************
13
13
14 #****************************************************************************
14 #****************************************************************************
15 # Required modules
15 # Required modules
16 import __builtin__
16 import __builtin__
17 import os
17 import os
18 import socket
18 import socket
19 import sys
19 import sys
20 import time
20 import time
21
21
22 # IPython's own
22 # IPython's own
23 from IPython import ColorANSI
23 from IPython import ColorANSI
24 from IPython import Release
24 from IPython import Release
25 from IPython.external.Itpl import ItplNS
25 from IPython.external.Itpl import ItplNS
26 from IPython.ipapi import TryNext
26 from IPython.ipapi import TryNext
27 from IPython.ipstruct import Struct
27 from IPython.ipstruct import Struct
28 from IPython.macro import Macro
28 from IPython.macro import Macro
29
29
30 from IPython.genutils import *
30 from IPython.genutils import *
31
31
32 #****************************************************************************
32 #****************************************************************************
33 #Color schemes for Prompts.
33 #Color schemes for Prompts.
34
34
35 PromptColors = ColorANSI.ColorSchemeTable()
35 PromptColors = ColorANSI.ColorSchemeTable()
36 InputColors = ColorANSI.InputTermColors # just a shorthand
36 InputColors = ColorANSI.InputTermColors # just a shorthand
37 Colors = ColorANSI.TermColors # just a shorthand
37 Colors = ColorANSI.TermColors # just a shorthand
38
38
39 PromptColors.add_scheme(ColorANSI.ColorScheme(
39 PromptColors.add_scheme(ColorANSI.ColorScheme(
40 'NoColor',
40 'NoColor',
41 in_prompt = InputColors.NoColor, # Input prompt
41 in_prompt = InputColors.NoColor, # Input prompt
42 in_number = InputColors.NoColor, # Input prompt number
42 in_number = InputColors.NoColor, # Input prompt number
43 in_prompt2 = InputColors.NoColor, # Continuation prompt
43 in_prompt2 = InputColors.NoColor, # Continuation prompt
44 in_normal = InputColors.NoColor, # color off (usu. Colors.Normal)
44 in_normal = InputColors.NoColor, # color off (usu. Colors.Normal)
45
45
46 out_prompt = Colors.NoColor, # Output prompt
46 out_prompt = Colors.NoColor, # Output prompt
47 out_number = Colors.NoColor, # Output prompt number
47 out_number = Colors.NoColor, # Output prompt number
48
48
49 normal = Colors.NoColor # color off (usu. Colors.Normal)
49 normal = Colors.NoColor # color off (usu. Colors.Normal)
50 ))
50 ))
51
51
52 # make some schemes as instances so we can copy them for modification easily:
52 # make some schemes as instances so we can copy them for modification easily:
53 __PColLinux = ColorANSI.ColorScheme(
53 __PColLinux = ColorANSI.ColorScheme(
54 'Linux',
54 'Linux',
55 in_prompt = InputColors.Green,
55 in_prompt = InputColors.Green,
56 in_number = InputColors.LightGreen,
56 in_number = InputColors.LightGreen,
57 in_prompt2 = InputColors.Green,
57 in_prompt2 = InputColors.Green,
58 in_normal = InputColors.Normal, # color off (usu. Colors.Normal)
58 in_normal = InputColors.Normal, # color off (usu. Colors.Normal)
59
59
60 out_prompt = Colors.Red,
60 out_prompt = Colors.Red,
61 out_number = Colors.LightRed,
61 out_number = Colors.LightRed,
62
62
63 normal = Colors.Normal
63 normal = Colors.Normal
64 )
64 )
65 # Don't forget to enter it into the table!
65 # Don't forget to enter it into the table!
66 PromptColors.add_scheme(__PColLinux)
66 PromptColors.add_scheme(__PColLinux)
67
67
68 # Slightly modified Linux for light backgrounds
68 # Slightly modified Linux for light backgrounds
69 __PColLightBG = __PColLinux.copy('LightBG')
69 __PColLightBG = __PColLinux.copy('LightBG')
70
70
71 __PColLightBG.colors.update(
71 __PColLightBG.colors.update(
72 in_prompt = InputColors.Blue,
72 in_prompt = InputColors.Blue,
73 in_number = InputColors.LightBlue,
73 in_number = InputColors.LightBlue,
74 in_prompt2 = InputColors.Blue
74 in_prompt2 = InputColors.Blue
75 )
75 )
76 PromptColors.add_scheme(__PColLightBG)
76 PromptColors.add_scheme(__PColLightBG)
77
77
78 del Colors,InputColors
78 del Colors,InputColors
79
79
80 #-----------------------------------------------------------------------------
80 #-----------------------------------------------------------------------------
81 def multiple_replace(dict, text):
81 def multiple_replace(dict, text):
82 """ Replace in 'text' all occurences of any key in the given
82 """ Replace in 'text' all occurences of any key in the given
83 dictionary by its corresponding value. Returns the new string."""
83 dictionary by its corresponding value. Returns the new string."""
84
84
85 # Function by Xavier Defrang, originally found at:
85 # Function by Xavier Defrang, originally found at:
86 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81330
86 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81330
87
87
88 # Create a regular expression from the dictionary keys
88 # Create a regular expression from the dictionary keys
89 regex = re.compile("(%s)" % "|".join(map(re.escape, dict.keys())))
89 regex = re.compile("(%s)" % "|".join(map(re.escape, dict.keys())))
90 # For each match, look-up corresponding value in dictionary
90 # For each match, look-up corresponding value in dictionary
91 return regex.sub(lambda mo: dict[mo.string[mo.start():mo.end()]], text)
91 return regex.sub(lambda mo: dict[mo.string[mo.start():mo.end()]], text)
92
92
93 #-----------------------------------------------------------------------------
93 #-----------------------------------------------------------------------------
94 # Special characters that can be used in prompt templates, mainly bash-like
94 # Special characters that can be used in prompt templates, mainly bash-like
95
95
96 # If $HOME isn't defined (Windows), make it an absurd string so that it can
96 # If $HOME isn't defined (Windows), make it an absurd string so that it can
97 # never be expanded out into '~'. Basically anything which can never be a
97 # never be expanded out into '~'. Basically anything which can never be a
98 # reasonable directory name will do, we just want the $HOME -> '~' operation
98 # reasonable directory name will do, we just want the $HOME -> '~' operation
99 # to become a no-op. We pre-compute $HOME here so it's not done on every
99 # to become a no-op. We pre-compute $HOME here so it's not done on every
100 # prompt call.
100 # prompt call.
101
101
102 # FIXME:
102 # FIXME:
103
103
104 # - This should be turned into a class which does proper namespace management,
104 # - This should be turned into a class which does proper namespace management,
105 # since the prompt specials need to be evaluated in a certain namespace.
105 # since the prompt specials need to be evaluated in a certain namespace.
106 # Currently it's just globals, which need to be managed manually by code
106 # Currently it's just globals, which need to be managed manually by code
107 # below.
107 # below.
108
108
109 # - I also need to split up the color schemes from the prompt specials
109 # - I also need to split up the color schemes from the prompt specials
110 # somehow. I don't have a clean design for that quite yet.
110 # somehow. I don't have a clean design for that quite yet.
111
111
112 HOME = os.environ.get("HOME","//////:::::ZZZZZ,,,~~~")
112 HOME = os.environ.get("HOME","//////:::::ZZZZZ,,,~~~")
113
113
114 # We precompute a few more strings here for the prompt_specials, which are
114 # We precompute a few more strings here for the prompt_specials, which are
115 # fixed once ipython starts. This reduces the runtime overhead of computing
115 # fixed once ipython starts. This reduces the runtime overhead of computing
116 # prompt strings.
116 # prompt strings.
117 USER = os.environ.get("USER")
117 USER = os.environ.get("USER")
118 HOSTNAME = socket.gethostname()
118 HOSTNAME = socket.gethostname()
119 HOSTNAME_SHORT = HOSTNAME.split(".")[0]
119 HOSTNAME_SHORT = HOSTNAME.split(".")[0]
120 ROOT_SYMBOL = "$#"[os.name=='nt' or os.getuid()==0]
120 ROOT_SYMBOL = "$#"[os.name=='nt' or os.getuid()==0]
121
121
122 prompt_specials_color = {
122 prompt_specials_color = {
123 # Prompt/history count
123 # Prompt/history count
124 '%n' : '${self.col_num}' '${self.cache.prompt_count}' '${self.col_p}',
124 '%n' : '${self.col_num}' '${self.cache.prompt_count}' '${self.col_p}',
125 r'\#': '${self.col_num}' '${self.cache.prompt_count}' '${self.col_p}',
125 r'\#': '${self.col_num}' '${self.cache.prompt_count}' '${self.col_p}',
126 # Just the prompt counter number, WITHOUT any coloring wrappers, so users
126 # Just the prompt counter number, WITHOUT any coloring wrappers, so users
127 # can get numbers displayed in whatever color they want.
127 # can get numbers displayed in whatever color they want.
128 r'\N': '${self.cache.prompt_count}',
128 r'\N': '${self.cache.prompt_count}',
129 # Prompt/history count, with the actual digits replaced by dots. Used
129 # Prompt/history count, with the actual digits replaced by dots. Used
130 # mainly in continuation prompts (prompt_in2)
130 # mainly in continuation prompts (prompt_in2)
131 r'\D': '${"."*len(str(self.cache.prompt_count))}',
131 r'\D': '${"."*__builtins__.len(__builtins__.str(self.cache.prompt_count))}',
132 # Current working directory
132 # Current working directory
133 r'\w': '${os.getcwd()}',
133 r'\w': '${os.getcwd()}',
134 # Current time
134 # Current time
135 r'\t' : '${time.strftime("%H:%M:%S")}',
135 r'\t' : '${time.strftime("%H:%M:%S")}',
136 # Basename of current working directory.
136 # Basename of current working directory.
137 # (use os.sep to make this portable across OSes)
137 # (use os.sep to make this portable across OSes)
138 r'\W' : '${os.getcwd().split("%s")[-1]}' % os.sep,
138 r'\W' : '${os.getcwd().split("%s")[-1]}' % os.sep,
139 # These X<N> are an extension to the normal bash prompts. They return
139 # These X<N> are an extension to the normal bash prompts. They return
140 # N terms of the path, after replacing $HOME with '~'
140 # N terms of the path, after replacing $HOME with '~'
141 r'\X0': '${os.getcwd().replace("%s","~")}' % HOME,
141 r'\X0': '${os.getcwd().replace("%s","~")}' % HOME,
142 r'\X1': '${self.cwd_filt(1)}',
142 r'\X1': '${self.cwd_filt(1)}',
143 r'\X2': '${self.cwd_filt(2)}',
143 r'\X2': '${self.cwd_filt(2)}',
144 r'\X3': '${self.cwd_filt(3)}',
144 r'\X3': '${self.cwd_filt(3)}',
145 r'\X4': '${self.cwd_filt(4)}',
145 r'\X4': '${self.cwd_filt(4)}',
146 r'\X5': '${self.cwd_filt(5)}',
146 r'\X5': '${self.cwd_filt(5)}',
147 # Y<N> are similar to X<N>, but they show '~' if it's the directory
147 # Y<N> are similar to X<N>, but they show '~' if it's the directory
148 # N+1 in the list. Somewhat like %cN in tcsh.
148 # N+1 in the list. Somewhat like %cN in tcsh.
149 r'\Y0': '${self.cwd_filt2(0)}',
149 r'\Y0': '${self.cwd_filt2(0)}',
150 r'\Y1': '${self.cwd_filt2(1)}',
150 r'\Y1': '${self.cwd_filt2(1)}',
151 r'\Y2': '${self.cwd_filt2(2)}',
151 r'\Y2': '${self.cwd_filt2(2)}',
152 r'\Y3': '${self.cwd_filt2(3)}',
152 r'\Y3': '${self.cwd_filt2(3)}',
153 r'\Y4': '${self.cwd_filt2(4)}',
153 r'\Y4': '${self.cwd_filt2(4)}',
154 r'\Y5': '${self.cwd_filt2(5)}',
154 r'\Y5': '${self.cwd_filt2(5)}',
155 # Hostname up to first .
155 # Hostname up to first .
156 r'\h': HOSTNAME_SHORT,
156 r'\h': HOSTNAME_SHORT,
157 # Full hostname
157 # Full hostname
158 r'\H': HOSTNAME,
158 r'\H': HOSTNAME,
159 # Username of current user
159 # Username of current user
160 r'\u': USER,
160 r'\u': USER,
161 # Escaped '\'
161 # Escaped '\'
162 '\\\\': '\\',
162 '\\\\': '\\',
163 # Newline
163 # Newline
164 r'\n': '\n',
164 r'\n': '\n',
165 # Carriage return
165 # Carriage return
166 r'\r': '\r',
166 r'\r': '\r',
167 # Release version
167 # Release version
168 r'\v': Release.version,
168 r'\v': Release.version,
169 # Root symbol ($ or #)
169 # Root symbol ($ or #)
170 r'\$': ROOT_SYMBOL,
170 r'\$': ROOT_SYMBOL,
171 }
171 }
172
172
173 # A copy of the prompt_specials dictionary but with all color escapes removed,
173 # A copy of the prompt_specials dictionary but with all color escapes removed,
174 # so we can correctly compute the prompt length for the auto_rewrite method.
174 # so we can correctly compute the prompt length for the auto_rewrite method.
175 prompt_specials_nocolor = prompt_specials_color.copy()
175 prompt_specials_nocolor = prompt_specials_color.copy()
176 prompt_specials_nocolor['%n'] = '${self.cache.prompt_count}'
176 prompt_specials_nocolor['%n'] = '${self.cache.prompt_count}'
177 prompt_specials_nocolor[r'\#'] = '${self.cache.prompt_count}'
177 prompt_specials_nocolor[r'\#'] = '${self.cache.prompt_count}'
178
178
179 # Add in all the InputTermColors color escapes as valid prompt characters.
179 # Add in all the InputTermColors color escapes as valid prompt characters.
180 # They all get added as \\C_COLORNAME, so that we don't have any conflicts
180 # They all get added as \\C_COLORNAME, so that we don't have any conflicts
181 # with a color name which may begin with a letter used by any other of the
181 # with a color name which may begin with a letter used by any other of the
182 # allowed specials. This of course means that \\C will never be allowed for
182 # allowed specials. This of course means that \\C will never be allowed for
183 # anything else.
183 # anything else.
184 input_colors = ColorANSI.InputTermColors
184 input_colors = ColorANSI.InputTermColors
185 for _color in dir(input_colors):
185 for _color in dir(input_colors):
186 if _color[0] != '_':
186 if _color[0] != '_':
187 c_name = r'\C_'+_color
187 c_name = r'\C_'+_color
188 prompt_specials_color[c_name] = getattr(input_colors,_color)
188 prompt_specials_color[c_name] = getattr(input_colors,_color)
189 prompt_specials_nocolor[c_name] = ''
189 prompt_specials_nocolor[c_name] = ''
190
190
191 # we default to no color for safety. Note that prompt_specials is a global
191 # we default to no color for safety. Note that prompt_specials is a global
192 # variable used by all prompt objects.
192 # variable used by all prompt objects.
193 prompt_specials = prompt_specials_nocolor
193 prompt_specials = prompt_specials_nocolor
194
194
195 #-----------------------------------------------------------------------------
195 #-----------------------------------------------------------------------------
196 def str_safe(arg):
196 def str_safe(arg):
197 """Convert to a string, without ever raising an exception.
197 """Convert to a string, without ever raising an exception.
198
198
199 If str(arg) fails, <ERROR: ... > is returned, where ... is the exception
199 If str(arg) fails, <ERROR: ... > is returned, where ... is the exception
200 error message."""
200 error message."""
201
201
202 try:
202 try:
203 out = str(arg)
203 out = str(arg)
204 except UnicodeError:
204 except UnicodeError:
205 try:
205 try:
206 out = arg.encode('utf_8','replace')
206 out = arg.encode('utf_8','replace')
207 except Exception,msg:
207 except Exception,msg:
208 # let's keep this little duplication here, so that the most common
208 # let's keep this little duplication here, so that the most common
209 # case doesn't suffer from a double try wrapping.
209 # case doesn't suffer from a double try wrapping.
210 out = '<ERROR: %s>' % msg
210 out = '<ERROR: %s>' % msg
211 except Exception,msg:
211 except Exception,msg:
212 out = '<ERROR: %s>' % msg
212 out = '<ERROR: %s>' % msg
213 return out
213 return out
214
214
215 class BasePrompt(object):
215 class BasePrompt(object):
216 """Interactive prompt similar to Mathematica's."""
216 """Interactive prompt similar to Mathematica's."""
217
217
218 def _get_p_template(self):
218 def _get_p_template(self):
219 return self._p_template
219 return self._p_template
220
220
221 def _set_p_template(self,val):
221 def _set_p_template(self,val):
222 self._p_template = val
222 self._p_template = val
223 self.set_p_str()
223 self.set_p_str()
224
224
225 p_template = property(_get_p_template,_set_p_template,
225 p_template = property(_get_p_template,_set_p_template,
226 doc='Template for prompt string creation')
226 doc='Template for prompt string creation')
227
227
228 def __init__(self,cache,sep,prompt,pad_left=False):
228 def __init__(self,cache,sep,prompt,pad_left=False):
229
229
230 # Hack: we access information about the primary prompt through the
230 # Hack: we access information about the primary prompt through the
231 # cache argument. We need this, because we want the secondary prompt
231 # cache argument. We need this, because we want the secondary prompt
232 # to be aligned with the primary one. Color table info is also shared
232 # to be aligned with the primary one. Color table info is also shared
233 # by all prompt classes through the cache. Nice OO spaghetti code!
233 # by all prompt classes through the cache. Nice OO spaghetti code!
234 self.cache = cache
234 self.cache = cache
235 self.sep = sep
235 self.sep = sep
236
236
237 # regexp to count the number of spaces at the end of a prompt
237 # regexp to count the number of spaces at the end of a prompt
238 # expression, useful for prompt auto-rewriting
238 # expression, useful for prompt auto-rewriting
239 self.rspace = re.compile(r'(\s*)$')
239 self.rspace = re.compile(r'(\s*)$')
240 # Flag to left-pad prompt strings to match the length of the primary
240 # Flag to left-pad prompt strings to match the length of the primary
241 # prompt
241 # prompt
242 self.pad_left = pad_left
242 self.pad_left = pad_left
243
243
244 # Set template to create each actual prompt (where numbers change).
244 # Set template to create each actual prompt (where numbers change).
245 # Use a property
245 # Use a property
246 self.p_template = prompt
246 self.p_template = prompt
247 self.set_p_str()
247 self.set_p_str()
248
248
249 def set_p_str(self):
249 def set_p_str(self):
250 """ Set the interpolating prompt strings.
250 """ Set the interpolating prompt strings.
251
251
252 This must be called every time the color settings change, because the
252 This must be called every time the color settings change, because the
253 prompt_specials global may have changed."""
253 prompt_specials global may have changed."""
254
254
255 import os,time # needed in locals for prompt string handling
255 import os,time # needed in locals for prompt string handling
256 loc = locals()
256 loc = locals()
257 try:
257 try:
258 self.p_str = ItplNS('%s%s%s' %
258 self.p_str = ItplNS('%s%s%s' %
259 ('${self.sep}${self.col_p}',
259 ('${self.sep}${self.col_p}',
260 multiple_replace(prompt_specials, self.p_template),
260 multiple_replace(prompt_specials, self.p_template),
261 '${self.col_norm}'),self.cache.user_ns,loc)
261 '${self.col_norm}'),self.cache.user_ns,loc)
262
262
263 self.p_str_nocolor = ItplNS(multiple_replace(prompt_specials_nocolor,
263 self.p_str_nocolor = ItplNS(multiple_replace(prompt_specials_nocolor,
264 self.p_template),
264 self.p_template),
265 self.cache.user_ns,loc)
265 self.cache.user_ns,loc)
266 except:
266 except:
267 print "Illegal prompt template (check $ usage!):",self.p_template
267 print "Illegal prompt template (check $ usage!):",self.p_template
268 self.p_str = self.p_template
268 self.p_str = self.p_template
269 self.p_str_nocolor = self.p_template
269 self.p_str_nocolor = self.p_template
270
270
271 def write(self,msg): # dbg
271 def write(self,msg): # dbg
272 sys.stdout.write(msg)
272 sys.stdout.write(msg)
273 return ''
273 return ''
274
274
275 def __str__(self):
275 def __str__(self):
276 """Return a string form of the prompt.
276 """Return a string form of the prompt.
277
277
278 This for is useful for continuation and output prompts, since it is
278 This for is useful for continuation and output prompts, since it is
279 left-padded to match lengths with the primary one (if the
279 left-padded to match lengths with the primary one (if the
280 self.pad_left attribute is set)."""
280 self.pad_left attribute is set)."""
281
281
282 out_str = str_safe(self.p_str)
282 out_str = str_safe(self.p_str)
283 if self.pad_left:
283 if self.pad_left:
284 # We must find the amount of padding required to match lengths,
284 # We must find the amount of padding required to match lengths,
285 # taking the color escapes (which are invisible on-screen) into
285 # taking the color escapes (which are invisible on-screen) into
286 # account.
286 # account.
287 esc_pad = len(out_str) - len(str_safe(self.p_str_nocolor))
287 esc_pad = len(out_str) - len(str_safe(self.p_str_nocolor))
288 format = '%%%ss' % (len(str(self.cache.last_prompt))+esc_pad)
288 format = '%%%ss' % (len(str(self.cache.last_prompt))+esc_pad)
289 return format % out_str
289 return format % out_str
290 else:
290 else:
291 return out_str
291 return out_str
292
292
293 # these path filters are put in as methods so that we can control the
293 # these path filters are put in as methods so that we can control the
294 # namespace where the prompt strings get evaluated
294 # namespace where the prompt strings get evaluated
295 def cwd_filt(self,depth):
295 def cwd_filt(self,depth):
296 """Return the last depth elements of the current working directory.
296 """Return the last depth elements of the current working directory.
297
297
298 $HOME is always replaced with '~'.
298 $HOME is always replaced with '~'.
299 If depth==0, the full path is returned."""
299 If depth==0, the full path is returned."""
300
300
301 cwd = os.getcwd().replace(HOME,"~")
301 cwd = os.getcwd().replace(HOME,"~")
302 out = os.sep.join(cwd.split(os.sep)[-depth:])
302 out = os.sep.join(cwd.split(os.sep)[-depth:])
303 if out:
303 if out:
304 return out
304 return out
305 else:
305 else:
306 return os.sep
306 return os.sep
307
307
308 def cwd_filt2(self,depth):
308 def cwd_filt2(self,depth):
309 """Return the last depth elements of the current working directory.
309 """Return the last depth elements of the current working directory.
310
310
311 $HOME is always replaced with '~'.
311 $HOME is always replaced with '~'.
312 If depth==0, the full path is returned."""
312 If depth==0, the full path is returned."""
313
313
314 full_cwd = os.getcwd()
314 full_cwd = os.getcwd()
315 cwd = full_cwd.replace(HOME,"~").split(os.sep)
315 cwd = full_cwd.replace(HOME,"~").split(os.sep)
316 if '~' in cwd and len(cwd) == depth+1:
316 if '~' in cwd and len(cwd) == depth+1:
317 depth += 1
317 depth += 1
318 drivepart = ''
318 drivepart = ''
319 if sys.platform == 'win32' and len(cwd) > depth:
319 if sys.platform == 'win32' and len(cwd) > depth:
320 drivepart = os.path.splitdrive(full_cwd)[0]
320 drivepart = os.path.splitdrive(full_cwd)[0]
321 out = drivepart + '/'.join(cwd[-depth:])
321 out = drivepart + '/'.join(cwd[-depth:])
322
322
323 if out:
323 if out:
324 return out
324 return out
325 else:
325 else:
326 return os.sep
326 return os.sep
327
327
328 def __nonzero__(self):
328 def __nonzero__(self):
329 """Implement boolean behavior.
329 """Implement boolean behavior.
330
330
331 Checks whether the p_str attribute is non-empty"""
331 Checks whether the p_str attribute is non-empty"""
332
332
333 return bool(self.p_template)
333 return bool(self.p_template)
334
334
335 class Prompt1(BasePrompt):
335 class Prompt1(BasePrompt):
336 """Input interactive prompt similar to Mathematica's."""
336 """Input interactive prompt similar to Mathematica's."""
337
337
338 def __init__(self,cache,sep='\n',prompt='In [\\#]: ',pad_left=True):
338 def __init__(self,cache,sep='\n',prompt='In [\\#]: ',pad_left=True):
339 BasePrompt.__init__(self,cache,sep,prompt,pad_left)
339 BasePrompt.__init__(self,cache,sep,prompt,pad_left)
340
340
341 def set_colors(self):
341 def set_colors(self):
342 self.set_p_str()
342 self.set_p_str()
343 Colors = self.cache.color_table.active_colors # shorthand
343 Colors = self.cache.color_table.active_colors # shorthand
344 self.col_p = Colors.in_prompt
344 self.col_p = Colors.in_prompt
345 self.col_num = Colors.in_number
345 self.col_num = Colors.in_number
346 self.col_norm = Colors.in_normal
346 self.col_norm = Colors.in_normal
347 # We need a non-input version of these escapes for the '--->'
347 # We need a non-input version of these escapes for the '--->'
348 # auto-call prompts used in the auto_rewrite() method.
348 # auto-call prompts used in the auto_rewrite() method.
349 self.col_p_ni = self.col_p.replace('\001','').replace('\002','')
349 self.col_p_ni = self.col_p.replace('\001','').replace('\002','')
350 self.col_norm_ni = Colors.normal
350 self.col_norm_ni = Colors.normal
351
351
352 def __str__(self):
352 def __str__(self):
353 self.cache.prompt_count += 1
353 self.cache.prompt_count += 1
354 self.cache.last_prompt = str_safe(self.p_str_nocolor).split('\n')[-1]
354 self.cache.last_prompt = str_safe(self.p_str_nocolor).split('\n')[-1]
355 return str_safe(self.p_str)
355 return str_safe(self.p_str)
356
356
357 def auto_rewrite(self):
357 def auto_rewrite(self):
358 """Print a string of the form '--->' which lines up with the previous
358 """Print a string of the form '--->' which lines up with the previous
359 input string. Useful for systems which re-write the user input when
359 input string. Useful for systems which re-write the user input when
360 handling automatically special syntaxes."""
360 handling automatically special syntaxes."""
361
361
362 curr = str(self.cache.last_prompt)
362 curr = str(self.cache.last_prompt)
363 nrspaces = len(self.rspace.search(curr).group())
363 nrspaces = len(self.rspace.search(curr).group())
364 return '%s%s>%s%s' % (self.col_p_ni,'-'*(len(curr)-nrspaces-1),
364 return '%s%s>%s%s' % (self.col_p_ni,'-'*(len(curr)-nrspaces-1),
365 ' '*nrspaces,self.col_norm_ni)
365 ' '*nrspaces,self.col_norm_ni)
366
366
367 class PromptOut(BasePrompt):
367 class PromptOut(BasePrompt):
368 """Output interactive prompt similar to Mathematica's."""
368 """Output interactive prompt similar to Mathematica's."""
369
369
370 def __init__(self,cache,sep='',prompt='Out[\\#]: ',pad_left=True):
370 def __init__(self,cache,sep='',prompt='Out[\\#]: ',pad_left=True):
371 BasePrompt.__init__(self,cache,sep,prompt,pad_left)
371 BasePrompt.__init__(self,cache,sep,prompt,pad_left)
372 if not self.p_template:
372 if not self.p_template:
373 self.__str__ = lambda: ''
373 self.__str__ = lambda: ''
374
374
375 def set_colors(self):
375 def set_colors(self):
376 self.set_p_str()
376 self.set_p_str()
377 Colors = self.cache.color_table.active_colors # shorthand
377 Colors = self.cache.color_table.active_colors # shorthand
378 self.col_p = Colors.out_prompt
378 self.col_p = Colors.out_prompt
379 self.col_num = Colors.out_number
379 self.col_num = Colors.out_number
380 self.col_norm = Colors.normal
380 self.col_norm = Colors.normal
381
381
382 class Prompt2(BasePrompt):
382 class Prompt2(BasePrompt):
383 """Interactive continuation prompt."""
383 """Interactive continuation prompt."""
384
384
385 def __init__(self,cache,prompt=' .\\D.: ',pad_left=True):
385 def __init__(self,cache,prompt=' .\\D.: ',pad_left=True):
386 self.cache = cache
386 self.cache = cache
387 self.p_template = prompt
387 self.p_template = prompt
388 self.pad_left = pad_left
388 self.pad_left = pad_left
389 self.set_p_str()
389 self.set_p_str()
390
390
391 def set_p_str(self):
391 def set_p_str(self):
392 import os,time # needed in locals for prompt string handling
392 import os,time # needed in locals for prompt string handling
393 loc = locals()
393 loc = locals()
394 self.p_str = ItplNS('%s%s%s' %
394 self.p_str = ItplNS('%s%s%s' %
395 ('${self.col_p2}',
395 ('${self.col_p2}',
396 multiple_replace(prompt_specials, self.p_template),
396 multiple_replace(prompt_specials, self.p_template),
397 '$self.col_norm'),
397 '$self.col_norm'),
398 self.cache.user_ns,loc)
398 self.cache.user_ns,loc)
399 self.p_str_nocolor = ItplNS(multiple_replace(prompt_specials_nocolor,
399 self.p_str_nocolor = ItplNS(multiple_replace(prompt_specials_nocolor,
400 self.p_template),
400 self.p_template),
401 self.cache.user_ns,loc)
401 self.cache.user_ns,loc)
402
402
403 def set_colors(self):
403 def set_colors(self):
404 self.set_p_str()
404 self.set_p_str()
405 Colors = self.cache.color_table.active_colors
405 Colors = self.cache.color_table.active_colors
406 self.col_p2 = Colors.in_prompt2
406 self.col_p2 = Colors.in_prompt2
407 self.col_norm = Colors.in_normal
407 self.col_norm = Colors.in_normal
408 # FIXME (2004-06-16) HACK: prevent crashes for users who haven't
408 # FIXME (2004-06-16) HACK: prevent crashes for users who haven't
409 # updated their prompt_in2 definitions. Remove eventually.
409 # updated their prompt_in2 definitions. Remove eventually.
410 self.col_p = Colors.out_prompt
410 self.col_p = Colors.out_prompt
411 self.col_num = Colors.out_number
411 self.col_num = Colors.out_number
412
412
413
413
414 #-----------------------------------------------------------------------------
414 #-----------------------------------------------------------------------------
415 class CachedOutput:
415 class CachedOutput:
416 """Class for printing output from calculations while keeping a cache of
416 """Class for printing output from calculations while keeping a cache of
417 reults. It dynamically creates global variables prefixed with _ which
417 reults. It dynamically creates global variables prefixed with _ which
418 contain these results.
418 contain these results.
419
419
420 Meant to be used as a sys.displayhook replacement, providing numbered
420 Meant to be used as a sys.displayhook replacement, providing numbered
421 prompts and cache services.
421 prompts and cache services.
422
422
423 Initialize with initial and final values for cache counter (this defines
423 Initialize with initial and final values for cache counter (this defines
424 the maximum size of the cache."""
424 the maximum size of the cache."""
425
425
426 def __init__(self,shell,cache_size,Pprint,
426 def __init__(self,shell,cache_size,Pprint,
427 colors='NoColor',input_sep='\n',
427 colors='NoColor',input_sep='\n',
428 output_sep='\n',output_sep2='',
428 output_sep='\n',output_sep2='',
429 ps1 = None, ps2 = None,ps_out = None,pad_left=True):
429 ps1 = None, ps2 = None,ps_out = None,pad_left=True):
430
430
431 cache_size_min = 3
431 cache_size_min = 3
432 if cache_size <= 0:
432 if cache_size <= 0:
433 self.do_full_cache = 0
433 self.do_full_cache = 0
434 cache_size = 0
434 cache_size = 0
435 elif cache_size < cache_size_min:
435 elif cache_size < cache_size_min:
436 self.do_full_cache = 0
436 self.do_full_cache = 0
437 cache_size = 0
437 cache_size = 0
438 warn('caching was disabled (min value for cache size is %s).' %
438 warn('caching was disabled (min value for cache size is %s).' %
439 cache_size_min,level=3)
439 cache_size_min,level=3)
440 else:
440 else:
441 self.do_full_cache = 1
441 self.do_full_cache = 1
442
442
443 self.cache_size = cache_size
443 self.cache_size = cache_size
444 self.input_sep = input_sep
444 self.input_sep = input_sep
445
445
446 # we need a reference to the user-level namespace
446 # we need a reference to the user-level namespace
447 self.shell = shell
447 self.shell = shell
448 self.user_ns = shell.user_ns
448 self.user_ns = shell.user_ns
449 # and to the user's input
449 # and to the user's input
450 self.input_hist = shell.input_hist
450 self.input_hist = shell.input_hist
451 # and to the user's logger, for logging output
451 # and to the user's logger, for logging output
452 self.logger = shell.logger
452 self.logger = shell.logger
453
453
454 # Set input prompt strings and colors
454 # Set input prompt strings and colors
455 if cache_size == 0:
455 if cache_size == 0:
456 if ps1.find('%n') > -1 or ps1.find(r'\#') > -1 \
456 if ps1.find('%n') > -1 or ps1.find(r'\#') > -1 \
457 or ps1.find(r'\N') > -1:
457 or ps1.find(r'\N') > -1:
458 ps1 = '>>> '
458 ps1 = '>>> '
459 if ps2.find('%n') > -1 or ps2.find(r'\#') > -1 \
459 if ps2.find('%n') > -1 or ps2.find(r'\#') > -1 \
460 or ps2.find(r'\N') > -1:
460 or ps2.find(r'\N') > -1:
461 ps2 = '... '
461 ps2 = '... '
462 self.ps1_str = self._set_prompt_str(ps1,'In [\\#]: ','>>> ')
462 self.ps1_str = self._set_prompt_str(ps1,'In [\\#]: ','>>> ')
463 self.ps2_str = self._set_prompt_str(ps2,' .\\D.: ','... ')
463 self.ps2_str = self._set_prompt_str(ps2,' .\\D.: ','... ')
464 self.ps_out_str = self._set_prompt_str(ps_out,'Out[\\#]: ','')
464 self.ps_out_str = self._set_prompt_str(ps_out,'Out[\\#]: ','')
465
465
466 self.color_table = PromptColors
466 self.color_table = PromptColors
467 self.prompt1 = Prompt1(self,sep=input_sep,prompt=self.ps1_str,
467 self.prompt1 = Prompt1(self,sep=input_sep,prompt=self.ps1_str,
468 pad_left=pad_left)
468 pad_left=pad_left)
469 self.prompt2 = Prompt2(self,prompt=self.ps2_str,pad_left=pad_left)
469 self.prompt2 = Prompt2(self,prompt=self.ps2_str,pad_left=pad_left)
470 self.prompt_out = PromptOut(self,sep='',prompt=self.ps_out_str,
470 self.prompt_out = PromptOut(self,sep='',prompt=self.ps_out_str,
471 pad_left=pad_left)
471 pad_left=pad_left)
472 self.set_colors(colors)
472 self.set_colors(colors)
473
473
474 # other more normal stuff
474 # other more normal stuff
475 # b/c each call to the In[] prompt raises it by 1, even the first.
475 # b/c each call to the In[] prompt raises it by 1, even the first.
476 self.prompt_count = 0
476 self.prompt_count = 0
477 # Store the last prompt string each time, we need it for aligning
477 # Store the last prompt string each time, we need it for aligning
478 # continuation and auto-rewrite prompts
478 # continuation and auto-rewrite prompts
479 self.last_prompt = ''
479 self.last_prompt = ''
480 self.Pprint = Pprint
480 self.Pprint = Pprint
481 self.output_sep = output_sep
481 self.output_sep = output_sep
482 self.output_sep2 = output_sep2
482 self.output_sep2 = output_sep2
483 self._,self.__,self.___ = '','',''
483 self._,self.__,self.___ = '','',''
484 self.pprint_types = map(type,[(),[],{}])
484 self.pprint_types = map(type,[(),[],{}])
485
485
486 # these are deliberately global:
486 # these are deliberately global:
487 to_user_ns = {'_':self._,'__':self.__,'___':self.___}
487 to_user_ns = {'_':self._,'__':self.__,'___':self.___}
488 self.user_ns.update(to_user_ns)
488 self.user_ns.update(to_user_ns)
489
489
490 def _set_prompt_str(self,p_str,cache_def,no_cache_def):
490 def _set_prompt_str(self,p_str,cache_def,no_cache_def):
491 if p_str is None:
491 if p_str is None:
492 if self.do_full_cache:
492 if self.do_full_cache:
493 return cache_def
493 return cache_def
494 else:
494 else:
495 return no_cache_def
495 return no_cache_def
496 else:
496 else:
497 return p_str
497 return p_str
498
498
499 def set_colors(self,colors):
499 def set_colors(self,colors):
500 """Set the active color scheme and configure colors for the three
500 """Set the active color scheme and configure colors for the three
501 prompt subsystems."""
501 prompt subsystems."""
502
502
503 # FIXME: the prompt_specials global should be gobbled inside this
503 # FIXME: the prompt_specials global should be gobbled inside this
504 # class instead. Do it when cleaning up the whole 3-prompt system.
504 # class instead. Do it when cleaning up the whole 3-prompt system.
505 global prompt_specials
505 global prompt_specials
506 if colors.lower()=='nocolor':
506 if colors.lower()=='nocolor':
507 prompt_specials = prompt_specials_nocolor
507 prompt_specials = prompt_specials_nocolor
508 else:
508 else:
509 prompt_specials = prompt_specials_color
509 prompt_specials = prompt_specials_color
510
510
511 self.color_table.set_active_scheme(colors)
511 self.color_table.set_active_scheme(colors)
512 self.prompt1.set_colors()
512 self.prompt1.set_colors()
513 self.prompt2.set_colors()
513 self.prompt2.set_colors()
514 self.prompt_out.set_colors()
514 self.prompt_out.set_colors()
515
515
516 def __call__(self,arg=None):
516 def __call__(self,arg=None):
517 """Printing with history cache management.
517 """Printing with history cache management.
518
518
519 This is invoked everytime the interpreter needs to print, and is
519 This is invoked everytime the interpreter needs to print, and is
520 activated by setting the variable sys.displayhook to it."""
520 activated by setting the variable sys.displayhook to it."""
521
521
522 # If something injected a '_' variable in __builtin__, delete
522 # If something injected a '_' variable in __builtin__, delete
523 # ipython's automatic one so we don't clobber that. gettext() in
523 # ipython's automatic one so we don't clobber that. gettext() in
524 # particular uses _, so we need to stay away from it.
524 # particular uses _, so we need to stay away from it.
525 if '_' in __builtin__.__dict__:
525 if '_' in __builtin__.__dict__:
526 try:
526 try:
527 del self.user_ns['_']
527 del self.user_ns['_']
528 except KeyError:
528 except KeyError:
529 pass
529 pass
530 if arg is not None:
530 if arg is not None:
531 cout_write = Term.cout.write # fast lookup
531 cout_write = Term.cout.write # fast lookup
532 # first handle the cache and counters
532 # first handle the cache and counters
533
533
534 # do not print output if input ends in ';'
534 # do not print output if input ends in ';'
535 try:
535 try:
536 if self.input_hist[self.prompt_count].endswith(';\n'):
536 if self.input_hist[self.prompt_count].endswith(';\n'):
537 return
537 return
538 except IndexError:
538 except IndexError:
539 # some uses of ipshellembed may fail here
539 # some uses of ipshellembed may fail here
540 pass
540 pass
541 # don't use print, puts an extra space
541 # don't use print, puts an extra space
542 cout_write(self.output_sep)
542 cout_write(self.output_sep)
543 outprompt = self.shell.hooks.generate_output_prompt()
543 outprompt = self.shell.hooks.generate_output_prompt()
544 if self.do_full_cache:
544 if self.do_full_cache:
545 cout_write(outprompt)
545 cout_write(outprompt)
546
546
547 # and now call a possibly user-defined print mechanism
547 # and now call a possibly user-defined print mechanism
548 manipulated_val = self.display(arg)
548 manipulated_val = self.display(arg)
549
549
550 # user display hooks can change the variable to be stored in
550 # user display hooks can change the variable to be stored in
551 # output history
551 # output history
552
552
553 if manipulated_val is not None:
553 if manipulated_val is not None:
554 arg = manipulated_val
554 arg = manipulated_val
555
555
556 # avoid recursive reference when displaying _oh/Out
556 # avoid recursive reference when displaying _oh/Out
557 if arg is not self.user_ns['_oh']:
557 if arg is not self.user_ns['_oh']:
558 self.update(arg)
558 self.update(arg)
559
559
560 if self.logger.log_output:
560 if self.logger.log_output:
561 self.logger.log_write(repr(arg),'output')
561 self.logger.log_write(repr(arg),'output')
562 cout_write(self.output_sep2)
562 cout_write(self.output_sep2)
563 Term.cout.flush()
563 Term.cout.flush()
564
564
565 def _display(self,arg):
565 def _display(self,arg):
566 """Default printer method, uses pprint.
566 """Default printer method, uses pprint.
567
567
568 Do ip.set_hook("result_display", my_displayhook) for custom result
568 Do ip.set_hook("result_display", my_displayhook) for custom result
569 display, e.g. when your own objects need special formatting.
569 display, e.g. when your own objects need special formatting.
570 """
570 """
571 try:
571 try:
572 return IPython.generics.result_display(arg)
572 return IPython.generics.result_display(arg)
573 except TryNext:
573 except TryNext:
574 return self.shell.hooks.result_display(arg)
574 return self.shell.hooks.result_display(arg)
575
575
576 # Assign the default display method:
576 # Assign the default display method:
577 display = _display
577 display = _display
578
578
579 def update(self,arg):
579 def update(self,arg):
580 #print '***cache_count', self.cache_count # dbg
580 #print '***cache_count', self.cache_count # dbg
581 if len(self.user_ns['_oh']) >= self.cache_size and self.do_full_cache:
581 if len(self.user_ns['_oh']) >= self.cache_size and self.do_full_cache:
582 warn('Output cache limit (currently '+
582 warn('Output cache limit (currently '+
583 `self.cache_size`+' entries) hit.\n'
583 `self.cache_size`+' entries) hit.\n'
584 'Flushing cache and resetting history counter...\n'
584 'Flushing cache and resetting history counter...\n'
585 'The only history variables available will be _,__,___ and _1\n'
585 'The only history variables available will be _,__,___ and _1\n'
586 'with the current result.')
586 'with the current result.')
587
587
588 self.flush()
588 self.flush()
589 # Don't overwrite '_' and friends if '_' is in __builtin__ (otherwise
589 # Don't overwrite '_' and friends if '_' is in __builtin__ (otherwise
590 # we cause buggy behavior for things like gettext).
590 # we cause buggy behavior for things like gettext).
591 if '_' not in __builtin__.__dict__:
591 if '_' not in __builtin__.__dict__:
592 self.___ = self.__
592 self.___ = self.__
593 self.__ = self._
593 self.__ = self._
594 self._ = arg
594 self._ = arg
595 self.user_ns.update({'_':self._,'__':self.__,'___':self.___})
595 self.user_ns.update({'_':self._,'__':self.__,'___':self.___})
596
596
597 # hackish access to top-level namespace to create _1,_2... dynamically
597 # hackish access to top-level namespace to create _1,_2... dynamically
598 to_main = {}
598 to_main = {}
599 if self.do_full_cache:
599 if self.do_full_cache:
600 new_result = '_'+`self.prompt_count`
600 new_result = '_'+`self.prompt_count`
601 to_main[new_result] = arg
601 to_main[new_result] = arg
602 self.user_ns.update(to_main)
602 self.user_ns.update(to_main)
603 self.user_ns['_oh'][self.prompt_count] = arg
603 self.user_ns['_oh'][self.prompt_count] = arg
604
604
605 def flush(self):
605 def flush(self):
606 if not self.do_full_cache:
606 if not self.do_full_cache:
607 raise ValueError,"You shouldn't have reached the cache flush "\
607 raise ValueError,"You shouldn't have reached the cache flush "\
608 "if full caching is not enabled!"
608 "if full caching is not enabled!"
609 # delete auto-generated vars from global namespace
609 # delete auto-generated vars from global namespace
610
610
611 for n in range(1,self.prompt_count + 1):
611 for n in range(1,self.prompt_count + 1):
612 key = '_'+`n`
612 key = '_'+`n`
613 try:
613 try:
614 del self.user_ns[key]
614 del self.user_ns[key]
615 except: pass
615 except: pass
616 self.user_ns['_oh'].clear()
616 self.user_ns['_oh'].clear()
617
617
618 if '_' not in __builtin__.__dict__:
618 if '_' not in __builtin__.__dict__:
619 self.user_ns.update({'_':None,'__':None, '___':None})
619 self.user_ns.update({'_':None,'__':None, '___':None})
620 import gc
620 import gc
621 gc.collect() # xxx needed?
621 gc.collect() # xxx needed?
622
622
General Comments 0
You need to be logged in to leave comments. Login now