##// END OF EJS Templates
Remove more unused functions from utils.text
Thomas Kluyver -
Show More
@@ -1,781 +1,717 b''
1 1 # encoding: utf-8
2 2 """
3 3 Utilities for working with strings and text.
4 4
5 5 Inheritance diagram:
6 6
7 7 .. inheritance-diagram:: IPython.utils.text
8 8 :parts: 3
9 9 """
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Copyright (C) 2008-2011 The IPython Development Team
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 __main__
23 23
24 24 import os
25 25 import re
26 26 import sys
27 27 import textwrap
28 28 from string import Formatter
29 29
30 30 from IPython.external.path import path
31 31 from IPython.testing.skipdoctest import skip_doctest_py3, skip_doctest
32 32 from IPython.utils import py3compat
33 33 from IPython.utils.data import flatten
34 34
35 35 #-----------------------------------------------------------------------------
36 36 # Code
37 37 #-----------------------------------------------------------------------------
38 38
39 def unquote_ends(istr):
40 """Remove a single pair of quotes from the endpoints of a string."""
41
42 if not istr:
43 return istr
44 if (istr[0]=="'" and istr[-1]=="'") or \
45 (istr[0]=='"' and istr[-1]=='"'):
46 return istr[1:-1]
47 else:
48 return istr
49
50
51 39 class LSString(str):
52 40 """String derivative with a special access attributes.
53 41
54 42 These are normal strings, but with the special attributes:
55 43
56 44 .l (or .list) : value as list (split on newlines).
57 45 .n (or .nlstr): original value (the string itself).
58 46 .s (or .spstr): value as whitespace-separated string.
59 47 .p (or .paths): list of path objects
60 48
61 49 Any values which require transformations are computed only once and
62 50 cached.
63 51
64 52 Such strings are very useful to efficiently interact with the shell, which
65 53 typically only understands whitespace-separated options for commands."""
66 54
67 55 def get_list(self):
68 56 try:
69 57 return self.__list
70 58 except AttributeError:
71 59 self.__list = self.split('\n')
72 60 return self.__list
73 61
74 62 l = list = property(get_list)
75 63
76 64 def get_spstr(self):
77 65 try:
78 66 return self.__spstr
79 67 except AttributeError:
80 68 self.__spstr = self.replace('\n',' ')
81 69 return self.__spstr
82 70
83 71 s = spstr = property(get_spstr)
84 72
85 73 def get_nlstr(self):
86 74 return self
87 75
88 76 n = nlstr = property(get_nlstr)
89 77
90 78 def get_paths(self):
91 79 try:
92 80 return self.__paths
93 81 except AttributeError:
94 82 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
95 83 return self.__paths
96 84
97 85 p = paths = property(get_paths)
98 86
99 87 # FIXME: We need to reimplement type specific displayhook and then add this
100 88 # back as a custom printer. This should also be moved outside utils into the
101 89 # core.
102 90
103 91 # def print_lsstring(arg):
104 92 # """ Prettier (non-repr-like) and more informative printer for LSString """
105 93 # print "LSString (.p, .n, .l, .s available). Value:"
106 94 # print arg
107 95 #
108 96 #
109 97 # print_lsstring = result_display.when_type(LSString)(print_lsstring)
110 98
111 99
112 100 class SList(list):
113 101 """List derivative with a special access attributes.
114 102
115 103 These are normal lists, but with the special attributes:
116 104
117 105 .l (or .list) : value as list (the list itself).
118 106 .n (or .nlstr): value as a string, joined on newlines.
119 107 .s (or .spstr): value as a string, joined on spaces.
120 108 .p (or .paths): list of path objects
121 109
122 110 Any values which require transformations are computed only once and
123 111 cached."""
124 112
125 113 def get_list(self):
126 114 return self
127 115
128 116 l = list = property(get_list)
129 117
130 118 def get_spstr(self):
131 119 try:
132 120 return self.__spstr
133 121 except AttributeError:
134 122 self.__spstr = ' '.join(self)
135 123 return self.__spstr
136 124
137 125 s = spstr = property(get_spstr)
138 126
139 127 def get_nlstr(self):
140 128 try:
141 129 return self.__nlstr
142 130 except AttributeError:
143 131 self.__nlstr = '\n'.join(self)
144 132 return self.__nlstr
145 133
146 134 n = nlstr = property(get_nlstr)
147 135
148 136 def get_paths(self):
149 137 try:
150 138 return self.__paths
151 139 except AttributeError:
152 140 self.__paths = [path(p) for p in self if os.path.exists(p)]
153 141 return self.__paths
154 142
155 143 p = paths = property(get_paths)
156 144
157 145 def grep(self, pattern, prune = False, field = None):
158 146 """ Return all strings matching 'pattern' (a regex or callable)
159 147
160 148 This is case-insensitive. If prune is true, return all items
161 149 NOT matching the pattern.
162 150
163 151 If field is specified, the match must occur in the specified
164 152 whitespace-separated field.
165 153
166 154 Examples::
167 155
168 156 a.grep( lambda x: x.startswith('C') )
169 157 a.grep('Cha.*log', prune=1)
170 158 a.grep('chm', field=-1)
171 159 """
172 160
173 161 def match_target(s):
174 162 if field is None:
175 163 return s
176 164 parts = s.split()
177 165 try:
178 166 tgt = parts[field]
179 167 return tgt
180 168 except IndexError:
181 169 return ""
182 170
183 171 if isinstance(pattern, basestring):
184 172 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
185 173 else:
186 174 pred = pattern
187 175 if not prune:
188 176 return SList([el for el in self if pred(match_target(el))])
189 177 else:
190 178 return SList([el for el in self if not pred(match_target(el))])
191 179
192 180 def fields(self, *fields):
193 181 """ Collect whitespace-separated fields from string list
194 182
195 183 Allows quick awk-like usage of string lists.
196 184
197 185 Example data (in var a, created by 'a = !ls -l')::
198 186 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
199 187 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
200 188
201 189 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
202 190 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
203 191 (note the joining by space).
204 192 a.fields(-1) is ['ChangeLog', 'IPython']
205 193
206 194 IndexErrors are ignored.
207 195
208 196 Without args, fields() just split()'s the strings.
209 197 """
210 198 if len(fields) == 0:
211 199 return [el.split() for el in self]
212 200
213 201 res = SList()
214 202 for el in [f.split() for f in self]:
215 203 lineparts = []
216 204
217 205 for fd in fields:
218 206 try:
219 207 lineparts.append(el[fd])
220 208 except IndexError:
221 209 pass
222 210 if lineparts:
223 211 res.append(" ".join(lineparts))
224 212
225 213 return res
226 214
227 215 def sort(self,field= None, nums = False):
228 216 """ sort by specified fields (see fields())
229 217
230 218 Example::
231 219 a.sort(1, nums = True)
232 220
233 221 Sorts a by second field, in numerical order (so that 21 > 3)
234 222
235 223 """
236 224
237 225 #decorate, sort, undecorate
238 226 if field is not None:
239 227 dsu = [[SList([line]).fields(field), line] for line in self]
240 228 else:
241 229 dsu = [[line, line] for line in self]
242 230 if nums:
243 231 for i in range(len(dsu)):
244 232 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
245 233 try:
246 234 n = int(numstr)
247 235 except ValueError:
248 236 n = 0;
249 237 dsu[i][0] = n
250 238
251 239
252 240 dsu.sort()
253 241 return SList([t[1] for t in dsu])
254 242
255 243
256 244 # FIXME: We need to reimplement type specific displayhook and then add this
257 245 # back as a custom printer. This should also be moved outside utils into the
258 246 # core.
259 247
260 248 # def print_slist(arg):
261 249 # """ Prettier (non-repr-like) and more informative printer for SList """
262 250 # print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
263 251 # if hasattr(arg, 'hideonce') and arg.hideonce:
264 252 # arg.hideonce = False
265 253 # return
266 254 #
267 255 # nlprint(arg) # This was a nested list printer, now removed.
268 256 #
269 257 # print_slist = result_display.when_type(SList)(print_slist)
270 258
271 259
272 def esc_quotes(strng):
273 """Return the input string with single and double quotes escaped out"""
274
275 return strng.replace('"','\\"').replace("'","\\'")
276
277
278 def qw(words,flat=0,sep=None,maxsplit=-1):
279 """Similar to Perl's qw() operator, but with some more options.
280
281 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
282
283 words can also be a list itself, and with flat=1, the output will be
284 recursively flattened.
285
286 Examples:
287
288 >>> qw('1 2')
289 ['1', '2']
290
291 >>> qw(['a b','1 2',['m n','p q']])
292 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
293
294 >>> qw(['a b','1 2',['m n','p q']],flat=1)
295 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q']
296 """
297
298 if isinstance(words, basestring):
299 return [word.strip() for word in words.split(sep,maxsplit)
300 if word and not word.isspace() ]
301 if flat:
302 return flatten(map(qw,words,[1]*len(words)))
303 return map(qw,words)
304
305
306 def qwflat(words,sep=None,maxsplit=-1):
307 """Calls qw(words) in flat mode. It's just a convenient shorthand."""
308 return qw(words,1,sep,maxsplit)
309
310
311 def qw_lol(indata):
312 """qw_lol('a b') -> [['a','b']],
313 otherwise it's just a call to qw().
314
315 We need this to make sure the modules_some keys *always* end up as a
316 list of lists."""
317
318 if isinstance(indata, basestring):
319 return [qw(indata)]
320 else:
321 return qw(indata)
322
323
324 260 def indent(instr,nspaces=4, ntabs=0, flatten=False):
325 261 """Indent a string a given number of spaces or tabstops.
326 262
327 263 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
328 264
329 265 Parameters
330 266 ----------
331 267
332 268 instr : basestring
333 269 The string to be indented.
334 270 nspaces : int (default: 4)
335 271 The number of spaces to be indented.
336 272 ntabs : int (default: 0)
337 273 The number of tabs to be indented.
338 274 flatten : bool (default: False)
339 275 Whether to scrub existing indentation. If True, all lines will be
340 276 aligned to the same indentation. If False, existing indentation will
341 277 be strictly increased.
342 278
343 279 Returns
344 280 -------
345 281
346 282 str|unicode : string indented by ntabs and nspaces.
347 283
348 284 """
349 285 if instr is None:
350 286 return
351 287 ind = '\t'*ntabs+' '*nspaces
352 288 if flatten:
353 289 pat = re.compile(r'^\s*', re.MULTILINE)
354 290 else:
355 291 pat = re.compile(r'^', re.MULTILINE)
356 292 outstr = re.sub(pat, ind, instr)
357 293 if outstr.endswith(os.linesep+ind):
358 294 return outstr[:-len(ind)]
359 295 else:
360 296 return outstr
361 297
362 298
363 299 def list_strings(arg):
364 300 """Always return a list of strings, given a string or list of strings
365 301 as input.
366 302
367 303 :Examples:
368 304
369 305 In [7]: list_strings('A single string')
370 306 Out[7]: ['A single string']
371 307
372 308 In [8]: list_strings(['A single string in a list'])
373 309 Out[8]: ['A single string in a list']
374 310
375 311 In [9]: list_strings(['A','list','of','strings'])
376 312 Out[9]: ['A', 'list', 'of', 'strings']
377 313 """
378 314
379 315 if isinstance(arg,basestring): return [arg]
380 316 else: return arg
381 317
382 318
383 319 def marquee(txt='',width=78,mark='*'):
384 320 """Return the input string centered in a 'marquee'.
385 321
386 322 :Examples:
387 323
388 324 In [16]: marquee('A test',40)
389 325 Out[16]: '**************** A test ****************'
390 326
391 327 In [17]: marquee('A test',40,'-')
392 328 Out[17]: '---------------- A test ----------------'
393 329
394 330 In [18]: marquee('A test',40,' ')
395 331 Out[18]: ' A test '
396 332
397 333 """
398 334 if not txt:
399 335 return (mark*width)[:width]
400 336 nmark = (width-len(txt)-2)//len(mark)//2
401 337 if nmark < 0: nmark =0
402 338 marks = mark*nmark
403 339 return '%s %s %s' % (marks,txt,marks)
404 340
405 341
406 342 ini_spaces_re = re.compile(r'^(\s+)')
407 343
408 344 def num_ini_spaces(strng):
409 345 """Return the number of initial spaces in a string"""
410 346
411 347 ini_spaces = ini_spaces_re.match(strng)
412 348 if ini_spaces:
413 349 return ini_spaces.end()
414 350 else:
415 351 return 0
416 352
417 353
418 354 def format_screen(strng):
419 355 """Format a string for screen printing.
420 356
421 357 This removes some latex-type format codes."""
422 358 # Paragraph continue
423 359 par_re = re.compile(r'\\$',re.MULTILINE)
424 360 strng = par_re.sub('',strng)
425 361 return strng
426 362
427 363
428 364 def dedent(text):
429 365 """Equivalent of textwrap.dedent that ignores unindented first line.
430 366
431 367 This means it will still dedent strings like:
432 368 '''foo
433 369 is a bar
434 370 '''
435 371
436 372 For use in wrap_paragraphs.
437 373 """
438 374
439 375 if text.startswith('\n'):
440 376 # text starts with blank line, don't ignore the first line
441 377 return textwrap.dedent(text)
442 378
443 379 # split first line
444 380 splits = text.split('\n',1)
445 381 if len(splits) == 1:
446 382 # only one line
447 383 return textwrap.dedent(text)
448 384
449 385 first, rest = splits
450 386 # dedent everything but the first line
451 387 rest = textwrap.dedent(rest)
452 388 return '\n'.join([first, rest])
453 389
454 390
455 391 def wrap_paragraphs(text, ncols=80):
456 392 """Wrap multiple paragraphs to fit a specified width.
457 393
458 394 This is equivalent to textwrap.wrap, but with support for multiple
459 395 paragraphs, as separated by empty lines.
460 396
461 397 Returns
462 398 -------
463 399
464 400 list of complete paragraphs, wrapped to fill `ncols` columns.
465 401 """
466 402 paragraph_re = re.compile(r'\n(\s*\n)+', re.MULTILINE)
467 403 text = dedent(text).strip()
468 404 paragraphs = paragraph_re.split(text)[::2] # every other entry is space
469 405 out_ps = []
470 406 indent_re = re.compile(r'\n\s+', re.MULTILINE)
471 407 for p in paragraphs:
472 408 # presume indentation that survives dedent is meaningful formatting,
473 409 # so don't fill unless text is flush.
474 410 if indent_re.search(p) is None:
475 411 # wrap paragraph
476 412 p = textwrap.fill(p, ncols)
477 413 out_ps.append(p)
478 414 return out_ps
479 415
480 416
481 417 def long_substr(data):
482 418 """Return the longest common substring in a list of strings.
483 419
484 420 Credit: http://stackoverflow.com/questions/2892931/longest-common-substring-from-more-than-two-strings-python
485 421 """
486 422 substr = ''
487 423 if len(data) > 1 and len(data[0]) > 0:
488 424 for i in range(len(data[0])):
489 425 for j in range(len(data[0])-i+1):
490 426 if j > len(substr) and all(data[0][i:i+j] in x for x in data):
491 427 substr = data[0][i:i+j]
492 428 elif len(data) == 1:
493 429 substr = data[0]
494 430 return substr
495 431
496 432
497 433 def strip_email_quotes(text):
498 434 """Strip leading email quotation characters ('>').
499 435
500 436 Removes any combination of leading '>' interspersed with whitespace that
501 437 appears *identically* in all lines of the input text.
502 438
503 439 Parameters
504 440 ----------
505 441 text : str
506 442
507 443 Examples
508 444 --------
509 445
510 446 Simple uses::
511 447
512 448 In [2]: strip_email_quotes('> > text')
513 449 Out[2]: 'text'
514 450
515 451 In [3]: strip_email_quotes('> > text\\n> > more')
516 452 Out[3]: 'text\\nmore'
517 453
518 454 Note how only the common prefix that appears in all lines is stripped::
519 455
520 456 In [4]: strip_email_quotes('> > text\\n> > more\\n> more...')
521 457 Out[4]: '> text\\n> more\\nmore...'
522 458
523 459 So if any line has no quote marks ('>') , then none are stripped from any
524 460 of them ::
525 461
526 462 In [5]: strip_email_quotes('> > text\\n> > more\\nlast different')
527 463 Out[5]: '> > text\\n> > more\\nlast different'
528 464 """
529 465 lines = text.splitlines()
530 466 matches = set()
531 467 for line in lines:
532 468 prefix = re.match(r'^(\s*>[ >]*)', line)
533 469 if prefix:
534 470 matches.add(prefix.group(1))
535 471 else:
536 472 break
537 473 else:
538 474 prefix = long_substr(list(matches))
539 475 if prefix:
540 476 strip = len(prefix)
541 477 text = '\n'.join([ ln[strip:] for ln in lines])
542 478 return text
543 479
544 480
545 481 class EvalFormatter(Formatter):
546 482 """A String Formatter that allows evaluation of simple expressions.
547 483
548 484 Note that this version interprets a : as specifying a format string (as per
549 485 standard string formatting), so if slicing is required, you must explicitly
550 486 create a slice.
551 487
552 488 This is to be used in templating cases, such as the parallel batch
553 489 script templates, where simple arithmetic on arguments is useful.
554 490
555 491 Examples
556 492 --------
557 493
558 494 In [1]: f = EvalFormatter()
559 495 In [2]: f.format('{n//4}', n=8)
560 496 Out [2]: '2'
561 497
562 498 In [3]: f.format("{greeting[slice(2,4)]}", greeting="Hello")
563 499 Out [3]: 'll'
564 500 """
565 501 def get_field(self, name, args, kwargs):
566 502 v = eval(name, kwargs)
567 503 return v, name
568 504
569 505
570 506 @skip_doctest_py3
571 507 class FullEvalFormatter(Formatter):
572 508 """A String Formatter that allows evaluation of simple expressions.
573 509
574 510 Any time a format key is not found in the kwargs,
575 511 it will be tried as an expression in the kwargs namespace.
576 512
577 513 Note that this version allows slicing using [1:2], so you cannot specify
578 514 a format string. Use :class:`EvalFormatter` to permit format strings.
579 515
580 516 Examples
581 517 --------
582 518
583 519 In [1]: f = FullEvalFormatter()
584 520 In [2]: f.format('{n//4}', n=8)
585 521 Out[2]: u'2'
586 522
587 523 In [3]: f.format('{list(range(5))[2:4]}')
588 524 Out[3]: u'[2, 3]'
589 525
590 526 In [4]: f.format('{3*2}')
591 527 Out[4]: u'6'
592 528 """
593 529 # copied from Formatter._vformat with minor changes to allow eval
594 530 # and replace the format_spec code with slicing
595 531 def _vformat(self, format_string, args, kwargs, used_args, recursion_depth):
596 532 if recursion_depth < 0:
597 533 raise ValueError('Max string recursion exceeded')
598 534 result = []
599 535 for literal_text, field_name, format_spec, conversion in \
600 536 self.parse(format_string):
601 537
602 538 # output the literal text
603 539 if literal_text:
604 540 result.append(literal_text)
605 541
606 542 # if there's a field, output it
607 543 if field_name is not None:
608 544 # this is some markup, find the object and do
609 545 # the formatting
610 546
611 547 if format_spec:
612 548 # override format spec, to allow slicing:
613 549 field_name = ':'.join([field_name, format_spec])
614 550
615 551 # eval the contents of the field for the object
616 552 # to be formatted
617 553 obj = eval(field_name, kwargs)
618 554
619 555 # do any conversion on the resulting object
620 556 obj = self.convert_field(obj, conversion)
621 557
622 558 # format the object and append to the result
623 559 result.append(self.format_field(obj, ''))
624 560
625 561 return u''.join(py3compat.cast_unicode(s) for s in result)
626 562
627 563
628 564 @skip_doctest_py3
629 565 class DollarFormatter(FullEvalFormatter):
630 566 """Formatter allowing Itpl style $foo replacement, for names and attribute
631 567 access only. Standard {foo} replacement also works, and allows full
632 568 evaluation of its arguments.
633 569
634 570 Examples
635 571 --------
636 572 In [1]: f = DollarFormatter()
637 573 In [2]: f.format('{n//4}', n=8)
638 574 Out[2]: u'2'
639 575
640 576 In [3]: f.format('23 * 76 is $result', result=23*76)
641 577 Out[3]: u'23 * 76 is 1748'
642 578
643 579 In [4]: f.format('$a or {b}', a=1, b=2)
644 580 Out[4]: u'1 or 2'
645 581 """
646 582 _dollar_pattern = re.compile("(.*?)\$(\$?[\w\.]+)")
647 583 def parse(self, fmt_string):
648 584 for literal_txt, field_name, format_spec, conversion \
649 585 in Formatter.parse(self, fmt_string):
650 586
651 587 # Find $foo patterns in the literal text.
652 588 continue_from = 0
653 589 txt = ""
654 590 for m in self._dollar_pattern.finditer(literal_txt):
655 591 new_txt, new_field = m.group(1,2)
656 592 # $$foo --> $foo
657 593 if new_field.startswith("$"):
658 594 txt += new_txt + new_field
659 595 else:
660 596 yield (txt + new_txt, new_field, "", None)
661 597 txt = ""
662 598 continue_from = m.end()
663 599
664 600 # Re-yield the {foo} style pattern
665 601 yield (txt + literal_txt[continue_from:], field_name, format_spec, conversion)
666 602
667 603 #-----------------------------------------------------------------------------
668 604 # Utils to columnize a list of string
669 605 #-----------------------------------------------------------------------------
670 606
671 607 def _chunks(l, n):
672 608 """Yield successive n-sized chunks from l."""
673 609 for i in xrange(0, len(l), n):
674 610 yield l[i:i+n]
675 611
676 612
677 613 def _find_optimal(rlist , separator_size=2 , displaywidth=80):
678 614 """Calculate optimal info to columnize a list of string"""
679 615 for nrow in range(1, len(rlist)+1) :
680 616 chk = map(max,_chunks(rlist, nrow))
681 617 sumlength = sum(chk)
682 618 ncols = len(chk)
683 619 if sumlength+separator_size*(ncols-1) <= displaywidth :
684 620 break;
685 621 return {'columns_numbers' : ncols,
686 622 'optimal_separator_width':(displaywidth - sumlength)/(ncols-1) if (ncols -1) else 0,
687 623 'rows_numbers' : nrow,
688 624 'columns_width' : chk
689 625 }
690 626
691 627
692 628 def _get_or_default(mylist, i, default=None):
693 629 """return list item number, or default if don't exist"""
694 630 if i >= len(mylist):
695 631 return default
696 632 else :
697 633 return mylist[i]
698 634
699 635
700 636 @skip_doctest
701 637 def compute_item_matrix(items, empty=None, *args, **kwargs) :
702 638 """Returns a nested list, and info to columnize items
703 639
704 640 Parameters
705 641 ----------
706 642
707 643 items :
708 644 list of strings to columize
709 645 empty : (default None)
710 646 default value to fill list if needed
711 647 separator_size : int (default=2)
712 648 How much caracters will be used as a separation between each columns.
713 649 displaywidth : int (default=80)
714 650 The width of the area onto wich the columns should enter
715 651
716 652 Returns
717 653 -------
718 654
719 655 Returns a tuple of (strings_matrix, dict_info)
720 656
721 657 strings_matrix :
722 658
723 659 nested list of string, the outer most list contains as many list as
724 660 rows, the innermost lists have each as many element as colums. If the
725 661 total number of elements in `items` does not equal the product of
726 662 rows*columns, the last element of some lists are filled with `None`.
727 663
728 664 dict_info :
729 665 some info to make columnize easier:
730 666
731 667 columns_numbers : number of columns
732 668 rows_numbers : number of rows
733 669 columns_width : list of with of each columns
734 670 optimal_separator_width : best separator width between columns
735 671
736 672 Examples
737 673 --------
738 674
739 675 In [1]: l = ['aaa','b','cc','d','eeeee','f','g','h','i','j','k','l']
740 676 ...: compute_item_matrix(l,displaywidth=12)
741 677 Out[1]:
742 678 ([['aaa', 'f', 'k'],
743 679 ['b', 'g', 'l'],
744 680 ['cc', 'h', None],
745 681 ['d', 'i', None],
746 682 ['eeeee', 'j', None]],
747 683 {'columns_numbers': 3,
748 684 'columns_width': [5, 1, 1],
749 685 'optimal_separator_width': 2,
750 686 'rows_numbers': 5})
751 687
752 688 """
753 689 info = _find_optimal(map(len, items), *args, **kwargs)
754 690 nrow, ncol = info['rows_numbers'], info['columns_numbers']
755 691 return ([[ _get_or_default(items, c*nrow+i, default=empty) for c in range(ncol) ] for i in range(nrow) ], info)
756 692
757 693
758 694 def columnize(items, separator=' ', displaywidth=80):
759 695 """ Transform a list of strings into a single string with columns.
760 696
761 697 Parameters
762 698 ----------
763 699 items : sequence of strings
764 700 The strings to process.
765 701
766 702 separator : str, optional [default is two spaces]
767 703 The string that separates columns.
768 704
769 705 displaywidth : int, optional [default is 80]
770 706 Width of the display in number of characters.
771 707
772 708 Returns
773 709 -------
774 710 The formatted string.
775 711 """
776 712 if not items :
777 713 return '\n'
778 714 matrix, info = compute_item_matrix(items, separator_size=len(separator), displaywidth=displaywidth)
779 715 fmatrix = [filter(None, x) for x in matrix]
780 716 sjoin = lambda x : separator.join([ y.ljust(w, ' ') for y, w in zip(x, info['columns_width'])])
781 717 return '\n'.join(map(sjoin, fmatrix))+'\n'
General Comments 0
You need to be logged in to leave comments. Login now