Commit 9a143718bb4eb2b3dbf44a9c2a26cdc0f2d68076

Authored by Philippe Lagadec
1 parent 1577d688

added tablestream into thirdparty folder

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