##// END OF EJS Templates
Reverting debugging code left in b4b4ede
Jörgen Stenarson -
Show More
@@ -1,436 +1,436
1 1 # -*- coding: utf-8 -*-
2 2 """Classes for handling input/output prompts.
3 3
4 4 Authors:
5 5
6 6 * Fernando Perez
7 7 * Brian Granger
8 8 """
9 9
10 10 #-----------------------------------------------------------------------------
11 11 # Copyright (C) 2008-2010 The IPython Development Team
12 12 # Copyright (C) 2001-2007 Fernando Perez <fperez@colorado.edu>
13 13 #
14 14 # Distributed under the terms of the BSD License. The full license is in
15 15 # the file COPYING, distributed as part of this software.
16 16 #-----------------------------------------------------------------------------
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21
22 22 import os
23 23 import re
24 24 import socket
25 25 import sys
26 26
27 27 from IPython.core import release
28 28 from IPython.external.Itpl import ItplNS
29 29 from IPython.utils import coloransi
30 30
31 31 #-----------------------------------------------------------------------------
32 32 # Color schemes for prompts
33 33 #-----------------------------------------------------------------------------
34 34
35 35 PromptColors = coloransi.ColorSchemeTable()
36 36 InputColors = coloransi.InputTermColors # just a shorthand
37 37 Colors = coloransi.TermColors # just a shorthand
38 38
39 39 PromptColors.add_scheme(coloransi.ColorScheme(
40 40 'NoColor',
41 41 in_prompt = InputColors.NoColor, # Input prompt
42 42 in_number = InputColors.NoColor, # Input prompt number
43 43 in_prompt2 = InputColors.NoColor, # Continuation prompt
44 44 in_normal = InputColors.NoColor, # color off (usu. Colors.Normal)
45 45
46 46 out_prompt = Colors.NoColor, # Output prompt
47 47 out_number = Colors.NoColor, # Output prompt number
48 48
49 49 normal = Colors.NoColor # color off (usu. Colors.Normal)
50 50 ))
51 51
52 52 # make some schemes as instances so we can copy them for modification easily:
53 53 __PColLinux = coloransi.ColorScheme(
54 54 'Linux',
55 55 in_prompt = InputColors.Green,
56 56 in_number = InputColors.LightGreen,
57 57 in_prompt2 = InputColors.Green,
58 58 in_normal = InputColors.Normal, # color off (usu. Colors.Normal)
59 59
60 60 out_prompt = Colors.Red,
61 61 out_number = Colors.LightRed,
62 62
63 63 normal = Colors.Normal
64 64 )
65 65 # Don't forget to enter it into the table!
66 66 PromptColors.add_scheme(__PColLinux)
67 67
68 68 # Slightly modified Linux for light backgrounds
69 69 __PColLightBG = __PColLinux.copy('LightBG')
70 70
71 71 __PColLightBG.colors.update(
72 72 in_prompt = InputColors.Blue,
73 73 in_number = InputColors.LightBlue,
74 74 in_prompt2 = InputColors.Blue
75 75 )
76 76 PromptColors.add_scheme(__PColLightBG)
77 77
78 78 del Colors,InputColors
79 79
80 80 #-----------------------------------------------------------------------------
81 81 # Utilities
82 82 #-----------------------------------------------------------------------------
83 83
84 84 def multiple_replace(dict, text):
85 85 """ Replace in 'text' all occurences of any key in the given
86 86 dictionary by its corresponding value. Returns the new string."""
87 87
88 88 # Function by Xavier Defrang, originally found at:
89 89 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81330
90 90
91 91 # Create a regular expression from the dictionary keys
92 92 regex = re.compile("(%s)" % "|".join(map(re.escape, dict.keys())))
93 93 # For each match, look-up corresponding value in dictionary
94 94 return regex.sub(lambda mo: dict[mo.string[mo.start():mo.end()]], text)
95 95
96 96 #-----------------------------------------------------------------------------
97 97 # Special characters that can be used in prompt templates, mainly bash-like
98 98 #-----------------------------------------------------------------------------
99 99
100 100 # If $HOME isn't defined (Windows), make it an absurd string so that it can
101 101 # never be expanded out into '~'. Basically anything which can never be a
102 102 # reasonable directory name will do, we just want the $HOME -> '~' operation
103 103 # to become a no-op. We pre-compute $HOME here so it's not done on every
104 104 # prompt call.
105 105
106 106 # FIXME:
107 107
108 108 # - This should be turned into a class which does proper namespace management,
109 109 # since the prompt specials need to be evaluated in a certain namespace.
110 110 # Currently it's just globals, which need to be managed manually by code
111 111 # below.
112 112
113 113 # - I also need to split up the color schemes from the prompt specials
114 114 # somehow. I don't have a clean design for that quite yet.
115 115
116 116 HOME = os.environ.get("HOME","//////:::::ZZZZZ,,,~~~")
117 117
118 118 # We precompute a few more strings here for the prompt_specials, which are
119 119 # fixed once ipython starts. This reduces the runtime overhead of computing
120 120 # prompt strings.
121 121 USER = os.environ.get("USER")
122 122 HOSTNAME = socket.gethostname()
123 123 HOSTNAME_SHORT = HOSTNAME.split(".")[0]
124 124 ROOT_SYMBOL = "$#"[os.name=='nt' or os.getuid()==0]
125 125
126 126 prompt_specials_color = {
127 127 # Prompt/history count
128 128 '%n' : '${self.col_num}' '${self.cache.prompt_count}' '${self.col_p}',
129 129 r'\#': '${self.col_num}' '${self.cache.prompt_count}' '${self.col_p}',
130 130 # Just the prompt counter number, WITHOUT any coloring wrappers, so users
131 131 # can get numbers displayed in whatever color they want.
132 132 r'\N': '${self.cache.prompt_count}',
133 133
134 134 # Prompt/history count, with the actual digits replaced by dots. Used
135 135 # mainly in continuation prompts (prompt_in2)
136 136 #r'\D': '${"."*len(str(self.cache.prompt_count))}',
137 137
138 138 # More robust form of the above expression, that uses the __builtin__
139 139 # module. Note that we can NOT use __builtins__ (note the 's'), because
140 140 # that can either be a dict or a module, and can even mutate at runtime,
141 141 # depending on the context (Python makes no guarantees on it). In
142 142 # contrast, __builtin__ is always a module object, though it must be
143 143 # explicitly imported.
144 144 r'\D': '${"."*__builtin__.len(__builtin__.str(self.cache.prompt_count))}',
145 145
146 146 # Current working directory
147 147 r'\w': '${os.getcwd()}',
148 148 # Current time
149 149 r'\t' : '${time.strftime("%H:%M:%S")}',
150 150 # Basename of current working directory.
151 151 # (use os.sep to make this portable across OSes)
152 152 r'\W' : '${os.getcwd().split("%s")[-1]}' % os.sep,
153 153 # These X<N> are an extension to the normal bash prompts. They return
154 154 # N terms of the path, after replacing $HOME with '~'
155 155 r'\X0': '${os.getcwd().replace("%s","~")}' % HOME,
156 156 r'\X1': '${self.cwd_filt(1)}',
157 157 r'\X2': '${self.cwd_filt(2)}',
158 158 r'\X3': '${self.cwd_filt(3)}',
159 159 r'\X4': '${self.cwd_filt(4)}',
160 160 r'\X5': '${self.cwd_filt(5)}',
161 161 # Y<N> are similar to X<N>, but they show '~' if it's the directory
162 162 # N+1 in the list. Somewhat like %cN in tcsh.
163 163 r'\Y0': '${self.cwd_filt2(0)}',
164 164 r'\Y1': '${self.cwd_filt2(1)}',
165 165 r'\Y2': '${self.cwd_filt2(2)}',
166 166 r'\Y3': '${self.cwd_filt2(3)}',
167 167 r'\Y4': '${self.cwd_filt2(4)}',
168 168 r'\Y5': '${self.cwd_filt2(5)}',
169 169 # Hostname up to first .
170 170 r'\h': HOSTNAME_SHORT,
171 171 # Full hostname
172 172 r'\H': HOSTNAME,
173 173 # Username of current user
174 174 r'\u': USER,
175 175 # Escaped '\'
176 176 '\\\\': '\\',
177 177 # Newline
178 178 r'\n': '\n',
179 179 # Carriage return
180 180 r'\r': '\r',
181 181 # Release version
182 182 r'\v': release.version,
183 183 # Root symbol ($ or #)
184 184 r'\$': ROOT_SYMBOL,
185 185 }
186 186
187 187 # A copy of the prompt_specials dictionary but with all color escapes removed,
188 188 # so we can correctly compute the prompt length for the auto_rewrite method.
189 189 prompt_specials_nocolor = prompt_specials_color.copy()
190 190 prompt_specials_nocolor['%n'] = '${self.cache.prompt_count}'
191 191 prompt_specials_nocolor[r'\#'] = '${self.cache.prompt_count}'
192 192
193 193 # Add in all the InputTermColors color escapes as valid prompt characters.
194 194 # They all get added as \\C_COLORNAME, so that we don't have any conflicts
195 195 # with a color name which may begin with a letter used by any other of the
196 196 # allowed specials. This of course means that \\C will never be allowed for
197 197 # anything else.
198 198 input_colors = coloransi.InputTermColors
199 199 for _color in dir(input_colors):
200 200 if _color[0] != '_':
201 201 c_name = r'\C_'+_color
202 202 prompt_specials_color[c_name] = getattr(input_colors,_color)
203 203 prompt_specials_nocolor[c_name] = ''
204 204
205 205 # we default to no color for safety. Note that prompt_specials is a global
206 206 # variable used by all prompt objects.
207 207 prompt_specials = prompt_specials_nocolor
208 208
209 209 #-----------------------------------------------------------------------------
210 210 # More utilities
211 211 #-----------------------------------------------------------------------------
212 212
213 213 def str_safe(arg):
214 214 """Convert to a string, without ever raising an exception.
215 215
216 216 If str(arg) fails, <ERROR: ... > is returned, where ... is the exception
217 217 error message."""
218 218
219 219 try:
220 220 out = str(arg)
221 221 except UnicodeError:
222 222 try:
223 223 out = arg.encode('utf_8','replace')
224 224 except Exception,msg:
225 225 # let's keep this little duplication here, so that the most common
226 226 # case doesn't suffer from a double try wrapping.
227 out = '<ERRORx: %s>' % msg
227 out = '<ERROR: %s>' % msg
228 228 except Exception,msg:
229 out = '<ERRORy: %s>' % msg
229 out = '<ERROR: %s>' % msg
230 230 #raise # dbg
231 231 return out
232 232
233 233 #-----------------------------------------------------------------------------
234 234 # Prompt classes
235 235 #-----------------------------------------------------------------------------
236 236
237 237 class BasePrompt(object):
238 238 """Interactive prompt similar to Mathematica's."""
239 239
240 240 def _get_p_template(self):
241 241 return self._p_template
242 242
243 243 def _set_p_template(self,val):
244 244 self._p_template = val
245 245 self.set_p_str()
246 246
247 247 p_template = property(_get_p_template,_set_p_template,
248 248 doc='Template for prompt string creation')
249 249
250 250 def __init__(self, cache, sep, prompt, pad_left=False):
251 251
252 252 # Hack: we access information about the primary prompt through the
253 253 # cache argument. We need this, because we want the secondary prompt
254 254 # to be aligned with the primary one. Color table info is also shared
255 255 # by all prompt classes through the cache. Nice OO spaghetti code!
256 256 self.cache = cache
257 257 self.sep = sep
258 258
259 259 # regexp to count the number of spaces at the end of a prompt
260 260 # expression, useful for prompt auto-rewriting
261 261 self.rspace = re.compile(r'(\s*)$')
262 262 # Flag to left-pad prompt strings to match the length of the primary
263 263 # prompt
264 264 self.pad_left = pad_left
265 265
266 266 # Set template to create each actual prompt (where numbers change).
267 267 # Use a property
268 268 self.p_template = prompt
269 269 self.set_p_str()
270 270
271 271 def set_p_str(self):
272 272 """ Set the interpolating prompt strings.
273 273
274 274 This must be called every time the color settings change, because the
275 275 prompt_specials global may have changed."""
276 276
277 277 import os,time # needed in locals for prompt string handling
278 278 loc = locals()
279 279 try:
280 280 self.p_str = ItplNS('%s%s%s' %
281 281 ('${self.sep}${self.col_p}',
282 282 multiple_replace(prompt_specials, self.p_template),
283 283 '${self.col_norm}'),self.cache.shell.user_ns,loc)
284 284
285 285 self.p_str_nocolor = ItplNS(multiple_replace(prompt_specials_nocolor,
286 286 self.p_template),
287 287 self.cache.shell.user_ns,loc)
288 288 except:
289 289 print "Illegal prompt template (check $ usage!):",self.p_template
290 290 self.p_str = self.p_template
291 291 self.p_str_nocolor = self.p_template
292 292
293 293 def write(self, msg):
294 294 sys.stdout.write(msg)
295 295 return ''
296 296
297 297 def __str__(self):
298 298 """Return a string form of the prompt.
299 299
300 300 This for is useful for continuation and output prompts, since it is
301 301 left-padded to match lengths with the primary one (if the
302 302 self.pad_left attribute is set)."""
303 303
304 304 out_str = str_safe(self.p_str)
305 305 if self.pad_left:
306 306 # We must find the amount of padding required to match lengths,
307 307 # taking the color escapes (which are invisible on-screen) into
308 308 # account.
309 309 esc_pad = len(out_str) - len(str_safe(self.p_str_nocolor))
310 310 format = '%%%ss' % (len(str(self.cache.last_prompt))+esc_pad)
311 311 return format % out_str
312 312 else:
313 313 return out_str
314 314
315 315 # these path filters are put in as methods so that we can control the
316 316 # namespace where the prompt strings get evaluated
317 317 def cwd_filt(self, depth):
318 318 """Return the last depth elements of the current working directory.
319 319
320 320 $HOME is always replaced with '~'.
321 321 If depth==0, the full path is returned."""
322 322
323 323 cwd = os.getcwd().replace(HOME,"~")
324 324 out = os.sep.join(cwd.split(os.sep)[-depth:])
325 325 if out:
326 326 return out
327 327 else:
328 328 return os.sep
329 329
330 330 def cwd_filt2(self, depth):
331 331 """Return the last depth elements of the current working directory.
332 332
333 333 $HOME is always replaced with '~'.
334 334 If depth==0, the full path is returned."""
335 335
336 336 full_cwd = os.getcwd()
337 337 cwd = full_cwd.replace(HOME,"~").split(os.sep)
338 338 if '~' in cwd and len(cwd) == depth+1:
339 339 depth += 1
340 340 drivepart = ''
341 341 if sys.platform == 'win32' and len(cwd) > depth:
342 342 drivepart = os.path.splitdrive(full_cwd)[0]
343 343 out = drivepart + '/'.join(cwd[-depth:])
344 344
345 345 if out:
346 346 return out
347 347 else:
348 348 return os.sep
349 349
350 350 def __nonzero__(self):
351 351 """Implement boolean behavior.
352 352
353 353 Checks whether the p_str attribute is non-empty"""
354 354
355 355 return bool(self.p_template)
356 356
357 357
358 358 class Prompt1(BasePrompt):
359 359 """Input interactive prompt similar to Mathematica's."""
360 360
361 361 def __init__(self, cache, sep='\n', prompt='In [\\#]: ', pad_left=True):
362 362 BasePrompt.__init__(self, cache, sep, prompt, pad_left)
363 363
364 364 def set_colors(self):
365 365 self.set_p_str()
366 366 Colors = self.cache.color_table.active_colors # shorthand
367 367 self.col_p = Colors.in_prompt
368 368 self.col_num = Colors.in_number
369 369 self.col_norm = Colors.in_normal
370 370 # We need a non-input version of these escapes for the '--->'
371 371 # auto-call prompts used in the auto_rewrite() method.
372 372 self.col_p_ni = self.col_p.replace('\001','').replace('\002','')
373 373 self.col_norm_ni = Colors.normal
374 374
375 375 def __str__(self):
376 376 self.cache.last_prompt = str_safe(self.p_str_nocolor).split('\n')[-1]
377 377 return str_safe(self.p_str)
378 378
379 379 def auto_rewrite(self):
380 380 """Return a string of the form '--->' which lines up with the previous
381 381 input string. Useful for systems which re-write the user input when
382 382 handling automatically special syntaxes."""
383 383
384 384 curr = str(self.cache.last_prompt)
385 385 nrspaces = len(self.rspace.search(curr).group())
386 386 return '%s%s>%s%s' % (self.col_p_ni,'-'*(len(curr)-nrspaces-1),
387 387 ' '*nrspaces,self.col_norm_ni)
388 388
389 389
390 390 class PromptOut(BasePrompt):
391 391 """Output interactive prompt similar to Mathematica's."""
392 392
393 393 def __init__(self, cache, sep='', prompt='Out[\\#]: ', pad_left=True):
394 394 BasePrompt.__init__(self, cache, sep, prompt, pad_left)
395 395 if not self.p_template:
396 396 self.__str__ = lambda: ''
397 397
398 398 def set_colors(self):
399 399 self.set_p_str()
400 400 Colors = self.cache.color_table.active_colors # shorthand
401 401 self.col_p = Colors.out_prompt
402 402 self.col_num = Colors.out_number
403 403 self.col_norm = Colors.normal
404 404
405 405
406 406 class Prompt2(BasePrompt):
407 407 """Interactive continuation prompt."""
408 408
409 409 def __init__(self, cache, prompt=' .\\D.: ', pad_left=True):
410 410 self.cache = cache
411 411 self.p_template = prompt
412 412 self.pad_left = pad_left
413 413 self.set_p_str()
414 414
415 415 def set_p_str(self):
416 416 import os,time # needed in locals for prompt string handling
417 417 loc = locals()
418 418 self.p_str = ItplNS('%s%s%s' %
419 419 ('${self.col_p2}',
420 420 multiple_replace(prompt_specials, self.p_template),
421 421 '$self.col_norm'),
422 422 self.cache.shell.user_ns,loc)
423 423 self.p_str_nocolor = ItplNS(multiple_replace(prompt_specials_nocolor,
424 424 self.p_template),
425 425 self.cache.shell.user_ns,loc)
426 426
427 427 def set_colors(self):
428 428 self.set_p_str()
429 429 Colors = self.cache.color_table.active_colors
430 430 self.col_p2 = Colors.in_prompt2
431 431 self.col_norm = Colors.in_normal
432 432 # FIXME (2004-06-16) HACK: prevent crashes for users who haven't
433 433 # updated their prompt_in2 definitions. Remove eventually.
434 434 self.col_p = Colors.out_prompt
435 435 self.col_num = Colors.out_number
436 436
@@ -1,277 +1,274
1 1 # -*- coding: utf-8 -*-
2 2 """String interpolation for Python (by Ka-Ping Yee, 14 Feb 2000).
3 3
4 4 This module lets you quickly and conveniently interpolate values into
5 5 strings (in the flavour of Perl or Tcl, but with less extraneous
6 6 punctuation). You get a bit more power than in the other languages,
7 7 because this module allows subscripting, slicing, function calls,
8 8 attribute lookup, or arbitrary expressions. Variables and expressions
9 9 are evaluated in the namespace of the caller.
10 10
11 11 The itpl() function returns the result of interpolating a string, and
12 12 printpl() prints out an interpolated string. Here are some examples:
13 13
14 14 from Itpl import printpl
15 15 printpl("Here is a $string.")
16 16 printpl("Here is a $module.member.")
17 17 printpl("Here is an $object.member.")
18 18 printpl("Here is a $functioncall(with, arguments).")
19 19 printpl("Here is an ${arbitrary + expression}.")
20 20 printpl("Here is an $array[3] member.")
21 21 printpl("Here is a $dictionary['member'].")
22 22
23 23 The filter() function filters a file object so that output through it
24 24 is interpolated. This lets you produce the illusion that Python knows
25 25 how to do interpolation:
26 26
27 27 import Itpl
28 28 sys.stdout = Itpl.filter()
29 29 f = "fancy"
30 30 print "Is this not $f?"
31 31 print "Standard output has been replaced with a $sys.stdout object."
32 32 sys.stdout = Itpl.unfilter()
33 33 print "Okay, back $to $normal."
34 34
35 35 Under the hood, the Itpl class represents a string that knows how to
36 36 interpolate values. An instance of the class parses the string once
37 37 upon initialization; the evaluation and substitution can then be done
38 38 each time the instance is evaluated with str(instance). For example:
39 39
40 40 from Itpl import Itpl
41 41 s = Itpl("Here is $foo.")
42 42 foo = 5
43 43 print str(s)
44 44 foo = "bar"
45 45 print str(s)
46 46 """
47 47
48 48 #*****************************************************************************
49 49 #
50 50 # Copyright (c) 2001 Ka-Ping Yee <ping@lfw.org>
51 51 #
52 52 #
53 53 # Published under the terms of the MIT license, hereby reproduced:
54 54 #
55 55 # Permission is hereby granted, free of charge, to any person obtaining a copy
56 56 # of this software and associated documentation files (the "Software"), to
57 57 # deal in the Software without restriction, including without limitation the
58 58 # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
59 59 # sell copies of the Software, and to permit persons to whom the Software is
60 60 # furnished to do so, subject to the following conditions:
61 61 #
62 62 # The above copyright notice and this permission notice shall be included in
63 63 # all copies or substantial portions of the Software.
64 64 #
65 65 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
66 66 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
67 67 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
68 68 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
69 69 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
70 70 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
71 71 # IN THE SOFTWARE.
72 72 #
73 73 #*****************************************************************************
74 74
75 75 __author__ = 'Ka-Ping Yee <ping@lfw.org>'
76 76 __license__ = 'MIT'
77 77
78 78 import sys
79 79 from tokenize import tokenprog
80 80
81 81 class ItplError(ValueError):
82 82 def __init__(self, text, pos):
83 83 self.text = text
84 84 self.pos = pos
85 85 def __str__(self):
86 86 return "unfinished expression in %s at char %d" % (
87 87 repr(self.text), self.pos)
88 88
89 89 def matchorfail(text, pos):
90 90 match = tokenprog.match(text, pos)
91 91 if match is None:
92 92 raise ItplError(text, pos)
93 93 return match, match.end()
94 94
95 95 class Itpl:
96 96 """Class representing a string with interpolation abilities.
97 97
98 98 Upon creation, an instance works out what parts of the format
99 99 string are literal and what parts need to be evaluated. The
100 100 evaluation and substitution happens in the namespace of the
101 101 caller when str(instance) is called."""
102 102
103 103 def __init__(self, format,codec='utf_8',encoding_errors='backslashreplace'):
104 104 """The single mandatory argument to this constructor is a format
105 105 string.
106 106
107 107 The format string is parsed according to the following rules:
108 108
109 109 1. A dollar sign and a name, possibly followed by any of:
110 110 - an open-paren, and anything up to the matching paren
111 111 - an open-bracket, and anything up to the matching bracket
112 112 - a period and a name
113 113 any number of times, is evaluated as a Python expression.
114 114
115 115 2. A dollar sign immediately followed by an open-brace, and
116 116 anything up to the matching close-brace, is evaluated as
117 117 a Python expression.
118 118
119 119 3. Outside of the expressions described in the above two rules,
120 120 two dollar signs in a row give you one literal dollar sign.
121 121
122 122 Optional arguments:
123 123
124 124 - codec('utf_8'): a string containing the name of a valid Python
125 125 codec.
126 126
127 127 - encoding_errors('backslashreplace'): a string with a valid error handling
128 128 policy. See the codecs module documentation for details.
129 129
130 130 These are used to encode the format string if a call to str() fails on
131 131 the expanded result."""
132 132
133 133 if not isinstance(format,basestring):
134 134 raise TypeError, "needs string initializer"
135 135 self.format = format
136 136 self.codec = codec
137 137 self.encoding_errors = encoding_errors
138 138
139 139 namechars = "abcdefghijklmnopqrstuvwxyz" \
140 140 "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
141 141 chunks = []
142 142 pos = 0
143 143
144 144 while 1:
145 145 dollar = format.find("$", pos)
146 146 if dollar < 0: break
147 147 nextchar = format[dollar+1]
148 148
149 149 if nextchar == "{":
150 150 chunks.append((0, format[pos:dollar]))
151 151 pos, level = dollar+2, 1
152 152 while level:
153 153 match, pos = matchorfail(format, pos)
154 154 tstart, tend = match.regs[3]
155 155 token = format[tstart:tend]
156 156 if token == "{": level = level+1
157 157 elif token == "}": level = level-1
158 158 chunks.append((1, format[dollar+2:pos-1]))
159 159
160 160 elif nextchar in namechars:
161 161 chunks.append((0, format[pos:dollar]))
162 162 match, pos = matchorfail(format, dollar+1)
163 163 while pos < len(format):
164 164 if format[pos] == "." and \
165 165 pos+1 < len(format) and format[pos+1] in namechars:
166 166 match, pos = matchorfail(format, pos+1)
167 167 elif format[pos] in "([":
168 168 pos, level = pos+1, 1
169 169 while level:
170 170 match, pos = matchorfail(format, pos)
171 171 tstart, tend = match.regs[3]
172 172 token = format[tstart:tend]
173 173 if token[0] in "([": level = level+1
174 174 elif token[0] in ")]": level = level-1
175 175 else: break
176 176 chunks.append((1, format[dollar+1:pos]))
177 177
178 178 else:
179 179 chunks.append((0, format[pos:dollar+1]))
180 180 pos = dollar + 1 + (nextchar == "$")
181 181
182 182 if pos < len(format): chunks.append((0, format[pos:]))
183 183 self.chunks = chunks
184 184
185 185 def __repr__(self):
186 186 return "<Itpl %s >" % repr(self.format)
187 187
188 188 def _str(self,glob,loc):
189 189 """Evaluate to a string in the given globals/locals.
190 190
191 191 The final output is built by calling str(), but if this fails, the
192 192 result is encoded with the instance's codec and error handling policy,
193 193 via a call to out.encode(self.codec,self.encoding_errors)"""
194 194 result = []
195 195 app = result.append
196 196 for live, chunk in self.chunks:
197 197 if live: app(str(eval(chunk,glob,loc)))
198 198 else: app(chunk)
199 out = u''.join(result)
199 out = ''.join(result)
200 200 try:
201 201 return str(out)
202 202 except UnicodeError:
203 203 return out.encode(self.codec,self.encoding_errors)
204 204
205 205 def __str__(self):
206 206 """Evaluate and substitute the appropriate parts of the string."""
207 207
208 208 # We need to skip enough frames to get to the actual caller outside of
209 209 # Itpl.
210 210 frame = sys._getframe(1)
211 211 while frame.f_globals["__name__"] == __name__: frame = frame.f_back
212 212 loc, glob = frame.f_locals, frame.f_globals
213 213
214 214 return self._str(glob,loc)
215
216 def encode(self, encoding, errors):
217 return str(self)#.encode(encoding, errors)
218 215
219 216 class ItplNS(Itpl):
220 217 """Class representing a string with interpolation abilities.
221 218
222 219 This inherits from Itpl, but at creation time a namespace is provided
223 220 where the evaluation will occur. The interpolation becomes a bit more
224 221 efficient, as no traceback needs to be extracte. It also allows the
225 222 caller to supply a different namespace for the interpolation to occur than
226 223 its own."""
227 224
228 225 def __init__(self, format,globals,locals=None,
229 226 codec='utf_8',encoding_errors='backslashreplace'):
230 227 """ItplNS(format,globals[,locals]) -> interpolating string instance.
231 228
232 229 This constructor, besides a format string, takes a globals dictionary
233 230 and optionally a locals (which defaults to globals if not provided).
234 231
235 232 For further details, see the Itpl constructor."""
236 233
237 234 if locals is None:
238 235 locals = globals
239 236 self.globals = globals
240 237 self.locals = locals
241 238 Itpl.__init__(self,format,codec,encoding_errors)
242 239
243 240 def __str__(self):
244 241 """Evaluate and substitute the appropriate parts of the string."""
245 242 return self._str(self.globals,self.locals)
246 243
247 244 def __repr__(self):
248 245 return "<ItplNS %s >" % repr(self.format)
249 246
250 247 # utilities for fast printing
251 248 def itpl(text): return str(Itpl(text))
252 249 def printpl(text): print itpl(text)
253 250 # versions with namespace
254 251 def itplns(text,globals,locals=None): return str(ItplNS(text,globals,locals))
255 252 def printplns(text,globals,locals=None): print itplns(text,globals,locals)
256 253
257 254 class ItplFile:
258 255 """A file object that filters each write() through an interpolator."""
259 256 def __init__(self, file): self.file = file
260 257 def __repr__(self): return "<interpolated " + repr(self.file) + ">"
261 258 def __getattr__(self, attr): return getattr(self.file, attr)
262 259 def write(self, text): self.file.write(str(Itpl(text)))
263 260
264 261 def filter(file=sys.stdout):
265 262 """Return an ItplFile that filters writes to the given file object.
266 263
267 264 'file = filter(file)' replaces 'file' with a filtered object that
268 265 has a write() method. When called with no argument, this creates
269 266 a filter to sys.stdout."""
270 267 return ItplFile(file)
271 268
272 269 def unfilter(ifile=None):
273 270 """Return the original file that corresponds to the given ItplFile.
274 271
275 272 'file = unfilter(file)' undoes the effect of 'file = filter(file)'.
276 273 'sys.stdout = unfilter()' undoes the effect of 'sys.stdout = filter()'."""
277 274 return ifile and ifile.file or sys.stdout.file
General Comments 0
You need to be logged in to leave comments. Login now