##// END OF EJS Templates
Copy EvalFormatter and FullEvalFormatter from prompt-manager branch.
Thomas Kluyver -
Show More
@@ -1,739 +1,753 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Utilities for working with strings and text.
3 Utilities for working with strings and text.
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 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 import __main__
17 import __main__
18
18
19 import locale
19 import locale
20 import os
20 import os
21 import re
21 import re
22 import shutil
22 import shutil
23 import sys
23 import sys
24 import textwrap
24 import textwrap
25 from string import Formatter
25 from string import Formatter
26
26
27 from IPython.external.path import path
27 from IPython.external.path import path
28 from IPython.utils import py3compat
28 from IPython.utils import py3compat
29 from IPython.utils.io import nlprint
29 from IPython.utils.io import nlprint
30 from IPython.utils.data import flatten
30 from IPython.utils.data import flatten
31
31
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33 # Code
33 # Code
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35
35
36 # Less conservative replacement for sys.getdefaultencoding, that will try
36 # Less conservative replacement for sys.getdefaultencoding, that will try
37 # to match the environment.
37 # to match the environment.
38 # Defined here as central function, so if we find better choices, we
38 # Defined here as central function, so if we find better choices, we
39 # won't need to make changes all over IPython.
39 # won't need to make changes all over IPython.
40 def getdefaultencoding():
40 def getdefaultencoding():
41 """Return IPython's guess for the default encoding for bytes as text.
41 """Return IPython's guess for the default encoding for bytes as text.
42
42
43 Asks for stdin.encoding first, to match the calling Terminal, but that
43 Asks for stdin.encoding first, to match the calling Terminal, but that
44 is often None for subprocesses. Fall back on locale.getpreferredencoding()
44 is often None for subprocesses. Fall back on locale.getpreferredencoding()
45 which should be a sensible platform default (that respects LANG environment),
45 which should be a sensible platform default (that respects LANG environment),
46 and finally to sys.getdefaultencoding() which is the most conservative option,
46 and finally to sys.getdefaultencoding() which is the most conservative option,
47 and usually ASCII.
47 and usually ASCII.
48 """
48 """
49 enc = sys.stdin.encoding
49 enc = sys.stdin.encoding
50 if not enc or enc=='ascii':
50 if not enc or enc=='ascii':
51 try:
51 try:
52 # There are reports of getpreferredencoding raising errors
52 # There are reports of getpreferredencoding raising errors
53 # in some cases, which may well be fixed, but let's be conservative here.
53 # in some cases, which may well be fixed, but let's be conservative here.
54 enc = locale.getpreferredencoding()
54 enc = locale.getpreferredencoding()
55 except Exception:
55 except Exception:
56 pass
56 pass
57 return enc or sys.getdefaultencoding()
57 return enc or sys.getdefaultencoding()
58
58
59 def unquote_ends(istr):
59 def unquote_ends(istr):
60 """Remove a single pair of quotes from the endpoints of a string."""
60 """Remove a single pair of quotes from the endpoints of a string."""
61
61
62 if not istr:
62 if not istr:
63 return istr
63 return istr
64 if (istr[0]=="'" and istr[-1]=="'") or \
64 if (istr[0]=="'" and istr[-1]=="'") or \
65 (istr[0]=='"' and istr[-1]=='"'):
65 (istr[0]=='"' and istr[-1]=='"'):
66 return istr[1:-1]
66 return istr[1:-1]
67 else:
67 else:
68 return istr
68 return istr
69
69
70
70
71 class LSString(str):
71 class LSString(str):
72 """String derivative with a special access attributes.
72 """String derivative with a special access attributes.
73
73
74 These are normal strings, but with the special attributes:
74 These are normal strings, but with the special attributes:
75
75
76 .l (or .list) : value as list (split on newlines).
76 .l (or .list) : value as list (split on newlines).
77 .n (or .nlstr): original value (the string itself).
77 .n (or .nlstr): original value (the string itself).
78 .s (or .spstr): value as whitespace-separated string.
78 .s (or .spstr): value as whitespace-separated string.
79 .p (or .paths): list of path objects
79 .p (or .paths): list of path objects
80
80
81 Any values which require transformations are computed only once and
81 Any values which require transformations are computed only once and
82 cached.
82 cached.
83
83
84 Such strings are very useful to efficiently interact with the shell, which
84 Such strings are very useful to efficiently interact with the shell, which
85 typically only understands whitespace-separated options for commands."""
85 typically only understands whitespace-separated options for commands."""
86
86
87 def get_list(self):
87 def get_list(self):
88 try:
88 try:
89 return self.__list
89 return self.__list
90 except AttributeError:
90 except AttributeError:
91 self.__list = self.split('\n')
91 self.__list = self.split('\n')
92 return self.__list
92 return self.__list
93
93
94 l = list = property(get_list)
94 l = list = property(get_list)
95
95
96 def get_spstr(self):
96 def get_spstr(self):
97 try:
97 try:
98 return self.__spstr
98 return self.__spstr
99 except AttributeError:
99 except AttributeError:
100 self.__spstr = self.replace('\n',' ')
100 self.__spstr = self.replace('\n',' ')
101 return self.__spstr
101 return self.__spstr
102
102
103 s = spstr = property(get_spstr)
103 s = spstr = property(get_spstr)
104
104
105 def get_nlstr(self):
105 def get_nlstr(self):
106 return self
106 return self
107
107
108 n = nlstr = property(get_nlstr)
108 n = nlstr = property(get_nlstr)
109
109
110 def get_paths(self):
110 def get_paths(self):
111 try:
111 try:
112 return self.__paths
112 return self.__paths
113 except AttributeError:
113 except AttributeError:
114 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
114 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
115 return self.__paths
115 return self.__paths
116
116
117 p = paths = property(get_paths)
117 p = paths = property(get_paths)
118
118
119 # FIXME: We need to reimplement type specific displayhook and then add this
119 # FIXME: We need to reimplement type specific displayhook and then add this
120 # back as a custom printer. This should also be moved outside utils into the
120 # back as a custom printer. This should also be moved outside utils into the
121 # core.
121 # core.
122
122
123 # def print_lsstring(arg):
123 # def print_lsstring(arg):
124 # """ Prettier (non-repr-like) and more informative printer for LSString """
124 # """ Prettier (non-repr-like) and more informative printer for LSString """
125 # print "LSString (.p, .n, .l, .s available). Value:"
125 # print "LSString (.p, .n, .l, .s available). Value:"
126 # print arg
126 # print arg
127 #
127 #
128 #
128 #
129 # print_lsstring = result_display.when_type(LSString)(print_lsstring)
129 # print_lsstring = result_display.when_type(LSString)(print_lsstring)
130
130
131
131
132 class SList(list):
132 class SList(list):
133 """List derivative with a special access attributes.
133 """List derivative with a special access attributes.
134
134
135 These are normal lists, but with the special attributes:
135 These are normal lists, but with the special attributes:
136
136
137 .l (or .list) : value as list (the list itself).
137 .l (or .list) : value as list (the list itself).
138 .n (or .nlstr): value as a string, joined on newlines.
138 .n (or .nlstr): value as a string, joined on newlines.
139 .s (or .spstr): value as a string, joined on spaces.
139 .s (or .spstr): value as a string, joined on spaces.
140 .p (or .paths): list of path objects
140 .p (or .paths): list of path objects
141
141
142 Any values which require transformations are computed only once and
142 Any values which require transformations are computed only once and
143 cached."""
143 cached."""
144
144
145 def get_list(self):
145 def get_list(self):
146 return self
146 return self
147
147
148 l = list = property(get_list)
148 l = list = property(get_list)
149
149
150 def get_spstr(self):
150 def get_spstr(self):
151 try:
151 try:
152 return self.__spstr
152 return self.__spstr
153 except AttributeError:
153 except AttributeError:
154 self.__spstr = ' '.join(self)
154 self.__spstr = ' '.join(self)
155 return self.__spstr
155 return self.__spstr
156
156
157 s = spstr = property(get_spstr)
157 s = spstr = property(get_spstr)
158
158
159 def get_nlstr(self):
159 def get_nlstr(self):
160 try:
160 try:
161 return self.__nlstr
161 return self.__nlstr
162 except AttributeError:
162 except AttributeError:
163 self.__nlstr = '\n'.join(self)
163 self.__nlstr = '\n'.join(self)
164 return self.__nlstr
164 return self.__nlstr
165
165
166 n = nlstr = property(get_nlstr)
166 n = nlstr = property(get_nlstr)
167
167
168 def get_paths(self):
168 def get_paths(self):
169 try:
169 try:
170 return self.__paths
170 return self.__paths
171 except AttributeError:
171 except AttributeError:
172 self.__paths = [path(p) for p in self if os.path.exists(p)]
172 self.__paths = [path(p) for p in self if os.path.exists(p)]
173 return self.__paths
173 return self.__paths
174
174
175 p = paths = property(get_paths)
175 p = paths = property(get_paths)
176
176
177 def grep(self, pattern, prune = False, field = None):
177 def grep(self, pattern, prune = False, field = None):
178 """ Return all strings matching 'pattern' (a regex or callable)
178 """ Return all strings matching 'pattern' (a regex or callable)
179
179
180 This is case-insensitive. If prune is true, return all items
180 This is case-insensitive. If prune is true, return all items
181 NOT matching the pattern.
181 NOT matching the pattern.
182
182
183 If field is specified, the match must occur in the specified
183 If field is specified, the match must occur in the specified
184 whitespace-separated field.
184 whitespace-separated field.
185
185
186 Examples::
186 Examples::
187
187
188 a.grep( lambda x: x.startswith('C') )
188 a.grep( lambda x: x.startswith('C') )
189 a.grep('Cha.*log', prune=1)
189 a.grep('Cha.*log', prune=1)
190 a.grep('chm', field=-1)
190 a.grep('chm', field=-1)
191 """
191 """
192
192
193 def match_target(s):
193 def match_target(s):
194 if field is None:
194 if field is None:
195 return s
195 return s
196 parts = s.split()
196 parts = s.split()
197 try:
197 try:
198 tgt = parts[field]
198 tgt = parts[field]
199 return tgt
199 return tgt
200 except IndexError:
200 except IndexError:
201 return ""
201 return ""
202
202
203 if isinstance(pattern, basestring):
203 if isinstance(pattern, basestring):
204 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
204 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
205 else:
205 else:
206 pred = pattern
206 pred = pattern
207 if not prune:
207 if not prune:
208 return SList([el for el in self if pred(match_target(el))])
208 return SList([el for el in self if pred(match_target(el))])
209 else:
209 else:
210 return SList([el for el in self if not pred(match_target(el))])
210 return SList([el for el in self if not pred(match_target(el))])
211
211
212 def fields(self, *fields):
212 def fields(self, *fields):
213 """ Collect whitespace-separated fields from string list
213 """ Collect whitespace-separated fields from string list
214
214
215 Allows quick awk-like usage of string lists.
215 Allows quick awk-like usage of string lists.
216
216
217 Example data (in var a, created by 'a = !ls -l')::
217 Example data (in var a, created by 'a = !ls -l')::
218 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
218 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
219 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
219 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
220
220
221 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
221 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
222 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
222 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
223 (note the joining by space).
223 (note the joining by space).
224 a.fields(-1) is ['ChangeLog', 'IPython']
224 a.fields(-1) is ['ChangeLog', 'IPython']
225
225
226 IndexErrors are ignored.
226 IndexErrors are ignored.
227
227
228 Without args, fields() just split()'s the strings.
228 Without args, fields() just split()'s the strings.
229 """
229 """
230 if len(fields) == 0:
230 if len(fields) == 0:
231 return [el.split() for el in self]
231 return [el.split() for el in self]
232
232
233 res = SList()
233 res = SList()
234 for el in [f.split() for f in self]:
234 for el in [f.split() for f in self]:
235 lineparts = []
235 lineparts = []
236
236
237 for fd in fields:
237 for fd in fields:
238 try:
238 try:
239 lineparts.append(el[fd])
239 lineparts.append(el[fd])
240 except IndexError:
240 except IndexError:
241 pass
241 pass
242 if lineparts:
242 if lineparts:
243 res.append(" ".join(lineparts))
243 res.append(" ".join(lineparts))
244
244
245 return res
245 return res
246
246
247 def sort(self,field= None, nums = False):
247 def sort(self,field= None, nums = False):
248 """ sort by specified fields (see fields())
248 """ sort by specified fields (see fields())
249
249
250 Example::
250 Example::
251 a.sort(1, nums = True)
251 a.sort(1, nums = True)
252
252
253 Sorts a by second field, in numerical order (so that 21 > 3)
253 Sorts a by second field, in numerical order (so that 21 > 3)
254
254
255 """
255 """
256
256
257 #decorate, sort, undecorate
257 #decorate, sort, undecorate
258 if field is not None:
258 if field is not None:
259 dsu = [[SList([line]).fields(field), line] for line in self]
259 dsu = [[SList([line]).fields(field), line] for line in self]
260 else:
260 else:
261 dsu = [[line, line] for line in self]
261 dsu = [[line, line] for line in self]
262 if nums:
262 if nums:
263 for i in range(len(dsu)):
263 for i in range(len(dsu)):
264 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
264 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
265 try:
265 try:
266 n = int(numstr)
266 n = int(numstr)
267 except ValueError:
267 except ValueError:
268 n = 0;
268 n = 0;
269 dsu[i][0] = n
269 dsu[i][0] = n
270
270
271
271
272 dsu.sort()
272 dsu.sort()
273 return SList([t[1] for t in dsu])
273 return SList([t[1] for t in dsu])
274
274
275
275
276 # FIXME: We need to reimplement type specific displayhook and then add this
276 # FIXME: We need to reimplement type specific displayhook and then add this
277 # back as a custom printer. This should also be moved outside utils into the
277 # back as a custom printer. This should also be moved outside utils into the
278 # core.
278 # core.
279
279
280 # def print_slist(arg):
280 # def print_slist(arg):
281 # """ Prettier (non-repr-like) and more informative printer for SList """
281 # """ Prettier (non-repr-like) and more informative printer for SList """
282 # print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
282 # print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
283 # if hasattr(arg, 'hideonce') and arg.hideonce:
283 # if hasattr(arg, 'hideonce') and arg.hideonce:
284 # arg.hideonce = False
284 # arg.hideonce = False
285 # return
285 # return
286 #
286 #
287 # nlprint(arg)
287 # nlprint(arg)
288 #
288 #
289 # print_slist = result_display.when_type(SList)(print_slist)
289 # print_slist = result_display.when_type(SList)(print_slist)
290
290
291
291
292 def esc_quotes(strng):
292 def esc_quotes(strng):
293 """Return the input string with single and double quotes escaped out"""
293 """Return the input string with single and double quotes escaped out"""
294
294
295 return strng.replace('"','\\"').replace("'","\\'")
295 return strng.replace('"','\\"').replace("'","\\'")
296
296
297
297
298 def make_quoted_expr(s):
298 def make_quoted_expr(s):
299 """Return string s in appropriate quotes, using raw string if possible.
299 """Return string s in appropriate quotes, using raw string if possible.
300
300
301 XXX - example removed because it caused encoding errors in documentation
301 XXX - example removed because it caused encoding errors in documentation
302 generation. We need a new example that doesn't contain invalid chars.
302 generation. We need a new example that doesn't contain invalid chars.
303
303
304 Note the use of raw string and padding at the end to allow trailing
304 Note the use of raw string and padding at the end to allow trailing
305 backslash.
305 backslash.
306 """
306 """
307
307
308 tail = ''
308 tail = ''
309 tailpadding = ''
309 tailpadding = ''
310 raw = ''
310 raw = ''
311 ucode = '' if py3compat.PY3 else 'u'
311 ucode = '' if py3compat.PY3 else 'u'
312 if "\\" in s:
312 if "\\" in s:
313 raw = 'r'
313 raw = 'r'
314 if s.endswith('\\'):
314 if s.endswith('\\'):
315 tail = '[:-1]'
315 tail = '[:-1]'
316 tailpadding = '_'
316 tailpadding = '_'
317 if '"' not in s:
317 if '"' not in s:
318 quote = '"'
318 quote = '"'
319 elif "'" not in s:
319 elif "'" not in s:
320 quote = "'"
320 quote = "'"
321 elif '"""' not in s and not s.endswith('"'):
321 elif '"""' not in s and not s.endswith('"'):
322 quote = '"""'
322 quote = '"""'
323 elif "'''" not in s and not s.endswith("'"):
323 elif "'''" not in s and not s.endswith("'"):
324 quote = "'''"
324 quote = "'''"
325 else:
325 else:
326 # give up, backslash-escaped string will do
326 # give up, backslash-escaped string will do
327 return '"%s"' % esc_quotes(s)
327 return '"%s"' % esc_quotes(s)
328 res = ucode + raw + quote + s + tailpadding + quote + tail
328 res = ucode + raw + quote + s + tailpadding + quote + tail
329 return res
329 return res
330
330
331
331
332 def qw(words,flat=0,sep=None,maxsplit=-1):
332 def qw(words,flat=0,sep=None,maxsplit=-1):
333 """Similar to Perl's qw() operator, but with some more options.
333 """Similar to Perl's qw() operator, but with some more options.
334
334
335 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
335 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
336
336
337 words can also be a list itself, and with flat=1, the output will be
337 words can also be a list itself, and with flat=1, the output will be
338 recursively flattened.
338 recursively flattened.
339
339
340 Examples:
340 Examples:
341
341
342 >>> qw('1 2')
342 >>> qw('1 2')
343 ['1', '2']
343 ['1', '2']
344
344
345 >>> qw(['a b','1 2',['m n','p q']])
345 >>> qw(['a b','1 2',['m n','p q']])
346 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
346 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
347
347
348 >>> qw(['a b','1 2',['m n','p q']],flat=1)
348 >>> qw(['a b','1 2',['m n','p q']],flat=1)
349 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q']
349 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q']
350 """
350 """
351
351
352 if isinstance(words, basestring):
352 if isinstance(words, basestring):
353 return [word.strip() for word in words.split(sep,maxsplit)
353 return [word.strip() for word in words.split(sep,maxsplit)
354 if word and not word.isspace() ]
354 if word and not word.isspace() ]
355 if flat:
355 if flat:
356 return flatten(map(qw,words,[1]*len(words)))
356 return flatten(map(qw,words,[1]*len(words)))
357 return map(qw,words)
357 return map(qw,words)
358
358
359
359
360 def qwflat(words,sep=None,maxsplit=-1):
360 def qwflat(words,sep=None,maxsplit=-1):
361 """Calls qw(words) in flat mode. It's just a convenient shorthand."""
361 """Calls qw(words) in flat mode. It's just a convenient shorthand."""
362 return qw(words,1,sep,maxsplit)
362 return qw(words,1,sep,maxsplit)
363
363
364
364
365 def qw_lol(indata):
365 def qw_lol(indata):
366 """qw_lol('a b') -> [['a','b']],
366 """qw_lol('a b') -> [['a','b']],
367 otherwise it's just a call to qw().
367 otherwise it's just a call to qw().
368
368
369 We need this to make sure the modules_some keys *always* end up as a
369 We need this to make sure the modules_some keys *always* end up as a
370 list of lists."""
370 list of lists."""
371
371
372 if isinstance(indata, basestring):
372 if isinstance(indata, basestring):
373 return [qw(indata)]
373 return [qw(indata)]
374 else:
374 else:
375 return qw(indata)
375 return qw(indata)
376
376
377
377
378 def grep(pat,list,case=1):
378 def grep(pat,list,case=1):
379 """Simple minded grep-like function.
379 """Simple minded grep-like function.
380 grep(pat,list) returns occurrences of pat in list, None on failure.
380 grep(pat,list) returns occurrences of pat in list, None on failure.
381
381
382 It only does simple string matching, with no support for regexps. Use the
382 It only does simple string matching, with no support for regexps. Use the
383 option case=0 for case-insensitive matching."""
383 option case=0 for case-insensitive matching."""
384
384
385 # This is pretty crude. At least it should implement copying only references
385 # This is pretty crude. At least it should implement copying only references
386 # to the original data in case it's big. Now it copies the data for output.
386 # to the original data in case it's big. Now it copies the data for output.
387 out=[]
387 out=[]
388 if case:
388 if case:
389 for term in list:
389 for term in list:
390 if term.find(pat)>-1: out.append(term)
390 if term.find(pat)>-1: out.append(term)
391 else:
391 else:
392 lpat=pat.lower()
392 lpat=pat.lower()
393 for term in list:
393 for term in list:
394 if term.lower().find(lpat)>-1: out.append(term)
394 if term.lower().find(lpat)>-1: out.append(term)
395
395
396 if len(out): return out
396 if len(out): return out
397 else: return None
397 else: return None
398
398
399
399
400 def dgrep(pat,*opts):
400 def dgrep(pat,*opts):
401 """Return grep() on dir()+dir(__builtins__).
401 """Return grep() on dir()+dir(__builtins__).
402
402
403 A very common use of grep() when working interactively."""
403 A very common use of grep() when working interactively."""
404
404
405 return grep(pat,dir(__main__)+dir(__main__.__builtins__),*opts)
405 return grep(pat,dir(__main__)+dir(__main__.__builtins__),*opts)
406
406
407
407
408 def idgrep(pat):
408 def idgrep(pat):
409 """Case-insensitive dgrep()"""
409 """Case-insensitive dgrep()"""
410
410
411 return dgrep(pat,0)
411 return dgrep(pat,0)
412
412
413
413
414 def igrep(pat,list):
414 def igrep(pat,list):
415 """Synonym for case-insensitive grep."""
415 """Synonym for case-insensitive grep."""
416
416
417 return grep(pat,list,case=0)
417 return grep(pat,list,case=0)
418
418
419
419
420 def indent(instr,nspaces=4, ntabs=0, flatten=False):
420 def indent(instr,nspaces=4, ntabs=0, flatten=False):
421 """Indent a string a given number of spaces or tabstops.
421 """Indent a string a given number of spaces or tabstops.
422
422
423 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
423 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
424
424
425 Parameters
425 Parameters
426 ----------
426 ----------
427
427
428 instr : basestring
428 instr : basestring
429 The string to be indented.
429 The string to be indented.
430 nspaces : int (default: 4)
430 nspaces : int (default: 4)
431 The number of spaces to be indented.
431 The number of spaces to be indented.
432 ntabs : int (default: 0)
432 ntabs : int (default: 0)
433 The number of tabs to be indented.
433 The number of tabs to be indented.
434 flatten : bool (default: False)
434 flatten : bool (default: False)
435 Whether to scrub existing indentation. If True, all lines will be
435 Whether to scrub existing indentation. If True, all lines will be
436 aligned to the same indentation. If False, existing indentation will
436 aligned to the same indentation. If False, existing indentation will
437 be strictly increased.
437 be strictly increased.
438
438
439 Returns
439 Returns
440 -------
440 -------
441
441
442 str|unicode : string indented by ntabs and nspaces.
442 str|unicode : string indented by ntabs and nspaces.
443
443
444 """
444 """
445 if instr is None:
445 if instr is None:
446 return
446 return
447 ind = '\t'*ntabs+' '*nspaces
447 ind = '\t'*ntabs+' '*nspaces
448 if flatten:
448 if flatten:
449 pat = re.compile(r'^\s*', re.MULTILINE)
449 pat = re.compile(r'^\s*', re.MULTILINE)
450 else:
450 else:
451 pat = re.compile(r'^', re.MULTILINE)
451 pat = re.compile(r'^', re.MULTILINE)
452 outstr = re.sub(pat, ind, instr)
452 outstr = re.sub(pat, ind, instr)
453 if outstr.endswith(os.linesep+ind):
453 if outstr.endswith(os.linesep+ind):
454 return outstr[:-len(ind)]
454 return outstr[:-len(ind)]
455 else:
455 else:
456 return outstr
456 return outstr
457
457
458 def native_line_ends(filename,backup=1):
458 def native_line_ends(filename,backup=1):
459 """Convert (in-place) a file to line-ends native to the current OS.
459 """Convert (in-place) a file to line-ends native to the current OS.
460
460
461 If the optional backup argument is given as false, no backup of the
461 If the optional backup argument is given as false, no backup of the
462 original file is left. """
462 original file is left. """
463
463
464 backup_suffixes = {'posix':'~','dos':'.bak','nt':'.bak','mac':'.bak'}
464 backup_suffixes = {'posix':'~','dos':'.bak','nt':'.bak','mac':'.bak'}
465
465
466 bak_filename = filename + backup_suffixes[os.name]
466 bak_filename = filename + backup_suffixes[os.name]
467
467
468 original = open(filename).read()
468 original = open(filename).read()
469 shutil.copy2(filename,bak_filename)
469 shutil.copy2(filename,bak_filename)
470 try:
470 try:
471 new = open(filename,'wb')
471 new = open(filename,'wb')
472 new.write(os.linesep.join(original.splitlines()))
472 new.write(os.linesep.join(original.splitlines()))
473 new.write(os.linesep) # ALWAYS put an eol at the end of the file
473 new.write(os.linesep) # ALWAYS put an eol at the end of the file
474 new.close()
474 new.close()
475 except:
475 except:
476 os.rename(bak_filename,filename)
476 os.rename(bak_filename,filename)
477 if not backup:
477 if not backup:
478 try:
478 try:
479 os.remove(bak_filename)
479 os.remove(bak_filename)
480 except:
480 except:
481 pass
481 pass
482
482
483
483
484 def list_strings(arg):
484 def list_strings(arg):
485 """Always return a list of strings, given a string or list of strings
485 """Always return a list of strings, given a string or list of strings
486 as input.
486 as input.
487
487
488 :Examples:
488 :Examples:
489
489
490 In [7]: list_strings('A single string')
490 In [7]: list_strings('A single string')
491 Out[7]: ['A single string']
491 Out[7]: ['A single string']
492
492
493 In [8]: list_strings(['A single string in a list'])
493 In [8]: list_strings(['A single string in a list'])
494 Out[8]: ['A single string in a list']
494 Out[8]: ['A single string in a list']
495
495
496 In [9]: list_strings(['A','list','of','strings'])
496 In [9]: list_strings(['A','list','of','strings'])
497 Out[9]: ['A', 'list', 'of', 'strings']
497 Out[9]: ['A', 'list', 'of', 'strings']
498 """
498 """
499
499
500 if isinstance(arg,basestring): return [arg]
500 if isinstance(arg,basestring): return [arg]
501 else: return arg
501 else: return arg
502
502
503
503
504 def marquee(txt='',width=78,mark='*'):
504 def marquee(txt='',width=78,mark='*'):
505 """Return the input string centered in a 'marquee'.
505 """Return the input string centered in a 'marquee'.
506
506
507 :Examples:
507 :Examples:
508
508
509 In [16]: marquee('A test',40)
509 In [16]: marquee('A test',40)
510 Out[16]: '**************** A test ****************'
510 Out[16]: '**************** A test ****************'
511
511
512 In [17]: marquee('A test',40,'-')
512 In [17]: marquee('A test',40,'-')
513 Out[17]: '---------------- A test ----------------'
513 Out[17]: '---------------- A test ----------------'
514
514
515 In [18]: marquee('A test',40,' ')
515 In [18]: marquee('A test',40,' ')
516 Out[18]: ' A test '
516 Out[18]: ' A test '
517
517
518 """
518 """
519 if not txt:
519 if not txt:
520 return (mark*width)[:width]
520 return (mark*width)[:width]
521 nmark = (width-len(txt)-2)//len(mark)//2
521 nmark = (width-len(txt)-2)//len(mark)//2
522 if nmark < 0: nmark =0
522 if nmark < 0: nmark =0
523 marks = mark*nmark
523 marks = mark*nmark
524 return '%s %s %s' % (marks,txt,marks)
524 return '%s %s %s' % (marks,txt,marks)
525
525
526
526
527 ini_spaces_re = re.compile(r'^(\s+)')
527 ini_spaces_re = re.compile(r'^(\s+)')
528
528
529 def num_ini_spaces(strng):
529 def num_ini_spaces(strng):
530 """Return the number of initial spaces in a string"""
530 """Return the number of initial spaces in a string"""
531
531
532 ini_spaces = ini_spaces_re.match(strng)
532 ini_spaces = ini_spaces_re.match(strng)
533 if ini_spaces:
533 if ini_spaces:
534 return ini_spaces.end()
534 return ini_spaces.end()
535 else:
535 else:
536 return 0
536 return 0
537
537
538
538
539 def format_screen(strng):
539 def format_screen(strng):
540 """Format a string for screen printing.
540 """Format a string for screen printing.
541
541
542 This removes some latex-type format codes."""
542 This removes some latex-type format codes."""
543 # Paragraph continue
543 # Paragraph continue
544 par_re = re.compile(r'\\$',re.MULTILINE)
544 par_re = re.compile(r'\\$',re.MULTILINE)
545 strng = par_re.sub('',strng)
545 strng = par_re.sub('',strng)
546 return strng
546 return strng
547
547
548 def dedent(text):
548 def dedent(text):
549 """Equivalent of textwrap.dedent that ignores unindented first line.
549 """Equivalent of textwrap.dedent that ignores unindented first line.
550
550
551 This means it will still dedent strings like:
551 This means it will still dedent strings like:
552 '''foo
552 '''foo
553 is a bar
553 is a bar
554 '''
554 '''
555
555
556 For use in wrap_paragraphs.
556 For use in wrap_paragraphs.
557 """
557 """
558
558
559 if text.startswith('\n'):
559 if text.startswith('\n'):
560 # text starts with blank line, don't ignore the first line
560 # text starts with blank line, don't ignore the first line
561 return textwrap.dedent(text)
561 return textwrap.dedent(text)
562
562
563 # split first line
563 # split first line
564 splits = text.split('\n',1)
564 splits = text.split('\n',1)
565 if len(splits) == 1:
565 if len(splits) == 1:
566 # only one line
566 # only one line
567 return textwrap.dedent(text)
567 return textwrap.dedent(text)
568
568
569 first, rest = splits
569 first, rest = splits
570 # dedent everything but the first line
570 # dedent everything but the first line
571 rest = textwrap.dedent(rest)
571 rest = textwrap.dedent(rest)
572 return '\n'.join([first, rest])
572 return '\n'.join([first, rest])
573
573
574 def wrap_paragraphs(text, ncols=80):
574 def wrap_paragraphs(text, ncols=80):
575 """Wrap multiple paragraphs to fit a specified width.
575 """Wrap multiple paragraphs to fit a specified width.
576
576
577 This is equivalent to textwrap.wrap, but with support for multiple
577 This is equivalent to textwrap.wrap, but with support for multiple
578 paragraphs, as separated by empty lines.
578 paragraphs, as separated by empty lines.
579
579
580 Returns
580 Returns
581 -------
581 -------
582
582
583 list of complete paragraphs, wrapped to fill `ncols` columns.
583 list of complete paragraphs, wrapped to fill `ncols` columns.
584 """
584 """
585 paragraph_re = re.compile(r'\n(\s*\n)+', re.MULTILINE)
585 paragraph_re = re.compile(r'\n(\s*\n)+', re.MULTILINE)
586 text = dedent(text).strip()
586 text = dedent(text).strip()
587 paragraphs = paragraph_re.split(text)[::2] # every other entry is space
587 paragraphs = paragraph_re.split(text)[::2] # every other entry is space
588 out_ps = []
588 out_ps = []
589 indent_re = re.compile(r'\n\s+', re.MULTILINE)
589 indent_re = re.compile(r'\n\s+', re.MULTILINE)
590 for p in paragraphs:
590 for p in paragraphs:
591 # presume indentation that survives dedent is meaningful formatting,
591 # presume indentation that survives dedent is meaningful formatting,
592 # so don't fill unless text is flush.
592 # so don't fill unless text is flush.
593 if indent_re.search(p) is None:
593 if indent_re.search(p) is None:
594 # wrap paragraph
594 # wrap paragraph
595 p = textwrap.fill(p, ncols)
595 p = textwrap.fill(p, ncols)
596 out_ps.append(p)
596 out_ps.append(p)
597 return out_ps
597 return out_ps
598
598
599
599
600
601 class EvalFormatter(Formatter):
600 class EvalFormatter(Formatter):
602 """A String Formatter that allows evaluation of simple expressions.
601 """A String Formatter that allows evaluation of simple expressions.
603
602
604 Any time a format key is not found in the kwargs,
603 Note that this version interprets a : as specifying a format string (as per
605 it will be tried as an expression in the kwargs namespace.
604 standard string formatting), so if slicing is required, you must explicitly
606
605 create a slice.
606
607 This is to be used in templating cases, such as the parallel batch
607 This is to be used in templating cases, such as the parallel batch
608 script templates, where simple arithmetic on arguments is useful.
608 script templates, where simple arithmetic on arguments is useful.
609
609
610 Examples
610 Examples
611 --------
611 --------
612
613 In [1]: f = EvalFormatter()
614 In [2]: f.format('{n//4}', n=8)
615 Out [2]: '2'
616
617 In [3]: f.format("{greeting[slice(2,4)]}", greeting="Hello")
618 Out [3]: 'll'
619 """
620 def get_field(self, name, args, kwargs):
621 v = eval(name, kwargs)
622 return v, name
612
623
613 In [1]: f = EvalFormatter()
624 class FullEvalFormatter(Formatter):
625 """A String Formatter that allows evaluation of simple expressions.
626
627 Any time a format key is not found in the kwargs,
628 it will be tried as an expression in the kwargs namespace.
629
630 Note that this version allows slicing using [1:2], so you cannot specify
631 a format string. Use :class:`EvalFormatter` to permit format strings.
632
633 Examples
634 --------
635
636 In [1]: f = FullEvalFormatter()
614 In [2]: f.format('{n//4}', n=8)
637 In [2]: f.format('{n//4}', n=8)
615 Out[2]: '2'
638 Out[2]: '2'
616
639
617 In [3]: f.format('{list(range(3))}')
640 In [3]: f.format('{list(range(5))[2:4]}')
618 Out[3]: '[0, 1, 2]'
641 Out[3]: '[2, 3]'
619
642
620 In [4]: f.format('{3*2}')
643 In [4]: f.format('{3*2}')
621 Out[4]: '6'
644 Out[4]: '6'
622 """
645 """
623
624 # should we allow slicing by disabling the format_spec feature?
625 allow_slicing = True
626
627 # copied from Formatter._vformat with minor changes to allow eval
646 # copied from Formatter._vformat with minor changes to allow eval
628 # and replace the format_spec code with slicing
647 # and replace the format_spec code with slicing
629 def _vformat(self, format_string, args, kwargs, used_args, recursion_depth):
648 def _vformat(self, format_string, args, kwargs, used_args, recursion_depth):
630 if recursion_depth < 0:
649 if recursion_depth < 0:
631 raise ValueError('Max string recursion exceeded')
650 raise ValueError('Max string recursion exceeded')
632 result = []
651 result = []
633 for literal_text, field_name, format_spec, conversion in \
652 for literal_text, field_name, format_spec, conversion in \
634 self.parse(format_string):
653 self.parse(format_string):
635
654
636 # output the literal text
655 # output the literal text
637 if literal_text:
656 if literal_text:
638 result.append(literal_text)
657 result.append(literal_text)
639
658
640 # if there's a field, output it
659 # if there's a field, output it
641 if field_name is not None:
660 if field_name is not None:
642 # this is some markup, find the object and do
661 # this is some markup, find the object and do
643 # the formatting
662 # the formatting
644
663
645 if self.allow_slicing and format_spec:
664 if format_spec:
646 # override format spec, to allow slicing:
665 # override format spec, to allow slicing:
647 field_name = ':'.join([field_name, format_spec])
666 field_name = ':'.join([field_name, format_spec])
648 format_spec = ''
649
667
650 # eval the contents of the field for the object
668 # eval the contents of the field for the object
651 # to be formatted
669 # to be formatted
652 obj = eval(field_name, kwargs)
670 obj = eval(field_name, kwargs)
653
671
654 # do any conversion on the resulting object
672 # do any conversion on the resulting object
655 obj = self.convert_field(obj, conversion)
673 obj = self.convert_field(obj, conversion)
656
674
657 # expand the format spec, if needed
658 format_spec = self._vformat(format_spec, args, kwargs,
659 used_args, recursion_depth-1)
660
661 # format the object and append to the result
675 # format the object and append to the result
662 result.append(self.format_field(obj, format_spec))
676 result.append(self.format_field(obj, ''))
663
677
664 return ''.join(result)
678 return ''.join(result)
665
679
666
680
667 def columnize(items, separator=' ', displaywidth=80):
681 def columnize(items, separator=' ', displaywidth=80):
668 """ Transform a list of strings into a single string with columns.
682 """ Transform a list of strings into a single string with columns.
669
683
670 Parameters
684 Parameters
671 ----------
685 ----------
672 items : sequence of strings
686 items : sequence of strings
673 The strings to process.
687 The strings to process.
674
688
675 separator : str, optional [default is two spaces]
689 separator : str, optional [default is two spaces]
676 The string that separates columns.
690 The string that separates columns.
677
691
678 displaywidth : int, optional [default is 80]
692 displaywidth : int, optional [default is 80]
679 Width of the display in number of characters.
693 Width of the display in number of characters.
680
694
681 Returns
695 Returns
682 -------
696 -------
683 The formatted string.
697 The formatted string.
684 """
698 """
685 # Note: this code is adapted from columnize 0.3.2.
699 # Note: this code is adapted from columnize 0.3.2.
686 # See http://code.google.com/p/pycolumnize/
700 # See http://code.google.com/p/pycolumnize/
687
701
688 # Some degenerate cases.
702 # Some degenerate cases.
689 size = len(items)
703 size = len(items)
690 if size == 0:
704 if size == 0:
691 return '\n'
705 return '\n'
692 elif size == 1:
706 elif size == 1:
693 return '%s\n' % items[0]
707 return '%s\n' % items[0]
694
708
695 # Special case: if any item is longer than the maximum width, there's no
709 # Special case: if any item is longer than the maximum width, there's no
696 # point in triggering the logic below...
710 # point in triggering the logic below...
697 item_len = map(len, items) # save these, we can reuse them below
711 item_len = map(len, items) # save these, we can reuse them below
698 longest = max(item_len)
712 longest = max(item_len)
699 if longest >= displaywidth:
713 if longest >= displaywidth:
700 return '\n'.join(items+[''])
714 return '\n'.join(items+[''])
701
715
702 # Try every row count from 1 upwards
716 # Try every row count from 1 upwards
703 array_index = lambda nrows, row, col: nrows*col + row
717 array_index = lambda nrows, row, col: nrows*col + row
704 for nrows in range(1, size):
718 for nrows in range(1, size):
705 ncols = (size + nrows - 1) // nrows
719 ncols = (size + nrows - 1) // nrows
706 colwidths = []
720 colwidths = []
707 totwidth = -len(separator)
721 totwidth = -len(separator)
708 for col in range(ncols):
722 for col in range(ncols):
709 # Get max column width for this column
723 # Get max column width for this column
710 colwidth = 0
724 colwidth = 0
711 for row in range(nrows):
725 for row in range(nrows):
712 i = array_index(nrows, row, col)
726 i = array_index(nrows, row, col)
713 if i >= size: break
727 if i >= size: break
714 x, len_x = items[i], item_len[i]
728 x, len_x = items[i], item_len[i]
715 colwidth = max(colwidth, len_x)
729 colwidth = max(colwidth, len_x)
716 colwidths.append(colwidth)
730 colwidths.append(colwidth)
717 totwidth += colwidth + len(separator)
731 totwidth += colwidth + len(separator)
718 if totwidth > displaywidth:
732 if totwidth > displaywidth:
719 break
733 break
720 if totwidth <= displaywidth:
734 if totwidth <= displaywidth:
721 break
735 break
722
736
723 # The smallest number of rows computed and the max widths for each
737 # The smallest number of rows computed and the max widths for each
724 # column has been obtained. Now we just have to format each of the rows.
738 # column has been obtained. Now we just have to format each of the rows.
725 string = ''
739 string = ''
726 for row in range(nrows):
740 for row in range(nrows):
727 texts = []
741 texts = []
728 for col in range(ncols):
742 for col in range(ncols):
729 i = row + nrows*col
743 i = row + nrows*col
730 if i >= size:
744 if i >= size:
731 texts.append('')
745 texts.append('')
732 else:
746 else:
733 texts.append(items[i])
747 texts.append(items[i])
734 while texts and not texts[-1]:
748 while texts and not texts[-1]:
735 del texts[-1]
749 del texts[-1]
736 for col in range(len(texts)):
750 for col in range(len(texts)):
737 texts[col] = texts[col].ljust(colwidths[col])
751 texts[col] = texts[col].ljust(colwidths[col])
738 string += '%s\n' % separator.join(texts)
752 string += '%s\n' % separator.join(texts)
739 return string
753 return string
General Comments 0
You need to be logged in to leave comments. Login now