Commit 19b6fd45322bb829568d2473922ce9a670acab7b

Authored by decalage2
1 parent e636b4f8

tablestream: reverted to python 2.7 version, moved python 3 version to tablestream3.py

oletools/thirdparty/tablestream/tablestream.py
... ... @@ -236,7 +236,7 @@ class TableStream(object):
236 236 assert len(row) == self.num_columns
237 237 columns = []
238 238 max_lines = 0
239   - for i in range(self.num_columns):
  239 + for i in xrange(self.num_columns):
240 240 cell = row[i]
241 241 # Convert to string:
242 242 # TODO: handle unicode properly
... ... @@ -245,7 +245,7 @@ class TableStream(object):
245 245 # encode to UTF8, avoiding errors
246 246 cell = cell.decode('utf-8', errors='replace')
247 247 else:
248   - cell = cell
  248 + cell = unicode(cell)
249 249 # Wrap cell text according to the column width
250 250 # TODO: use a TextWrapper object for each column instead
251 251 # split the string if it contains newline characters, otherwise
... ... @@ -257,16 +257,16 @@ class TableStream(object):
257 257 if colors is not None and self.outfile.isatty():
258 258 color = colors[i]
259 259 if color:
260   - for j in range(len(column)):
  260 + for j in xrange(len(column)):
261 261 # print '%r: %s' % (column[j], type(column[j]))
262 262 column[j] = colorclass.Color('{auto%s}%s{/%s}' % (color, column[j], color))
263 263 columns.append(column)
264 264 # determine which column has the highest number of lines
265 265 max_lines = max(len(columns[i]), max_lines)
266 266 # transpose: write output line by line
267   - for j in range(max_lines):
  267 + for j in xrange(max_lines):
268 268 self.write(self.style.vertical_left)
269   - for i in range(self.num_columns):
  269 + for i in xrange(self.num_columns):
270 270 column = columns[i]
271 271 if j<len(column):
272 272 # text to be written
... ...
oletools/thirdparty/tablestream/tablestream3.py 0 → 100644
  1 +#!/usr/bin/env python
  2 +"""
  3 +tablestream
  4 +
  5 +tablestream can format table data for pretty printing as text,
  6 +to be displayed on the console or written to any file-like object.
  7 +The table data can be provided as rows, each row is an iterable of
  8 +cells. The text in each cell is wrapped to fit into a maximum width
  9 +set for each column.
  10 +Contrary to many table pretty printing libraries, TableStream writes
  11 +each row to the output as soon as it is provided, and the whole table
  12 +does not need to be built in memory before printing.
  13 +It is therefore suitable for large tables, or tables that take time to
  14 +be processed row by row.
  15 +
  16 +Author: Philippe Lagadec - http://www.decalage.info
  17 +License: BSD, see source code or documentation
  18 +"""
  19 +
  20 +#=== LICENSE ==================================================================
  21 +
  22 +# tablestream is copyright (c) 2015-2016 Philippe Lagadec (http://www.decalage.info)
  23 +# All rights reserved.
  24 +#
  25 +# Redistribution and use in source and binary forms, with or without modification,
  26 +# are permitted provided that the following conditions are met:
  27 +#
  28 +# * Redistributions of source code must retain the above copyright notice, this
  29 +# list of conditions and the following disclaimer.
  30 +# * Redistributions in binary form must reproduce the above copyright notice,
  31 +# this list of conditions and the following disclaimer in the documentation
  32 +# and/or other materials provided with the distribution.
  33 +#
  34 +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  35 +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  36 +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  37 +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  38 +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  39 +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  40 +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  41 +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  42 +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  43 +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  44 +
  45 +from __future__ import print_function
  46 +
  47 +#------------------------------------------------------------------------------
  48 +# CHANGELOG:
  49 +# 2015-11-01 v0.01 PL: - first version
  50 +# 2016-01-01 v0.02 PL: - added styles, color support
  51 +# 2016-04-19 v0.03 PL: - enable colorclass on Windows, fixed issue #39
  52 +# 2016-05-25 v0.04 PL: - updated for colorclass 2.2.0 (now a package)
  53 +# 2016-07-29 v0.05 PL: - fixed oletools issue #57, bug when importing colorclass
  54 +# 2016-07-31 v0.06 PL: - handle newline characters properly in each cell
  55 +
  56 +__version__ = '0.06'
  57 +
  58 +#------------------------------------------------------------------------------
  59 +# TODO:
  60 +# - several styles
  61 +# - colorized rows or cells
  62 +# - automatic width for the last column, based on max total width
  63 +# - automatic width for selected columns, based on N first lines
  64 +# - determine the console width
  65 +
  66 +# === IMPORTS =================================================================
  67 +
  68 +import textwrap
  69 +import sys, os
  70 +
  71 +# add the thirdparty subfolder to sys.path (absolute+normalized path):
  72 +_thismodule_dir = os.path.normpath(os.path.abspath(os.path.dirname(__file__)))
  73 +# print('_thismodule_dir = %r' % _thismodule_dir)
  74 +# assumption: this module is in a subfolder of thirdparty:
  75 +_thirdparty_dir = os.path.normpath(os.path.join(_thismodule_dir, '..'))
  76 +# print('_thirdparty_dir = %r' % _thirdparty_dir)
  77 +if not _thirdparty_dir in sys.path:
  78 + sys.path.insert(0, _thirdparty_dir)
  79 +
  80 +import colorclass
  81 +
  82 +# On Windows, colorclass needs to be enabled:
  83 +if os.name == 'nt':
  84 + colorclass.Windows.enable(auto_colors=True)
  85 +
  86 +
  87 +# === CLASSES =================================================================
  88 +
  89 +
  90 +class TableStyle(object):
  91 + """
  92 + Style for a TableStream.
  93 + This base class can be derived to create new styles.
  94 + Default style:
  95 + +------+---+
  96 + |Header| +
  97 + +------+---+
  98 + | | |
  99 + +------+---+
  100 + """
  101 + # Header rows:
  102 + header_top = True
  103 + header_top_left = '+'
  104 + header_top_horiz = '-'
  105 + header_top_middle = '+'
  106 + header_top_right = '+'
  107 +
  108 + header_vertical_left = '|'
  109 + header_vertical_middle = '|'
  110 + header_vertical_right = '|'
  111 +
  112 + # Separator line between header and normal rows:
  113 + header_sep = True
  114 + header_sep_left = '+'
  115 + header_sep_horiz = '-'
  116 + header_sep_middle = '+'
  117 + header_sep_right = '+'
  118 +
  119 + # Top row if there is no header:
  120 + noheader_top = True
  121 + noheader_top_left = '+'
  122 + noheader_top_horiz = '-'
  123 + noheader_top_middle = '+'
  124 + noheader_top_right = '+'
  125 +
  126 + # Normal rows
  127 + vertical_left = '|'
  128 + vertical_middle = '|'
  129 + vertical_right = '|'
  130 +
  131 + # Separator line between rows:
  132 + sep = False
  133 + sep_left = '+'
  134 + sep_horiz = '-'
  135 + sep_middle = '+'
  136 + sep_right = '+'
  137 +
  138 + # Bottom line
  139 + bottom = True
  140 + bottom_left = '+'
  141 + bottom_horiz = '-'
  142 + bottom_middle = '+'
  143 + bottom_right = '+'
  144 +
  145 +
  146 +class TableStyleSlim(object):
  147 + """
  148 + Style for a TableStream.
  149 + Example:
  150 + ------+---
  151 + Header|
  152 + ------+---
  153 + |
  154 + ------+---
  155 + """
  156 + # Header rows:
  157 + header_top = True
  158 + header_top_left = ''
  159 + header_top_horiz = '-'
  160 + header_top_middle = '+'
  161 + header_top_right = ''
  162 +
  163 + header_vertical_left = ''
  164 + header_vertical_middle = '|'
  165 + header_vertical_right = ''
  166 +
  167 + # Separator line between header and normal rows:
  168 + header_sep = True
  169 + header_sep_left = ''
  170 + header_sep_horiz = '-'
  171 + header_sep_middle = '+'
  172 + header_sep_right = ''
  173 +
  174 + # Top row if there is no header:
  175 + noheader_top = True
  176 + noheader_top_left = ''
  177 + noheader_top_horiz = '-'
  178 + noheader_top_middle = '+'
  179 + noheader_top_right = ''
  180 +
  181 + # Normal rows
  182 + vertical_left = ''
  183 + vertical_middle = '|'
  184 + vertical_right = ''
  185 +
  186 + # Separator line between rows:
  187 + sep = False
  188 + sep_left = ''
  189 + sep_horiz = '-'
  190 + sep_middle = '+'
  191 + sep_right = ''
  192 +
  193 + # Bottom line
  194 + bottom = True
  195 + bottom_left = ''
  196 + bottom_horiz = '-'
  197 + bottom_middle = '+'
  198 + bottom_right = ''
  199 +
  200 +
  201 +
  202 +class TableStream(object):
  203 + """
  204 + a TableStream object can format table data for pretty printing as text,
  205 + to be displayed on the console or written to any file-like object.
  206 + The table data can be provided as rows, each row is an iterable of
  207 + cells. The text in each cell is wrapped to fit into a maximum width
  208 + set for each column.
  209 + Contrary to many table pretty printing libraries, TableStream writes
  210 + each row to the output as soon as it is provided, and the whole table
  211 + does not need to be built in memory before printing.
  212 + It is therefore suitable for large tables, or tables that take time to
  213 + be processed row by row.
  214 + """
  215 +
  216 + def __init__(self, column_width, header_row=None, style=TableStyle, outfile=sys.stdout):
  217 + self.column_width = column_width
  218 + self.num_columns = len(column_width)
  219 + self.header_row = header_row
  220 + assert (header_row is None) or len(header_row) == self.num_columns
  221 + self.style = style
  222 + self.outfile = outfile
  223 + if header_row is not None:
  224 + self.write_header()
  225 + elif self.style.noheader_top:
  226 + self.write_noheader_top()
  227 +
  228 +
  229 + def write(self, s):
  230 + """
  231 + shortcut for self.outfile.write()
  232 + """
  233 + self.outfile.write(s)
  234 +
  235 + def write_row(self, row, last=False, colors=None):
  236 + assert len(row) == self.num_columns
  237 + columns = []
  238 + max_lines = 0
  239 + for i in range(self.num_columns):
  240 + cell = row[i]
  241 + # Convert to string:
  242 + # TODO: handle unicode properly
  243 + # TODO: use only unicode for textwrapper, to avoid str length issues
  244 + if isinstance(cell, bytes):
  245 + # encode to UTF8, avoiding errors
  246 + cell = cell.decode('utf-8', errors='replace')
  247 + else:
  248 + cell = cell
  249 + # Wrap cell text according to the column width
  250 + # TODO: use a TextWrapper object for each column instead
  251 + # split the string if it contains newline characters, otherwise
  252 + # textwrap replaces them with spaces:
  253 + column = []
  254 + for line in cell.splitlines():
  255 + column.extend(textwrap.wrap(line, width=self.column_width[i]))
  256 + # apply colors to each line of the cell if needed:
  257 + if colors is not None and self.outfile.isatty():
  258 + color = colors[i]
  259 + if color:
  260 + for j in range(len(column)):
  261 + # print '%r: %s' % (column[j], type(column[j]))
  262 + column[j] = colorclass.Color('{auto%s}%s{/%s}' % (color, column[j], color))
  263 + columns.append(column)
  264 + # determine which column has the highest number of lines
  265 + max_lines = max(len(columns[i]), max_lines)
  266 + # transpose: write output line by line
  267 + for j in range(max_lines):
  268 + self.write(self.style.vertical_left)
  269 + for i in range(self.num_columns):
  270 + column = columns[i]
  271 + if j<len(column):
  272 + # text to be written
  273 + text_width = len(column[j])
  274 + self.write(column[j] + ' '*(self.column_width[i]-text_width))
  275 + else:
  276 + # no more lines for this column
  277 + # TODO: precompute empty cells once
  278 + self.write(' '*(self.column_width[i]))
  279 + if i < (self.num_columns - 1):
  280 + self.write(self.style.vertical_middle)
  281 + self.write(self.style.vertical_right)
  282 + self.write('\n')
  283 + if self.style.sep and not last:
  284 + self.write_sep()
  285 +
  286 + def make_line(self, left, horiz, middle, right):
  287 + """
  288 + build a line based on the provided elements
  289 + example: '+---+--+-------+'
  290 + :param left:
  291 + :param horiz:
  292 + :param middle:
  293 + :param right:
  294 + :return:
  295 + """
  296 + return left + middle.join([horiz * width for width in self.column_width]) + right + '\n'
  297 +
  298 + def write_header_top(self):
  299 + s = self.style
  300 + line = self.make_line(left=s.header_top_left, horiz=s.header_top_horiz,
  301 + middle=s.header_top_middle, right=s.header_top_right)
  302 + self.write(line)
  303 +
  304 + def write_header_sep(self):
  305 + s = self.style
  306 + line = self.make_line(left=s.header_sep_left, horiz=s.header_sep_horiz,
  307 + middle=s.header_sep_middle, right=s.header_sep_right)
  308 + self.write(line)
  309 +
  310 + def write_header(self):
  311 + if self.style.header_top:
  312 + self.write_header_top()
  313 + self.write_row(self.header_row)
  314 + if self.style.header_sep:
  315 + self.write_header_sep()
  316 +
  317 + def write_noheader_top(self):
  318 + s = self.style
  319 + line = self.make_line(left=s.noheader_top_left, horiz=s.noheader_top_horiz,
  320 + middle=s.noheader_top_middle, right=s.noheader_top_right)
  321 + self.write(line)
  322 +
  323 + def write_sep(self):
  324 + s = self.style
  325 + line = self.make_line(left=s.sep_left, horiz=s.sep_horiz,
  326 + middle=s.sep_middle, right=s.sep_right)
  327 + self.write(line)
  328 +
  329 + def write_bottom(self):
  330 + s = self.style
  331 + line = self.make_line(left=s.bottom_left, horiz=s.bottom_horiz,
  332 + middle=s.bottom_middle, right=s.bottom_right)
  333 + self.write(line)
  334 +
  335 + def close(self):
  336 + self.write_bottom()
  337 +
  338 +
  339 +if __name__ == '__main__':
  340 + t = TableStream([10, 5, 20], header_row=['i', 'i*i', '2**i'], style=TableStyleSlim)
  341 + t.write_row(['test', 'test', 'test'])
  342 + cell = 'a very very long text'
  343 + t.write_row([cell, cell, cell], colors=['blue', None, 'red'])
  344 + for i in range(1, 11):
  345 + t.write_row([i, i*i, 2**i])
  346 + t.close()
  347 +
  348 +
... ...