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