##// END OF EJS Templates
ipipe patch 5 from Walter Doerwald, featuring:...
vivainio -
Show More
This diff has been collapsed as it changes many lines, (750 lines changed) Show them Hide them
@@ -8,7 +8,7 b' objects imported this way starts with ``i`` to minimize collisions.'
8 ``ipipe`` supports "pipeline expressions", which is something resembling Unix
8 ``ipipe`` supports "pipeline expressions", which is something resembling Unix
9 pipes. An example is:
9 pipes. An example is:
10
10
11 >>> ienv | isort("_.key.lower()")
11 >>> ienv | isort("key.lower()")
12
12
13 This gives a listing of all environment variables sorted by name.
13 This gives a listing of all environment variables sorted by name.
14
14
@@ -46,13 +46,22 b' three extensions points (all of them optional):'
46 be being displayed. The global function ``xattrs()`` implements this
46 be being displayed. The global function ``xattrs()`` implements this
47 functionality.
47 functionality.
48
48
49 * When an object ``foo`` is displayed in the header (or footer) of the browser
49 * When an object ``foo`` is displayed in the header, footer or table cell of the
50 ``foo.__xrepr__("header")`` (or ``foo.__xrepr__("footer")``) is called.
50 browser ``foo.__xrepr__(mode)`` is called. Mode can be ``"header"`` or
51 Currently only ``"header"`` and ``"footer"`` are used as the mode argument.
51 ``"footer"`` for the header or footer line and ``"cell"`` for a table cell.
52 When the method doesn't recognize the mode argument, it should fall back to
52 ``__xrepr__()```must return an iterable (e.g. by being a generator) which
53 the ``repr()`` output. If the method ``__xrepr__()`` isn't defined the browser
53 produces the following items: The first item should be a tuple containing
54 falls back to ``repr()``. The global function ``xrepr()`` implements this
54 the alignment (-1 left aligned, 0 centered and 1 right aligned) and whether
55 functionality.
55 the complete output must be displayed or if the browser is allowed to stop
56 output after enough text has been produced (e.g. a syntax highlighted text
57 line would use ``True``, but for a large data structure (i.e. a nested list,
58 tuple or dictionary) ``False`` would be used). The other output ``__xrepr__()``
59 may produce is tuples of ``Style```objects and text (which contain the text
60 representation of the object). If ``__xrepr__()`` recursively outputs a data
61 structure the function ``xrepr(object, mode)`` can be used and ``"default"``
62 must be passed as the mode in these calls. This in turn calls the
63 ``__xrepr__()`` method on ``object`` (or uses ``repr(object)`` as the string
64 representation if ``__xrepr__()`` doesn't exist.
56
65
57 * Objects that can be iterated by ``Pipe``s must implement the method
66 * Objects that can be iterated by ``Pipe``s must implement the method
58 ``__xiter__(self, mode)``. ``mode`` can take the following values:
67 ``__xiter__(self, mode)``. ``mode`` can take the following values:
@@ -71,7 +80,7 b' three extensions points (all of them optional):'
71 possible to use dictionaries and modules in pipeline expressions, for example:
80 possible to use dictionaries and modules in pipeline expressions, for example:
72
81
73 >>> import sys
82 >>> import sys
74 >>> sys | ifilter("isinstance(_.value, int)") | idump
83 >>> sys | ifilter("isinstance(value, int)") | idump
75 key |value
84 key |value
76 api_version| 1012
85 api_version| 1012
77 dllhandle | 503316480
86 dllhandle | 503316480
@@ -82,13 +91,12 b' three extensions points (all of them optional):'
82 ...
91 ...
83
92
84 Note: The expression strings passed to ``ifilter()`` and ``isort()`` can
93 Note: The expression strings passed to ``ifilter()`` and ``isort()`` can
85 refer to the object to be filtered or sorted via the variable ``_``. When
94 refer to the object to be filtered or sorted via the variable ``_`` and to any
86 running under Python 2.4 it's also possible to refer to the attributes of
95 of the attributes of the object, i.e.:
87 the object directy, i.e.:
88
96
89 >>> sys.modules | ifilter("_.value is not None") | isort("_.key.lower()")
97 >>> sys.modules | ifilter("_.value is not None") | isort("_.key.lower()")
90
98
91 can be replaced with
99 does the same as
92
100
93 >>> sys.modules | ifilter("value is not None") | isort("key.lower()")
101 >>> sys.modules | ifilter("value is not None") | isort("key.lower()")
94
102
@@ -105,7 +113,7 b' three extensions points (all of them optional):'
105 maxunicode |0xffff
113 maxunicode |0xffff
106 """
114 """
107
115
108 import sys, os, os.path, stat, glob, new, csv, datetime
116 import sys, os, os.path, stat, glob, new, csv, datetime, types
109 import textwrap, itertools, mimetypes
117 import textwrap, itertools, mimetypes
110
118
111 try: # Python 2.3 compatibility
119 try: # Python 2.3 compatibility
@@ -164,8 +172,6 b' class _AttrNamespace(object):'
164 """
172 """
165 Internal helper class that is used for providing a namespace for evaluating
173 Internal helper class that is used for providing a namespace for evaluating
166 expressions containg attribute names of an object.
174 expressions containg attribute names of an object.
167
168 This class can only be used with Python 2.4.
169 """
175 """
170 def __init__(self, wrapped):
176 def __init__(self, wrapped):
171 self.wrapped = wrapped
177 self.wrapped = wrapped
@@ -179,13 +185,35 b' class _AttrNamespace(object):'
179 raise KeyError(name)
185 raise KeyError(name)
180
186
181 # Python 2.3 compatibility
187 # Python 2.3 compatibility
182 # fall back to a real dictionary (containing just the object itself) if we can't
188 # use eval workaround to find out which names are used in the
183 # use the _AttrNamespace class in eval()
189 # eval string and put them into the locals. This works for most
190 # normal uses case, bizarre ones like accessing the locals()
191 # will fail
184 try:
192 try:
185 eval("_", None, _AttrNamespace(None))
193 eval("_", None, _AttrNamespace(None))
186 except TypeError:
194 except TypeError:
187 def _AttrNamespace(wrapped):
195 real_eval = eval
188 return {"_": wrapped}
196 def eval(codestring, _globals, _locals):
197 """
198 eval(source[, globals[, locals]]) -> value
199
200 Evaluate the source in the context of globals and locals.
201 The source may be a string representing a Python expression
202 or a code object as returned by compile().
203 The globals must be a dictionary and locals can be any mappping.
204
205 This function is a workaround for the shortcomings of
206 Python 2.3's eval.
207 """
208
209 code = compile(codestring, "_eval", "eval")
210 newlocals = {}
211 for name in code.co_names:
212 try:
213 newlocals[name] = _locals[name]
214 except KeyError:
215 pass
216 return real_eval(code, _globals, newlocals)
189
217
190
218
191 _default = object()
219 _default = object()
@@ -339,21 +367,211 b' def _attrname(name):'
339 return str(name)
367 return str(name)
340
368
341
369
370 class Style(object):
371 """
372 Store foreground color, background color and attribute (bold, underlined
373 etc.).
374 """
375 __slots__ = ("fg", "bg", "attrs")
376
377 def __init__(self, fg, bg, attrs=0):
378 self.fg = fg
379 self.bg = bg
380 self.attrs = attrs
381
382
383 COLOR_BLACK = 0
384 COLOR_RED = 1
385 COLOR_GREEN = 2
386 COLOR_YELLOW = 3
387 COLOR_BLUE = 4
388 COLOR_MAGENTA = 5
389 COLOR_CYAN = 6
390 COLOR_WHITE = 7
391
392 A_BLINK = 1<<0 # Blinking text
393 A_BOLD = 1<<1 # Extra bright or bold text
394 A_DIM = 1<<2 # Half bright text
395 A_REVERSE = 1<<3 # Reverse-video text
396 A_STANDOUT = 1<<4 # The best highlighting mode available
397 A_UNDERLINE = 1<<5 # Underlined text
398
399 if curses is not None:
400 # This is probably just range(8)
401 COLOR2CURSES = [
402 COLOR_BLACK,
403 COLOR_RED,
404 COLOR_GREEN,
405 COLOR_YELLOW,
406 COLOR_BLUE,
407 COLOR_MAGENTA,
408 COLOR_CYAN,
409 COLOR_WHITE,
410 ]
411
412 A2CURSES = {
413 A_BLINK: curses.A_BLINK,
414 A_BOLD: curses.A_BOLD,
415 A_DIM: curses.A_DIM,
416 A_REVERSE: curses.A_REVERSE,
417 A_STANDOUT: curses.A_STANDOUT,
418 A_UNDERLINE: curses.A_UNDERLINE,
419 }
420
421
422 # default style
423 style_default = Style(COLOR_WHITE, COLOR_BLACK)
424
425 # Styles for datatypes
426 style_type_none = Style(COLOR_MAGENTA, COLOR_BLACK)
427 style_type_bool = Style(COLOR_MAGENTA, COLOR_BLACK)
428 style_type_number = Style(COLOR_YELLOW, COLOR_BLACK)
429 style_type_datetime = Style(COLOR_CYAN, COLOR_BLACK)
430
431 # Style for URLs and filenames
432 style_url = Style(COLOR_GREEN, COLOR_BLACK)
433
434 # Style for ellipsis (when an output has been shortened
435 style_ellisis = Style(COLOR_RED, COLOR_BLACK)
436
437 # Style for displaying ewxceptions
438 style_error = Style(COLOR_RED, COLOR_BLACK)
439
440
342 def xrepr(item, mode):
441 def xrepr(item, mode):
343 try:
442 try:
344 func = item.__xrepr__
443 func = item.__xrepr__
345 except AttributeError:
444 except AttributeError:
346 return repr(item)
445 pass
347 else:
446 else:
348 return func(mode)
447 for x in func(mode):
448 yield x
449 return
450 if item is None:
451 yield (-1, True)
452 yield (style_type_none, repr(item))
453 elif isinstance(item, bool):
454 yield (-1, True)
455 yield (style_type_bool, repr(item))
456 elif isinstance(item, str):
457 yield (-1, True)
458 if mode == "cell":
459 yield (style_default, repr(item.expandtabs(tab))[1:-1])
460 else:
461 yield (style_default, repr(item))
462 elif isinstance(item, unicode):
463 yield (-1, True)
464 if mode == "cell":
465 yield (style_default, repr(item.expandtabs(tab))[2:-1])
466 else:
467 yield (style_default, repr(item))
468 elif isinstance(item, (int, long, float)):
469 yield (1, True)
470 yield (style_type_number, repr(item))
471 elif isinstance(item, complex):
472 yield (-1, True)
473 yield (style_type_number, repr(item))
474 elif isinstance(item, datetime.datetime):
475 yield (-1, True)
476 if mode == "cell":
477 # Don't use strftime() here, as this requires year >= 1900
478 yield (style_type_datetime,
479 "%04d-%02d-%02d %02d:%02d:%02d.%06d" % \
480 (item.year, item.month, item.day,
481 item.hour, item.minute, item.second,
482 item.microsecond),
483 )
484 else:
485 yield (style_type_datetime, repr(item))
486 elif isinstance(item, datetime.date):
487 yield (-1, True)
488 if mode == "cell":
489 yield (style_type_datetime,
490 "%04d-%02d-%02d" % (item.year, item.month, item.day))
491 else:
492 yield (style_type_datetime, repr(item))
493 elif isinstance(item, datetime.time):
494 yield (-1, True)
495 if mode == "cell":
496 yield (style_type_datetime,
497 "%02d:%02d:%02d.%06d" % \
498 (item.hour, item.minute, item.second, item.microsecond))
499 else:
500 yield (style_type_datetime, repr(item))
501 elif isinstance(item, datetime.timedelta):
502 yield (-1, True)
503 yield (style_type_datetime, repr(item))
504 elif isinstance(item, Exception):
505 yield (-1, True)
506 if item.__class__.__module__ == "exceptions":
507 classname = item.__class__.__name__
508 else:
509 classname = "%s.%s: %s" % \
510 (item.__class__.__module__, item.__class__.__name__)
511 if mode == "header" or mode == "footer":
512 yield (style_error, "%s: %s" % (classname, item))
513 else:
514 yield (style_error, classname)
515 elif isinstance(item, (list, tuple)):
516 yield (-1, False)
517 if mode == "header" or mode == "footer":
518 if item.__class__.__module__ == "__builtin__":
519 classname = item.__class__.__name__
520 else:
521 classname = "%s.%s" % \
522 (item.__class__.__module__,item.__class__.__name__)
523 yield (style_default,
524 "<%s object with %d items at 0x%x>" % \
525 (classname, len(item), id(item)))
526 else:
527 if isinstance(item, list):
528 yield (style_default, "[")
529 end = "]"
530 else:
531 yield (style_default, "(")
532 end = ")"
533 for (i, subitem) in enumerate(item):
534 if i:
535 yield (style_default, ", ")
536 for part in xrepr(subitem, "default"):
537 yield part
538 yield (style_default, end)
539 elif isinstance(item, (dict, types.DictProxyType)):
540 yield (-1, False)
541 if mode == "header" or mode == "footer":
542 if item.__class__.__module__ == "__builtin__":
543 classname = item.__class__.__name__
544 else:
545 classname = "%s.%s" % \
546 (item.__class__.__module__,item.__class__.__name__)
547 yield (style_default,
548 "<%s object with %d items at 0x%x>" % \
549 (classname, len(item), id(item)))
550 else:
551 if isinstance(item, dict):
552 yield (style_default, "{")
553 end = "}"
554 else:
555 yield (style_default, "dictproxy((")
556 end = "})"
557 for (i, (key, value)) in enumerate(item.iteritems()):
558 if i:
559 yield (style_default, ", ")
560 for part in xrepr(key, "default"):
561 yield part
562 yield (style_default, ": ")
563 for part in xrepr(value, "default"):
564 yield part
565 yield (style_default, end)
566 else:
567 yield (-1, True)
568 yield (style_default, repr(item))
349
569
350
570
351 def xattrs(item, mode):
571 def xattrs(item, mode):
352 try:
572 try:
353 func = item.__xattrs__
573 func = item.__xattrs__
354 except AttributeError:
574 except AttributeError:
355 if isinstance(item, (list, tuple)):
356 return xrange(len(item))
357 return (None,)
575 return (None,)
358 else:
576 else:
359 return func(mode)
577 return func(mode)
@@ -368,7 +586,7 b' def xiter(item, mode):'
368 try:
586 try:
369 func = item.__xiter__
587 func = item.__xiter__
370 except AttributeError:
588 except AttributeError:
371 if isinstance(item, dict):
589 if isinstance(item, (dict, types.DictProxyType)):
372 def items(item):
590 def items(item):
373 fields = ("key", "value")
591 fields = ("key", "value")
374 for (key, value) in item.iteritems():
592 for (key, value) in item.iteritems():
@@ -623,15 +841,19 b' class ifile(object):'
623 return ("name", "type", "size", "access", "owner", "group", "mdate")
841 return ("name", "type", "size", "access", "owner", "group", "mdate")
624
842
625 def __xrepr__(self, mode):
843 def __xrepr__(self, mode):
626 if mode == "header" or mode == "footer":
844 yield (-1, True)
845 if mode in "header" or mode == "footer" or mode == "cell":
627 name = "ifile"
846 name = "ifile"
628 try:
847 try:
629 if self.isdir:
848 if self.isdir:
630 name = "idir"
849 name = "idir"
631 except IOError:
850 except IOError:
632 pass
851 pass
633 return "%s(%r)" % (name, self.abspath)
852 yield (style_url, "%s(%r)" % (name, self.abspath))
634 return repr(self)
853 elif mode == "cell":
854 yield (style_url, repr(self.abspath)[1:-1])
855 else:
856 yield (style_url, repr(self))
635
857
636 def __xiter__(self, mode):
858 def __xiter__(self, mode):
637 if self.isdir:
859 if self.isdir:
@@ -687,9 +909,13 b' class ils(Table):'
687 return xiter(ifile(self.base), mode)
909 return xiter(ifile(self.base), mode)
688
910
689 def __xrepr__(self, mode):
911 def __xrepr__(self, mode):
690 if mode == "header" or mode == "footer":
912 yield (-1, True)
691 return "idir(%r)" % (os.path.abspath(self.base))
913 if mode == "header" or mode == "footer" or mode == "cell":
692 return repr(self)
914 yield (style_url, "idir(%r)" % (os.path.abspath(self.base)))
915 elif mode == "cell":
916 yield (style_url, repr(os.path.abspath(self.base))[1:-1])
917 else:
918 yield (style_url, repr(self))
693
919
694 def __repr__(self):
920 def __repr__(self):
695 return "%s.%s(%r)" % \
921 return "%s.%s(%r)" % \
@@ -709,9 +935,11 b' class iglob(Table):'
709 yield ifile(name)
935 yield ifile(name)
710
936
711 def __xrepr__(self, mode):
937 def __xrepr__(self, mode):
712 if mode == "header" or mode == "footer":
938 yield (-1, True)
713 return "%s(%r)" % (self.__class__.__name__, self.glob)
939 if mode == "header" or mode == "footer" or mode == "cell":
714 return repr(self)
940 yield (style_default, "%s(%r)" % (self.__class__.__name__, self.glob))
941 else:
942 yield (style_default, repr(self))
715
943
716 def __repr__(self):
944 def __repr__(self):
717 return "%s.%s(%r)" % \
945 return "%s.%s(%r)" % \
@@ -738,9 +966,11 b' class iwalk(Table):'
738 yield ifile(os.path.join(dirpath, name))
966 yield ifile(os.path.join(dirpath, name))
739
967
740 def __xrepr__(self, mode):
968 def __xrepr__(self, mode):
741 if mode == "header" or mode == "footer":
969 yield (-1, True)
742 return "%s(%r)" % (self.__class__.__name__, self.base)
970 if mode == "header" or mode == "footer" or mode == "cell":
743 return repr(self)
971 yield (style_default, "%s(%r)" % (self.__class__.__name__, self.base))
972 else:
973 yield (style_default, repr(self))
744
974
745 def __repr__(self):
975 def __repr__(self):
746 return "%s.%s(%r)" % \
976 return "%s.%s(%r)" % \
@@ -820,9 +1050,11 b' class ipwd(Table):'
820 yield ipwdentry(entry.pw_name)
1050 yield ipwdentry(entry.pw_name)
821
1051
822 def __xrepr__(self, mode):
1052 def __xrepr__(self, mode):
823 if mode == "header" or mode == "footer":
1053 yield (-1, True)
824 return "%s()" % self.__class__.__name__
1054 if mode == "header" or mode == "footer" or mode == "cell":
825 return repr(self)
1055 yield (style_default, "%s()" % self.__class__.__name__)
1056 else:
1057 yield (style_default, repr(self))
826
1058
827
1059
828 class igrpentry(object):
1060 class igrpentry(object):
@@ -867,9 +1099,11 b' class igrpentry(object):'
867 return ("name", "passwd", "gid", "mem")
1099 return ("name", "passwd", "gid", "mem")
868
1100
869 def __xrepr__(self, mode):
1101 def __xrepr__(self, mode):
870 if mode == "header" or mode == "footer":
1102 yield (-1, True)
871 return "group %s" % self.name
1103 if mode == "header" or mode == "footer" or mode == "cell":
872 return repr(self)
1104 yield (style_default, "group %s" % self.name)
1105 else:
1106 yield (style_default, repr(self))
873
1107
874 def __xiter__(self, mode):
1108 def __xiter__(self, mode):
875 for member in self.mem:
1109 for member in self.mem:
@@ -889,9 +1123,11 b' class igrp(Table):'
889 yield igrpentry(entry.gr_name)
1123 yield igrpentry(entry.gr_name)
890
1124
891 def __xrepr__(self, mode):
1125 def __xrepr__(self, mode):
1126 yield (-1, False)
892 if mode == "header" or mode == "footer":
1127 if mode == "header" or mode == "footer":
893 return "%s()" % self.__class__.__name__
1128 yield (style_default, "%s()" % self.__class__.__name__)
894 return repr(self)
1129 else:
1130 yield (style_default, repr(self))
895
1131
896
1132
897 class Fields(object):
1133 class Fields(object):
@@ -904,10 +1140,28 b' class Fields(object):'
904 return self.__fieldnames
1140 return self.__fieldnames
905
1141
906 def __xrepr__(self, mode):
1142 def __xrepr__(self, mode):
907 if mode == "header" or mode == "footer":
1143 yield (-1, False)
908 args = ["%s=%r" % (f, getattr(self, f)) for f in self.__fieldnames]
1144 if mode == "header" or mode == "cell":
909 return "%s(%r)" % (self.__class__.__name__, ", ".join(args))
1145 yield (style_default, self.__class__.__name__)
910 return repr(self)
1146 yield (style_default, "(")
1147 for (i, f) in enumerate(self.__fieldnames):
1148 if i:
1149 yield (style_default, ", ")
1150 yield (style_default, f)
1151 yield (style_default, "=")
1152 for part in xrepr(getattr(self, f), "default"):
1153 yield part
1154 yield (style_default, ")")
1155 elif mode == "footer":
1156 yield (style_default, self.__class__.__name__)
1157 yield (style_default, "(")
1158 for (i, f) in enumerate(self.__fieldnames):
1159 if i:
1160 yield (style_default, ", ")
1161 yield (style_default, f)
1162 yield (style_default, ")")
1163 else:
1164 yield (style_default, repr(self))
911
1165
912
1166
913 class FieldTable(Table, list):
1167 class FieldTable(Table, list):
@@ -923,9 +1177,17 b' class FieldTable(Table, list):'
923 return list.__iter__(self)
1177 return list.__iter__(self)
924
1178
925 def __xrepr__(self, mode):
1179 def __xrepr__(self, mode):
1180 yield (-1, False)
926 if mode == "header" or mode == "footer":
1181 if mode == "header" or mode == "footer":
927 return "FieldTable(%r)" % ", ".join(map(repr, self.fields))
1182 yield (style_default, self.__class__.__name__)
928 return repr(self)
1183 yield (style_default, "(")
1184 for (i, f) in enumerate(self.__fieldnames):
1185 if i:
1186 yield (style_default, ", ")
1187 yield (style_default, f)
1188 yield (style_default, ")")
1189 else:
1190 yield (style_default, repr(self))
929
1191
930 def __repr__(self):
1192 def __repr__(self):
931 return "<%s.%s object with fields=%r at 0x%x>" % \
1193 return "<%s.%s object with fields=%r at 0x%x>" % \
@@ -933,6 +1195,25 b' class FieldTable(Table, list):'
933 ", ".join(map(repr, self.fields)), id(self))
1195 ", ".join(map(repr, self.fields)), id(self))
934
1196
935
1197
1198 class List(list):
1199 def __xattrs__(self, mode):
1200 return xrange(len(self))
1201
1202 def __xrepr__(self, mode):
1203 yield (-1, False)
1204 if mode == "header" or mode == "cell" or mode == "footer" or mode == "default":
1205 yield (style_default, self.__class__.__name__)
1206 yield (style_default, "(")
1207 for (i, item) in enumerate(self):
1208 if i:
1209 yield (style_default, ", ")
1210 for part in xrepr(item, "default"):
1211 yield part
1212 yield (style_default, ")")
1213 else:
1214 yield (style_default, repr(self))
1215
1216
936 class ienv(Table):
1217 class ienv(Table):
937 """
1218 """
938 This ``Table`` lists environment variables.
1219 This ``Table`` lists environment variables.
@@ -944,9 +1225,11 b' class ienv(Table):'
944 yield Fields(fields, key=key, value=value)
1225 yield Fields(fields, key=key, value=value)
945
1226
946 def __xrepr__(self, mode):
1227 def __xrepr__(self, mode):
947 if mode == "header" or mode == "footer":
1228 yield (-1, True)
948 return "%s()" % self.__class__.__name__
1229 if mode == "header" or mode == "cell":
949 return repr(self)
1230 yield (style_default, "%s()" % self.__class__.__name__)
1231 else:
1232 yield (style_default, repr(self))
950
1233
951
1234
952 class icsv(Pipe):
1235 class icsv(Pipe):
@@ -967,18 +1250,27 b' class icsv(Pipe):'
967 input = input.open("rb")
1250 input = input.open("rb")
968 reader = csv.reader(input, **self.csvargs)
1251 reader = csv.reader(input, **self.csvargs)
969 for line in reader:
1252 for line in reader:
970 yield line
1253 yield List(line)
971
1254
972 def __xrepr__(self, mode):
1255 def __xrepr__(self, mode):
1256 yield (-1, False)
973 if mode == "header" or mode == "footer":
1257 if mode == "header" or mode == "footer":
974 input = getattr(self, "input", None)
1258 input = getattr(self, "input", None)
975 if input is not None:
1259 if input is not None:
976 prefix = "%s | " % xrepr(input, mode)
1260 for part in xrepr(input, mode):
977 else:
1261 yield part
978 prefix = ""
1262 yield (style_default, " | ")
979 args = ", ".join(["%s=%r" % item for item in self.csvargs.iteritems()])
1263 yield (style_default, "%s(" % self.__class__.__name__)
980 return "%s%s(%s)" % (prefix, self.__class__.__name__, args)
1264 for (i, (name, value)) in enumerate(self.csvargs.iteritems()):
981 return repr(self)
1265 if i:
1266 yield (style_default, ", ")
1267 yield (style_default, name)
1268 yield (style_default, "=")
1269 for part in xrepr(value, "default"):
1270 yield part
1271 yield (style_default, ")")
1272 else:
1273 yield (style_default, repr(self))
982
1274
983 def __repr__(self):
1275 def __repr__(self):
984 args = ", ".join(["%s=%r" % item for item in self.csvargs.iteritems()])
1276 args = ", ".join(["%s=%r" % item for item in self.csvargs.iteritems()])
@@ -1008,9 +1300,11 b' class ix(Table):'
1008 self._pipe = None
1300 self._pipe = None
1009
1301
1010 def __xrepr__(self, mode):
1302 def __xrepr__(self, mode):
1303 yield (-1, True)
1011 if mode == "header" or mode == "footer":
1304 if mode == "header" or mode == "footer":
1012 return "%s(%r)" % (self.__class__.__name__, self.cmd)
1305 yield (style_default, "%s(%r)" % (self.__class__.__name__, self.cmd))
1013 return repr(self)
1306 else:
1307 yield (style_default, repr(self))
1014
1308
1015 def __repr__(self):
1309 def __repr__(self):
1016 return "%s.%s(%r)" % \
1310 return "%s.%s(%r)" % \
@@ -1051,15 +1345,19 b' class ifilter(Pipe):'
1051 pass # Ignore errors
1345 pass # Ignore errors
1052
1346
1053 def __xrepr__(self, mode):
1347 def __xrepr__(self, mode):
1348 yield (-1, True)
1054 if mode == "header" or mode == "footer":
1349 if mode == "header" or mode == "footer":
1055 input = getattr(self, "input", None)
1350 input = getattr(self, "input", None)
1056 if input is not None:
1351 if input is not None:
1057 prefix = "%s | " % xrepr(input, mode)
1352 for part in xrepr(input, mode):
1058 else:
1353 yield part
1059 prefix = ""
1354 yield (style_default, " | ")
1060 return "%s%s(%s)"% \
1355 yield (style_default, "%s(" % self.__class__.__name__)
1061 (prefix, self.__class__.__name__, xrepr(self.expr, mode))
1356 for part in xrepr(self.expr, "default"):
1062 return repr(self)
1357 yield part
1358 yield (style_default, ")")
1359 else:
1360 yield (style_default, repr(self))
1063
1361
1064 def __repr__(self):
1362 def __repr__(self):
1065 return "<%s.%s expr=%r at 0x%x>" % \
1363 return "<%s.%s expr=%r at 0x%x>" % \
@@ -1098,15 +1396,19 b' class ieval(Pipe):'
1098 pass # Ignore errors
1396 pass # Ignore errors
1099
1397
1100 def __xrepr__(self, mode):
1398 def __xrepr__(self, mode):
1399 yield (-1, True)
1101 if mode == "header" or mode == "footer":
1400 if mode == "header" or mode == "footer":
1102 input = getattr(self, "input", None)
1401 input = getattr(self, "input", None)
1103 if input is not None:
1402 if input is not None:
1104 prefix = "%s | " % xrepr(input, mode)
1403 for part in xrepr(input, mode):
1105 else:
1404 yield part
1106 prefix = ""
1405 yield (style_default, " | ")
1107 return "%s%s(%s)" % \
1406 yield (style_default, "%s(" % self.__class__.__name__)
1108 (prefix, self.__class__.__name__, xrepr(self.expr, mode))
1407 for part in xrepr(self.expr, "default"):
1109 return repr(self)
1408 yield part
1409 yield (style_default, ")")
1410 else:
1411 yield (style_default, repr(self))
1110
1412
1111 def __repr__(self):
1413 def __repr__(self):
1112 return "<%s.%s expr=%r at 0x%x>" % \
1414 return "<%s.%s expr=%r at 0x%x>" % \
@@ -1154,18 +1456,23 b' class isort(Pipe):'
1154 yield item
1456 yield item
1155
1457
1156 def __xrepr__(self, mode):
1458 def __xrepr__(self, mode):
1459 yield (-1, True)
1157 if mode == "header" or mode == "footer":
1460 if mode == "header" or mode == "footer":
1158 input = getattr(self, "input", None)
1461 input = getattr(self, "input", None)
1159 if input is not None:
1462 if input is not None:
1160 prefix = "%s | " % xrepr(input, mode)
1463 for part in xrepr(input, mode):
1161 else:
1464 yield part
1162 prefix = ""
1465 yield (style_default, " | ")
1466 yield (style_default, "%s(" % self.__class__.__name__)
1467 for part in xrepr(self.key, "default"):
1468 yield part
1163 if self.reverse:
1469 if self.reverse:
1164 return "%s%s(%r, %r)" % \
1470 yield (style_default, ", ")
1165 (prefix, self.__class__.__name__, self.key, self.reverse)
1471 for part in xrepr(True, "default"):
1166 else:
1472 yield part
1167 return "%s%s(%r)" % (prefix, self.__class__.__name__, self.key)
1473 yield (style_default, ")")
1168 return repr(self)
1474 else:
1475 yield (style_default, repr(self))
1169
1476
1170 def __repr__(self):
1477 def __repr__(self):
1171 return "<%s.%s key=%r reverse=%r at 0x%x>" % \
1478 return "<%s.%s key=%r reverse=%r at 0x%x>" % \
@@ -1509,19 +1816,6 b' if curses is not None:'
1509 """
1816 """
1510
1817
1511
1818
1512 class Style(object):
1513 """
1514 Store foreground color, background color and attribute (bold, underlined
1515 etc.) for ``curses``.
1516 """
1517 __slots__ = ("fg", "bg", "attrs")
1518
1519 def __init__(self, fg, bg, attrs=0):
1520 self.fg = fg
1521 self.bg = bg
1522 self.attrs = attrs
1523
1524
1525 class _BrowserCachedItem(object):
1819 class _BrowserCachedItem(object):
1526 # This is used internally by ``ibrowse`` to store a item together with its
1820 # This is used internally by ``ibrowse`` to store a item together with its
1527 # marked status.
1821 # marked status.
@@ -1580,7 +1874,7 b' if curses is not None:'
1580 def __init__(self, browser, input, iterator, mainsizey, *attrs):
1874 def __init__(self, browser, input, iterator, mainsizey, *attrs):
1581 self.browser = browser
1875 self.browser = browser
1582 self.input = input
1876 self.input = input
1583 self.header = xrepr(input, "header")
1877 self.header = list(x for x in xrepr(input, "header") if not isinstance(x[0], int))
1584 # iterator for the input
1878 # iterator for the input
1585 self.iterator = iterator
1879 self.iterator = iterator
1586
1880
@@ -1677,11 +1971,31 b' if curses is not None:'
1677 except (KeyboardInterrupt, SystemExit):
1971 except (KeyboardInterrupt, SystemExit):
1678 raise
1972 raise
1679 except Exception, exc:
1973 except Exception, exc:
1680 row[attrname] = self.browser.format(exc)
1974 value = exc
1681 else:
1975 # only store attribute if it exists (or we got an exception)
1682 # only store attribute if it exists
1976 if value is not _default:
1683 if value is not _default:
1977 parts = []
1684 row[attrname] = self.browser.format(value)
1978 totallength = 0
1979 align = None
1980 full = False
1981 # Collect parts until we have enough
1982 for part in xrepr(value, "cell"):
1983 # part gives (alignment, stop)
1984 # instead of (style, text)
1985 if isinstance(part[0], int):
1986 # only consider the first occurence
1987 if align is None:
1988 align = part[0]
1989 full = part[1]
1990 else:
1991 parts.append(part)
1992 totallength += len(part[1])
1993 if totallength >= self.browser.maxattrlength and not full:
1994 parts.append((style_ellisis, "..."))
1995 totallength += 3
1996 break
1997 # remember alignment, length and colored parts
1998 row[attrname] = (align, totallength, parts)
1685 return row
1999 return row
1686
2000
1687 def calcwidths(self):
2001 def calcwidths(self):
@@ -1693,11 +2007,14 b' if curses is not None:'
1693 self.colwidths = {}
2007 self.colwidths = {}
1694 for row in self.displayrows:
2008 for row in self.displayrows:
1695 for attrname in self.displayattrs:
2009 for attrname in self.displayattrs:
1696 (align, text, style) = row.get(attrname, (2, "", None))
2010 try:
2011 length = row[attrname][1]
2012 except KeyError:
2013 length = 0
1697 # always add attribute to colwidths, even if it doesn't exist
2014 # always add attribute to colwidths, even if it doesn't exist
1698 if attrname not in self.colwidths:
2015 if attrname not in self.colwidths:
1699 self.colwidths[attrname] = len(_attrname(attrname))
2016 self.colwidths[attrname] = len(_attrname(attrname))
1700 newwidth = max(self.colwidths[attrname], len(text))
2017 newwidth = max(self.colwidths[attrname], length)
1701 self.colwidths[attrname] = newwidth
2018 self.colwidths[attrname] = newwidth
1702
2019
1703 # How many characters do we need to paint the item number?
2020 # How many characters do we need to paint the item number?
@@ -1865,29 +2182,24 b' if curses is not None:'
1865 # if the nesting is deeper, only the innermost levels are displayed
2182 # if the nesting is deeper, only the innermost levels are displayed
1866 maxheaders = 5
2183 maxheaders = 5
1867
2184
2185 # The approximate maximum length of a column entry
2186 maxattrlength = 200
2187
1868 # Styles for various parts of the GUI
2188 # Styles for various parts of the GUI
1869 style_objheadertext = Style(curses.COLOR_WHITE, curses.COLOR_BLACK, curses.A_BOLD|curses.A_REVERSE)
2189 style_objheadertext = Style(COLOR_WHITE, COLOR_BLACK, A_BOLD|A_REVERSE)
1870 style_objheadernumber = Style(curses.COLOR_WHITE, curses.COLOR_BLUE, curses.A_BOLD|curses.A_REVERSE)
2190 style_objheadernumber = Style(COLOR_WHITE, COLOR_BLUE, A_BOLD|A_REVERSE)
1871 style_objheaderobject = Style(curses.COLOR_WHITE, curses.COLOR_BLACK, curses.A_REVERSE)
2191 style_objheaderobject = Style(COLOR_WHITE, COLOR_BLACK, A_REVERSE)
1872 style_colheader = Style(curses.COLOR_BLUE, curses.COLOR_WHITE, curses.A_REVERSE)
2192 style_colheader = Style(COLOR_BLUE, COLOR_WHITE, A_REVERSE)
1873 style_colheaderhere = Style(curses.COLOR_GREEN, curses.COLOR_BLACK, curses.A_BOLD|curses.A_REVERSE)
2193 style_colheaderhere = Style(COLOR_GREEN, COLOR_BLACK, A_BOLD|A_REVERSE)
1874 style_colheadersep = Style(curses.COLOR_BLUE, curses.COLOR_BLACK, curses.A_REVERSE)
2194 style_colheadersep = Style(COLOR_BLUE, COLOR_BLACK, A_REVERSE)
1875 style_number = Style(curses.COLOR_BLUE, curses.COLOR_WHITE, curses.A_REVERSE)
2195 style_number = Style(COLOR_BLUE, COLOR_WHITE, A_REVERSE)
1876 style_numberhere = Style(curses.COLOR_GREEN, curses.COLOR_BLACK, curses.A_BOLD|curses.A_REVERSE)
2196 style_numberhere = Style(COLOR_GREEN, COLOR_BLACK, A_BOLD|A_REVERSE)
1877 style_sep = Style(curses.COLOR_BLUE, curses.COLOR_BLACK)
2197 style_sep = Style(COLOR_BLUE, COLOR_BLACK)
1878 style_data = Style(curses.COLOR_WHITE, curses.COLOR_BLACK)
2198 style_data = Style(COLOR_WHITE, COLOR_BLACK)
1879 style_datapad = Style(curses.COLOR_BLUE, curses.COLOR_BLACK, curses.A_BOLD)
2199 style_datapad = Style(COLOR_BLUE, COLOR_BLACK, A_BOLD)
1880 style_footer = Style(curses.COLOR_BLACK, curses.COLOR_WHITE)
2200 style_footer = Style(COLOR_BLACK, COLOR_WHITE)
1881 style_noattr = Style(curses.COLOR_RED, curses.COLOR_BLACK)
2201 style_noattr = Style(COLOR_RED, COLOR_BLACK)
1882 style_error = Style(curses.COLOR_RED, curses.COLOR_BLACK)
2202 style_report = Style(COLOR_WHITE, COLOR_BLACK)
1883 style_default = Style(curses.COLOR_WHITE, curses.COLOR_BLACK)
1884 style_report = Style(curses.COLOR_WHITE, curses.COLOR_BLACK)
1885
1886 # Styles for datatypes
1887 style_type_none = Style(curses.COLOR_MAGENTA, curses.COLOR_BLACK)
1888 style_type_bool = Style(curses.COLOR_GREEN, curses.COLOR_BLACK)
1889 style_type_number = Style(curses.COLOR_YELLOW, curses.COLOR_BLACK)
1890 style_type_datetime = Style(curses.COLOR_CYAN, curses.COLOR_BLACK)
1891
2203
1892 # Column separator in header
2204 # Column separator in header
1893 headersepchar = "|"
2205 headersepchar = "|"
@@ -1913,7 +2225,9 b' if curses is not None:'
1913 curses.KEY_HOME: "home",
2225 curses.KEY_HOME: "home",
1914 curses.KEY_END: "end",
2226 curses.KEY_END: "end",
1915 ord("<"): "prevattr",
2227 ord("<"): "prevattr",
2228 0x1b: "prevattr", # SHIFT-TAB
1916 ord(">"): "nextattr",
2229 ord(">"): "nextattr",
2230 ord("\t"):"nextattr", # TAB
1917 ord("p"): "pick",
2231 ord("p"): "pick",
1918 ord("P"): "pickattr",
2232 ord("P"): "pickattr",
1919 ord("C"): "pickallattrs",
2233 ord("C"): "pickallattrs",
@@ -1957,7 +2271,8 b' if curses is not None:'
1957 # multiple beeps).
2271 # multiple beeps).
1958 self._dobeep = True
2272 self._dobeep = True
1959
2273
1960 # Cache for registered ``curses`` colors.
2274 # Cache for registered ``curses`` colors and styles.
2275 self._styles = {}
1961 self._colors = {}
2276 self._colors = {}
1962 self._maxcolor = 1
2277 self._maxcolor = 1
1963
2278
@@ -1996,60 +2311,62 b' if curses is not None:'
1996 if it has been registered before.
2311 if it has been registered before.
1997 """
2312 """
1998 try:
2313 try:
1999 return self._colors[style.fg, style.bg] | style.attrs
2314 return self._styles[style.fg, style.bg, style.attrs]
2000 except KeyError:
2315 except KeyError:
2001 curses.init_pair(self._maxcolor, style.fg, style.bg)
2316 attrs = 0
2002 pair = curses.color_pair(self._maxcolor)
2317 for b in A2CURSES:
2003 self._colors[style.fg, style.bg] = pair
2318 if style.attrs & b:
2004 c = pair | style.attrs
2319 attrs |= A2CURSES[b]
2005 self._maxcolor += 1
2320 try:
2321 color = self._colors[style.fg, style.bg]
2322 except KeyError:
2323 curses.init_pair(
2324 self._maxcolor,
2325 COLOR2CURSES[style.fg],
2326 COLOR2CURSES[style.bg]
2327 )
2328 color = curses.color_pair(self._maxcolor)
2329 self._colors[style.fg, style.bg] = color
2330 self._maxcolor += 1
2331 c = color | attrs
2332 self._styles[style.fg, style.bg, style.attrs] = c
2006 return c
2333 return c
2007
2334
2008 def addstr(self, y, x, begx, endx, s, style):
2009 """
2010 A version of ``curses.addstr()`` that can handle ``x`` coordinates
2011 that are outside the screen.
2012 """
2013 s2 = s[max(0, begx-x):max(0, endx-x)]
2014 if s2:
2015 self.scr.addstr(y, max(x, begx), s2, self.getstyle(style))
2016 return len(s)
2017
2018 def format(self, value):
2335 def format(self, value):
2019 """
2336 """
2020 Formats one attribute and returns an ``(alignment, string, style)``
2337 Formats one attribute and returns an ``(alignment, string, style)``
2021 tuple.
2338 tuple.
2022 """
2339 """
2023 if value is None:
2340 if value is None:
2024 return (-1, repr(value), self.style_type_none)
2341 return (-1, repr(value), style_type_none)
2025 elif isinstance(value, str):
2342 elif isinstance(value, str):
2026 return (-1, repr(value.expandtabs(tab))[1:-1], self.style_default)
2343 return (-1, repr(value.expandtabs(tab))[1:-1], style_default)
2027 elif isinstance(value, unicode):
2344 elif isinstance(value, unicode):
2028 return (-1, repr(value.expandtabs(tab))[2:-1], self.style_default)
2345 return (-1, repr(value.expandtabs(tab))[2:-1], style_default)
2029 elif isinstance(value, datetime.datetime):
2346 elif isinstance(value, datetime.datetime):
2030 # Don't use strftime() here, as this requires year >= 1900
2347 # Don't use strftime() here, as this requires year >= 1900
2031 return (-1, "%04d-%02d-%02d %02d:%02d:%02d.%06d" % \
2348 return (-1, "%04d-%02d-%02d %02d:%02d:%02d.%06d" % \
2032 (value.year, value.month, value.day,
2349 (value.year, value.month, value.day,
2033 value.hour, value.minute, value.second,
2350 value.hour, value.minute, value.second,
2034 value.microsecond),
2351 value.microsecond),
2035 self.style_type_datetime)
2352 style_type_datetime)
2036 elif isinstance(value, datetime.date):
2353 elif isinstance(value, datetime.date):
2037 return (-1, "%04d-%02d-%02d" % \
2354 return (-1, "%04d-%02d-%02d" % \
2038 (value.year, value.month, value.day),
2355 (value.year, value.month, value.day),
2039 self.style_type_datetime)
2356 style_type_datetime)
2040 elif isinstance(value, datetime.time):
2357 elif isinstance(value, datetime.time):
2041 return (-1, "%02d:%02d:%02d.%06d" % \
2358 return (-1, "%02d:%02d:%02d.%06d" % \
2042 (value.hour, value.minute, value.second,
2359 (value.hour, value.minute, value.second,
2043 value.microsecond),
2360 value.microsecond),
2044 self.style_type_datetime)
2361 style_type_datetime)
2045 elif isinstance(value, datetime.timedelta):
2362 elif isinstance(value, datetime.timedelta):
2046 return (-1, repr(value), self.style_type_datetime)
2363 return (-1, repr(value), style_type_datetime)
2047 elif isinstance(value, bool):
2364 elif isinstance(value, bool):
2048 return (-1, repr(value), self.style_type_bool)
2365 return (-1, repr(value), style_type_bool)
2049 elif isinstance(value, (int, long, float)):
2366 elif isinstance(value, (int, long, float)):
2050 return (1, repr(value), self.style_type_number)
2367 return (1, repr(value), style_type_number)
2051 elif isinstance(value, complex):
2368 elif isinstance(value, complex):
2052 return (-1, repr(value), self.style_type_number)
2369 return (-1, repr(value), style_type_number)
2053 elif isinstance(value, Exception):
2370 elif isinstance(value, Exception):
2054 if value.__class__.__module__ == "exceptions":
2371 if value.__class__.__module__ == "exceptions":
2055 value = "%s: %s" % (value.__class__.__name__, value)
2372 value = "%s: %s" % (value.__class__.__name__, value)
@@ -2057,8 +2374,25 b' if curses is not None:'
2057 value = "%s.%s: %s" % \
2374 value = "%s.%s: %s" % \
2058 (value.__class__.__module__, value.__class__.__name__,
2375 (value.__class__.__module__, value.__class__.__name__,
2059 value)
2376 value)
2060 return (-1, value, self.style_error)
2377 return (-1, value, style_error)
2061 return (-1, repr(value), self.style_default)
2378 return (-1, repr(value), style_default)
2379
2380 def addstr(self, y, x, begx, endx, text, style):
2381 """
2382 A version of ``curses.addstr()`` that can handle ``x`` coordinates
2383 that are outside the screen.
2384 """
2385 text2 = text[max(0, begx-x):max(0, endx-x)]
2386 if text2:
2387 self.scr.addstr(y, max(x, begx), text2, self.getstyle(style))
2388 return len(text)
2389
2390 def addchr(self, y, x, begx, endx, c, l, style):
2391 x0 = max(x, begx)
2392 x1 = min(x+l, endx)
2393 if x1>x0:
2394 self.scr.addstr(y, x0, c*(x1-x0), self.getstyle(style))
2395 return l
2062
2396
2063 def _calcheaderlines(self, levels):
2397 def _calcheaderlines(self, levels):
2064 # Calculate how many headerlines do we have to display, if we have
2398 # Calculate how many headerlines do we have to display, if we have
@@ -2073,7 +2407,7 b' if curses is not None:'
2073 Return a style for displaying the original style ``style``
2407 Return a style for displaying the original style ``style``
2074 in the row the cursor is on.
2408 in the row the cursor is on.
2075 """
2409 """
2076 return Style(style.fg, style.bg, style.attrs | curses.A_BOLD)
2410 return Style(style.fg, style.bg, style.attrs | A_BOLD)
2077
2411
2078 def report(self, msg):
2412 def report(self, msg):
2079 """
2413 """
@@ -2406,7 +2740,7 b' if curses is not None:'
2406 for (key, cmd) in self.keymap.iteritems():
2740 for (key, cmd) in self.keymap.iteritems():
2407 if cmd == "help":
2741 if cmd == "help":
2408 keys.append("%s=%s" % (self.keylabel(key), cmd))
2742 keys.append("%s=%s" % (self.keylabel(key), cmd))
2409 helpmsg = " %s" % " ".join(keys)
2743 helpmsg = " | %s" % " ".join(keys)
2410
2744
2411 scr.clear()
2745 scr.clear()
2412 msg = "Fetching first batch of objects..."
2746 msg = "Fetching first batch of objects..."
@@ -2431,16 +2765,22 b' if curses is not None:'
2431 for i in xrange(self._firstheaderline, self._firstheaderline+self._headerlines):
2765 for i in xrange(self._firstheaderline, self._firstheaderline+self._headerlines):
2432 lv = self.levels[i]
2766 lv = self.levels[i]
2433 posx = 0
2767 posx = 0
2434 posx += self.addstr(i-self._firstheaderline, posx, 0, self.scrsizex, " ibrowse #%d: " % i, self.style_objheadertext)
2768 posy = i-self._firstheaderline
2435 posx += self.addstr(i-self._firstheaderline, posx, 0, self.scrsizex, lv.header, self.style_objheaderobject)
2769 endx = self.scrsizex
2436 if i: # not the first level
2770 if i: # not the first level
2437 posx += self.addstr(i-self._firstheaderline, posx, 0, self.scrsizex, " == ", self.style_objheadertext)
2771 msg = " (%d/%d" % (self.levels[i-1].cury, len(self.levels[i-1].items))
2438 msg = "%d/%d" % (self.levels[i-1].cury, len(self.levels[i-1].items))
2439 if not self.levels[i-1].exhausted:
2772 if not self.levels[i-1].exhausted:
2440 msg += "+"
2773 msg += "+"
2441 posx += self.addstr(i-self._firstheaderline, posx, 0, self.scrsizex, msg, self.style_objheadernumber)
2774 msg += ") "
2442 if posx < self.scrsizex:
2775 endx -= len(msg)+1
2443 scr.addstr(" "*(self.scrsizex-posx), self.getstyle(self.style_objheadertext))
2776 posx += self.addstr(posy, posx, 0, endx, " ibrowse #%d: " % i, self.style_objheadertext)
2777 for (style, text) in lv.header:
2778 posx += self.addstr(posy, posx, 0, endx, text, self.style_objheaderobject)
2779 if posx >= endx:
2780 break
2781 if i:
2782 posx += self.addstr(posy, posx, 0, self.scrsizex, msg, self.style_objheadernumber)
2783 posx += self.addchr(posy, posx, 0, self.scrsizex, " ", self.scrsizex-posx, self.style_objheadernumber)
2444
2784
2445 # Paint column headers
2785 # Paint column headers
2446 scr.move(self._headerlines, 0)
2786 scr.move(self._headerlines, 0)
@@ -2481,33 +2821,35 b' if curses is not None:'
2481
2821
2482 for attrname in level.displayattrs:
2822 for attrname in level.displayattrs:
2483 cwidth = level.colwidths[attrname]
2823 cwidth = level.colwidths[attrname]
2484 (align, text, style) = level.displayrows[i-level.datastarty].get(attrname, (2, None, self.style_noattr))
2824 try:
2825 (align, length, parts) = level.displayrows[i-level.datastarty][attrname]
2826 except KeyError:
2827 align = 2
2485 padstyle = self.style_datapad
2828 padstyle = self.style_datapad
2486 sepstyle = self.style_sep
2829 sepstyle = self.style_sep
2487 if i == level.cury:
2830 if i == level.cury:
2488 style = self.getstylehere(style)
2489 padstyle = self.getstylehere(padstyle)
2831 padstyle = self.getstylehere(padstyle)
2490 sepstyle = self.getstylehere(sepstyle)
2832 sepstyle = self.getstylehere(sepstyle)
2491 if align == 2:
2833 if align == 2:
2492 text = self.nodatachar*cwidth
2834 posx += self.addchr(posy, posx, begx, self.scrsizex, self.nodatachar, cwidth, style)
2493 posx += self.addstr(posy, posx, begx, self.scrsizex, text, style)
2835 else:
2494 elif align == -1:
2836 if align == 1:
2495 pad = self.datapadchar*(cwidth-len(text))
2837 posx += self.addchr(posy, posx, begx, self.scrsizex, self.datapadchar, cwidth-length, padstyle)
2496 posx += self.addstr(posy, posx, begx, self.scrsizex, text, style)
2838 elif align == 0:
2497 posx += self.addstr(posy, posx, begx, self.scrsizex, pad, padstyle)
2839 pad1 = (cwidth-length)//2
2498 elif align == 0:
2840 pad2 = cwidth-length-len(pad1)
2499 pad1 = self.datapadchar*((cwidth-len(text))//2)
2841 posx += self.addchr(posy, posx, begx, self.scrsizex, self.datapadchar, pad1, padstyle)
2500 pad2 = self.datapadchar*(cwidth-len(text)-len(pad1))
2842 for (style, text) in parts:
2501 posx += self.addstr(posy, posx, begx, self.scrsizex, pad1, padstyle)
2843 if i == level.cury:
2502 posx += self.addstr(posy, posx, begx, self.scrsizex, text, style)
2844 style = self.getstylehere(style)
2503 posx += self.addstr(posy, posx, begx, self.scrsizex, pad2, padstyle)
2845 posx += self.addstr(posy, posx, begx, self.scrsizex, text, style)
2504 elif align == 1:
2846 if posx >= self.scrsizex:
2505 pad = self.datapadchar*(cwidth-len(text))
2847 break
2506 posx += self.addstr(posy, posx, begx, self.scrsizex, pad, padstyle)
2848 if align == -1:
2507 posx += self.addstr(posy, posx, begx, self.scrsizex, text, style)
2849 posx += self.addchr(posy, posx, begx, self.scrsizex, self.datapadchar, cwidth-length, padstyle)
2850 elif align == 0:
2851 posx += self.addchr(posy, posx, begx, self.scrsizex, self.datapadchar, pad2, padstyle)
2508 posx += self.addstr(posy, posx, begx, self.scrsizex, self.datasepchar, sepstyle)
2852 posx += self.addstr(posy, posx, begx, self.scrsizex, self.datasepchar, sepstyle)
2509 if posx >= self.scrsizex:
2510 break
2511 else:
2853 else:
2512 scr.clrtoeol()
2854 scr.clrtoeol()
2513
2855
@@ -2516,31 +2858,51 b' if curses is not None:'
2516 scr.addstr(posy, 0, " " * (level.numbersizex+2), self.getstyle(self.style_colheader))
2858 scr.addstr(posy, 0, " " * (level.numbersizex+2), self.getstyle(self.style_colheader))
2517 scr.clrtoeol()
2859 scr.clrtoeol()
2518
2860
2861 posy = self.scrsizey-footery
2519 # Display footer
2862 # Display footer
2520 scr.addstr(self.scrsizey-footery, 0, " "*self.scrsizex, self.getstyle(self.style_footer))
2863 scr.addstr(posy, 0, " "*self.scrsizex, self.getstyle(self.style_footer))
2521
2864
2522 if level.exhausted:
2865 if level.exhausted:
2523 flag = ""
2866 flag = ""
2524 else:
2867 else:
2525 flag = "+"
2868 flag = "+"
2526
2869
2527 scr.addstr(self.scrsizey-footery, self.scrsizex-len(helpmsg)-1, helpmsg, self.getstyle(self.style_footer))
2870 endx = self.scrsizex-len(helpmsg)-1
2871 scr.addstr(posy, endx, helpmsg, self.getstyle(self.style_footer))
2528
2872
2529 msg = "%d%s objects (%d marked)" % (len(level.items), flag, level.marked)
2873 posx = 0
2530 attrname = level.displayattr[1]
2874 msg = " %d%s objects (%d marked): " % (len(level.items), flag, level.marked)
2875 posx += self.addstr(posy, posx, 0, endx, msg, self.style_footer)
2531 try:
2876 try:
2532 if attrname is not _default:
2877 item = level.items[level.cury].item
2533 msg += ": %s > %s" % (xrepr(level.items[level.cury].item, "footer"), _attrname(attrname))
2534 else:
2535 msg += ": %s > no attribute" % xrepr(level.items[level.cury].item, "footer")
2536 except IndexError: # empty
2878 except IndexError: # empty
2537 pass
2879 pass
2538 self.addstr(self.scrsizey-footery, 1, 1, self.scrsizex-len(helpmsg)-1, msg, self.style_footer)
2880 else:
2881 for (nostyle, text) in xrepr(item, "footer"):
2882 if not isinstance(nostyle, int):
2883 posx += self.addstr(posy, posx, 0, endx, text, self.style_footer)
2884 if posx >= endx:
2885 break
2886 attrname = level.displayattr[1]
2887 if attrname is not _default and attrname is not None:
2888 posx += self.addstr(posy, posx, 0, endx, " | ", self.style_footer)
2889 posx += self.addstr(posy, posx, 0, endx, _attrname(attrname), self.style_footer)
2890 posx += self.addstr(posy, posx, 0, endx, ": ", self.style_footer)
2891 attr = _getattr(item, attrname)
2892 for (nostyle, text) in xrepr(attr, "footer"):
2893 if not isinstance(nostyle, int):
2894 posx += self.addstr(posy, posx, 0, endx, text, self.style_footer)
2895 if posx >= endx:
2896 break
2897
2898 #else:
2899 #msg += ": %s > no attribute" % xrepr(level.items[level.cury].item, "footer")
2900 #self.addstr(posy, 1, 1, self.scrsizex-len(helpmsg)-1, msg, self.style_footer)
2539
2901
2540 # Display report
2902 # Display report
2541 if self._report is not None:
2903 if self._report is not None:
2542 if isinstance(self._report, Exception):
2904 if isinstance(self._report, Exception):
2543 style = self.getstyle(self.style_error)
2905 style = self.getstyle(style_error)
2544 if self._report.__class__.__module__ == "exceptions":
2906 if self._report.__class__.__module__ == "exceptions":
2545 msg = "%s: %s" % \
2907 msg = "%s: %s" % \
2546 (self._report.__class__.__name__, self._report)
2908 (self._report.__class__.__name__, self._report)
General Comments 0
You need to be logged in to leave comments. Login now