Mercurial > repos > shellac > guppy_basecaller
diff env/lib/python3.7/site-packages/humanfriendly/tables.py @ 5:9b1c78e6ba9c draft default tip
"planemo upload commit 6c0a8142489327ece472c84e558c47da711a9142"
author | shellac |
---|---|
date | Mon, 01 Jun 2020 08:59:25 -0400 |
parents | 79f47841a781 |
children |
line wrap: on
line diff
--- a/env/lib/python3.7/site-packages/humanfriendly/tables.py Thu May 14 16:47:39 2020 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,341 +0,0 @@ -# Human friendly input/output in Python. -# -# Author: Peter Odding <peter@peterodding.com> -# Last Change: February 16, 2020 -# URL: https://humanfriendly.readthedocs.io - -""" -Functions that render ASCII tables. - -Some generic notes about the table formatting functions in this module: - -- These functions were not written with performance in mind (*at all*) because - they're intended to format tabular data to be presented on a terminal. If - someone were to run into a performance problem using these functions, they'd - be printing so much tabular data to the terminal that a human wouldn't be - able to digest the tabular data anyway, so the point is moot :-). - -- These functions ignore ANSI escape sequences (at least the ones generated by - the :mod:`~humanfriendly.terminal` module) in the calculation of columns - widths. On reason for this is that column names are highlighted in color when - connected to a terminal. It also means that you can use ANSI escape sequences - to highlight certain column's values if you feel like it (for example to - highlight deviations from the norm in an overview of calculated values). -""" - -# Standard library modules. -import collections -import re - -# Modules included in our package. -from humanfriendly.compat import coerce_string -from humanfriendly.terminal import ( - ansi_strip, - ansi_width, - ansi_wrap, - terminal_supports_colors, - find_terminal_size, - HIGHLIGHT_COLOR, -) - -# Public identifiers that require documentation. -__all__ = ( - 'format_pretty_table', - 'format_robust_table', - 'format_rst_table', - 'format_smart_table', -) - -# Compiled regular expression pattern to recognize table columns containing -# numeric data (integer and/or floating point numbers). Used to right-align the -# contents of such columns. -# -# Pre-emptive snarky comment: This pattern doesn't match every possible -# floating point number notation!?!1!1 -# -# Response: I know, that's intentional. The use of this regular expression -# pattern has a very high DWIM level and weird floating point notations do not -# fall under the DWIM umbrella :-). -NUMERIC_DATA_PATTERN = re.compile(r'^\d+(\.\d+)?$') - - -def format_smart_table(data, column_names): - """ - Render tabular data using the most appropriate representation. - - :param data: An iterable (e.g. a :func:`tuple` or :class:`list`) - containing the rows of the table, where each row is an - iterable containing the columns of the table (strings). - :param column_names: An iterable of column names (strings). - :returns: The rendered table (a string). - - If you want an easy way to render tabular data on a terminal in a human - friendly format then this function is for you! It works as follows: - - - If the input data doesn't contain any line breaks the function - :func:`format_pretty_table()` is used to render a pretty table. If the - resulting table fits in the terminal without wrapping the rendered pretty - table is returned. - - - If the input data does contain line breaks or if a pretty table would - wrap (given the width of the terminal) then the function - :func:`format_robust_table()` is used to render a more robust table that - can deal with data containing line breaks and long text. - """ - # Normalize the input in case we fall back from a pretty table to a robust - # table (in which case we'll definitely iterate the input more than once). - data = [normalize_columns(r) for r in data] - column_names = normalize_columns(column_names) - # Make sure the input data doesn't contain any line breaks (because pretty - # tables break horribly when a column's text contains a line break :-). - if not any(any('\n' in c for c in r) for r in data): - # Render a pretty table. - pretty_table = format_pretty_table(data, column_names) - # Check if the pretty table fits in the terminal. - table_width = max(map(ansi_width, pretty_table.splitlines())) - num_rows, num_columns = find_terminal_size() - if table_width <= num_columns: - # The pretty table fits in the terminal without wrapping! - return pretty_table - # Fall back to a robust table when a pretty table won't work. - return format_robust_table(data, column_names) - - -def format_pretty_table(data, column_names=None, horizontal_bar='-', vertical_bar='|'): - """ - Render a table using characters like dashes and vertical bars to emulate borders. - - :param data: An iterable (e.g. a :func:`tuple` or :class:`list`) - containing the rows of the table, where each row is an - iterable containing the columns of the table (strings). - :param column_names: An iterable of column names (strings). - :param horizontal_bar: The character used to represent a horizontal bar (a - string). - :param vertical_bar: The character used to represent a vertical bar (a - string). - :returns: The rendered table (a string). - - Here's an example: - - >>> from humanfriendly.tables import format_pretty_table - >>> column_names = ['Version', 'Uploaded on', 'Downloads'] - >>> humanfriendly_releases = [ - ... ['1.23', '2015-05-25', '218'], - ... ['1.23.1', '2015-05-26', '1354'], - ... ['1.24', '2015-05-26', '223'], - ... ['1.25', '2015-05-26', '4319'], - ... ['1.25.1', '2015-06-02', '197'], - ... ] - >>> print(format_pretty_table(humanfriendly_releases, column_names)) - ------------------------------------- - | Version | Uploaded on | Downloads | - ------------------------------------- - | 1.23 | 2015-05-25 | 218 | - | 1.23.1 | 2015-05-26 | 1354 | - | 1.24 | 2015-05-26 | 223 | - | 1.25 | 2015-05-26 | 4319 | - | 1.25.1 | 2015-06-02 | 197 | - ------------------------------------- - - Notes about the resulting table: - - - If a column contains numeric data (integer and/or floating point - numbers) in all rows (ignoring column names of course) then the content - of that column is right-aligned, as can be seen in the example above. The - idea here is to make it easier to compare the numbers in different - columns to each other. - - - The column names are highlighted in color so they stand out a bit more - (see also :data:`.HIGHLIGHT_COLOR`). The following screen shot shows what - that looks like (my terminals are always set to white text on a black - background): - - .. image:: images/pretty-table.png - """ - # Normalize the input because we'll have to iterate it more than once. - data = [normalize_columns(r, expandtabs=True) for r in data] - if column_names is not None: - column_names = normalize_columns(column_names) - if column_names: - if terminal_supports_colors(): - column_names = [highlight_column_name(n) for n in column_names] - data.insert(0, column_names) - # Calculate the maximum width of each column. - widths = collections.defaultdict(int) - numeric_data = collections.defaultdict(list) - for row_index, row in enumerate(data): - for column_index, column in enumerate(row): - widths[column_index] = max(widths[column_index], ansi_width(column)) - if not (column_names and row_index == 0): - numeric_data[column_index].append(bool(NUMERIC_DATA_PATTERN.match(ansi_strip(column)))) - # Create a horizontal bar of dashes as a delimiter. - line_delimiter = horizontal_bar * (sum(widths.values()) + len(widths) * 3 + 1) - # Start the table with a vertical bar. - lines = [line_delimiter] - # Format the rows and columns. - for row_index, row in enumerate(data): - line = [vertical_bar] - for column_index, column in enumerate(row): - padding = ' ' * (widths[column_index] - ansi_width(column)) - if all(numeric_data[column_index]): - line.append(' ' + padding + column + ' ') - else: - line.append(' ' + column + padding + ' ') - line.append(vertical_bar) - lines.append(u''.join(line)) - if column_names and row_index == 0: - lines.append(line_delimiter) - # End the table with a vertical bar. - lines.append(line_delimiter) - # Join the lines, returning a single string. - return u'\n'.join(lines) - - -def format_robust_table(data, column_names): - """ - Render tabular data with one column per line (allowing columns with line breaks). - - :param data: An iterable (e.g. a :func:`tuple` or :class:`list`) - containing the rows of the table, where each row is an - iterable containing the columns of the table (strings). - :param column_names: An iterable of column names (strings). - :returns: The rendered table (a string). - - Here's an example: - - >>> from humanfriendly.tables import format_robust_table - >>> column_names = ['Version', 'Uploaded on', 'Downloads'] - >>> humanfriendly_releases = [ - ... ['1.23', '2015-05-25', '218'], - ... ['1.23.1', '2015-05-26', '1354'], - ... ['1.24', '2015-05-26', '223'], - ... ['1.25', '2015-05-26', '4319'], - ... ['1.25.1', '2015-06-02', '197'], - ... ] - >>> print(format_robust_table(humanfriendly_releases, column_names)) - ----------------------- - Version: 1.23 - Uploaded on: 2015-05-25 - Downloads: 218 - ----------------------- - Version: 1.23.1 - Uploaded on: 2015-05-26 - Downloads: 1354 - ----------------------- - Version: 1.24 - Uploaded on: 2015-05-26 - Downloads: 223 - ----------------------- - Version: 1.25 - Uploaded on: 2015-05-26 - Downloads: 4319 - ----------------------- - Version: 1.25.1 - Uploaded on: 2015-06-02 - Downloads: 197 - ----------------------- - - The column names are highlighted in bold font and color so they stand out a - bit more (see :data:`.HIGHLIGHT_COLOR`). - """ - blocks = [] - column_names = ["%s:" % n for n in normalize_columns(column_names)] - if terminal_supports_colors(): - column_names = [highlight_column_name(n) for n in column_names] - # Convert each row into one or more `name: value' lines (one per column) - # and group each `row of lines' into a block (i.e. rows become blocks). - for row in data: - lines = [] - for column_index, column_text in enumerate(normalize_columns(row)): - stripped_column = column_text.strip() - if '\n' not in stripped_column: - # Columns without line breaks are formatted inline. - lines.append("%s %s" % (column_names[column_index], stripped_column)) - else: - # Columns with line breaks could very well contain indented - # lines, so we'll put the column name on a separate line. This - # way any indentation remains intact, and it's easier to - # copy/paste the text. - lines.append(column_names[column_index]) - lines.extend(column_text.rstrip().splitlines()) - blocks.append(lines) - # Calculate the width of the row delimiter. - num_rows, num_columns = find_terminal_size() - longest_line = max(max(map(ansi_width, lines)) for lines in blocks) - delimiter = u"\n%s\n" % ('-' * min(longest_line, num_columns)) - # Force a delimiter at the start and end of the table. - blocks.insert(0, "") - blocks.append("") - # Embed the row delimiter between every two blocks. - return delimiter.join(u"\n".join(b) for b in blocks).strip() - - -def format_rst_table(data, column_names=None): - """ - Render a table in reStructuredText_ format. - - :param data: An iterable (e.g. a :func:`tuple` or :class:`list`) - containing the rows of the table, where each row is an - iterable containing the columns of the table (strings). - :param column_names: An iterable of column names (strings). - :returns: The rendered table (a string). - - Here's an example: - - >>> from humanfriendly.tables import format_rst_table - >>> column_names = ['Version', 'Uploaded on', 'Downloads'] - >>> humanfriendly_releases = [ - ... ['1.23', '2015-05-25', '218'], - ... ['1.23.1', '2015-05-26', '1354'], - ... ['1.24', '2015-05-26', '223'], - ... ['1.25', '2015-05-26', '4319'], - ... ['1.25.1', '2015-06-02', '197'], - ... ] - >>> print(format_rst_table(humanfriendly_releases, column_names)) - ======= =========== ========= - Version Uploaded on Downloads - ======= =========== ========= - 1.23 2015-05-25 218 - 1.23.1 2015-05-26 1354 - 1.24 2015-05-26 223 - 1.25 2015-05-26 4319 - 1.25.1 2015-06-02 197 - ======= =========== ========= - - .. _reStructuredText: https://en.wikipedia.org/wiki/ReStructuredText - """ - data = [normalize_columns(r) for r in data] - if column_names: - data.insert(0, normalize_columns(column_names)) - # Calculate the maximum width of each column. - widths = collections.defaultdict(int) - for row in data: - for index, column in enumerate(row): - widths[index] = max(widths[index], len(column)) - # Pad the columns using whitespace. - for row in data: - for index, column in enumerate(row): - if index < (len(row) - 1): - row[index] = column.ljust(widths[index]) - # Add table markers. - delimiter = ['=' * w for i, w in sorted(widths.items())] - if column_names: - data.insert(1, delimiter) - data.insert(0, delimiter) - data.append(delimiter) - # Join the lines and columns together. - return '\n'.join(' '.join(r) for r in data) - - -def normalize_columns(row, expandtabs=False): - results = [] - for value in row: - text = coerce_string(value) - if expandtabs: - text = text.expandtabs() - results.append(text) - return results - - -def highlight_column_name(name): - return ansi_wrap(name, bold=True, color=HIGHLIGHT_COLOR)