Full Code of pri22296/beautifultable for AI

master 5bce31dcf0f4 cached
37 files
245.8 KB
67.4k tokens
349 symbols
1 requests
Download .txt
Showing preview only (260K chars total). Download the full file or copy to clipboard to get everything.
Repository: pri22296/beautifultable
Branch: master
Commit: 5bce31dcf0f4
Files: 37
Total size: 245.8 KB

Directory structure:
gitextract_qaaahoom/

├── .coveragerc
├── .github/
│   └── workflows/
│       ├── build.yml
│       ├── publish-test.yml
│       └── publish.yml
├── .gitignore
├── LICENSE.txt
├── MANIFEST.in
├── README.rst
├── beautifultable/
│   ├── __init__.py
│   ├── __version__.py
│   ├── ansi.py
│   ├── base.py
│   ├── beautifultable.py
│   ├── compat.py
│   ├── enums.py
│   ├── helpers.py
│   ├── meta.py
│   ├── styles.py
│   └── utils.py
├── coverage.xml
├── docs/
│   ├── Makefile
│   ├── badges.rst
│   ├── changelog.rst
│   ├── conf.py
│   ├── donation.rst
│   ├── index.rst
│   ├── install.rst
│   ├── introduction.rst
│   ├── links.rst
│   ├── make.bat
│   ├── quickstart.rst
│   └── source/
│       ├── beautifultable.rst
│       └── modules.rst
├── requirements.txt
├── setup.cfg
├── setup.py
└── test.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .coveragerc
================================================
[run]
branch = True
source = beautifultable

[report]
exclude_lines =
    if self.debug:
    pragma: no cover
    raise NotImplementedError
    if __name__ == .__main__.:
    pass
ignore_errors = True
omit =
    test.py


================================================
FILE: .github/workflows/build.yml
================================================
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions

name: Build

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]
  workflow_dispatch:

jobs:

  black:
    name: Check formatting
    runs-on: 'ubuntu-latest'
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-python@v2
      - uses: psf/black@stable
  
  coverage:
    name: Check code coverage
    runs-on: 'ubuntu-latest'
    steps:
      - uses: actions/checkout@v2
      - name: Set up Python
        uses: actions/setup-python@v2
        with:
          python-version: '3.x'
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          python -m pip install coverage
          if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
      - name: Generate coverage report
        run: |
          coverage run test.py
          coverage xml
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v2


  build:
    runs-on: ${{ matrix.os }}
    needs: black
    strategy:
      fail-fast: false
      matrix:
        os:
          - 'ubuntu-latest'
          - 'macos-latest'
          - 'windows-latest'
        python-version:
          - '3.7'
          - '3.8'
          - '3.9'
          - '3.10'
          - 'pypy-3.9'
    steps:
      - uses: actions/checkout@v2
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v2
        with:
          python-version: ${{ matrix.python-version }}
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          python -m pip install flake8
          python -m pip install -r requirements.txt
      - name: Lint with flake8
        run: |
          # stop the build if there are Python syntax errors or undefined names
          flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
          # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
          flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
      - name: Run Test
        run: |
          python test.py


================================================
FILE: .github/workflows/publish-test.yml
================================================
# This workflow will upload a Python Package using Twine when a release is created
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries

# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

name: Test Publish

on:
  release:
    types: [published]
  workflow_dispatch:

jobs:
  deploy:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: '3.x'
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install build
    - name: Build package
      run: python -m build
    - name: Publish package to TestPyPi
      uses: pypa/gh-action-pypi-publish@master
      with:
        user: __token__
        password: ${{ secrets.TEST_PYPI_API_TOKEN }}
        repository_url: https://test.pypi.org/legacy/


================================================
FILE: .github/workflows/publish.yml
================================================
# This workflow will upload a Python Package using Twine when a release is created
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries

# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

name: Publish

on:
  release:
    types: [published]
  workflow_dispatch:

jobs:
  deploy:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: '3.x'
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install build
    - name: Build package
      run: python -m build
    - name: Publish package to PyPi
      uses: pypa/gh-action-pypi-publish@master
      with:
        user: __token__
        password: ${{ secrets.PYPI_API_TOKEN }}


================================================
FILE: .gitignore
================================================
__pycache__/
*egg-info/
docs/_build/
dist/
build/
.tox/
*.pyc
.coverage
.vscode/
.venv
.idea


================================================
FILE: LICENSE.txt
================================================
MIT License

Copyright (c) 2022 Priyam Singh

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: MANIFEST.in
================================================
include LICENSE.txt
include README.rst
include docs/*.rst
include test.py


================================================
FILE: README.rst
================================================
##########################################################################
beautifultable
##########################################################################

.. inclusion-marker-badges-start

.. image:: https://badge.fury.io/py/beautifultable.svg
    :target: https://badge.fury.io/py/beautifultable

.. image:: https://img.shields.io/pypi/pyversions/beautifultable.svg
    :target: https://pypi.python.org/pypi/beautifultable/

.. image:: https://codecov.io/gh/pri22296/beautifultable/branch/master/graphs/badge.svg
    :target: https://codecov.io/gh/pri22296/beautifultable/branch/master/

.. image:: https://api.codacy.com/project/badge/Grade/7a76eb35ad4e450eaf00339e98381511
    :target: https://www.codacy.com/app/pri22296/beautifultable?utm_source=github.com&utm_medium=referral&utm_content=pri22296/beautifultable&utm_campaign=Badge_Grade

.. image:: https://github.com/pri22296/beautifultable/actions/workflows/build.yml/badge.svg?branch=master
    :target: https://github.com/pri22296/beautifultable/actions/workflows/build.yml

.. image:: https://readthedocs.org/projects/beautifultable/badge/?version=latest
    :alt: Documentation Status
    :target: http://beautifultable.readthedocs.io/en/latest/?badge=latest

.. image:: https://img.shields.io/badge/Donate-PayPal-yellow.svg
    :target: https://paypal.me/beautifultable

.. inclusion-marker-badges-end


.. inclusion-marker-introduction-start

**************************************************************************
Introduction
**************************************************************************

This Package provides BeautifulTable class for easily printing
tabular data in a visually appealing format to a terminal. 

Features included but not limited to:

* Full customization of the look and feel of the table
* Build the Table as you wish, By adding rows, or by columns or even
  mixing both these approaches.
* Full support for *colors* using ANSI sequences or any library of your
  choice. It just works.
* Plenty of predefined *styles* for multiple use cases and option to
  create custom ones.
* Support for *Unicode* characters.
* Supports streaming table when data is slow to retrieve.
  
.. inclusion-marker-introduction-end


 
.. inclusion-marker-links-start

**************************************************************************
Links
**************************************************************************

* `Documentation <http://beautifultable.readthedocs.io/en/latest/>`_

* `Source <https://github.com/pri22296/beautifultable>`_

* `API Reference <http://beautifultable.readthedocs.io/en/latest/source/beautifultable.html#module-beautifultable>`_


.. inclusion-marker-links-end



.. inclusion-marker-usage-start

**************************************************************************
Usage
**************************************************************************

Here is an example of how you can use beautifultable::

    >>> from beautifultable import BeautifulTable
    >>> table = BeautifulTable()
    >>> table.rows.append(["Jacob", 1, "boy"])
    >>> table.rows.append(["Isabella", 1, "girl"])
    >>> table.rows.append(["Ethan", 2, "boy"])
    >>> table.rows.append(["Sophia", 2, "girl"])
    >>> table.rows.append(["Michael", 3, "boy"])
    >>> table.rows.header = ["S1", "S2", "S3", "S4", "S5"]
    >>> table.columns.header = ["name", "rank", "gender"]
    >>> print(table)
    +----+----------+------+--------+
    |    |   name   | rank | gender |
    +----+----------+------+--------+
    | S1 |  Jacob   |  1   |  boy   |
    +----+----------+------+--------+
    | S2 | Isabella |  1   |  girl  |
    +----+----------+------+--------+
    | S3 |  Ethan   |  2   |  boy   |
    +----+----------+------+--------+
    | S4 |  Sophia  |  2   |  girl  |
    +----+----------+------+--------+
    | S5 | Michael  |  3   |  boy   |
    +----+----------+------+--------+


You can learn more about beautifultable at this `Tutorial <http://beautifultable.readthedocs.io/en/latest/quickstart.html>`_

.. inclusion-marker-usage-end



.. inclusion-marker-install-start

**************************************************************************
Installation
**************************************************************************

::

    python3 -m pip install beautifultable

.. inclusion-marker-install-end



.. inclusion-marker-changelog-start

**************************************************************************
Changelog
**************************************************************************

===========
Development
===========


==========
v1.1.0
==========

* Drop support for Python 3.4, 3.5 and 3.6
* Add official support for python 3.9 and 3.10
* Added `asdict` and `aslist` method on the row object. (Thanks to `@Agent-Hellboy <https://github.com/Agent-Hellboy>`_)
* Added `from_csv` and `to_csv` methods to export/import a csv file. (Thanks to `@Agent-Hellboy <https://github.com/Agent-Hellboy>`_)
* Added `from_df` and `to_df` methods to export/import a dataframe. (Thanks to `@Agent-Hellboy <https://github.com/Agent-Hellboy>`_)

==========
v1.0.1
==========

* Fixed an issue where appending a column with a header to an empty table left the table instance in
  an inconsistent state.

==========
v1.0.0
==========

* Added two new views ``rows`` and ``columns`` to the ``BeautifulTable`` class. Most of the existing
  methods have been deprecated. Methods of the form ``{}_row`` and ``{}_column`` have been moved to
  views ``rows.{}`` and ``columns.{}``(ex. ``append_row`` is now ``rows.append``). Calling older
  deprecated methods will now raise a ``FutureWarning``. Special methods such as ``__len__``, ``__iter__``,
  etc. have also been moved to the respective views. For details, refer the
  API documentation and the Updated Tutorial
* The existing styling attributes have also been deprecated. A new ``border`` property can be accessed
  to control all styling attributes affecting the border. Rest of the attributes can be accessed from
  it's respective view.
* Added support for row headers. As a result rows can now be accessed by their keys similar
  to columns
* Added two new methods ``to_csv`` and ``from_csv`` to directly export/import to a
  csv file. (Thanks to `@dinko-pehar <https://github.com/dinko-pehar>`_)
* Added ``BeautifulTable.rows.filter`` method to generate a new table with only certain rows
* Added a new ``shape`` attribute to the ``BeautifulTable`` class which returns a tuple of form (nrow, ncol)
* Added new attribute ``BeautifulTable.columns.header.alignment`` which can be used to have
  a seperate header alignment. The default behaviour is to inherit ``BeautifulTable.columns.alignment``
* Updated ``BeautifulTable.rows.sort`` (earlier ``BeautifulTable.sort``) method to now
  also accept any callables as a key.
* Updated behaviour of ``BeautifulTable.columns.width`` (earlier ``BeautifulTable.column_widths``).
  It no longer overrides user specified widths by default. You can reset it to default
  by setting it to **"auto"**
* Deprecated attribute ``serialno`` and ``serialno_header``. User can now easily implement
  this functionality by using row headers if required
* Deprecated methods ``get_table_width()``, ``copy()`` and ``get_string()``.
* Deprecated constructor arguments and class attributes named ``sign_mode``, ``numeric_precision``,
  ``max_width`` and renamed to ``sign``, ``precision`` and ``maxwidth`` respectively
* Fixed an issue where table was malformed if ``blessings`` module was used to generate colored strings.
* Fixed issues with the existing implementation of ``__iter__``, ``__copy__`` and ``__deepcopy__`` which
  should now work more reliably.
* Fixed an issue where default padding could not be set to 0. (Thanks to `@furlongm <https://github.com/furlongm>`_)
* Fixed several memory leak issues by ensuring that all internal objects hold only a weak reference
  to the table instance.
* Dropped support for Python 2

==========
v0.8.0
==========

* Dropped support for Python 3.3
* Added support for streaming tables using a generator for cases  where data retrieval is slow
* Alignment, padding, width can now be set for all columns using a simplified syntax like
  ``table.column_alignments = beautifultable.ALIGN_LEFT``

==========
v0.7.0
==========

* Added 4 new styles, **STYLE_BOX**, **STYLE_BOX_DOUBLED**, **STYLE_BOX_ROUNDED**,
  **STYLE_GRID**.
* Renamed **STYLE_RESTRUCTURED_TEXT** to **STYLE_RST**
* **wcwidth** is now an optional dependency
* Updated the algorithm for calculating width of columns(better division of space among columns)
* Added support for Paragraphs(using ``\n`` character)
* Added finer control for intersection characters using 12 new
  attributes ``intersect_{top|header|row|bottom}_{left|mid|right}``
* Added the ability to also accept bytestrings instead of unicode
* Deprecated attribute ``intersection_char``
* Deprecated methods ``get_top_border()``, ``get_bottom_border()``, ``get_header_separator()``,
  ``get_row_separator()``, ``auto_calculate_width()``
* Fixed an issue with **WEP_ELLIPSIS** and **WEP_STRIP** when using multibyte characters
* Fixed an issue where table would not be in proper form if ``column_width`` is too low

==========
v0.6.0
==========

* Added support for handling Multi byte strings
* Added support for colored strings using ANSI escape sequences
* Added constraint where all strings must be unicode
* Fixed an issue where sometimes width was calculated as higher than intended

==========
v0.5.3
==========

* Added support for handing color codes using ANSI escape sequences(experimental)
* Fixed collections ABCs deprecation warning

==========
v0.5.2
==========

* Added new style **STYLE_NONE**
* Fixed issue regarding improper conversion of non-string floats

==========
v0.5.1
==========

* Added ``detect_numerics`` boolean for toggling automatic numeric conversion

==========
v0.5.0
==========

* Added new property ``serialno_header``
* Deprecated methods with misspelled *"seperator"* in their name.
* Fixed an issue where table was corrupted when ``column_count`` was too high


==========
v0.4.0
==========

* Added predefined styles for easier customization
* Added *reverse* argument to ``sort()`` method
* Fixed *enum34* dependency for python versions prior to 3.4

==========
v0.3.0
==========

* Added property ``serialno`` for auto printing serial number
* Fixed an issue with ``sign_mode`` related to str conversion
* Fixed bugs related to python version prior to 3.3
* Fixed exception on **WEP_ELLIPSIS** and token length less than 3
* Fixed printing issues with empty table

==========
v0.2.0
==========

* Added python 2 support

==========
v0.1.3
==========

* Fixed minor issues

==========
v0.1.2
==========

* Added new property ``default_padding``
* Added new method ``update_row``
* Fixed an issue in ``auto_calculate_width()``

==========
v0.1.1
==========

* Initial release on PyPI


.. inclusion-marker-changelog-end


.. inclusion-marker-contribution-start

**************************************************************************
Contribute
**************************************************************************

If you have any suggestions or bug reports, Please create a Issue. Pull
Requests are always welcome.

.. inclusion-marker-contribution-end



.. inclusion-marker-license-start

**************************************************************************
License
**************************************************************************

This project is licensed under the MIT License - see the `LICENSE.txt <https://github.com/pri22296/beautifultable/blob/master/LICENSE.txt>`_ file for details.


.. inclusion-marker-license-end



.. inclusion-marker-donation-start

**************************************************************************
Donation
**************************************************************************

Love *beautifultable*? Consider supporting the development :)

.. image:: https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif
    :target: https://paypal.me/beautifultable


.. inclusion-marker-donation-end


================================================
FILE: beautifultable/__init__.py
================================================
from .__version__ import __title__, __description__, __url__, __version__
from .__version__ import __copyright__, __author__, __author_email__
from .__version__ import __license__

from .beautifultable import (  # noqa F401
    BeautifulTable,
    BTRowCollection,
    BTColumnCollection,
    BTRowHeader,
    BTColumnHeader,
    BTBorder,
    __all__,
)
from . import enums
from .enums import *  # noqa


__all__ = __all__ + [
    "__title__",
    "__description__",
    "__url__",
    "__version__",
    "__copyright__",
    "__author__",
    "__author_email__",
    "__license__",
]


# To avoid duplicating enums name, dynamically add them to BeautifulTable
# class and __all__
for token in dir(enums):
    if (
        token.startswith("WEP_")
        or token.startswith("ALIGN_")
        or token.startswith("SM_")
        or token.startswith("STYLE_")
    ):
        setattr(BeautifulTable, token, getattr(enums, token))
        __all__.append(token)


================================================
FILE: beautifultable/__version__.py
================================================
__title__ = "beautifultable"
__description__ = "Print text tables for terminals"
__url__ = "https://github.com/pri22296/beautifultable"
__version__ = "1.1.0"
__author__ = "Priyam Singh"
__author_email__ = "priyamsingh.22296@gmail.com"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 Priyam Singh"


================================================
FILE: beautifultable/ansi.py
================================================
"""Module Containing class required for handling ANSI and east asian chars"""

from __future__ import unicode_literals


import re

try:
    from wcwidth import wcwidth
except ImportError:  # pragma: no cover
    wcwidth = len  # pragma: no cover

from .compat import to_unicode


class ANSIMultiByteString(object):

    ANSI_REGEX = re.compile(r"(\x1B(?:[()][AB012]|[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]))")
    ANSI_RESET = "\x1b[0m"

    def __init__(self, string):
        self._string = []
        self._state = []
        self._width = []
        self._termwidth = 0

        state = set()

        for token in re.split(self.ANSI_REGEX, to_unicode(string)):
            if token:
                if re.match(self.ANSI_REGEX, token):
                    if token == self.ANSI_RESET:
                        state.clear()
                    else:
                        state.add(token)
                else:
                    s_copy = set(state)
                    for char in token:
                        w = wcwidth(char)
                        if w == -1:
                            raise ValueError(
                                f"Unsupported Literal {repr(char)} in string {repr(token)}"
                            )
                        self._termwidth += w
                        self._string.append(char)
                        self._width.append(w)
                        self._state.append(s_copy)

    def __len__(self):
        return len(self._string)

    def __getitem__(self, key):
        if isinstance(key, int):
            if self._state[key]:
                return "".join(self._state[key]) + self._string[key] + self.ANSI_RESET
            return self._string[key]
        if isinstance(key, slice):
            return self._slice(key)
        raise TypeError(
            f"table indices must be integers or slices, not {type(key).__name__}"
        )

    def _slice(self, key):
        res = []
        prev_state = set()
        for char, state in zip(self._string[key], self._state[key]):
            if prev_state == state:
                pass
            elif prev_state <= state:
                res.extend(state - prev_state)
            else:
                res.append(self.ANSI_RESET)
                res.extend(state)
            prev_state = state
            res.append(char)
        if prev_state:
            res.append(self.ANSI_RESET)
        return "".join(res)

    def termwidth(self):
        """Returns the width of string as when printed to a terminal"""
        return self._termwidth

    def wrap(self, width):
        """Returns a partition of the string based on `width`"""
        res = []
        prev_state = set()
        part = []
        cwidth = 0
        for char, _width, state in zip(self._string, self._width, self._state):
            if cwidth + _width > width:
                if prev_state:
                    part.append(self.ANSI_RESET)
                res.append("".join(part))
                prev_state = set()
                part = []
                cwidth = 0
            cwidth += _width
            if prev_state == state:
                pass
            elif prev_state <= state:
                part.extend(state - prev_state)
            else:
                part.append(self.ANSI_RESET)
                part.extend(state)
            prev_state = state
            part.append(char)
        if prev_state:
            part.append(self.ANSI_RESET)
        if part:
            res.append("".join(part))
        return res


================================================
FILE: beautifultable/base.py
================================================
import abc
import weakref


class BTBaseList(metaclass=abc.ABCMeta):
    def __init__(self, table, value):
        self._table = table
        self._value = self._validate(list(value))

    @property
    def _table(self):
        return self._table_ref()

    @_table.setter
    def _table(self, value):
        self._table_ref = weakref.ref(value)

    @property
    def value(self):
        return self._value

    def __len__(self):
        return len(self._value)

    def __iter__(self):
        return iter(self._value)

    def __next__(self):
        return next(self._value)

    def __repr__(self):
        class_ = type(self).__name__
        data = ", ".join(repr(v) for v in self._value)
        return "{}<{}>".format(class_, data)

    def __eq__(self, other):
        if len(self) != len(other):
            return False
        for i, j in zip(self, other):
            if i != j:
                return False
        return True

    def __contains__(self, item):
        """Returns whether `item` is present"""
        return item in self._value

    def _append(self, item):
        self._value.append(item)

    def _insert(self, i, item):
        self._value.insert(i, item)

    def _pop(self, i=-1):
        return self._value.pop(self._get_canonical_key(i))

    def _remove(self, item):
        self._value.remove(item)

    def _reverse(self):
        self._value.reverse()

    def _sort(self, key, reverse=False):
        self._value.sort(key=key, reverse=reverse)

    def _clear(self):
        self._value.clear()

    def count(self, item):
        return self._value.count(item)

    def index(self, item, *args):
        """Returns the index of `item`"""
        try:
            return self._value.index(item, *args)
        except ValueError:
            raise KeyError(f"Key {item} is not available")

    def __getitem__(self, key):
        """Returns item at index or header `key`"""
        return self._value[self._get_canonical_key(key)]

    def __setitem__(self, key, value):
        """Updates item at index or header `key`"""
        self._value[self._get_canonical_key(key)] = value

    def __delitem__(self, key):
        del self._value[self._get_canonical_key(key)]

    def _validate(self, value):
        if len(value) != self._get_ideal_length():
            raise ValueError(
                f"'Expected iterable of length {self._get_ideal_length()}, got {len(value)}"
            )

        return value

    @abc.abstractmethod
    def _get_canonical_key(self, key):
        pass

    @abc.abstractmethod
    def _get_ideal_length(self):
        pass


class BTBaseRow(BTBaseList):
    def _get_canonical_key(self, key):
        return self._table.columns._canonical_key(key)

    def _get_ideal_length(self):
        return self._table._ncol

    def _validate(self, value):
        if self._get_ideal_length() == 0 and len(value) > 0:
            self._table.columns._reset_state(len(value))
        return super(BTBaseRow, self)._validate(value)


class BTBaseColumn(BTBaseList):
    def _get_canonical_key(self, key):
        return self._table.rows._canonical_key(key)

    def _get_ideal_length(self):
        return len(self._table._data)

    def _validate(self, value):
        if self._get_ideal_length() == 0 and len(value) > 0:
            self._table.rows._reset_state(len(value))
        return super(BTBaseColumn, self)._validate(value)


================================================
FILE: beautifultable/beautifultable.py
================================================
"""This module provides BeautifulTable class

It is intended for printing Tabular data to terminals.

Example
-------
>>> from beautifultable import BeautifulTable
>>> table = BeautifulTable()
>>> table.columns.header = ['1st column', '2nd column']
>>> for i in range(5):
...    table.rows.apppend([i, i*i])
...
>>> print(table)
+------------+------------+
| 1st column | 2nd column |
+------------+------------+
|     0      |     0      |
+------------+------------+
|     1      |     1      |
+------------+------------+
|     2      |     4      |
+------------+------------+
|     3      |     9      |
+------------+------------+
|     4      |     16     |
+------------+------------+
"""
from __future__ import division, unicode_literals

import copy
import csv
import warnings

from . import enums

from .utils import (
    pre_process,
    termwidth,
    deprecated,
    deprecated_param,
    deprecation_message,
    ensure_type,
)
from .compat import basestring, Iterable, to_unicode
from .base import BTBaseList
from .helpers import (
    BTRowCollection,
    BTColumnCollection,
    BTRowHeader,
    BTColumnHeader,
)


__all__ = [
    "BeautifulTable",
    "BTRowCollection",
    "BTColumnCollection",
    "BTRowHeader",
    "BTColumnHeader",
    "BTBorder",
]


class BTBorder:
    """Class to control how each section of the table's border is rendered.

    To disable a behaviour, just set its corresponding attribute
    to an empty string

    Attributes
    ----------

    top : str
        Character used to draw the top border.

    left : str
        Character used to draw the left border.

    bottom : str
        Character used to draw the bottom border.

    right : str
        Character used to draw the right border.

    top_left : str
        Left most character of the top border.

    bottom_left : str
        Left most character of the bottom border.

    bottom_right : str
        Right most character of the bottom border.

    top_right : str
        Right most character of the top border.

    header_left : str
        Left most character of the header separator.

    header_right : str
        Right most character of the header separator.

    top_junction : str
        Junction character for top border.

    left_junction : str
        Junction character for left border.

    bottom_junction : str
        Junction character for bottom border.

    right_junction : str
        Junction character for right border.
    """

    def __init__(
        self,
        top,
        left,
        bottom,
        right,
        top_left,
        bottom_left,
        bottom_right,
        top_right,
        header_left,
        header_right,
        top_junction,
        left_junction,
        bottom_junction,
        right_junction,
    ):
        self.top = top
        self.left = left
        self.bottom = bottom
        self.right = right

        self.top_left = top_left
        self.bottom_left = bottom_left
        self.bottom_right = bottom_right
        self.top_right = top_right

        self.header_left = header_left
        self.header_right = header_right

        self.top_junction = top_junction
        self.left_junction = left_junction
        self.bottom_junction = bottom_junction
        self.right_junction = right_junction


def _make_getter(attr):
    return lambda self: getattr(self, attr)


def _make_setter(attr):
    return lambda self, value: setattr(self, attr, ensure_type(value, basestring))


for prop, attr in [
    (x, "_{}".format(x))
    for x in (
        "top",
        "left",
        "bottom",
        "right",
        "top_left",
        "bottom_left",
        "bottom_right",
        "top_right",
        "header_left",
        "header_right",
        "top_junction",
        "left_junction",
        "bottom_junction",
        "right_junction",
    )
]:
    setattr(BTBorder, prop, property(_make_getter(attr), _make_setter(attr)))


class BTTableData(BTBaseList):
    def __init__(self, table, value=None):
        if value is None:
            value = []
        self._table = table
        self._value = value

    def _get_canonical_key(self, key):
        return self._table.rows._canonical_key(key)

    def _get_ideal_length(self):
        pass


class BeautifulTable:
    """Utility Class to print data in tabular format to terminal.

    Parameters
    ----------
    maxwidth: int, optional
        maximum width of the table in number of characters. this is ignored
        when manually setting the width of the columns. if this value is too
        low with respect to the number of columns and width of padding, the
        resulting table may override it(default 80).

    default_alignment : int, optional
        Default alignment for new columns(default beautifultable.ALIGN_CENTER).

    default_padding : int, optional
        Default width of the left and right padding for new columns(default 1).

    precision : int, optional
        All float values will have maximum number of digits after the decimal,
        capped by this value(Default 3).

    serialno : bool, optional
        If true, a column will be rendered with serial numbers(**DEPRECATED**).

    serialno_header: str, optional
        The header of the serial number column if rendered(**DEPRECATED**).

    detect_numerics : bool, optional
        Whether numeric strings should be automatically detected(Default True).

    sign : SignMode, optional
        Parameter to control how signs in numeric data are displayed.
        (default beautifultable.SM_MINUS).

    Attributes
    ----------
    precision : int
        All float values will have maximum number of digits after the decimal,
        capped by this value(Default 3).

    detect_numerics : bool
        Whether numeric strings should be automatically detected(Default True).
    """

    @deprecated_param("1.0.0", "1.2.0", "sign_mode", "sign")
    @deprecated_param("1.0.0", "1.2.0", "numeric_precision", "precision")
    @deprecated_param("1.0.0", "1.2.0", "max_width", "maxwidth")
    @deprecated_param("1.0.0", "1.2.0", "serialno")
    @deprecated_param("1.0.0", "1.2.0", "serialno_header")
    def __init__(
        self,
        maxwidth=80,
        default_alignment=enums.ALIGN_CENTER,
        default_padding=1,
        precision=3,
        serialno=False,
        serialno_header="SN",
        detect_numerics=True,
        sign=enums.SM_MINUS,
        **kwargs,
    ):

        kwargs.setdefault("max_width", None)
        if kwargs["max_width"] is not None:
            maxwidth = kwargs["max_width"]

        kwargs.setdefault("numeric_precision", None)
        if kwargs["numeric_precision"] is not None:
            precision = kwargs["numeric_precision"]

        kwargs.setdefault("sign_mode", None)
        if kwargs["sign_mode"] is not None:
            sign = kwargs["sign_mode"]

        self.precision = precision
        self._serialno = serialno
        self._serialno_header = serialno_header
        self.detect_numerics = detect_numerics

        self._sign = sign
        self.maxwidth = maxwidth

        self._ncol = 0
        self._data = BTTableData(self)

        self.rows = BTRowCollection(self)
        self.columns = BTColumnCollection(self, default_alignment, default_padding)

        self._header_separator = ""
        self._header_junction = ""
        self._column_separator = ""
        self._row_separator = ""
        self.border = ""
        self.set_style(enums.STYLE_DEFAULT)

    def __copy__(self):
        obj = type(self)()
        obj.__dict__.update({k: copy.copy(v) for k, v in self.__dict__.items()})

        obj.rows._table = obj
        obj.rows.header._table = obj

        obj.columns._table = obj
        obj.columns.header._table = obj
        obj.columns.alignment._table = obj
        obj.columns.width._table = obj
        obj.columns.padding_left._table = obj
        obj.columns.padding_right._table = obj

        obj._data._table = obj
        for row in obj._data:
            row._table = obj

        return obj

    def __deepcopy__(self, memo):
        obj = type(self)()
        obj.__dict__.update(
            {k: copy.deepcopy(v, memo) for k, v in self.__dict__.items()}
        )

        obj.rows._table = obj
        obj.rows.header._table = obj

        obj.columns._table = obj
        obj.columns.header._table = obj
        obj.columns.alignment._table = obj
        obj.columns.width._table = obj
        obj.columns.padding_left._table = obj
        obj.columns.padding_right._table = obj

        obj._data._table = obj
        for row in obj._data:
            row._table = obj

        return obj

    def __setattr__(self, name, value):
        attrs = (
            "left_border_char",
            "right_border_char",
            "top_border_char",
            "bottom_border_char",
            "header_separator_char",
            "column_separator_char",
            "row_separator_char",
            "intersect_top_left",
            "intersect_top_mid",
            "intersect_top_right",
            "intersect_header_left",
            "intersect_header_mid",
            "intersect_header_right",
            "intersect_row_left",
            "intersect_row_mid",
            "intersect_row_right",
            "intersect_bottom_left",
            "intersect_bottom_mid",
            "intersect_bottom_right",
        )
        if to_unicode(name) in attrs:
            warnings.warn(
                deprecation_message(name, "1.0.0", "1.2.0", None),
                FutureWarning,
            )
            value = ensure_type(value, basestring, name)
        super(BeautifulTable, self).__setattr__(name, value)

    @deprecated(
        "1.0.0",
        "1.2.0",
        BTRowCollection.__len__,
        details="Use len(BeautifulTable.rows)' instead.",
    )
    def __len__(self):  # pragma: no cover
        return len(self.rows)

    @deprecated(
        "1.0.0" "1.2.0",
        BTRowCollection.__iter__,
        details="Use iter(BeautifulTable.rows)' instead.",
    )
    def __iter__(self):  # pragma: no cover
        return iter(self.rows)

    @deprecated(
        "1.0.0",
        "1.2.0",
        BTColumnCollection.__contains__,
        details="Use ''value' in BeautifulTable.{columns|rows}' instead.",
    )
    def __contains__(self, key):  # pragma: no cover
        if isinstance(key, basestring):
            return key in self.columns
        elif isinstance(key, Iterable):
            return key in self.rows
        else:
            raise TypeError(f"'key' must be str or Iterable, not {type(key).__name__}")

    def __repr__(self):
        return repr(self._data)

    def __str__(self):
        if len(self.rows) == 0 or len(self.columns) == 0:
            return ""

        string_ = []
        for line in self._get_string([], append=False):
            string_.append(line)

        return "\n".join(string_)

    # ************************Properties Begin Here************************
    @property
    def shape(self):
        """Read only attribute which returns the shape of the table."""
        return (len(self.rows), len(self.columns))

    @property
    def sign(self):
        """Attribute to control how signs are displayed for numerical data.

        It can be one of the following:

        ========================  =============================================
         Option                    Meaning
        ========================  =============================================
         beautifultable.SM_PLUS    A sign should be used for both +ve and -ve
                                   numbers.

         beautifultable.SM_MINUS   A sign should only be used for -ve numbers.

         beautifultable.SM_SPACE   A leading space should be used for +ve
                                   numbers and a minus sign for -ve numbers.
        ========================  =============================================
        """
        return self._sign

    @sign.setter
    def sign(self, value):
        if not isinstance(value, enums.SignMode):
            allowed = (f"{type(self).__name__}.{i.name}" for i in enums.SignMode)
            error_msg = "allowed values for sign are: " + ", ".join(allowed)
            raise ValueError(error_msg)
        self._sign = value

    @property
    def border(self):
        """Characters used to draw the border of the table.

        You can set this directly to a character or use it's several attribute
        to control how each section of the table is rendered.
        It is an instance of :class:`~.BTBorder`
        """
        return self._border

    @border.setter
    def border(self, value):
        self._border = BTBorder(
            top=value,
            left=value,
            bottom=value,
            right=value,
            top_left=value,
            bottom_left=value,
            bottom_right=value,
            top_right=value,
            header_left=value,
            header_right=value,
            top_junction=value,
            left_junction=value,
            bottom_junction=value,
            right_junction=value,
        )

    @property
    def junction(self):
        """Character used to draw junctions in the row separator."""
        return self._junction

    @junction.setter
    def junction(self, value):
        self._junction = ensure_type(value, basestring)

    @property
    @deprecated("1.0.0", "1.2.0", BTRowCollection.header.fget)
    def serialno(self):  # pragma: no cover
        return self._serialno

    @serialno.setter
    @deprecated("1.0.0", "1.2.0", BTRowCollection.header.fget)
    def serialno(self, value):  # pragma: no cover
        self._serialno = value

    @property
    @deprecated("1.0.0", "1.2.0")
    def serialno_header(self):  # pragma: no cover
        return self._serialno_header

    @serialno_header.setter
    @deprecated("1.0.0", "1.2.0")
    def serialno_header(self, value):  # pragma: no cover
        self._serialno_header = value

    @property
    @deprecated("1.0.0", "1.2.0", sign.fget)
    def sign_mode(self):  # pragma: no cover
        return self.sign

    @sign_mode.setter
    @deprecated("1.0.0", "1.2.0", sign.fget)
    def sign_mode(self, value):  # pragma: no cover
        self.sign = value

    @property
    def maxwidth(self):
        """get/set the maximum width of the table.

        The width of the table is guaranteed to not exceed this value. If it
        is not possible to print a given table with the width provided, this
        value will automatically adjust.
        """
        offset = (len(self.columns) - 1) * termwidth(self.columns.separator)
        offset += termwidth(self.border.left)
        offset += termwidth(self.border.right)
        self._maxwidth = max(self._maxwidth, offset + len(self.columns))
        return self._maxwidth

    @maxwidth.setter
    def maxwidth(self, value):
        self._maxwidth = value

    @property
    @deprecated("1.0.0", "1.2.0", maxwidth.fget)
    def max_table_width(self):  # pragma: no cover
        return self.maxwidth

    @max_table_width.setter
    @deprecated("1.0.0", "1.2.0", maxwidth.fget)
    def max_table_width(self, value):  # pragma: no cover
        self.maxwidth = value

    @property
    @deprecated(
        "1.0.0",
        "1.2.0",
        BTColumnCollection.__len__,
        details="Use 'len(self.columns)' instead.",
    )
    def column_count(self):  # pragma: no cover
        return len(self.columns)

    @property
    @deprecated("1.0.0", "1.2.0", BTColumnCollection.width_exceed_policy.fget)
    def width_exceed_policy(self):  # pragma: no cover
        return self.columns.width_exceed_policy

    @width_exceed_policy.setter
    @deprecated("1.0.0", "1.2.0", BTColumnCollection.width_exceed_policy.fget)
    def width_exceed_policy(self, value):  # pragma: no cover
        self.columns.width_exceed_policy = value

    @property
    @deprecated("1.0.0", "1.2.0", BTColumnCollection.default_alignment.fget)
    def default_alignment(self):  # pragma: no cover
        return self.columns.default_alignment

    @default_alignment.setter
    @deprecated("1.0.0", "1.2.0", BTColumnCollection.default_alignment.fget)
    def default_alignment(self, value):  # pragma: no cover
        self.columns.default_alignment = value

    @property
    @deprecated("1.0.0", "1.2.0", BTColumnCollection.default_padding.fget)
    def default_padding(self):  # pragma: no cover
        return self.columns.default_padding

    @default_padding.setter
    @deprecated("1.0.0", "1.2.0", BTColumnCollection.default_padding.fget)
    def default_padding(self, value):  # pragma: no cover
        self.columns.default_padding = value

    @property
    @deprecated("1.0.0", "1.2.0", BTColumnCollection.width.fget)
    def column_widths(self):  # pragma: no cover
        return self.columns.width

    @column_widths.setter
    @deprecated("1.0.0", "1.2.0", BTColumnCollection.width.fget)
    def column_widths(self, value):  # pragma: no cover
        self.columns.width = value

    @property
    @deprecated("1.0.0", "1.2.0", BTColumnCollection.header.fget)
    def column_headers(self):  # pragma: no cover
        return self.columns.header

    @column_headers.setter
    @deprecated("1.0.0", "1.2.0", BTColumnCollection.header.fget)
    def column_headers(self, value):  # pragma: no cover
        self.columns.header = value

    @property
    @deprecated("1.0.0", "1.2.0", BTColumnCollection.alignment.fget)
    def column_alignments(self):  # pragma: no cover
        return self.columns.alignment

    @column_alignments.setter
    @deprecated("1.0.0", "1.2.0", BTColumnCollection.alignment.fget)
    def column_alignments(self, value):  # pragma: no cover
        self.columns.alignment = value

    @property
    @deprecated("1.0.0", "1.2.0", BTColumnCollection.padding_left.fget)
    def left_padding_widths(self):  # pragma: no cover
        return self.columns.padding_left

    @left_padding_widths.setter
    @deprecated("1.0.0", "1.2.0", BTColumnCollection.padding_left.fget)
    def left_padding_widths(self, value):  # pragma: no cover
        self.columns.padding_left = value

    @property
    @deprecated("1.0.0", "1.2.0", BTColumnCollection.padding_right.fget)
    def right_padding_widths(self):  # pragma: no cover
        return self.columns.padding_right

    @right_padding_widths.setter
    @deprecated("1.0.0", "1.2.0", BTColumnCollection.padding_right.fget)
    def right_padding_widths(self, value):  # pragma: no cover
        self.columns.padding_right = value

    @deprecated(
        "1.0.0",
        "1.2.0",
        BTColumnCollection.__getitem__,
        details="Use 'BeautifulTable.{columns|rows}[key]' instead.",
    )
    def __getitem__(self, key):  # pragma: no cover
        return self.columns[key] if isinstance(key, basestring) else self.rows[key]

    @deprecated(
        "1.0.0",
        "1.2.0",
        BTColumnCollection.__setitem__,
        details="Use 'BeautifulTable.{columns|rows}[key]' instead.",
    )
    def __setitem__(self, key, value):  # pragma: no cover
        if isinstance(key, basestring):
            self.columns[key] = value
        else:
            self.rows[key] = value

    @deprecated(
        "1.0.0",
        "1.2.0",
        BTColumnCollection.__delitem__,
        details="Use 'BeautifulTable.{columns|rows}[key]' instead.",
    )
    def __delitem__(self, key):  # pragma: no cover
        if isinstance(key, basestring):
            del self.columns[key]
        else:
            del self.rows[key]

    # *************************Properties End Here*************************

    @deprecated(
        "1.0.0",
        "1.2.0",
        BTColumnCollection.__getitem__,
        details="Use 'BeautifulTable.columns[key]' instead.",
    )
    def get_column(self, key):  # pragma: no cover
        return self.columns[key]

    @deprecated(
        "1.0.0",
        "1.2.0",
        BTColumnHeader.__getitem__,
        details="Use 'BeautifulTable.columns.header[key]' instead.",
    )
    def get_column_header(self, index):  # pragma: no cover
        return self.columns.header[index]

    @deprecated(
        "1.0.0",
        "1.2.0",
        BTColumnHeader.__getitem__,
        details="Use 'BeautifulTable.columns.header.index(header)' instead.",
    )
    def get_column_index(self, header):  # pragma: no cover
        return self.columns.header.index(header)

    @deprecated("1.0.0", "1.2.0", BTRowCollection.filter)
    def filter(self, key):  # pragma: no cover
        return self.rows.filter(key)

    @deprecated("1.0.0", "1.2.0", BTRowCollection.sort)
    def sort(self, key, reverse=False):  # pragma: no cover
        self.rows.sort(key, reverse=reverse)

    @deprecated("1.0.0", "1.2.0", BTRowCollection.reverse)
    def reverse(self, value):  # pragma: no cover
        self.rows.reverse()

    @deprecated("1.0.0", "1.2.0", BTRowCollection.pop)
    def pop_row(self, index=-1):  # pragma: no cover
        return self.rows.pop(index)

    @deprecated("1.0.0", "1.2.0", BTRowCollection.insert)
    def insert_row(self, index, row):  # pragma: no cover
        return self.rows.insert(index, row)

    @deprecated("1.0.0", "1.2.0", BTRowCollection.append)
    def append_row(self, value):  # pragma: no cover
        self.rows.append(value)

    @deprecated("1.0.0", "1.2.0", BTRowCollection.update)
    def update_row(self, key, value):  # pragma: no cover
        self.rows.update(key, value)

    @deprecated("1.0.0", "1.2.0", BTColumnCollection.pop)
    def pop_column(self, index=-1):  # pragma: no cover
        return self.columns.pop(index)

    @deprecated("1.0.0", "1.2.0", BTColumnCollection.insert)
    def insert_column(self, index, header, column):  # pragma: no cover
        self.columns.insert(index, column, header)

    @deprecated("1.0.0", "1.2.0", BTColumnCollection.append)
    def append_column(self, header, column):  # pragma: no cover
        self.columns.append(column, header)

    @deprecated("1.0.0", "1.2.0", BTColumnCollection.update)
    def update_column(self, header, column):  # pragma: no cover
        self.columns.update(header, column)

    def set_style(self, style):
        """Set the style of the table from a predefined set of styles.

        Parameters
        ----------
        style: Style

            It can be one of the following:

            * beautifultable.STYLE_DEFAULT
            * beautifultable.STYLE_NONE
            * beautifultable.STYLE_DOTTED
            * beautifultable.STYLE_MYSQL
            * beautifultable.STYLE_SEPARATED
            * beautifultable.STYLE_COMPACT
            * beautifultable.STYLE_MARKDOWN
            * beautifultable.STYLE_RESTRUCTURED_TEXT
            * beautifultable.STYLE_BOX
            * beautifultable.STYLE_BOX_DOUBLED
            * beautifultable.STYLE_BOX_ROUNDED
            * beautifultable.STYLE_GRID
        """
        if not isinstance(style, enums.Style):
            allowed = (f"{type(self).__name__}.{i.name}" for i in enums.Style)
            error_msg = "allowed values for style are: " + ", ".join(allowed)
            raise ValueError(error_msg)
        style_template = style.value
        self.border.left = style_template.left_border_char
        self.border.right = style_template.right_border_char
        self.border.top = style_template.top_border_char
        self.border.bottom = style_template.bottom_border_char

        self.border.top_left = style_template.intersect_top_left
        self.border.bottom_left = style_template.intersect_bottom_left
        self.border.bottom_right = style_template.intersect_bottom_right
        self.border.top_right = style_template.intersect_top_right

        self.border.header_left = style_template.intersect_header_left
        self.border.header_right = style_template.intersect_header_right

        self.columns.header.separator = style_template.header_separator_char
        self.columns.separator = style_template.column_separator_char
        self.rows.separator = style_template.row_separator_char

        self.border.top_junction = style_template.intersect_top_mid
        self.border.left_junction = style_template.intersect_row_left
        self.border.bottom_junction = style_template.intersect_bottom_mid
        self.border.right_junction = style_template.intersect_row_right
        self.columns.header.junction = style_template.intersect_header_mid
        self.junction = style_template.intersect_row_mid

    def _compute_width(self):
        """Calculate width of column automatically based on data."""
        table_width = self._width
        lpw, rpw = self.columns.padding_left, self.columns.padding_right
        pad_widths = [(lpw[i] + rpw[i]) for i in range(len(self.columns))]
        maxwidths = [0 for index in range(len(self.columns))]
        offset = table_width - sum(self.columns.width) + sum(pad_widths)
        self._maxwidth = max(self._maxwidth, offset + len(self.columns))

        for index, header in enumerate(self.columns.header):
            max_length = 0
            for i in pre_process(
                header, self.detect_numerics, self.precision, self.sign.value
            ).split("\n"):
                output_str = pre_process(
                    i,
                    self.detect_numerics,
                    self.precision,
                    self.sign.value,
                )
                max_length = max(max_length, termwidth(output_str))
            maxwidths[index] += max_length

        for index, column in enumerate(zip(*self._data)):
            max_length = maxwidths[index]
            for i in column:
                for j in pre_process(
                    i, self.detect_numerics, self.precision, self.sign.value
                ).split("\n"):
                    output_str = pre_process(
                        j,
                        self.detect_numerics,
                        self.precision,
                        self.sign.value,
                    )
                    max_length = max(max_length, termwidth(output_str))
            maxwidths[index] = max_length

        sum_ = sum(maxwidths)
        desired_sum = self._maxwidth - offset

        # Set flag for columns who are within their fair share
        temp_sum = 0
        flag = [0] * len(maxwidths)
        for i, width in enumerate(maxwidths):
            if width <= int(desired_sum / len(self.columns)):
                temp_sum += width
                flag[i] = 1
            else:
                # Allocate atleast 1 character width to the column
                temp_sum += 1

        avail_space = desired_sum - temp_sum
        actual_space = sum_ - temp_sum
        shrinked_columns = {}

        # Columns which exceed their fair share should be shrinked based on
        # how much space is left for the table
        for i, width in enumerate(maxwidths):
            self.columns.width[i] = width
            if not flag[i]:
                new_width = 1 + int((width - 1) * avail_space / actual_space)
                if new_width < width:
                    self.columns.width[i] = new_width
                    shrinked_columns[new_width] = i

        # Divide any remaining space among shrinked columns
        if shrinked_columns:
            extra = self._maxwidth - offset - sum(self.columns.width)
            actual_space = sum(shrinked_columns)

            if extra > 0:
                for i, width in enumerate(sorted(shrinked_columns)):
                    index = shrinked_columns[width]
                    extra_width = int(width * extra / actual_space)
                    self.columns.width[i] += extra_width
                    if i == (len(shrinked_columns) - 1):
                        extra = self._maxwidth - offset - sum(self.columns.width)
                        self.columns.width[index] += extra

        for i in range(len(self.columns)):
            self.columns.width[i] += pad_widths[i]

    @deprecated("1.0.0", "1.2.0", BTColumnCollection.padding.fget)
    def set_padding_widths(self, pad_width):  # pragma: no cover
        self.columns.padding_left = pad_width
        self.columns.padding_right = pad_width

    @deprecated("1.0.0", "1.2.0")
    def copy(self):
        return copy.copy(self)

    @deprecated_param("1.0.0", "1.2.0", "clear_metadata", "reset_columns")
    def clear(self, reset_columns=False, **kwargs):  # pragma: no cover
        """Clear the contents of the table.

        Clear all rows of the table, and if specified clears all column
        specific data.

        Parameters
        ----------
        reset_columns : bool, optional
            If it is true(default False), all metadata of columns such as their
            alignment, padding, width, etc. are also cleared and number of
            columns is set to 0.
        """
        kwargs.setdefault("clear_metadata", None)
        if kwargs["clear_metadata"]:
            reset_columns = kwargs["clear_metadata"]
        self.rows.clear()
        if reset_columns:
            self.columns.clear()

    def _get_horizontal_line(
        self, char, intersect_left, intersect_mid, intersect_right, mask=None
    ):
        """Get a horizontal line for the table.

        Internal method used to draw all horizontal lines in the table.
        Column width should be set prior to calling this method. This method
        detects intersection and handles it according to the values of
        `intersect_*_*` attributes.

        Parameters
        ----------
        char : str
            Character used to draw the line.

        Returns
        -------
        str
            String which will be printed as a line in the table.
        """
        width = self._width

        if mask is None:
            mask = [True] * len(self.columns)

        try:
            line = list(char * (int(width / termwidth(char)) + 1))[:width]
        except ZeroDivisionError:
            line = [" "] * width

        if len(line) == 0:
            return ""

        # Only if Special Intersection is enabled and horizontal line is
        # visible
        if not char.isspace():
            # If left border is enabled and it is visible
            visible_junc = not intersect_left.isspace()
            if termwidth(self.border.left) > 0:
                if not (self.border.left.isspace() and visible_junc):
                    length = min(
                        termwidth(self.border.left),
                        termwidth(intersect_left),
                    )
                    for i in range(length):
                        line[i] = intersect_left[i] if mask[0] else " "
            visible_junc = not intersect_right.isspace()
            # If right border is enabled and it is visible
            if termwidth(self.border.right) > 0:
                if not (self.border.right.isspace() and visible_junc):
                    length = min(
                        termwidth(self.border.right),
                        termwidth(intersect_right),
                    )
                    for i in range(length):
                        line[-i - 1] = intersect_right[-i - 1] if mask[-1] else " "
            visible_junc = not intersect_mid.isspace()
            # If column separator is enabled and it is visible
            if termwidth(self.columns.separator):
                if not (self.columns.separator.isspace() and visible_junc):
                    index = termwidth(self.border.left)
                    for i in range(len(self.columns) - 1):
                        if not mask[i]:
                            for j in range(self.columns.width[i]):
                                line[index + j] = " "
                        index += self.columns.width[i]
                        length = min(
                            termwidth(self.columns.separator),
                            termwidth(intersect_mid),
                        )
                        for j in range(length):
                            # TODO: we should also hide junctions based on mask
                            line[index + j] = (
                                intersect_mid[j] if (mask[i] or mask[i + 1]) else " "
                            )
                        index += termwidth(self.columns.separator)

        return "".join(line)

    def _get_top_border(self, *args, **kwargs):
        return self._get_horizontal_line(
            self.border.top,
            self.border.top_left,
            self.border.top_junction,
            self.border.top_right,
            *args,
            **kwargs,
        )

    def _get_header_separator(self, *args, **kwargs):
        return self._get_horizontal_line(
            self.columns.header.separator,
            self.border.header_left,
            self.columns.header.junction,
            self.border.header_right,
            *args,
            **kwargs,
        )

    def _get_row_separator(self, *args, **kwargs):
        return self._get_horizontal_line(
            self.rows.separator,
            self.border.left_junction,
            self.junction,
            self.border.right_junction,
            *args,
            **kwargs,
        )

    def _get_bottom_border(self, *args, **kwargs):
        return self._get_horizontal_line(
            self.border.bottom,
            self.border.bottom_left,
            self.border.bottom_junction,
            self.border.bottom_right,
            *args,
            **kwargs,
        )

    @property
    def _width(self):
        """Get the actual width of the table as number of characters.

        Column width should be set prior to calling this method.

        Returns
        -------
        int
            Width of the table as number of characters.
        """
        if len(self.columns) == 0:
            return 0
        width = sum(self.columns.width)
        width += (len(self.columns) - 1) * termwidth(self.columns.separator)
        width += termwidth(self.border.left)
        width += termwidth(self.border.right)
        return width

    @deprecated("1.0.0", "1.2.0", _width.fget)
    def get_table_width(self):  # pragma: no cover
        return self._width

    def _get_string(self, rows=None, append=False, recalculate_width=True):
        row_header_visible = bool(
            "".join(x if x is not None else "" for x in self.rows.header).strip()
        ) and (len(self.columns) > 0)

        column_header_visible = bool(
            "".join(x if x is not None else "" for x in self.columns.header).strip()
        ) and (len(self.rows) > 0 or rows is not None)

        # Preparing table for printing serialno, row headers and column headers
        if len(self.columns) > 0:
            if self._serialno:
                self.columns.insert(
                    0, range(1, len(self.rows) + 1), self._serialno_header
                )

        if row_header_visible:
            self.columns.insert(0, self.rows.header)

        if column_header_visible:
            self.rows.insert(0, self.columns.header)

        if (self.columns._auto_width and recalculate_width) or sum(
            self.columns.width
        ) == 0:
            self._compute_width()

        try:
            # Rendering the top border
            if self.border.top:
                yield self._get_top_border()

            # Print column headers if not empty or only spaces
            row_iterator = iter(self.rows)
            if column_header_visible:
                yield next(row_iterator)._get_string(
                    align=self.columns.header.alignment
                )
                if self.columns.header.separator:
                    yield self._get_header_separator()

            # Printing rows
            first_row_encountered = False
            for i, row in enumerate(row_iterator):
                if first_row_encountered and self.rows.separator:
                    yield self._get_row_separator()
                first_row_encountered = True
                content = to_unicode(row)
                yield content

            if rows is not None:
                # Printing additional rows
                prev_length = len(self.rows)
                for i, row in enumerate(rows, start=1):
                    if first_row_encountered and self.rows.separator:
                        yield self._get_row_separator()
                    first_row_encountered = True
                    if self._serialno:
                        row.insert(0, prev_length + i)
                    if row_header_visible:
                        self.rows.append([None] + list(row))
                    else:
                        self.rows.append(row)
                    content = to_unicode(self.rows[-1])
                    if not append:
                        self.rows.pop()
                    yield content

            # Rendering the bottom border
            if self.border.bottom:
                yield self._get_bottom_border()
        except Exception:
            raise
        finally:
            # Cleanup
            if column_header_visible:
                self.rows.pop(0)

            if row_header_visible:
                self.columns.pop(0)

            if len(self.columns) > 0:
                if self._serialno:
                    self.columns.pop(0)
        return

    def stream(self, rows, append=False):
        """Get a generator for the table.

        This should be used in cases where data takes time to retrieve and it
        is required to be displayed as soon as possible. Any existing rows in
        the table shall also be returned. It is essential that atleast one of
        column header, width or existing rows set before calling this method.

        Parameters
        ----------
        rows : iterable
            A generator which yields one row at a time.

        append : bool, optional
            If rows should also be appended to the table.(Default False)

        Returns
        -------
        iterable:
            string representation of the table as a generators
        """
        for line in self._get_string(rows, append=append, recalculate_width=False):
            yield line

    @deprecated("1.0.0", "1.2.0", str)
    def get_string(self):
        return str(self)

    def to_csv(self, file_name, *args, **kwargs):
        """Export table to CSV format.

        Parameters
        ----------
        file_name : str
            Path to CSV file.
        """

        if not isinstance(file_name, str):
            raise ValueError(
                f"Expected 'file_name' to be string, got {type(file_name).__name__}"
            )

        with open(file_name, mode="wt", newline="") as csv_file:
            csv_writer = csv.writer(csv_file, *args, **kwargs)
            if bool(
                "".join(x if x is not None else "" for x in self.columns.header).strip()
            ):
                csv_writer.writerow(self.columns.header)
            csv_writer.writerows(self.rows)

    def from_csv(self, file_name, header=True, **kwargs):
        """Create table from CSV file.

        Parameters
        ----------
        file_name : str
            Path to CSV file.
        header : bool, optional
            Whether First row in CSV file should be parsed as table header.

        Raises
        ------
        ValueError
            If `file_name` is not str type.
        FileNotFoundError
            If `file_name` is not valid path to file.
        """

        if not isinstance(file_name, str):
            raise ValueError(
                f"Expected 'file_name' to be string, got {type(file_name).__name__}"
            )

        with open(file_name, mode="rt", newline="") as csv_file:
            csv_reader = csv.reader(csv_file, **kwargs)

            if header:
                self.columns.header = next(csv_reader)
            for row in csv_reader:
                self.rows.append(row)
            return self

    def to_df(self):
        """Export table to dataframe.

        This method requires that you have `pandas` already installed in your machine.

        Returns
        -------
        pandas.Dataframe:
            The exported dataframe
        """
        try:
            import pandas as pd
        except ImportError:
            warnings.warn(
                "This method requires that 'pandas' is installed.", RuntimeWarning
            )
            raise

        # If there are column headers then it will act as a column of dataframe
        headers = list(self.columns.header)
        if headers.count(None) == len(headers):
            headers = None

        # If there are row headers then it will act as an Index
        index = list(self.rows.header)
        if index.count(None) == len(index):
            index = None

        return pd.DataFrame(
            [list(row) for row in self.rows], columns=headers, index=index
        )

    def from_df(self, df):
        """Import table from dataframe.

        Parameters
        ----------
        df : pandas.Dataframe
            input dataframe
        """
        data = df.to_dict()

        # Dataframe columns will act as a column headers
        headers = list(data.keys())

        # Index of dataframe will act as a row headers
        row_header = list(df.index)

        for header in headers:
            self.columns.append(
                [data[header][indx] for indx in row_header], header=str(header)
            )

        self.rows.header = row_header
        return self


================================================
FILE: beautifultable/compat.py
================================================
from itertools import zip_longest  # noqa: F401
from collections.abc import Iterable  # noqa: F401

to_unicode = str
basestring = (str, bytes)


================================================
FILE: beautifultable/enums.py
================================================
from __future__ import unicode_literals
import enum

from .styles import (
    DefaultStyle,
    NoStyle,
    DottedStyle,
    MySQLStyle,
    SeparatedStyle,
    CompactStyle,
    MarkdownStyle,
    RestructuredTextStyle,
    BoxStyle,
    DoubledBoxStyle,
    RoundedStyle,
    GridStyle,
)


class WidthExceedPolicy(enum.Enum):
    WEP_WRAP = 1
    WEP_STRIP = 2
    WEP_ELLIPSIS = 3

    def __repr__(self):
        return self.name


class SignMode(enum.Enum):
    SM_PLUS = "+"
    SM_MINUS = "-"
    SM_SPACE = " "

    def __repr__(self):
        return self.name


class Alignment(enum.Enum):
    ALIGN_LEFT = "<"
    ALIGN_CENTER = "^"
    ALIGN_RIGHT = ">"

    def __repr__(self):
        return self.name


class Style(enum.Enum):
    STYLE_DEFAULT = DefaultStyle
    STYLE_NONE = NoStyle
    STYLE_DOTTED = DottedStyle
    STYLE_MYSQL = MySQLStyle
    STYLE_SEPARATED = SeparatedStyle
    STYLE_COMPACT = CompactStyle
    STYLE_MARKDOWN = MarkdownStyle
    STYLE_RST = RestructuredTextStyle
    STYLE_BOX = BoxStyle
    STYLE_BOX_DOUBLED = DoubledBoxStyle
    STYLE_BOX_ROUNDED = RoundedStyle
    STYLE_GRID = GridStyle

    def __repr__(self):
        return self.name


WEP_WRAP = WidthExceedPolicy.WEP_WRAP
WEP_STRIP = WidthExceedPolicy.WEP_STRIP
WEP_ELLIPSIS = WidthExceedPolicy.WEP_ELLIPSIS
SM_PLUS = SignMode.SM_PLUS
SM_MINUS = SignMode.SM_MINUS
SM_SPACE = SignMode.SM_SPACE
ALIGN_LEFT = Alignment.ALIGN_LEFT
ALIGN_CENTER = Alignment.ALIGN_CENTER
ALIGN_RIGHT = Alignment.ALIGN_RIGHT
STYLE_DEFAULT = Style.STYLE_DEFAULT
STYLE_NONE = Style.STYLE_NONE
STYLE_DOTTED = Style.STYLE_DOTTED
STYLE_SEPARATED = Style.STYLE_SEPARATED
STYLE_COMPACT = Style.STYLE_COMPACT
STYLE_MYSQL = Style.STYLE_MYSQL
STYLE_MARKDOWN = Style.STYLE_MARKDOWN
STYLE_RST = Style.STYLE_RST
STYLE_BOX = Style.STYLE_BOX
STYLE_BOX_DOUBLED = Style.STYLE_BOX_DOUBLED
STYLE_BOX_ROUNDED = Style.STYLE_BOX_ROUNDED
STYLE_GRID = Style.STYLE_GRID


================================================
FILE: beautifultable/helpers.py
================================================
import copy
import weakref
import operator

from . import enums
from .base import BTBaseRow, BTBaseColumn
from .utils import pre_process, termwidth, textwrap, ensure_type
from .compat import basestring, Iterable, to_unicode, zip_longest
from .meta import AlignmentMetaData, NonNegativeIntegerMetaData


class BTRowHeader(BTBaseColumn):
    def __init__(self, table, value):
        for i in value:
            self._validate_item(i)
        super(BTRowHeader, self).__init__(table, value)

    def __setitem__(self, key, value):
        self._validate_item(value)
        super(BTRowHeader, self).__setitem__(key, value)

    def _validate_item(self, value):
        if not (isinstance(value, basestring) or value is None):
            raise TypeError(f"header must be of type 'str', got {type(value).__name__}")


class BTColumnHeader(BTBaseRow):
    def __init__(self, table, value):
        for i in value:
            self._validate_item(i)
        super(BTColumnHeader, self).__init__(table, value)
        self.alignment = None

    @property
    def alignment(self):
        """get/set alignment of the column header of the table.

        It can be any iterable containing only the following:

        * beautifultable.ALIGN_LEFT
        * beautifultable.ALIGN_CENTER
        * beautifultable.ALIGN_RIGHT
        """
        return self._alignment

    @alignment.setter
    def alignment(self, value):
        if value is None:
            self._alignment = None
            return
        if isinstance(value, enums.Alignment):
            value = [value] * len(self)
        self._alignment = AlignmentMetaData(self._table, value)

    @property
    def separator(self):
        """Character used to draw the line seperating header from the table."""
        return self._table._header_separator

    @separator.setter
    def separator(self, value):
        self._table._header_separator = ensure_type(value, basestring)

    @property
    def junction(self):
        """Character used to draw junctions in the header separator."""
        return self._table._header_junction

    @junction.setter
    def junction(self, value):
        self._table._header_junction = ensure_type(value, basestring)

    def __setitem__(self, key, value):
        self._validate_item(value)
        super(BTColumnHeader, self).__setitem__(key, value)

    def _validate_item(self, value):
        if not (isinstance(value, basestring) or value is None):
            raise TypeError(f"header must be of type 'str', got {type(value).__name__}")


class BTRowData(BTBaseRow):
    def _get_padding(self):
        return (
            self._table.columns.padding_left,
            self._table.columns.padding_right,
        )

    def _clamp_row(self, row):
        """Process a row so that it is clamped by column_width.

        Parameters
        ----------
        row : array_like
             A single row.

        Returns
        -------
        list of list:
            List representation of the `row` after it has been processed
            according to width exceed policy.
        """
        table = self._table
        lpw, rpw = self._get_padding()
        wep = table.columns.width_exceed_policy

        result = []

        if (
            wep is enums.WidthExceedPolicy.WEP_STRIP
            or wep is enums.WidthExceedPolicy.WEP_ELLIPSIS
        ):

            # Let's strip the row
            delimiter = "" if wep is enums.WidthExceedPolicy.WEP_STRIP else "..."
            row_item_list = []
            for index, row_item in enumerate(row):
                left_pad = table.columns._pad_character * lpw[index]
                right_pad = table.columns._pad_character * rpw[index]
                clmp_str = (
                    left_pad
                    + self._clamp_string(row_item, index, delimiter)
                    + right_pad
                )
                row_item_list.append(clmp_str)
            result.append(row_item_list)
        elif wep is enums.WidthExceedPolicy.WEP_WRAP:

            # Let's wrap the row
            string_partition = []

            for index, row_item in enumerate(row):
                width = table.columns.width[index] - lpw[index] - rpw[index]
                string_partition.append(textwrap(row_item, width))

            for row_items in zip_longest(*string_partition, fillvalue=""):
                row_item_list = []
                for index, row_item in enumerate(row_items):
                    left_pad = table.columns._pad_character * lpw[index]
                    right_pad = table.columns._pad_character * rpw[index]
                    row_item_list.append(left_pad + row_item + right_pad)
                result.append(row_item_list)

        return result or [[""] * len(table.columns)]

    def _clamp_string(self, row_item, index, delimiter=""):
        """Clamp `row_item` to fit in column referred by index.

        This method considers padding and appends the delimiter if `row_item`
        needs to be truncated.

        Parameters
        ----------
        row_item: str
            String which should be clamped.

        index: int
            Index of the column `row_item` belongs to.

        delimiter: str
            String which is to be appended to the clamped string.

        Returns
        -------
        str
            The modified string which fits in it's column.
        """
        lpw, rpw = self._get_padding()
        width = self._table.columns.width[index] - lpw[index] - rpw[index]

        if termwidth(row_item) <= width:
            return row_item
        else:
            if width - len(delimiter) >= 0:
                clamped_string = (
                    textwrap(row_item, width - len(delimiter))[0] + delimiter
                )
            else:
                clamped_string = delimiter[:width]
            return clamped_string

    def _get_string(
        self,
        align=None,
        mask=None,
        draw_left_border=True,
        draw_right_border=True,
    ):
        """Return a string representation of a row."""

        rows = []

        table = self._table
        width = table.columns.width
        sign = table.sign

        if align is None:
            align = table.columns.alignment

        if mask is None:
            mask = [True] * len(table.columns)

        lpw, rpw = self._get_padding()

        string = []
        for i, item in enumerate(self._value):
            if isinstance(item, type(table)):
                # temporarily change the max width of the table
                curr_maxwidth = item.maxwidth
                item.maxwidth = width[i] - lpw[i] - rpw[i]
                rows.append(
                    pre_process(
                        item,
                        table.detect_numerics,
                        table.precision,
                        sign.value,
                    ).split("\n")
                )
                item.maxwidth = curr_maxwidth
            else:
                rows.append(
                    pre_process(
                        item,
                        table.detect_numerics,
                        table.precision,
                        sign.value,
                    ).split("\n")
                )
        for row in map(list, zip_longest(*rows, fillvalue="")):
            for i in range(len(row)):
                row[i] = pre_process(
                    row[i],
                    table.detect_numerics,
                    table.precision,
                    sign.value,
                )
            for row_ in self._clamp_row(row):
                for i in range(len(table.columns)):
                    # str.format method doesn't work for multibyte strings
                    # hence, we need to manually align the texts instead
                    # of using the align property of the str.format method
                    pad_len = width[i] - termwidth(row_[i])
                    if align[i].value == "<":
                        right_pad = " " * pad_len
                        row_[i] = to_unicode(row_[i]) + right_pad
                    elif align[i].value == ">":
                        left_pad = " " * pad_len
                        row_[i] = left_pad + to_unicode(row_[i])
                    else:
                        left_pad = " " * (pad_len // 2)
                        right_pad = " " * (pad_len - pad_len // 2)
                        row_[i] = left_pad + to_unicode(row_[i]) + right_pad
                content = []
                for j, item in enumerate(row_):
                    if j > 0:
                        content.append(
                            table.columns.separator
                            if (mask[j - 1] or mask[j])
                            else " " * termwidth(table.columns.separator)
                        )
                    content.append(item)
                content = "".join(content)
                content = (
                    table.border.left if mask[0] else " " * termwidth(table.border.left)
                ) + content
                content += (
                    table.border.right
                    if mask[-1]
                    else " " * termwidth(table.border.right)
                )
                string.append(content)
        return "\n".join(string)

    def __str__(self):
        return self._get_string()

    def aslist(self):
        """Return list of row values."""
        return self.value

    def asdict(self):
        """
        Return dictionary where key is column header and value as row value and
        raise a Warning if coulmn header invalid(not provided) or empty.
        """
        header_rowval_map = {}
        for header, row_val in zip(self._table.columns.header, self.value):
            if header is None or header == "":
                raise Warning("Column header is not provided or invalid")
            header_rowval_map[header] = row_val
        return header_rowval_map


class BTColumnData(BTBaseColumn):
    def aslist(self):
        """Return list of column values."""
        return self.value

    def asdict(self):
        """
        Raise a NotImplementedError as currently it is not implemented
        """
        raise NotImplementedError("Currently supported for rows only")


class BTRowCollection(object):
    def __init__(self, table):
        self._table = table
        self._reset_state(0)

    @property
    def _table(self):
        return self._table_ref()

    @_table.setter
    def _table(self, value):
        self._table_ref = weakref.ref(value)

    def _reset_state(self, nrow):
        self._table._data = type(self._table._data)(
            self._table,
            [BTRowData(self._table, [None] * self._table._ncol) for i in range(nrow)],
        )
        self.header = BTRowHeader(self._table, [None] * nrow)

    @property
    def header(self):
        return self._header

    @header.setter
    def header(self, value):
        self._header = BTRowHeader(self._table, value)

    @property
    def separator(self):
        """Character used to draw the line seperating two rows."""
        return self._table._row_separator

    @separator.setter
    def separator(self, value):
        self._table._row_separator = ensure_type(value, basestring)

    def _canonical_key(self, key):
        if isinstance(key, (int, slice)):
            return key
        elif isinstance(key, basestring):
            return self.header.index(key)
        raise TypeError(
            f"row indices must be int, str or slices, not {type(key).__name__}"
        )

    def __len__(self):
        return len(self._table._data)

    def __getitem__(self, key):
        """Get a particular row, or a new table by slicing.

        Parameters
        ----------
        key : int, slice, str
            If key is an `int`, returns a row at index `key`.
            If key is an `str`, returns the first row with heading `key`.
            If key is a slice object, returns a new sliced table.

        Raises
        ------
        TypeError
            If key is not of type int, slice or str.
        IndexError
            If `int` index is out of range.
        KeyError
            If `str` key is not found in header.
        """
        if isinstance(key, slice):
            new_table = copy.deepcopy(self._table)
            new_table.rows.clear()
            new_table.rows.header = self._table.rows.header[key]
            for i, r in enumerate(self._table._data[key]):
                new_table.rows[i] = r.value
            return new_table
        if isinstance(key, (int, basestring)):
            return self._table._data[key]
        raise TypeError(
            f"row indices must be int, str or a slice object, not {type(key).__name__}"
        )

    def __delitem__(self, key):
        """Delete a row, or multiple rows by slicing.

        Parameters
        ----------
        key : int, slice, str
            If key is an `int`, deletes a row at index `key`.
            If key is an `str`, deletes the first row with heading `key`.
            If key is a slice object, deletes multiple rows.

        Raises
        ------
        TypeError
            If key is not of type int, slice or str.
        IndexError
            If `int` key is out of range.
        KeyError
            If `str` key is not in header.
        """
        if isinstance(key, (int, basestring, slice)):
            del self._table._data[key]
            del self.header[key]
        else:
            raise TypeError(
                f"row indices must be int, str or a slice object, not {type(key).__name__}"
            )

    def __setitem__(self, key, value):
        """Update a row, or multiple rows by slicing.

        Parameters
        ----------
        key : int, slice, str
            If key is an `int`, updates a row.
            If key is an `str`, updates the first row with heading `key`.
            If key is a slice object, updates multiple rows.

        Raises
        ------
        TypeError
            If key is not of type int, slice or str.
        IndexError
            If `int` key is out of range.
        KeyError
            If `str` key is not in header.
        """
        if isinstance(key, (int, basestring)):
            self._table._data[key] = BTRowData(self._table, value)
        elif isinstance(key, slice):
            value = [list(row) for row in value]
            if len(self._table.columns) == 0:
                self._table.columns._initialize(len(value[0]))
            self._table._data[key] = [BTRowData(self._table, row) for row in value]
        else:
            raise TypeError("key must be int, str or a slice object")

    def __contains__(self, key):
        if isinstance(key, basestring):
            return key in self.header
        elif isinstance(key, Iterable):
            return key in self._table._data
        else:
            raise TypeError(f"'key' must be str or Iterable, not {type(key).__name__}")

    def __iter__(self):
        return BTCollectionIterator(self)

    def __repr__(self):
        return repr(self._table._data)

    def __str__(self):
        return str(self._table._data)

    def reverse(self):
        """Reverse the table row-wise *IN PLACE*."""
        self._table._data._reverse()

    def pop(self, index=-1):
        """Remove and return row at index (default last).

        Parameters
        ----------
        index : int, str
            index or heading of the row. Normal list rules apply.
        """
        if not isinstance(index, (int, basestring)):
            raise TypeError(f"row index must be int or str, not {type(index).__name__}")
        if len(self._table._data) == 0:
            raise IndexError("pop from empty table")
        else:
            res = self._table._data._pop(index)
            self.header._pop(index)
            return res

    def insert(self, index, row, header=None):
        """Insert a row before index in the table.

        Parameters
        ----------
        index : int
            List index rules apply

        row : iterable
            Any iterable of appropriate length.

        header : str, optional
            Heading of the row

        Raises
        ------
        TypeError:
            If `row` is not an iterable.

        ValueError:
            If size of `row` is inconsistent with the current number
            of columns.
        """
        if self._table._ncol == 0:
            row = list(row)
            self._table.columns._reset_state(len(row))
        self.header._insert(index, header)
        self._table._data._insert(index, BTRowData(self._table, row))

    def append(self, row, header=None):
        """Append a row to end of the table.

        Parameters
        ----------
        row : iterable
            Any iterable of appropriate length.

        header : str, optional
            Heading of the row

        """
        self.insert(len(self), row, header)

    def update(self, key, value):
        """Update row(s) identified with `key` in the table.

        `key` can be a index or a slice object.

        Parameters
        ----------
        key : int or slice
            index of the row, or a slice object.

        value : iterable
            If an index is specified, `value` should be an iterable
            of appropriate length. Instead if a slice object is
            passed as key, value should be an iterable of rows.

        Raises
        ------
        IndexError:
            If index specified is out of range.

        TypeError:
            If `value` is of incorrect type.

        ValueError:
            If length of row does not matches number of columns.
        """
        self[key] = value

    def clear(self):
        self._reset_state(0)

    def sort(self, key, reverse=False):
        """Stable sort of the table *IN-PLACE* with respect to a column.

        Parameters
        ----------
        key: int, str
            index or header of the column. Normal list rules apply.
        reverse : bool
            If `True` then table is sorted as if each comparison was reversed.
        """
        if isinstance(key, (int, basestring)):
            key = operator.itemgetter(key)
        elif callable(key):
            pass
        else:
            raise TypeError("'key' must either be 'int' or 'str' or a 'callable'")

        indices = sorted(
            range(len(self)),
            key=lambda x: key(self._table._data[x]),
            reverse=reverse,
        )
        self._table._data._sort(key=key, reverse=reverse)
        self.header = [self.header[i] for i in indices]

    def filter(self, key):
        """Return a copy of the table with only those rows which satisfy a
        certain condition.

        Returns
        -------
        BeautifulTable:
            Filtered copy of the BeautifulTable instance.
        """
        new_table = self._table.rows[:]
        new_table.rows.clear()
        for row in filter(key, self):
            new_table.rows.append(row)
        return new_table


class BTCollectionIterator(object):
    def __init__(self, collection):
        self._collection = collection
        self._index = -1

    def __iter__(self):
        return self

    def __next__(self):
        self._index += 1
        if self._index == len(self._collection):
            raise StopIteration
        return self._collection[self._index]


class BTColumnCollection(object):
    def __init__(self, table, default_alignment, default_padding):
        self._table = table
        self._width_exceed_policy = enums.WEP_WRAP
        self._pad_character = " "
        self.default_alignment = default_alignment
        self.default_padding = default_padding

        self._reset_state(0)

    @property
    def _table(self):
        return self._table_ref()

    @_table.setter
    def _table(self, value):
        self._table_ref = weakref.ref(value)

    @property
    def padding(self):
        """Set width for left and rigth padding of the columns of the table."""
        raise AttributeError(
            "cannot read attribute 'padding'. use 'padding_{left|right}'"
        )

    @padding.setter
    def padding(self, value):
        self.padding_left = value
        self.padding_right = value

    def _reset_state(self, ncol):
        self._table._ncol = ncol
        self._header = BTColumnHeader(self._table, [None] * ncol)
        self._auto_width = True
        self._alignment = AlignmentMetaData(
            self._table, [self.default_alignment] * ncol
        )
        self._width = NonNegativeIntegerMetaData(self._table, [0] * ncol)
        self._padding_left = NonNegativeIntegerMetaData(
            self._table, [self.default_padding] * ncol
        )
        self._padding_right = NonNegativeIntegerMetaData(
            self._table, [self.default_padding] * ncol
        )
        self._table._data = type(self._table._data)(
            self._table,
            [
                BTRowData(self._table, [None] * ncol)
                for i in range(len(self._table._data))
            ],
        )

    def _canonical_key(self, key):
        if isinstance(key, (int, slice)):
            return key
        elif isinstance(key, basestring):
            return self.header.index(key)
        raise TypeError(
            f"column indices must be int, str or slices, not {type(key).__name__}"
        )

    @property
    def header(self):
        """get/set headings for the columns of the table.

        It can be any iterable with all members an instance of `str` or None.
        """
        return self._header

    @header.setter
    def header(self, value):
        self._header = BTColumnHeader(self._table, value)

    @property
    def alignment(self):
        """get/set alignment of the columns of the table.

        It can be any iterable containing only the following:

        * beautifultable.ALIGN_LEFT
        * beautifultable.ALIGN_CENTER
        * beautifultable.ALIGN_RIGHT
        """
        return self._alignment

    @alignment.setter
    def alignment(self, value):
        if isinstance(value, enums.Alignment):
            value = [value] * len(self)
        self._alignment = AlignmentMetaData(self._table, value)

    @property
    def width(self):
        """get/set width for the columns of the table.

        Width of the column specifies the max number of characters
        a column can contain. Larger characters are handled according to
        `width_exceed_policy`. This can be one of `'auto'`, a non-negative
        integer or an iterable of the same length as the number of columns.
        If set to anything other than 'auto', the user is responsible for
        updating it if new columns are added or existing ones are updated.
        """
        return self._width

    @width.setter
    def width(self, value):
        if isinstance(value, str):
            if value == "auto":
                self._auto_width = True
                return
            raise ValueError(f"Invalid value '{value}'")
        if isinstance(value, int):
            value = [value] * len(self)
        self._width = NonNegativeIntegerMetaData(self._table, value)
        self._auto_width = False

    @property
    def padding_left(self):
        """get/set width for left padding of the columns of the table.

        Left Width of the padding specifies the number of characters
        on the left of a column reserved for padding. By Default It is 1.
        """
        return self._padding_left

    @padding_left.setter
    def padding_left(self, value):
        if isinstance(value, int):
            value = [value] * len(self)
        self._padding_left = NonNegativeIntegerMetaData(self._table, value)

    @property
    def padding_right(self):
        """get/set width for right padding of the columns of the table.

        Right Width of the padding specifies the number of characters
        on the rigth of a column reserved for padding. By default It is 1.
        """
        return self._padding_right

    @padding_right.setter
    def padding_right(self, value):
        if isinstance(value, int):
            value = [value] * len(self)
        self._padding_right = NonNegativeIntegerMetaData(self._table, value)

    @property
    def width_exceed_policy(self):
        """Attribute to control how exceeding column width should be handled.

        It can be one of the following:

        ============================  =========================================
         Option                        Meaning
        ============================  =========================================
         beautifultable.WEP_WRAP       An item is wrapped so every line fits
                                       within it's column width.

         beautifultable.WEP_STRIP      An item is stripped to fit in it's
                                       column.

         beautifultable.WEP_ELLIPSIS   An item is stripped to fit in it's
                                       column and appended with ...(Ellipsis).
        ============================  =========================================
        """
        return self._width_exceed_policy

    @width_exceed_policy.setter
    def width_exceed_policy(self, value):
        if not isinstance(value, enums.WidthExceedPolicy):
            allowed = (
                f"{type(self).__name__}.{i.name}" for i in enums.WidthExceedPolicy
            )
            error_msg = "allowed values for width_exceed_policy are: " + ", ".join(
                allowed
            )
            raise ValueError(error_msg)
        self._width_exceed_policy = value

    @property
    def default_alignment(self):
        """Attribute to control the alignment of newly created columns.

        It can be one of the following:

        ============================  =========================================
         Option                        Meaning
        ============================  =========================================
         beautifultable.ALIGN_LEFT     New columns are left aligned.

         beautifultable.ALIGN_CENTER   New columns are center aligned.

         beautifultable.ALIGN_RIGHT    New columns are right aligned.
        ============================  =========================================
        """
        return self._default_alignment

    @default_alignment.setter
    def default_alignment(self, value):
        if not isinstance(value, enums.Alignment):
            allowed = (f"{type(self).__name__}.{i.name}" for i in enums.Alignment)
            error_msg = "allowed values for default_alignment are: " + ", ".join(
                allowed
            )
            raise ValueError(error_msg)
        self._default_alignment = value

    @property
    def default_padding(self):
        """Initial value for Left and Right padding widths for new columns."""
        return self._default_padding

    @default_padding.setter
    def default_padding(self, value):
        if not isinstance(value, int):
            raise TypeError("default_padding must be an integer")
        elif value < 0:
            raise ValueError("default_padding must be a non-negative integer")
        else:
            self._default_padding = value

    @property
    def separator(self):
        """Character used to draw the line seperating two columns."""
        return self._table._column_separator

    @separator.setter
    def separator(self, value):
        self._table._column_separator = ensure_type(value, basestring)

    def __len__(self):
        return self._table._ncol

    def __getitem__(self, key):
        """Get a column, or a new table by slicing.

        Parameters
        ----------

        key : int, slice, str
            If key is an `int`, returns column at index `key`.
            If key is an `str`, returns first column with heading `key`.
            If key is a slice object, returns a new sliced table.

        Raises
        ------

        TypeError
            If key is not of type int, slice or str.
        IndexError
            If `int` key is out of range.
        KeyError
            If `str` key is not in header.
        """
        if isinstance(key, int):
            pass
        elif isinstance(key, slice):
            new_table = copy.deepcopy(self._table)

            new_table.columns.clear()
            new_table.columns.header = self.header[key]
            new_table.columns.alignment = self.alignment[key]
            new_table.columns.padding_left = self.padding_left[key]
            new_table.columns.padding_right = self.padding_right[key]
            new_table.columns.width = self.width[key]
            new_table.columns._auto_width = self._auto_width
            for i, r in enumerate(self._table._data):
                new_table.rows[i] = r.value[key]
            return new_table
        elif isinstance(key, basestring):
            key = self.header.index(key)
        else:
            raise TypeError(
                f"column indices must be integers, strings or slices, not {type(key).__name__}"
            )

        return BTColumnData(self._table, [row[key] for row in self._table._data])

    def __delitem__(self, key):
        """Delete a column, or multiple columns by slicing.

        Parameters
        ----------

        key : int, slice, str
            If key is an `int`, deletes column at index `key`.
            If key is a slice object, deletes multiple columns.
            If key is an `str`, deletes the first column with heading `key`

        Raises
        ------

        TypeError
            If key is not of type int, slice or str.
        IndexError
            If `int` key is out of range.
        KeyError
            If `str` key is not in header.
        """
        if isinstance(key, (int, basestring, slice)):
            key = self._canonical_key(key)

            del self.alignment[key]
            del self.width[key]
            del self.padding_left[key]
            del self.padding_right[key]
            for row in self._table.rows:
                del row[key]
            del self.header[key]
            if self.header.alignment is not None:
                del self.header.alignment[key]
            self._table._ncol = len(self.header)
            if self._table._ncol == 0:
                del self._table.rows[:]
        else:
            raise TypeError(
                f"table indices must be int, str or slices, not {type(key).__name__}"
            )

    def __setitem__(self, key, value):
        """Update a column, or multiple columns by slicing.

        Parameters
        ----------

        key : int, slice, str
            If key is an `int`, updates column at index `key`.
            If key is an `str`, updates first column with heading `key`.
            If key is a slice object, updates multiple columns.

        Raises
        ------

        TypeError
            If key is not of type int, slice or str.
        IndexError
            If `int` key is out of range.
        KeyError
            If `str` key is not in header
        """
        if not isinstance(key, (int, basestring, slice)):
            raise TypeError("column indices must be of type int, str or a slice object")
        for row, new_item in zip(self._table.rows, value):
            row[key] = new_item

    def __contains__(self, key):
        if isinstance(key, basestring):
            return key in self.header
        elif isinstance(key, Iterable):
            key = list(key)
            return any(key == column for column in self)
        else:
            raise TypeError(f"'key' must be str or Iterable, not {type(key).__name__}")

    def __iter__(self):
        return BTCollectionIterator(self)

    def __repr__(self):
        return repr(self._table)

    def __str__(self):
        return str(self._table._data)

    def clear(self):
        self._reset_state(0)

    def pop(self, index=-1):
        """Remove and return column at index (default last).

        Parameters
        ----------
        index : int, str
            index of the column, or the header of the column.
            If index is specified, then normal list rules apply.

        Raises
        ------
        TypeError:
            If index is not an instance of `int`, or `str`.

        IndexError:
            If Table is empty.
        """
        if not isinstance(index, (int, basestring)):
            raise TypeError(
                f"column index must be int or str, not {type(index).__name__}"
            )
        if self._table._ncol == 0:
            raise IndexError("pop from empty table")
        else:
            res = []
            index = self._canonical_key(index)
            for row in self._table.rows:
                res.append(row._pop(index))
            res = BTColumnData(self._table, res)
            self.alignment._pop(index)
            self.width._pop(index)
            self.padding_left._pop(index)
            self.padding_right._pop(index)
            self.header._pop(index)

            self._table._ncol = len(self.header)
            if self._table._ncol == 0:
                del self._table.rows[:]
            return res

    def update(self, key, value):
        """Update a column named `header` in the table.

        If length of column is smaller than number of rows, lets say
        `k`, only the first `k` values in the column is updated.

        Parameters
        ----------
        key : int, str
            If `key` is int, column at index `key` is updated.
            If `key` is str, the first column with heading `key` is updated.

        value : iterable
            Any iterable of appropriate length.

        Raises
        ------
        TypeError:
            If length of `column` is shorter than number of rows.

        ValueError:
            If no column exists with heading `header`.
        """
        self[key] = value

    def insert(
        self,
        index,
        column,
        header=None,
        padding_left=None,
        padding_right=None,
        alignment=None,
    ):
        """Insert a column before `index` in the table.

        If length of column is bigger than number of rows, lets say
        `k`, only the first `k` values of `column` is considered.
        If column is shorter than 'k', ValueError is raised.

        Note that Table remains in consistent state even if column
        is too short. Any changes made by this method is rolled back
        before raising the exception.

        Parameters
        ----------
        index : int
            List index rules apply.

        column : iterable
            Any iterable of appropriate length.

        header : str, optional
            Heading of the column.

        padding_left : int, optional
            Left padding of the column.

        padding_right : int, optional
            Right padding of the column.

        alignment : Alignment, optional
            alignment of the column.

        Raises
        ------
        TypeError:
            If `header` is not of type `str`.

        ValueError:
            If length of `column` is shorter than number of rows.
        """
        padding_left = self.default_padding if padding_left is None else padding_left
        padding_right = self.default_padding if padding_right is None else padding_right
        alignment = self.default_alignment if alignment is None else alignment
        if not isinstance(padding_left, int):
            raise TypeError(
                f"'padding_left' should be of type 'int' not '{type(padding_left).__name__}'"
            )

        if not isinstance(padding_right, int):
            raise TypeError(
                f"'padding_right' should be of type 'int' not '{type(padding_right).__name__}'"
            )

        if not isinstance(alignment, enums.Alignment):
            raise TypeError(
                f"alignment should be of type '{enums.Alignment.__name__}' not '{type(alignment).__name__}'"
            )

        if self._table._ncol == 0:
            self.header = [header]
            self.padding_left = [padding_left]
            self.padding_right = [padding_right]
            self.alignment = [alignment]
            self._table._data = type(self._table._data)(
                self._table, [BTRowData(self._table, [i]) for i in column]
            )
        else:
            if (not isinstance(header, basestring)) and (header is not None):
                raise TypeError(
                    f"header must be of type 'str' not '{type(header).__name__}'"
                )
            column_length = 0
            for row, new_item in zip(self._table.rows, column):
                row._insert(index, new_item)
                column_length += 1
            if column_length == len(self._table.rows):
                self._table._ncol += 1
                self.header._insert(index, header)
                self.width._insert(index, 0)
                self.alignment._insert(index, alignment)
                self.padding_left._insert(index, padding_left)
                self.padding_right._insert(index, padding_right)
                if self.header.alignment is not None:
                    self.header.alignment._insert(index, alignment)
            else:
                # Roll back changes so that table remains in consistent state
                for j in range(column_length, -1, -1):
                    self._table.rows[j]._pop(index)
                raise ValueError(
                    f"length of 'column' should be atleast {len(self._table.rows)}, got {column_length}"
                )

    def append(
        self,
        column,
        header=None,
        padding_left=None,
        padding_right=None,
        alignment=None,
    ):
        """Append a column to end of the table.

        Parameters
        ----------
        column : iterable
            Any iterable of appropriate length.

        header : str, optional
            Heading of the column

        padding_left : int, optional
            Left padding of the column

        padding_right : int,  optional
            Right padding of the column

        alignment : Alignment, optional
            alignment of the column
        """
        self.insert(
            self._table._ncol,
            column,
            header,
            padding_left,
            padding_right,
            alignment,
        )


================================================
FILE: beautifultable/meta.py
================================================
from .base import BTBaseRow
from .enums import Alignment


class MetaData(BTBaseRow):
    def __init__(self, table, row):
        for i in row:
            self.validate(i)
        super(MetaData, self).__init__(table, row)

    def __setitem__(self, key, value):
        self.validate(value)
        super(MetaData, self).__setitem__(key, value)

    def validate(self, value):
        pass


class AlignmentMetaData(MetaData):
    def validate(self, value):
        if not isinstance(value, Alignment):
            allowed = (f"{type(self).__name__}.{i.name}" for i in Alignment)
            error_msg = (
                "allowed values for alignment are: "
                + ", ".join(allowed)
                + f", was {value}"
            )

            raise TypeError(error_msg)


class NonNegativeIntegerMetaData(MetaData):
    def validate(self, value):
        if isinstance(value, int) and value >= 0:
            pass
        else:
            raise TypeError(
                ("Value must a non-negative integer, " "was {}").format(value)
            )


================================================
FILE: beautifultable/styles.py
================================================
# -*- coding: utf-8 -*-
from __future__ import unicode_literals


class NoStyle:
    left_border_char = ""
    right_border_char = ""
    top_border_char = ""
    bottom_border_char = ""
    header_separator_char = ""
    column_separator_char = ""
    row_separator_char = ""
    intersect_top_left = ""
    intersect_top_mid = ""
    intersect_top_right = ""
    intersect_header_left = ""
    intersect_header_mid = ""
    intersect_header_right = ""
    intersect_row_left = ""
    intersect_row_mid = ""
    intersect_row_right = ""
    intersect_bottom_left = ""
    intersect_bottom_mid = ""
    intersect_bottom_right = ""


class DefaultStyle(NoStyle):
    left_border_char = "|"
    right_border_char = "|"
    top_border_char = "-"
    bottom_border_char = "-"
    header_separator_char = "-"
    column_separator_char = "|"
    row_separator_char = "-"
    intersect_top_left = "+"
    intersect_top_mid = "+"
    intersect_top_right = "+"
    intersect_header_left = "+"
    intersect_header_mid = "+"
    intersect_header_right = "+"
    intersect_row_left = "+"
    intersect_row_mid = "+"
    intersect_row_right = "+"
    intersect_bottom_left = "+"
    intersect_bottom_mid = "+"
    intersect_bottom_right = "+"


class MySQLStyle(DefaultStyle):
    pass


class SeparatedStyle(DefaultStyle):
    top_border_char = "="
    header_separator_char = "="


class CompactStyle(NoStyle):
    header_separator_char = "-"
    column_separator_char = " "
    intersect_top_left = " "
    intersect_top_mid = " "
    intersect_top_right = " "
    intersect_header_left = " "
    intersect_header_mid = " "
    intersect_header_right = " "
    intersect_row_left = " "
    intersect_row_mid = " "
    intersect_row_right = " "
    intersect_bottom_left = " "
    intersect_bottom_mid = " "
    intersect_bottom_right = " "


class DottedStyle(NoStyle):
    left_border_char = ":"
    right_border_char = ":"
    top_border_char = "."
    bottom_border_char = "."
    header_separator_char = "."
    column_separator_char = ":"


class MarkdownStyle(NoStyle):
    left_border_char = "|"
    right_border_char = "|"
    header_separator_char = "-"
    column_separator_char = "|"
    intersect_header_left = "|"
    intersect_header_mid = "|"
    intersect_header_right = "|"


class RestructuredTextStyle(CompactStyle):
    top_border_char = "="
    bottom_border_char = "="
    header_separator_char = "="


class BoxStyle(NoStyle):
    left_border_char = "│"
    right_border_char = "│"
    top_border_char = "─"
    bottom_border_char = "─"
    header_separator_char = "─"
    column_separator_char = "│"
    row_separator_char = "─"
    intersect_top_left = "┌"
    intersect_top_mid = "┬"
    intersect_top_right = "┐"
    intersect_header_left = "├"
    intersect_header_mid = "┼"
    intersect_header_right = "┤"
    intersect_row_left = "├"
    intersect_row_mid = "┼"
    intersect_row_right = "┤"
    intersect_bottom_left = "└"
    intersect_bottom_mid = "┴"
    intersect_bottom_right = "┘"


class DoubledBoxStyle(NoStyle):
    left_border_char = "║"
    right_border_char = "║"
    top_border_char = "═"
    bottom_border_char = "═"
    header_separator_char = "═"
    column_separator_char = "║"
    row_separator_char = "═"
    intersect_top_left = "╔"
    intersect_top_mid = "╦"
    intersect_top_right = "╗"
    intersect_header_left = "╠"
    intersect_header_mid = "╬"
    intersect_header_right = "╣"
    intersect_row_left = "╠"
    intersect_row_mid = "╬"
    intersect_row_right = "╣"
    intersect_bottom_left = "╚"
    intersect_bottom_mid = "╩"
    intersect_bottom_right = "╝"


class RoundedStyle(BoxStyle):
    intersect_top_left = "╭"
    intersect_top_right = "╮"
    intersect_bottom_left = "╰"
    intersect_bottom_right = "╯"


class GridStyle(BoxStyle):
    left_border_char = "║"
    right_border_char = "║"
    top_border_char = "═"
    bottom_border_char = "═"
    intersect_top_left = "╔"
    intersect_top_mid = "╤"
    intersect_top_right = "╗"
    intersect_header_left = "╟"
    intersect_header_right = "╢"
    intersect_row_left = "╟"
    intersect_row_right = "╢"
    intersect_bottom_left = "╚"
    intersect_bottom_mid = "╧"
    intersect_bottom_right = "╝"


================================================
FILE: beautifultable/utils.py
================================================
"""Module containing some utility methods"""

import warnings
import functools

from .ansi import ANSIMultiByteString
from .compat import to_unicode


def to_numeric(item):
    """
    Helper method to convert a string to float or int if possible.

    If the conversion is not possible, it simply returns the string.
    """
    num_types = (int, float)
    # We don't wan't to perform any conversions if item is already a number
    if isinstance(item, num_types):
        return item

    # First try for an int conversion so that strings like "5" are converted
    # to 5 instead of 5.0 . This is safe as a direct int cast for a non integer
    # string raises a ValueError.
    try:
        num = int(to_unicode(item))
    except ValueError:
        try:
            num = float(to_unicode(item))
        except ValueError:
            return item
        else:
            return num
    except TypeError:
        return item
    else:
        return num


def ensure_type(value, *types, varname="value"):
    if not isinstance(value, types):
        expected_types_str = "/".join([t.__name__ for t in types])
        raise TypeError(
            ("Expected '{}' to be of type '{}', " "got '{}'").format(
                varname, expected_types_str, type(value).__name__
            )
        )
    return value


def pre_process(item, detect_numerics, precision, sign_value):
    """Returns the final string which should be displayed"""
    if item is None:
        return ""
    if detect_numerics:
        item = to_numeric(item)
    if isinstance(item, float):
        item = round(item, precision)
    try:
        item = "{:{sign}}".format(item, sign=sign_value)
    except (ValueError, TypeError):
        pass
    return to_unicode(item)


def termwidth(item):
    """Returns the visible width of the string as shown on the terminal"""
    obj = ANSIMultiByteString(to_unicode(item))
    return obj.termwidth()


def textwrap(item, width):
    obj = ANSIMultiByteString(to_unicode(item))
    return obj.wrap(width)


def deprecation_message(
    old_name, deprecated_in, removed_in, extra_msg
):  # pragma: no cover
    return f"'{old_name}' has been deprecated in 'v{deprecated_in}' and will be removed in 'v{removed_in}'. {extra_msg}"


def deprecated(
    deprecated_in,
    removed_in,
    replacement=None,
    details=None,
):  # pragma: no cover
    def decorator(f):
        @functools.wraps(f)
        def wrapper(*args, **kwds):
            nonlocal details
            if not details:
                if replacement:
                    details = replacement.__qualname__
                    details = details.replace(
                        "BTColumns",
                        "BeautifulTable.columns",
                    )
                    details = details.replace(
                        "BTRows",
                        "BeautifulTable.rows",
                    )
                    details = details.replace(
                        "BTColumnHeader",
                        "BeautifulTable.columns.header",
                    )
                    details = details.replace(
                        "BTRowHeader",
                        "BeautifulTable.rows.header",
                    )
                    details = f"Use '{details}' instead."
                else:
                    details = ""
            message = deprecation_message(
                f.__qualname__,
                deprecated_in,
                removed_in,
                details,
            )
            if replacement:
                f.__doc__ = "{}\n\n{}".format(replacement.__doc__, message)
            warnings.warn(message, FutureWarning)
            return f(*args, **kwds)

        return wrapper

    return decorator


def deprecated_param(
    deprecated_in,
    removed_in,
    old_name,
    new_name=None,
    details=None,
):  # pragma: no cover
    def decorator(f):
        @functools.wraps(f)
        def wrapper(*args, **kwargs):
            nonlocal details
            if not details:
                details = f"Use '{new_name}' instead." if new_name else ""
            message = deprecation_message(
                old_name,
                deprecated_in,
                removed_in,
                details,
            )
            if old_name in kwargs:
                warnings.warn(message, FutureWarning)
            return f(*args, **kwargs)

        return wrapper

    return decorator


================================================
FILE: coverage.xml
================================================
<?xml version="1.0" ?>
<coverage branch-rate="0.7541" branches-covered="368" branches-valid="488" complexity="0" line-rate="0.883" lines-covered="1177" lines-valid="1333" timestamp="1640513662355" version="6.2">
	<!-- Generated by coverage.py: https://coverage.readthedocs.io -->
	<!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd -->
	<sources>
		<source>D:\Workspace\beautifultable\beautifultable</source>
	</sources>
	<packages>
		<package branch-rate="0.7541" complexity="0" line-rate="0.883" name=".">
			<classes>
				<class branch-rate="1" complexity="0" filename="__init__.py" line-rate="1" name="__init__.py">
					<methods/>
					<lines>
						<line hits="1" number="1"/>
						<line hits="1" number="2"/>
						<line hits="1" number="3"/>
						<line hits="1" number="5"/>
						<line hits="1" number="14"/>
						<line hits="1" number="15"/>
						<line hits="1" number="18"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="32"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="33"/>
						<line hits="1" number="39"/>
						<line hits="1" number="40"/>
					</lines>
				</class>
				<class branch-rate="1" complexity="0" filename="__version__.py" line-rate="1" name="__version__.py">
					<methods/>
					<lines>
						<line hits="1" number="1"/>
						<line hits="1" number="2"/>
						<line hits="1" number="3"/>
						<line hits="1" number="4"/>
						<line hits="1" number="5"/>
						<line hits="1" number="6"/>
						<line hits="1" number="7"/>
						<line hits="1" number="8"/>
					</lines>
				</class>
				<class branch-rate="0.6579" complexity="0" filename="ansi.py" line-rate="0.7284" name="ansi.py">
					<methods/>
					<lines>
						<line hits="1" number="3"/>
						<line hits="1" number="6"/>
						<line hits="1" number="8"/>
						<line hits="1" number="9"/>
						<line hits="1" number="13"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="16"/>
						<line hits="1" number="18"/>
						<line hits="1" number="19"/>
						<line hits="1" number="21"/>
						<line hits="1" number="22"/>
						<line hits="1" number="23"/>
						<line hits="1" number="24"/>
						<line hits="1" number="25"/>
						<line hits="1" number="27"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="29"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="30"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="31"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="32"/>
						<line hits="1" number="33"/>
						<line hits="1" number="35"/>
						<line hits="1" number="37"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="38"/>
						<line hits="1" number="39"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="41" number="40"/>
						<line hits="0" number="41"/>
						<line hits="1" number="46"/>
						<line hits="1" number="47"/>
						<line hits="1" number="48"/>
						<line hits="1" number="49"/>
						<line hits="1" number="51"/>
						<line hits="0" number="52"/>
						<line hits="1" number="54"/>
						<line branch="true" condition-coverage="0% (0/2)" hits="0" missing-branches="56,59" number="55"/>
						<line branch="true" condition-coverage="0% (0/2)" hits="0" missing-branches="57,58" number="56"/>
						<line hits="0" number="57"/>
						<line hits="0" number="58"/>
						<line branch="true" condition-coverage="0% (0/2)" hits="0" missing-branches="60,61" number="59"/>
						<line hits="0" number="60"/>
						<line hits="0" number="61"/>
						<line hits="1" number="67"/>
						<line hits="0" number="68"/>
						<line hits="0" number="69"/>
						<line branch="true" condition-coverage="0% (0/2)" hits="0" missing-branches="71,80" number="70"/>
						<line hits="0" number="71"/>
						<line branch="true" condition-coverage="0% (0/2)" hits="0" missing-branches="74,76" number="73"/>
						<line hits="0" number="74"/>
						<line hits="0" number="76"/>
						<line hits="0" number="77"/>
						<line hits="0" number="78"/>
						<line hits="0" number="79"/>
						<line branch="true" condition-coverage="0% (0/2)" hits="0" missing-branches="81,82" number="80"/>
						<line hits="0" number="81"/>
						<line hits="0" number="82"/>
						<line hits="1" number="84"/>
						<line hits="1" number="86"/>
						<line hits="1" number="88"/>
						<line hits="1" number="90"/>
						<line hits="1" number="91"/>
						<line hits="1" number="92"/>
						<line hits="1" number="93"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="94"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="95"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="96"/>
						<line hits="1" number="97"/>
						<line hits="1" number="98"/>
						<line hits="1" number="99"/>
						<line hits="1" number="100"/>
						<line hits="1" number="101"/>
						<line hits="1" number="102"/>
						<line hits="1" number="103"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="105"/>
						<line hits="1" number="106"/>
						<line hits="1" number="108"/>
						<line hits="1" number="109"/>
						<line hits="1" number="110"/>
						<line hits="1" number="111"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="112"/>
						<line hits="1" number="113"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="114"/>
						<line hits="1" number="115"/>
						<line hits="1" number="116"/>
					</lines>
				</class>
				<class branch-rate="0.8" complexity="0" filename="base.py" line-rate="0.8659" name="base.py">
					<methods/>
					<lines>
						<line hits="1" number="1"/>
						<line hits="1" number="2"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="5"/>
						<line hits="1" number="6"/>
						<line hits="1" number="7"/>
						<line hits="1" number="8"/>
						<line hits="1" number="10"/>
						<line hits="1" number="12"/>
						<line hits="1" number="14"/>
						<line hits="1" number="16"/>
						<line hits="1" number="18"/>
						<line hits="1" number="20"/>
						<line hits="1" number="22"/>
						<line hits="1" number="23"/>
						<line hits="1" number="25"/>
						<line hits="1" number="26"/>
						<line hits="1" number="28"/>
						<line hits="0" number="29"/>
						<line hits="1" number="31"/>
						<line hits="0" number="32"/>
						<line branch="true" condition-coverage="0% (0/2)" hits="0" missing-branches="exit,34" number="33"/>
						<line hits="0" number="34"/>
						<line hits="1" number="36"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="38" number="37"/>
						<line hits="0" number="38"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="39"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="40"/>
						<line hits="1" number="41"/>
						<line hits="1" number="42"/>
						<line hits="1" number="44"/>
						<line hits="1" number="46"/>
						<line hits="1" number="48"/>
						<line hits="0" number="49"/>
						<line hits="1" number="51"/>
						<line hits="1" number="52"/>
						<line hits="1" number="54"/>
						<line hits="1" number="55"/>
						<line hits="1" number="57"/>
						<line hits="0" number="58"/>
						<line hits="1" number="60"/>
						<line hits="0" number="61"/>
						<line hits="1" number="63"/>
						<line hits="1" number="64"/>
						<line hits="1" number="66"/>
						<line hits="0" number="67"/>
						<line hits="1" number="69"/>
						<line hits="0" number="70"/>
						<line hits="1" number="72"/>
						<line hits="1" number="74"/>
						<line hits="1" number="75"/>
						<line hits="1" number="76"/>
						<line hits="1" number="77"/>
						<line hits="1" number="79"/>
						<line hits="1" number="81"/>
						<line hits="1" number="83"/>
						<line hits="1" number="85"/>
						<line hits="1" number="87"/>
						<line hits="1" number="88"/>
						<line hits="1" number="90"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="92" number="91"/>
						<line hits="0" number="92"/>
						<line hits="1" number="97"/>
						<line hits="1" number="99"/>
						<line hits="1" number="103"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="108"/>
						<line hits="1" number="109"/>
						<line hits="1" number="110"/>
						<line hits="1" number="112"/>
						<line hits="1" number="113"/>
						<line hits="1" number="115"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="116"/>
						<line hits="1" number="117"/>
						<line hits="1" number="118"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="121"/>
						<line hits="1" number="122"/>
						<line hits="1" number="123"/>
						<line hits="1" number="125"/>
						<line hits="1" number="126"/>
						<line hits="1" number="128"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="129"/>
						<line hits="1" number="130"/>
						<line hits="1" number="131"/>
					</lines>
				</class>
				<class branch-rate="0.75" complexity="0" filename="beautifultable.py" line-rate="0.8944" name="beautifultable.py">
					<methods/>
					<lines>
						<line hits="1" number="28"/>
						<line hits="1" number="30"/>
						<line hits="1" number="31"/>
						<line hits="1" number="32"/>
						<line hits="1" number="34"/>
						<line hits="1" number="36"/>
						<line hits="1" number="44"/>
						<line hits="1" number="45"/>
						<line hits="1" number="46"/>
						<line hits="1" number="54"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="64"/>
						<line hits="1" number="116"/>
						<line hits="1" number="133"/>
						<line hits="1" number="134"/>
						<line hits="1" number="135"/>
						<line hits="1" number="136"/>
						<line hits="1" number="138"/>
						<line hits="1" number="139"/>
						<line hits="1" number="140"/>
						<line hits="1" number="141"/>
						<line hits="1" number="143"/>
						<line hits="1" number="144"/>
						<line hits="1" number="146"/>
						<line hits="1" number="147"/>
						<line hits="1" number="148"/>
						<line hits="1" number="149"/>
						<line hits="1" number="152"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="153"/>
						<line hits="1" number="156"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="157"/>
						<line branch="true" condition-coverage="100% (3/3)" hits="1" number="160"/>
						<line hits="1" number="179"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="182"/>
						<line hits="1" number="183"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="184"/>
						<line hits="1" number="185"/>
						<line hits="1" number="186"/>
						<line hits="1" number="187"/>
						<line hits="1" number="189"/>
						<line hits="1" number="190"/>
						<line hits="1" number="192"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="196"/>
						<line hits="1" number="240"/>
						<line hits="1" number="241"/>
						<line hits="1" number="242"/>
						<line hits="1" number="243"/>
						<line hits="1" number="244"/>
						<line hits="1" number="245"/>
						<line hits="1" number="258"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="260" number="259"/>
						<line hits="0" number="260"/>
						<line hits="1" number="262"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="264" number="263"/>
						<line hits="0" number="264"/>
						<line hits="1" number="266"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="268" number="267"/>
						<line hits="0" number="268"/>
						<line hits="1" number="270"/>
						<line hits="1" number="271"/>
						<line hits="1" number="272"/>
						<line hits="1" number="273"/>
						<line hits="1" number="275"/>
						<line hits="1" number="276"/>
						<line hits="1" number="278"/>
						<line hits="1" number="279"/>
						<line hits="1" number="281"/>
						<line hits="1" number="282"/>
						<line hits="1" number="284"/>
						<line hits="1" number="285"/>
						<line hits="1" number="286"/>
						<line hits="1" number="287"/>
						<line hits="1" number="288"/>
						<line hits="1" number="289"/>
						<line hits="1" number="291"/>
						<line hits="0" number="292"/>
						<line branch="true" condition-coverage="0% (0/2)" hits="0" missing-branches="exit,295" number="293"/>
						<line hits="0" number="295"/>
						<line hits="0" number="296"/>
						<line hits="0" number="298"/>
						<line hits="0" number="299"/>
						<line hits="0" number="300"/>
						<line hits="0" number="301"/>
						<line hits="0" number="302"/>
						<line hits="0" number="303"/>
						<line hits="0" number="305"/>
						<line branch="true" condition-coverage="0% (0/2)" hits="0" missing-branches="307,309" number="306"/>
						<line hits="0" number="307"/>
						<line hits="0" number="309"/>
						<line hits="1" number="311"/>
						<line hits="1" number="312"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="313"/>
						<line hits="1" number="317"/>
						<line hits="1" number="318"/>
						<line hits="1" number="320"/>
						<line hits="1" number="321"/>
						<line hits="1" number="322"/>
						<line hits="1" number="323"/>
						<line hits="1" number="324"/>
						<line hits="1" number="325"/>
						<line hits="1" number="327"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="328"/>
						<line hits="1" number="329"/>
						<line hits="1" number="331"/>
						<line hits="1" number="333"/>
						<line hits="1" number="334"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="356" number="355"/>
						<line hits="0" number="356"/>
						<line hits="0" number="360"/>
						<line hits="1" number="361"/>
						<line hits="1" number="363"/>
						<line hits="1" number="372"/>
						<line hits="1" number="380"/>
						<line hits="1" number="396"/>
						<line hits="0" number="397"/>
						<line hits="1" number="399"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="400"/>
						<line hits="1" number="401"/>
						<line hits="1" number="403"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="404"/>
						<line hits="1" number="405"/>
						<line hits="1" number="407"/>
						<line hits="1" number="410"/>
						<line hits="0" number="413"/>
						<line hits="1" number="415"/>
						<line hits="1" number="433"/>
						<line hits="1" number="435"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="438" number="437"/>
						<line branch="true" condition-coverage="0% (0/2)" hits="0" missing-branches="exit,441" number="438"/>
						<line hits="0" number="441"/>
						<line hits="0" number="442"/>
						<line hits="1" number="443"/>
						<line hits="1" number="445"/>
						<line hits="1" number="453"/>
						<line hits="1" number="455"/>
						<line hits="1" number="457"/>
						<line hits="1" number="474"/>
						<line hits="1" number="477"/>
						<line hits="1" number="479"/>
						<line hits="1" number="481"/>
						<line hits="1" number="483"/>
						<line hits="1" number="484"/>
						<line hits="1" number="488"/>
						<line hits="1" number="489"/>
						<line hits="1" number="493"/>
						<line hits="1" number="494"/>
						<line hits="1" number="498"/>
						<line hits="1" number="499"/>
						<line hits="1" number="503"/>
						<line hits="1" number="504"/>
						<line hits="1" number="508"/>
						<line hits="1" number="509"/>
						<line hits="1" number="513"/>
						<line hits="0" number="521"/>
						<line hits="0" number="522"/>
						<line hits="0" number="523"/>
						<line hits="0" number="524"/>
						<line hits="0" number="525"/>
						<line hits="1" number="527"/>
						<line hits="1" number="529"/>
						<line hits="1" number="531"/>
						<line hits="1" number="532"/>
						<line hits="1" number="536"/>
						<line hits="1" number="537"/>
						<line hits="1" number="541"/>
						<line hits="1" number="542"/>
						<line hits="1" number="551"/>
						<line hits="1" number="552"/>
						<line hits="1" number="556"/>
						<line hits="1" number="557"/>
						<line hits="1" number="561"/>
						<line hits="1" number="562"/>
						<line hits="1" number="566"/>
						<line hits="1" number="567"/>
						<line hits="1" number="571"/>
						<line hits="1" number="572"/>
						<line hits="1" number="576"/>
						<line hits="1" number="577"/>
						<line hits="1" number="581"/>
						<line hits="1" number="582"/>
						<line hits="1" number="586"/>
						<line hits="1" number="587"/>
						<line hits="1" number="591"/>
						<line hits="1" number="592"/>
						<line hits="1" number="596"/>
						<line hits="1" number="597"/>
						<line hits="1" number="601"/>
						<line hits="1" number="602"/>
						<line hits="1" number="606"/>
						<line hits="1" number="607"/>
						<line hits="1" number="611"/>
						<line hits="1" number="612"/>
						<line hits="1" number="616"/>
						<line hits="1" number="617"/>
						<line hits="1" number="621"/>
						<line hits="1" number="622"/>
						<line hits="1" number="626"/>
						<line hits="1" number="627"/>
						<line hits="1" number="631"/>
						<line hits="1" number="642"/>
						<line hits="1" number="654"/>
						<line hits="1" number="668"/>
						<line hits="1" number="677"/>
						<line hits="1" number="686"/>
						<line hits="1" number="695"/>
						<line hits="1" number="699"/>
						<line hits="1" number="703"/>
						<line hits="1" number="707"/>
						<line hits="1" number="711"/>
						<line hits="1" number="715"/>
						<line hits="1" number="719"/>
						<line hits="1" number="723"/>
						<line hits="1" number="727"/>
						<line hits="1" number="731"/>
						<line hits="1" number="735"/>
						<line hits="1" number="739"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="762" number="761"/>
						<line branch="true" condition-coverage="0% (0/2)" hits="0" missing-branches="exit,763" number="762"/>
						<line hits="0" number="763"/>
						<line hits="0" number="764"/>
						<line hits="1" number="765"/>
						<line hits="1" number="766"/>
						<line hits="1" number="767"/>
						<line hits="1" number="768"/>
						<line hits="1" number="769"/>
						<line hits="1" number="771"/>
						<line hits="1" number="772"/>
						<line hits="1" number="773"/>
						<line hits="1" number="774"/>
						<line hits="1" number="776"/>
						<line hits="1" number="777"/>
						<line hits="1" number="779"/>
						<line hits="1" number="780"/>
						<line hits="1" number="781"/>
						<line hits="1" number="783"/>
						<line hits="1" number="784"/>
						<line hits="1" number="785"/>
						<line hits="1" number="786"/>
						<line hits="1" number="787"/>
						<line hits="1" number="788"/>
						<line hits="1" number="790"/>
						<line hits="1" number="792"/>
						<line hits="1" number="793"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="794"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="795"/>
						<line hits="1" number="796"/>
						<line hits="1" number="797"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="799"/>
						<line hits="1" number="800"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="801"/>
						<line hits="1" number="804"/>
						<line hits="1" number="810"/>
						<line hits="1" number="811"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="813"/>
						<line hits="1" number="814"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="815"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="816"/>
						<line hits="1" number="819"/>
						<line hits="1" number="825"/>
						<line hits="1" number="826"/>
						<line hits="1" number="828"/>
						<line hits="1" number="829"/>
						<line hits="1" number="832"/>
						<line hits="1" number="833"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="834"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="835"/>
						<line hits="1" number="836"/>
						<line hits="1" number="837"/>
						<line hits="1" number="840"/>
						<line hits="1" number="842"/>
						<line hits="1" number="843"/>
						<line hits="1" number="844"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="848"/>
						<line hits="1" number="849"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="850"/>
						<line hits="1" number="851"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="852"/>
						<line hits="1" number="853"/>
						<line hits="1" number="854"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="857"/>
						<line hits="1" number="858"/>
						<line hits="1" number="859"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="861"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="862"/>
						<line hits="1" number="863"/>
						<line hits="1" number="864"/>
						<line hits="1" number="865"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="866"/>
						<line hits="1" number="867"/>
						<line hits="1" number="868"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="870"/>
						<line hits="1" number="871"/>
						<line hits="1" number="873"/>
						<line hits="1" number="878"/>
						<line hits="0" number="880"/>
						<line hits="1" number="882"/>
						<line hits="1" number="903"/>
						<line hits="1" number="923"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="928" number="925"/>
						<line hits="1" number="926"/>
						<line hits="1" number="928"/>
						<line hits="1" number="929"/>
						<line hits="0" number="930"/>
						<line hits="0" number="931"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="934" number="933"/>
						<line hits="0" number="934"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="980" number="938"/>
						<line hits="1" number="940"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="949" number="941"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="949" number="942"/>
						<line hits="1" number="943"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="947"/>
						<line hits="1" number="948"/>
						<line hits="1" number="949"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="959" number="951"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="959" number="952"/>
						<line hits="1" number="953"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="957"/>
						<line hits="1" number="958"/>
						<line hits="1" number="959"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="980" number="961"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="980" number="962"/>
						<line hits="1" number="963"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="964"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="966" number="965"/>
						<line branch="true" condition-coverage="0% (0/2)" hits="0" missing-branches="967,968" number="966"/>
						<line hits="0" number="967"/>
						<line hits="1" number="968"/>
						<line hits="1" number="969"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="973"/>
						<line hits="1" number="975"/>
						<line hits="1" number="978"/>
						<line hits="1" number="980"/>
						<line hits="1" number="982"/>
						<line hits="1" number="983"/>
						<line hits="1" number="992"/>
						<line hits="1" number="993"/>
						<line hits="1" number="1002"/>
						<line hits="1" number="1003"/>
						<line hits="1" number="1012"/>
						<line hits="1" number="1013"/>
						<line hits="1" number="1022"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="1033"/>
						<line hits="1" number="1034"/>
						<line hits="1" number="1035"/>
						<line hits="1" number="1036"/>
						<line hits="1" number="1037"/>
						<line hits="1" number="1038"/>
						<line hits="1" number="1039"/>
						<line hits="1" number="1041"/>
						<line hits="1" number="1045"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="1046"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="1050"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="1061" number="1055"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="1057" number="1056"/>
						<line hits="0" number="1057"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="1061"/>
						<line hits="1" number="1062"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="1064"/>
						<line hits="1" number="1065"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="1072" number="1067"/>
						<line hits="1" number="1070"/>
						<line hits="1" number="1072"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="1078" number="1074"/>
						<line hits="1" number="1075"/>
						<line hits="1" number="1078"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="1079"/>
						<line hits="1" number="1080"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="1087" number="1083"/>
						<line hits="1" number="1084"/>
						<line hits="1" number="1087"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="1088"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="1089"/>
						<line hits="1" number="1090"/>
						<line hits="1" number="1091"/>
						<line hits="1" number="1092"/>
						<line hits="1" number="1093"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="1114" number="1095"/>
						<line hits="1" number="1097"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="1098"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="1099"/>
						<line hits="1" number="1100"/>
						<line hits="1" number="1101"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="1103" number="1102"/>
						<line hits="0" number="1103"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="1105" number="1104"/>
						<line hits="0" number="1105"/>
						<line hits="1" number="1107"/>
						<line hits="1" number="1108"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="1111" number="1109"/>
						<line hits="1" number="1110"/>
						<line hits="1" number="1111"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="1120" number="1114"/>
						<line hits="1" number="1115"/>
						<line hits="0" number="1116"/>
						<line hits="0" number="1117"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="1120"/>
						<line hits="1" number="1121"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="1123"/>
						<line hits="1" number="1124"/>
						<line branch="true" condition-coverage="33% (1/3)" hits="1" missing-branches="exit,1129" number="1126"/>
						<line branch="true" condition-coverage="33% (1/3)" hits="1" missing-branches="exit,1128" number="1127"/>
						<line branch="true" condition-coverage="0% (0/2)" hits="0" missing-branches="exit,1129" number="1128"/>
						<line hits="1" number="1129"/>
						<line hits="1" number="1131"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="1152"/>
						<line hits="1" number="1153"/>
						<line hits="1" number="1155"/>
						<line hits="0" number="1157"/>
						<line hits="1" number="1159"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="1168"/>
						<line hits="1" number="1169"/>
						<line hits="1" number="1175"/>
						<line hits="1" number="1176"/>
						<line branch="true" condition-coverage="66% (2/3)" hits="1" missing-branches="1181" number="1177"/>
						<line hits="1" number="1180"/>
						<line hits="1" number="1181"/>
						<line hits="1" number="1183"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="1201"/>
						<line hits="1" number="1202"/>
						<line hits="1" number="1208"/>
						<line hits="1" number="1209"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="1211"/>
						<line hits="1" number="1212"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="1213"/>
						<line hits="1" number="1214"/>
						<line hits="1" number="1215"/>
					</lines>
				</class>
				<class branch-rate="1" complexity="0" filename="compat.py" line-rate="1" name="compat.py">
					<methods/>
					<lines>
						<line hits="1" number="1"/>
						<line hits="1" number="2"/>
						<line hits="1" number="4"/>
						<line hits="1" number="5"/>
					</lines>
				</class>
				<class branch-rate="1" complexity="0" filename="enums.py" line-rate="0.9298" name="enums.py">
					<methods/>
					<lines>
						<line hits="1" number="1"/>
						<line hits="1" number="2"/>
						<line hits="1" number="4"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="20"/>
						<line hits="1" number="21"/>
						<line hits="1" number="22"/>
						<line hits="1" number="23"/>
						<line hits="1" number="25"/>
						<line hits="0" number="26"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="29"/>
						<line hits="1" number="30"/>
						<line hits="1" number="31"/>
						<line hits="1" number="32"/>
						<line hits="1" number="34"/>
						<line hits="0" number="35"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="38"/>
						<line hits="1" number="39"/>
						<line hits="1" number="40"/>
						<line hits="1" number="41"/>
						<line hits="1" number="43"/>
						<line hits="0" number="44"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="47"/>
						<line hits="1" number="48"/>
						<line hits="1" number="49"/>
						<line hits="1" number="50"/>
						<line hits="1" number="51"/>
						<line hits="1" number="52"/>
						<line hits="1" number="53"/>
						<line hits="1" number="54"/>
						<line hits="1" number="55"/>
						<line hits="1" number="56"/>
						<line hits="1" number="57"/>
						<line hits="1" number="58"/>
						<line hits="1" number="59"/>
						<line hits="1" number="61"/>
						<line hits="0" number="62"/>
						<line hits="1" number="65"/>
						<line hits="1" number="66"/>
						<line hits="1" number="67"/>
						<line hits="1" number="68"/>
						<line hits="1" number="69"/>
						<line hits="1" number="70"/>
						<line hits="1" number="71"/>
						<line hits="1" number="72"/>
						<line hits="1" number="73"/>
						<line hits="1" number="74"/>
						<line hits="1" number="75"/>
						<line hits="1" number="76"/>
						<line hits="1" number="77"/>
						<line hits="1" number="78"/>
						<line hits="1" number="79"/>
						<line hits="1" number="80"/>
						<line hits="1" number="81"/>
						<line hits="1" number="82"/>
						<line hits="1" number="83"/>
						<line hits="1" number="84"/>
						<line hits="1" number="85"/>
					</lines>
				</class>
				<class branch-rate="0.7424" complexity="0" filename="helpers.py" line-rate="0.8618" name="helpers.py">
					<methods/>
					<lines>
						<line hits="1" number="1"/>
						<line hits="1" number="2"/>
						<line hits="1" number="3"/>
						<line hits="1" number="5"/>
						<line hits="1" number="6"/>
						<line hits="1" number="7"/>
						<line hits="1" number="8"/>
						<line hits="1" number="9"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="12"/>
						<line hits="1" number="13"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="14"/>
						<line hits="1" number="15"/>
						<line hits="1" number="16"/>
						<line hits="1" number="18"/>
						<line hits="0" number="19"/>
						<line hits="0" number="20"/>
						<line hits="1" number="22"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="24" number="23"/>
						<line hits="0" number="24"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="29"/>
						<line hits="1" number="30"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="31"/>
						<line hits="1" number="32"/>
						<line hits="1" number="33"/>
						<line hits="1" number="34"/>
						<line hits="1" number="36"/>
						<line hits="1" number="46"/>
						<line hits="1" number="48"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="53" number="50"/>
						<line hits="1" number="51"/>
						<line hits="1" number="52"/>
						<line branch="true" condition-coverage="0% (0/2)" hits="0" missing-branches="54,55" number="53"/>
						<line hits="0" number="54"/>
						<line hits="0" number="55"/>
						<line hits="1" number="57"/>
						<line hits="1" number="60"/>
						<line hits="1" number="62"/>
						<line hits="1" number="64"/>
						<line hits="1" number="66"/>
						<line hits="1" number="69"/>
						<line hits="1" number="71"/>
						<line hits="1" number="73"/>
						<line hits="1" number="75"/>
						<line hits="0" number="76"/>
						<line hits="0" number="77"/>
						<line hits="1" number="79"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="81" number="80"/>
						<line hits="0" number="81"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="86"/>
						<line hits="1" number="87"/>
						<line hits="1" number="88"/>
						<line hits="1" number="93"/>
						<line hits="1" number="107"/>
						<line hits="1" number="108"/>
						<line hits="1" number="109"/>
						<line hits="1" number="111"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="113"/>
						<line hits="1" number="119"/>
						<line hits="1" number="120"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="121"/>
						<line hits="1" number="122"/>
						<line hits="1" number="123"/>
						<line hits="1" number="124"/>
						<line hits="1" number="129"/>
						<line hits="1" number="130"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="148" number="131"/>
						<line hits="1" number="134"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="136"/>
						<line hits="1" number="137"/>
						<line hits="1" number="138"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="140"/>
						<line hits="1" number="141"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="142"/>
						<line hits="1" number="143"/>
						<line hits="1" number="144"/>
						<line hits="1" number="145"/>
						<line hits="1" number="146"/>
						<line hits="1" number="148"/>
						<line hits="1" number="150"/>
						<line hits="1" number="172"/>
						<line hits="1" number="173"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="175"/>
						<line hits="1" number="176"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="178"/>
						<line hits="1" number="179"/>
						<line hits="1" number="183"/>
						<line hits="1" number="184"/>
						<line hits="1" number="186"/>
						<line hits="1" number="195"/>
						<line hits="1" number="197"/>
						<line hits="1" number="198"/>
						<line hits="1" number="199"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="204" number="201"/>
						<line hits="1" number="202"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="207" number="204"/>
						<line hits="1" number="205"/>
						<line hits="1" number="207"/>
						<line hits="1" number="209"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="210"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="213" number="211"/>
						<line hits="0" number="213"/>
						<line hits="0" number="214"/>
						<line hits="0" number="215"/>
						<line hits="0" number="223"/>
						<line hits="1" number="225"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="233"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="234"/>
						<line hits="1" number="235"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="241"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="242"/>
						<line hits="1" number="246"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="247"/>
						<line hits="1" number="248"/>
						<line hits="1" number="249"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="250"/>
						<line hits="1" number="251"/>
						<line hits="1" number="252"/>
						<line hits="1" number="254"/>
						<line hits="1" number="255"/>
						<line hits="1" number="256"/>
						<line hits="1" number="257"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="258"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="259"/>
						<line hits="1" number="260"/>
						<line hits="1" number="265"/>
						<line hits="1" number="266"/>
						<line hits="1" number="267"/>
						<line hits="1" number="270"/>
						<line hits="1" number="275"/>
						<line hits="1" number="276"/>
						<line hits="1" number="278"/>
						<line hits="1" number="279"/>
						<line hits="1" number="281"/>
						<line hits="1" number="283"/>
						<line hits="1" number="285"/>
						<line hits="1" number="290"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="291"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="293" number="292"/>
						<line hits="0" number="293"/>
						<line hits="1" number="294"/>
						<line hits="1" number="295"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="298"/>
						<line hits="1" number="299"/>
						<line hits="1" number="301"/>
						<line hits="1" number="303"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="310"/>
						<line hits="1" number="311"/>
						<line hits="1" number="312"/>
						<line hits="1" number="313"/>
						<line hits="1" number="315"/>
						<line hits="1" number="317"/>
						<line hits="1" number="319"/>
						<line hits="1" number="321"/>
						<line hits="1" number="323"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="324"/>
						<line hits="1" number="328"/>
						<line hits="1" number="330"/>
						<line hits="1" number="332"/>
						<line hits="1" number="334"/>
						<line hits="1" number="336"/>
						<line hits="1" number="338"/>
						<line hits="1" number="341"/>
						<line hits="1" number="343"/>
						<line hits="1" number="345"/>
						<line hits="1" number="347"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="348"/>
						<line hits="1" number="349"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="352" number="350"/>
						<line hits="1" number="351"/>
						<line hits="0" number="352"/>
						<line hits="1" number="358"/>
						<line hits="1" number="359"/>
						<line hits="1" number="361"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="380"/>
						<line hits="1" number="381"/>
						<line hits="1" number="382"/>
						<line hits="1" number="383"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="384"/>
						<line hits="1" number="385"/>
						<line hits="1" number="386"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="389" number="387"/>
						<line hits="1" number="388"/>
						<line hits="0" number="389"/>
						<line hits="1" number="395"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="418" number="414"/>
						<line hits="1" number="415"/>
						<line hits="1" number="416"/>
						<line hits="0" number="418"/>
						<line hits="1" number="424"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="443"/>
						<line hits="1" number="444"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="451" number="445"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="446"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="448" number="447"/>
						<line hits="0" number="448"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="449"/>
						<line hits="0" number="451"/>
						<line hits="1" number="453"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="455" number="454"/>
						<line hits="0" number="455"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="459" number="456"/>
						<line hits="1" number="457"/>
						<line hits="0" number="459"/>
						<line hits="1" number="463"/>
						<line hits="1" number="464"/>
						<line hits="1" number="466"/>
						<line hits="0" number="467"/>
						<line hits="1" number="469"/>
						<line hits="0" number="470"/>
						<line hits="1" number="472"/>
						<line hits="0" number="474"/>
						<line hits="1" number="476"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="485" number="484"/>
						<line hits="0" number="485"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="489" number="488"/>
						<line hits="0" number="489"/>
						<line hits="1" number="491"/>
						<line hits="1" number="492"/>
						<line hits="1" number="493"/>
						<line hits="1" number="495"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="518"/>
						<line hits="1" number="519"/>
						<line hits="1" number="520"/>
						<line hits="1" number="521"/>
						<line hits="1" number="522"/>
						<line hits="1" number="524"/>
						<line hits="1" number="536"/>
						<line hits="1" number="538"/>
						<line hits="1" number="564"/>
						<line hits="1" number="566"/>
						<line hits="1" number="567"/>
						<line hits="1" number="569"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="579"/>
						<line hits="1" number="580"/>
						<line hits="1" number="581"/>
						<line hits="1" number="584"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="586"/>
						<line hits="1" number="591"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="592"/>
						<line hits="1" number="594"/>
						<line hits="1" number="603"/>
						<line hits="1" number="604"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="605"/>
						<line hits="1" number="606"/>
						<line hits="1" number="607"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="610"/>
						<line hits="1" number="611"/>
						<line hits="1" number="612"/>
						<line hits="1" number="613"/>
						<line hits="1" number="615"/>
						<line hits="1" number="616"/>
						<line hits="1" number="618"/>
						<line hits="1" number="619"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="620"/>
						<line hits="1" number="621"/>
						<line hits="1" number="622"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="625"/>
						<line hits="1" number="626"/>
						<line hits="1" number="627"/>
						<line hits="1" number="628"/>
						<line hits="1" number="629"/>
						<line hits="1" number="630"/>
						<line hits="1" number="631"/>
						<line hits="1" number="633"/>
						<line hits="1" number="635"/>
						<line hits="1" number="637"/>
						<line hits="1" number="639"/>
						<line hits="1" number="641"/>
						<line hits="1" number="643"/>
						<line hits="0" number="646"/>
						<line hits="1" number="650"/>
						<line hits="0" number="652"/>
						<line hits="0" number="653"/>
						<line hits="1" number="655"/>
						<line hits="1" number="656"/>
						<line hits="1" number="657"/>
						<line hits="1" number="658"/>
						<line hits="1" number="659"/>
						<line hits="1" number="662"/>
						<line hits="1" number="663"/>
						<line hits="1" number="666"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="669"/>
						<line hits="1" number="677"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="678"/>
						<line hits="1" number="679"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="682" number="680"/>
						<line hits="1" number="681"/>
						<line hits="0" number="682"/>
						<line hits="1" number="688"/>
						<line hits="1" number="694"/>
						<line hits="1" number="696"/>
						<line hits="1" number="698"/>
						<line hits="1" number="700"/>
						<line hits="1" number="710"/>
						<line hits="1" number="712"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="714"/>
						<line hits="1" number="715"/>
						<line hits="1" number="716"/>
						<line hits="1" number="718"/>
						<line hits="1" number="729"/>
						<line hits="1" number="731"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="734" number="733"/>
						<line branch="true" condition-coverage="0% (0/2)" hits="0" missing-branches="735,737" number="734"/>
						<line hits="0" number="735"/>
						<line hits="0" number="736"/>
						<line hits="0" number="737"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="739" number="738"/>
						<line hits="0" number="739"/>
						<line hits="1" number="740"/>
						<line hits="1" number="741"/>
						<line hits="1" number="743"/>
						<line hits="1" number="750"/>
						<line hits="1" number="752"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="755" number="754"/>
						<line hits="0" number="755"/>
						<line hits="1" number="756"/>
						<line hits="1" number="758"/>
						<line hits="1" number="765"/>
						<line hits="1" number="767"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="770" number="769"/>
						<line hits="0" number="770"/>
						<line hits="1" number="771"/>
						<line hits="1" number="773"/>
						<line hits="1" number="792"/>
						<line hits="1" number="794"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="797" number="796"/>
						<line branch="true" condition-coverage="0% (0/2)" hits="0" missing-branches="exit,801" number="797"/>
						<line hits="0" number="801"/>
						<line hits="0" number="804"/>
						<line hits="1" number="805"/>
						<line hits="1" number="807"/>
						<line hits="1" number="823"/>
						<line hits="1" number="825"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="828" number="827"/>
						<line branch="true" condition-coverage="0% (0/2)" hits="0" missing-branches="exit,831" number="828"/>
						<line hits="0" number="831"/>
						<line hits="0" number="834"/>
						<line hits="1" number="835"/>
						<line hits="1" number="837"/>
						<line hits="1" number="840"/>
						<line hits="1" number="842"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="845" number="844"/>
						<line hits="0" number="845"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="847" number="846"/>
						<line hits="0" number="847"/>
						<line hits="1" number="849"/>
						<line hits="1" number="851"/>
						<line hits="1" number="854"/>
						<line hits="1" number="856"/>
						<line hits="1" number="858"/>
						<line hits="1" number="860"/>
						<line hits="1" number="861"/>
						<line hits="1" number="863"/>
						<line hits="1" number="884"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="886"/>
						<line hits="1" number="887"/>
						<line hits="1" number="889"/>
						<line hits="1" number="890"/>
						<line hits="1" number="891"/>
						<line hits="1" number="892"/>
						<line hits="1" number="893"/>
						<line hits="1" number="894"/>
						<line hits="1" number="895"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="896"/>
						<line hits="1" number="897"/>
						<line hits="1" number="898"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="902" number="899"/>
						<line hits="1" number="900"/>
						<line hits="0" number="902"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="907"/>
						<line hits="1" number="909"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="946" number="930"/>
						<line hits="1" number="931"/>
						<line hits="1" number="933"/>
						<line hits="1" number="934"/>
						<line hits="1" number="935"/>
						<line hits="1" number="936"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="937"/>
						<line hits="1" number="938"/>
						<line hits="1" number="939"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="941" number="940"/>
						<line hits="0" number="941"/>
						<line hits="1" number="942"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="944" number="943"/>
						<line hits="0" number="944"/>
						<line hits="0" number="946"/>
						<line hits="1" number="952"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="974" number="973"/>
						<line hits="0" number="974"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="975"/>
						<line hits="1" number="976"/>
						<line hits="1" number="978"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="980" number="979"/>
						<line hits="0" number="980"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="985" number="981"/>
						<line hits="1" number="982"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="983"/>
						<line hits="0" number="985"/>
						<line hits="1" number="989"/>
						<line hits="1" number="990"/>
						<line hits="1" number="992"/>
						<line hits="0" number="993"/>
						<line hits="1" number="995"/>
						<line hits="0" number="996"/>
						<line hits="1" number="998"/>
						<line hits="1" number="999"/>
						<line hits="1" number="1001"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="1019" number="1018"/>
						<line hits="0" number="1019"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="1025" number="1024"/>
						<line hits="0" number="1025"/>
						<line hits="1" number="1027"/>
						<line hits="1" number="1028"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="1029"/>
						<line hits="1" number="1030"/>
						<line hits="1" number="1031"/>
						<line hits="1" number="1032"/>
						<line hits="1" number="1033"/>
						<line hits="1" number="1034"/>
						<line hits="1" number="1035"/>
						<line hits="1" number="1036"/>
						<line hits="1" number="1038"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="1039"/>
						<line hits="1" number="1040"/>
						<line hits="1" number="1041"/>
						<line hits="1" number="1043"/>
						<line hits="1" number="1066"/>
						<line hits="1" number="1068"/>
						<line hits="1" number="1115"/>
						<line hits="1" number="1116"/>
						<line hits="1" number="1117"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="1119" number="1118"/>
						<line hits="0" number="1119"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="1125" number="1124"/>
						<line hits="0" number="1125"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="1131" number="1130"/>
						<line hits="0" number="1131"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="1137"/>
						<line hits="1" number="1138"/>
						<line hits="1" number="1139"/>
						<line hits="1" number="1140"/>
						<line hits="1" number="1141"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="1142"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="1147" number="1146"/>
						<line hits="0" number="1147"/>
						<line hits="1" number="1152"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="1153"/>
						<line hits="1" number="1154"/>
						<line hits="1" number="1155"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="1167" number="1156"/>
						<line hits="1" number="1157"/>
						<line hits="1" number="1158"/>
						<line hits="1" number="1159"/>
						<line hits="1" number="1160"/>
						<line hits="1" number="1161"/>
						<line hits="1" number="1162"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="1164" number="1163"/>
						<line hits="0" number="1164"/>
						<line branch="true" condition-coverage="0% (0/2)" hits="0" missing-branches="1168,1169" number="1167"/>
						<line hits="0" number="1168"/>
						<line hits="0" number="1169"/>
						<line hits="1" number="1175"/>
						<line hits="1" number="1202"/>
					</lines>
				</class>
				<class branch-rate="0.75" complexity="0" filename="meta.py" line-rate="0.8095" name="meta.py">
					<methods/>
					<lines>
						<line hits="1" number="1"/>
						<line hits="1" number="2"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="5"/>
						<line hits="1" number="6"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="7"/>
						<line hits="1" number="8"/>
						<line hits="1" number="9"/>
						<line hits="1" number="11"/>
						<line hits="1" number="12"/>
						<line hits="1" number="13"/>
						<line hits="1" number="15"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="19"/>
						<line hits="1" number="20"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="22" number="21"/>
						<line branch="true" condition-coverage="0% (0/2)" hits="0" missing-branches="exit,23" number="22"/>
						<line hits="0" number="23"/>
						<line hits="0" number="28"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="31"/>
						<line hits="1" number="32"/>
						<line hits="1" number="33"/>
						<line hits="0" number="36"/>
					</lines>
				</class>
				<class branch-rate="1" complexity="0" filename="styles.py" line-rate="1" name="styles.py">
					<methods/>
					<lines>
						<line hits="1" number="2"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="5"/>
						<line hits="1" number="6"/>
						<line hits="1" number="7"/>
						<line hits="1" number="8"/>
						<line hits="1" number="9"/>
						<line hits="1" number="10"/>
						<line hits="1" number="11"/>
						<line hits="1" number="12"/>
						<line hits="1" number="13"/>
						<line hits="1" number="14"/>
						<line hits="1" number="15"/>
						<line hits="1" number="16"/>
						<line hits="1" number="17"/>
						<line hits="1" number="18"/>
						<line hits="1" number="19"/>
						<line hits="1" number="20"/>
						<line hits="1" number="21"/>
						<line hits="1" number="22"/>
						<line hits="1" number="23"/>
						<line hits="1" number="24"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="27"/>
						<line hits="1" number="28"/>
						<line hits="1" number="29"/>
						<line hits="1" number="30"/>
						<line hits="1" number="31"/>
						<line hits="1" number="32"/>
						<line hits="1" number="33"/>
						<line hits="1" number="34"/>
						<line hits="1" number="35"/>
						<line hits="1" number="36"/>
						<line hits="1" number="37"/>
						<line hits="1" number="38"/>
						<line hits="1" number="39"/>
						<line hits="1" number="40"/>
						<line hits="1" number="41"/>
						<line hits="1" number="42"/>
						<line hits="1" number="43"/>
						<line hits="1" number="44"/>
						<line hits="1" number="45"/>
						<line hits="1" number="46"/>
						<line hits="1" number="49"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="53"/>
						<line hits="1" number="54"/>
						<line hits="1" number="55"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="58"/>
						<line hits="1" number="59"/>
						<line hits="1" number="60"/>
						<line hits="1" number="61"/>
						<line hits="1" number="62"/>
						<line hits="1" number="63"/>
						<line hits="1" number="64"/>
						<line hits="1" number="65"/>
						<line hits="1" number="66"/>
						<line hits="1" number="67"/>
						<line hits="1" number="68"/>
						<line hits="1" number="69"/>
						<line hits="1" number="70"/>
						<line hits="1" number="71"/>
						<line hits="1" number="72"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="75"/>
						<line hits="1" number="76"/>
						<line hits="1" number="77"/>
						<line hits="1" number="78"/>
						<line hits="1" number="79"/>
						<line hits="1" number="80"/>
						<line hits="1" number="81"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="84"/>
						<line hits="1" number="85"/>
						<line hits="1" number="86"/>
						<line hits="1" number="87"/>
						<line hits="1" number="88"/>
						<line hits="1" number="89"/>
						<line hits="1" number="90"/>
						<line hits="1" number="91"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="94"/>
						<line hits="1" number="95"/>
						<line hits="1" number="96"/>
						<line hits="1" number="97"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="100"/>
						<line hits="1" number="101"/>
						<line hits="1" number="102"/>
						<line hits="1" number="103"/>
						<line hits="1" number="104"/>
						<line hits="1" number="105"/>
						<line hits="1" number="106"/>
						<line hits="1" number="107"/>
						<line hits="1" number="108"/>
						<line hits="1" number="109"/>
						<line hits="1" number="110"/>
						<line hits="1" number="111"/>
						<line hits="1" number="112"/>
						<line hits="1" number="113"/>
						<line hits="1" number="114"/>
						<line hits="1" number="115"/>
						<line hits="1" number="116"/>
						<line hits="1" number="117"/>
						<line hits="1" number="118"/>
						<line hits="1" number="119"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="122"/>
						<line hits="1" number="123"/>
						<line hits="1" number="124"/>
						<line hits="1" number="125"/>
						<line hits="1" number="126"/>
						<line hits="1" number="127"/>
						<line hits="1" number="128"/>
						<line hits="1" number="129"/>
						<line hits="1" number="130"/>
						<line hits="1" number="131"/>
						<line hits="1" number="132"/>
						<line hits="1" number="133"/>
						<line hits="1" number="134"/>
						<line hits="1" number="135"/>
						<line hits="1" number="136"/>
						<line hits="1" number="137"/>
						<line hits="1" number="138"/>
						<line hits="1" number="139"/>
						<line hits="1" number="140"/>
						<line hits="1" number="141"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="144"/>
						<line hits="1" number="145"/>
						<line hits="1" number="146"/>
						<line hits="1" number="147"/>
						<line hits="1" number="148"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="151"/>
						<line hits="1" number="152"/>
						<line hits="1" number="153"/>
						<line hits="1" number="154"/>
						<line hits="1" number="155"/>
						<line hits="1" number="156"/>
						<line hits="1" number="157"/>
						<line hits="1" number="158"/>
						<line hits="1" number="159"/>
						<line hits="1" number="160"/>
						<line hits="1" number="161"/>
						<line hits="1" number="162"/>
						<line hits="1" number="163"/>
						<line hits="1" number="164"/>
						<line hits="1" number="165"/>
					</lines>
				</class>
				<class branch-rate="0.5714" complexity="0" filename="utils.py" line-rate="0.8537" name="utils.py">
					<methods/>
					<lines>
						<line hits="1" number="3"/>
						<line hits="1" number="4"/>
						<line hits="1" number="6"/>
						<line hits="1" number="7"/>
						<line hits="1" number="10"/>
						<line hits="1" number="16"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="18"/>
						<line hits="1" number="19"/>
						<line hits="1" number="24"/>
						<line hits="1" number="25"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="33" number="26"/>
						<line hits="1" number="27"/>
						<line hits="1" number="28"/>
						<line hits="1" number="29"/>
						<line hits="1" number="30"/>
						<line hits="0" number="32"/>
						<line hits="0" number="33"/>
						<line hits="0" number="34"/>
						<line hits="1" number="36"/>
						<line hits="1" number="39"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="41" number="40"/>
						<line branch="true" condition-coverage="0% (0/2)" hits="0" missing-branches="exit,42" number="41"/>
						<line hits="0" number="42"/>
						<line hits="1" number="47"/>
						<line hits="1" number="50"/>
						<line branch="true" condition-coverage="100% (2/2)" hits="1" number="52"/>
						<line hits="1" number="53"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="56" number="54"/>
						<line hits="1" number="55"/>
						<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="57" number="56"/>
						<line hits="0" number="57"/>
						<line hits="1" number="58"/>
						<line hits="1" number="59"/>
						<line hits="1" number="60"/>
						<line hits="1" number="62"/>
						<line hits="1" number="65"/>
						<line hits="1" number="67"/>
						<line hits="1" number="68"/>
						<line hits="1" number="71"/>
						<line hits="1" number="72"/>
						<line hits="1" number="73"/>
					</lines>
				</class>
			</classes>
		</package>
	</packages>
</coverage>


================================================
FILE: docs/Makefile
================================================
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line.
SPHINXOPTS    =
SPHINXBUILD   = sphinx-build
SPHINXPROJ    = BeautifulTable
SOURCEDIR     = .
BUILDDIR      = _build

# Put it first so that "make" without argument is like "make help".
help:
	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

================================================
FILE: docs/badges.rst
================================================
.. include:: ../README.rst
   :start-after: inclusion-marker-badges-start
   :end-before: inclusion-marker-badges-end

================================================
FILE: docs/changelog.rst
================================================
.. include:: ../README.rst
   :start-after: inclusion-marker-changelog-start
   :end-before: inclusion-marker-changelog-end


================================================
FILE: docs/conf.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# BeautifulTable documentation build configuration file, created by
# sphinx-quickstart on Sun Dec 18 15:59:32 2016.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys

sys.path.insert(0, os.path.abspath("../"))

import beautifultable  # noqa E402


# -- General configuration ------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    "sphinx.ext.autodoc",
    "sphinx.ext.intersphinx",
    "sphinx.ext.todo",
    "sphinx.ext.coverage",
    "sphinx.ext.ifconfig",
    "sphinx.ext.viewcode",
    "sphinx.ext.napoleon",
]

napoleon_google_docstring = False
napoleon_include_special_with_doc = False
# napoleon_use_param = False
# napoleon_use_ivar = True

autodoc_member_order = "bysource"

# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = ".rst"

# The master toctree document.
master_doc = "index"

# General information about the project.
project = "BeautifulTable"
copyright = "2022, Priyam Singh"
author = beautifultable.__author__

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = beautifultable.__version__
# The full version, including alpha/beta/rc tags.
release = beautifultable.__version__

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = "sphinx"

# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True


# -- Options for HTML output ----------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
#
html_theme = "default"

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]


# -- Options for HTMLHelp output ------------------------------------------

# Output file base name for HTML help builder.
htmlhelp_basename = "BeautifulTabledoc"


# -- Options for LaTeX output ---------------------------------------------

latex_elements = {
    # The paper size ('letterpaper' or 'a4paper').
    #
    # 'papersize': 'letterpaper',
    # The font size ('10pt', '11pt' or '12pt').
    #
    # 'pointsize': '10pt',
    # Additional stuff for the LaTeX preamble.
    #
    # 'preamble': '',
    # Latex figure (float) alignment
    #
    # 'figure_align': 'htbp',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [
    (
        master_doc,
        "BeautifulTable.tex",
        "BeautifulTable Documentation",
        "Priyam Singh",
        "manual",
    ),
]


# -- Options for manual page output ---------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    (master_doc, "beautifultable", "BeautifulTable Documentation", [author], 1)
]


# -- Options for Texinfo output -------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
    (
        master_doc,
        "BeautifulTable",
        "BeautifulTable Documentation",
        author,
        "BeautifulTable",
        "One line description of project.",
        "Miscellaneous",
    ),
]


# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {"https://docs.python.org/": None}


================================================
FILE: docs/donation.rst
================================================
.. include:: ../README.rst
   :start-after: inclusion-marker-donation-start
   :end-before: inclusion-marker-donation-end

================================================
FILE: docs/index.rst
================================================
.. BeautifulTable documentation master file, created by
   sphinx-quickstart on Sun Dec 18 15:59:32 2016.
   You can adapt this file completely to your liking, but it should at least
   contain the root `toctree` directive.

##########################################
Welcome to beautifultable's documentation!
##########################################

.. include:: badges.rst

.. include:: introduction.rst
   
.. include:: links.rst


******************************************
Contents
******************************************

.. toctree::
   :maxdepth: 3
   
   install
   quickstart
   changelog


.. include:: donation.rst


================================================
FILE: docs/install.rst
================================================
.. include:: ../README.rst
   :start-after: inclusion-marker-install-start
   :end-before: inclusion-marker-install-end

================================================
FILE: docs/introduction.rst
================================================
.. include:: ../README.rst
   :start-after: inclusion-marker-introduction-start
   :end-before: inclusion-marker-introduction-end

================================================
FILE: docs/links.rst
================================================
.. include:: ../README.rst
   :start-after: inclusion-marker-links-start
   :end-before: inclusion-marker-links-end

================================================
FILE: docs/make.bat
================================================
@ECHO OFF

pushd %~dp0

REM Command file for Sphinx documentation

if "%SPHINXBUILD%" == "" (
	set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
set SPHINXPROJ=PrinterTools

if "%1" == "" goto help

%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
	echo.
	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
	echo.installed, then set the SPHINXBUILD environment variable to point
	echo.to the full path of the 'sphinx-build' executable. Alternatively you
	echo.may add the Sphinx directory to PATH.
	echo.
	echo.If you don't have Sphinx installed, grab it from
	echo.http://sphinx-doc.org/
	exit /b 1
)

%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end

:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%

:end
popd


================================================
FILE: docs/quickstart.rst
================================================
*************************************************************************
Quickstart
*************************************************************************

=========================================================================
Building the Table
=========================================================================

Building a table is very easy. :class:`.BeautifulTable` provides two views
``rows`` and ``columns``. You can use them to modify their respective properties.

Let's create our first table and add some rows.

.. code:: python

   >>> from beautifultable import BeautifulTable
   >>> table = BeautifulTable()
   >>> table.rows.append(["Jacob", 1, "boy"])
   >>> table.rows.append(["Isabella", 1, "girl"])
   >>> table.rows.append(["Ethan", 2, "boy"])
   >>> table.rows.append(["Sophia", 2, "girl"])
   >>> table.rows.append(["Michael", 3, "boy"])
   >>> table.columns.header = ["name", "rank", "gender"]
   >>> table.rows.header = ["S1", "S2", "S3", "S4", "S5"]
   >>> print(table)
   +----+----------+------+--------+
   |    |   name   | rank | gender |
   +----+----------+------+--------+
   | S1 |  Jacob   |  1   |  boy   |
   +----+----------+------+--------+
   | S2 | Isabella |  1   |  girl  |
   +----+----------+------+--------+
   | S3 |  Ethan   |  2   |  boy   |
   +----+----------+------+--------+
   | S4 |  Sophia  |  2   |  girl  |
   +----+----------+------+--------+
   | S5 | Michael  |  3   |  boy   |
   +----+----------+------+--------+

BeautifulTable initializes the shape lazily. Here when you appended the first row,
the number of columns was set to 3. Further rows had to be of length 3. If you had
set the columns and/or row headers beforehand as follows, the table shape would already be
set to (5, 3). Hence you would just set the rows directly using their indices or keys.

.. code:: python

   >>> from beautifultable import BeautifulTable
   >>> table = BeautifulTable()
   >>> table.columns.header = ["name", "rank", "gender"]
   >>> table.rows.header = ["S1", "S2", "S3", "S4", "S5"]
   >>> table.rows[0] = ["Jacob", 1, "boy"]
   >>> table.rows[1] = ["Isabella", 1, "girl"]
   >>> table.rows[2] = ["Ethan", 2, "boy"]
   >>> table.rows[3] = ["Sophia", 2, "girl"]
   >>> table.rows[4]  =["Michael", 3, "boy"]
   >>> print(table)
   +----+----------+------+--------+
   |    |   name   | rank | gender |
   +----+----------+------+--------+
   | S1 |  Jacob   |  1   |  boy   |
   +----+----------+------+--------+
   | S2 | Isabella |  1   |  girl  |
   +----+----------+------+--------+
   | S3 |  Ethan   |  2   |  boy   |
   +----+----------+------+--------+
   | S4 |  Sophia  |  2   |  girl  |
   +----+----------+------+--------+
   | S5 | Michael  |  3   |  boy   |
   +----+----------+------+--------+


So, We created our first table. Let's add a new column.

.. code:: python

   >>> table.columns.append(["2010", "2012", "2008", "2010", "2011"], header="year")
   >>> print(table)
   +----+----------+------+--------+------+
   |    |   name   | rank | gender | year |
   +----+----------+------+--------+------+
   | S1 |  Jacob   |  1   |  boy   | 2010 |
   +----+----------+------+--------+------+
   | S2 | Isabella |  1   |  girl  | 2012 |
   +----+----------+------+--------+------+
   | S3 |  Ethan   |  2   |  boy   | 2008 |
   +----+----------+------+--------+------+
   | S4 |  Sophia  |  2   |  girl  | 2010 |
   +----+----------+------+--------+------+
   | S5 | Michael  |  3   |  boy   | 2011 |
   +----+----------+------+--------+------+

You can also build a :class:`.BeautifulTable` using slicing. Slicing creates a
new table with it's own copy of data. But it retains the properties
of the original object. You can slice both rows or columns.

.. code:: python

   >>> new_table = table.rows[:3]
   >>> print(new_table)
   +----+----------+------+--------+------+
   |    |   name   | rank | gender | year |
   +----+----------+------+--------+------+
   | S1 |  Jacob   |  1   |  boy   | 2010 |
   +----+----------+------+--------+------+
   | S2 | Isabella |  1   |  girl  | 2012 |
   +----+----------+------+--------+------+
   | S3 |  Ethan   |  2   |  boy   | 2008 |
   +----+----------+------+--------+------+


.. code:: python

   >>> new_table = table.columns[:3]
   >>> print(new_table)
   +----+----------+------+--------+
   |    |   name   | rank | gender |
   +----+----------+------+--------+
   | S1 |  Jacob   |  1   |  boy   |
   +----+----------+------+--------+
   | S2 | Isabella |  1   |  girl  |
   +----+----------+------+--------+
   | S3 |  Ethan   |  2   |  boy   |
   +----+----------+------+--------+
   | S4 |  Sophia  |  2   |  girl  |
   +----+----------+------+--------+
   | S5 | Michael  |  3   |  boy   |
   +----+----------+------+--------+

As you can see how easy it is to create a Table with **beautifultable**.
Now lets move on to see some common use cases. For details, please refer the API Documentation.


=========================================================================
Accessing Rows
=========================================================================

You can access a row using it's index or it's header. It returns a **BTRowData** object.

.. code:: python

   >>> print(list(table.rows[3]))
   ['Sophia', 2, 'girl', '2010']

To access a particular field of a row, you can again use the index, or the header
of the required column.

.. code:: python

   >>> print(table.rows[3][2])
   girl
   >>> print(table.rows[3]['gender'])
   girl


=========================================================================
Accessing Columns
=========================================================================

You can access a column using it's index or it's header. It returns a **BTColumnData** object.

.. code:: python

   >>> print(list(table.columns['name']))
   ['Jacob', 'Isabella', 'Ethan', 'Sophia', 'Michael']

To access a particular field of a column, you can again use the index, or the header
of the required row.

.. code:: python

   >>> print(table.columns[2][3])
   girl
   >>> print(table.columns[2]['S4'])
   girl

=========================================================================
Counting Rows and Columns
=========================================================================

You can get the number of columns or rows in the table by using the
``len`` function. You can also use the :attr:`.BeautifulTable.shape`
attribute.

.. code:: python

   >>> print(len(table.columns))
   3
   >>> pri
Download .txt
gitextract_qaaahoom/

├── .coveragerc
├── .github/
│   └── workflows/
│       ├── build.yml
│       ├── publish-test.yml
│       └── publish.yml
├── .gitignore
├── LICENSE.txt
├── MANIFEST.in
├── README.rst
├── beautifultable/
│   ├── __init__.py
│   ├── __version__.py
│   ├── ansi.py
│   ├── base.py
│   ├── beautifultable.py
│   ├── compat.py
│   ├── enums.py
│   ├── helpers.py
│   ├── meta.py
│   ├── styles.py
│   └── utils.py
├── coverage.xml
├── docs/
│   ├── Makefile
│   ├── badges.rst
│   ├── changelog.rst
│   ├── conf.py
│   ├── donation.rst
│   ├── index.rst
│   ├── install.rst
│   ├── introduction.rst
│   ├── links.rst
│   ├── make.bat
│   ├── quickstart.rst
│   └── source/
│       ├── beautifultable.rst
│       └── modules.rst
├── requirements.txt
├── setup.cfg
├── setup.py
└── test.py
Download .txt
SYMBOL INDEX (349 symbols across 9 files)

FILE: beautifultable/ansi.py
  class ANSIMultiByteString (line 16) | class ANSIMultiByteString(object):
    method __init__ (line 21) | def __init__(self, string):
    method __len__ (line 49) | def __len__(self):
    method __getitem__ (line 52) | def __getitem__(self, key):
    method _slice (line 63) | def _slice(self, key):
    method termwidth (line 80) | def termwidth(self):
    method wrap (line 84) | def wrap(self, width):

FILE: beautifultable/base.py
  class BTBaseList (line 5) | class BTBaseList(metaclass=abc.ABCMeta):
    method __init__ (line 6) | def __init__(self, table, value):
    method _table (line 11) | def _table(self):
    method _table (line 15) | def _table(self, value):
    method value (line 19) | def value(self):
    method __len__ (line 22) | def __len__(self):
    method __iter__ (line 25) | def __iter__(self):
    method __next__ (line 28) | def __next__(self):
    method __repr__ (line 31) | def __repr__(self):
    method __eq__ (line 36) | def __eq__(self, other):
    method __contains__ (line 44) | def __contains__(self, item):
    method _append (line 48) | def _append(self, item):
    method _insert (line 51) | def _insert(self, i, item):
    method _pop (line 54) | def _pop(self, i=-1):
    method _remove (line 57) | def _remove(self, item):
    method _reverse (line 60) | def _reverse(self):
    method _sort (line 63) | def _sort(self, key, reverse=False):
    method _clear (line 66) | def _clear(self):
    method count (line 69) | def count(self, item):
    method index (line 72) | def index(self, item, *args):
    method __getitem__ (line 79) | def __getitem__(self, key):
    method __setitem__ (line 83) | def __setitem__(self, key, value):
    method __delitem__ (line 87) | def __delitem__(self, key):
    method _validate (line 90) | def _validate(self, value):
    method _get_canonical_key (line 99) | def _get_canonical_key(self, key):
    method _get_ideal_length (line 103) | def _get_ideal_length(self):
  class BTBaseRow (line 107) | class BTBaseRow(BTBaseList):
    method _get_canonical_key (line 108) | def _get_canonical_key(self, key):
    method _get_ideal_length (line 111) | def _get_ideal_length(self):
    method _validate (line 114) | def _validate(self, value):
  class BTBaseColumn (line 120) | class BTBaseColumn(BTBaseList):
    method _get_canonical_key (line 121) | def _get_canonical_key(self, key):
    method _get_ideal_length (line 124) | def _get_ideal_length(self):
    method _validate (line 127) | def _validate(self, value):

FILE: beautifultable/beautifultable.py
  class BTBorder (line 64) | class BTBorder:
    method __init__ (line 116) | def __init__(
  function _make_getter (line 152) | def _make_getter(attr):
  function _make_setter (line 156) | def _make_setter(attr):
  class BTTableData (line 182) | class BTTableData(BTBaseList):
    method __init__ (line 183) | def __init__(self, table, value=None):
    method _get_canonical_key (line 189) | def _get_canonical_key(self, key):
    method _get_ideal_length (line 192) | def _get_ideal_length(self):
  class BeautifulTable (line 196) | class BeautifulTable:
    method __init__ (line 245) | def __init__(
    method __copy__ (line 291) | def __copy__(self):
    method __deepcopy__ (line 311) | def __deepcopy__(self, memo):
    method __setattr__ (line 333) | def __setattr__(self, name, value):
    method __len__ (line 369) | def __len__(self):  # pragma: no cover
    method __iter__ (line 377) | def __iter__(self):  # pragma: no cover
    method __contains__ (line 386) | def __contains__(self, key):  # pragma: no cover
    method __repr__ (line 394) | def __repr__(self):
    method __str__ (line 397) | def __str__(self):
    method shape (line 409) | def shape(self):
    method sign (line 414) | def sign(self):
    method sign (line 434) | def sign(self, value):
    method border (line 442) | def border(self):
    method border (line 452) | def border(self, value):
    method junction (line 471) | def junction(self):
    method junction (line 476) | def junction(self, value):
    method serialno (line 481) | def serialno(self):  # pragma: no cover
    method serialno (line 486) | def serialno(self, value):  # pragma: no cover
    method serialno_header (line 491) | def serialno_header(self):  # pragma: no cover
    method serialno_header (line 496) | def serialno_header(self, value):  # pragma: no cover
    method sign_mode (line 501) | def sign_mode(self):  # pragma: no cover
    method sign_mode (line 506) | def sign_mode(self, value):  # pragma: no cover
    method maxwidth (line 510) | def maxwidth(self):
    method maxwidth (line 524) | def maxwidth(self, value):
    method max_table_width (line 529) | def max_table_width(self):  # pragma: no cover
    method max_table_width (line 534) | def max_table_width(self, value):  # pragma: no cover
    method column_count (line 544) | def column_count(self):  # pragma: no cover
    method width_exceed_policy (line 549) | def width_exceed_policy(self):  # pragma: no cover
    method width_exceed_policy (line 554) | def width_exceed_policy(self, value):  # pragma: no cover
    method default_alignment (line 559) | def default_alignment(self):  # pragma: no cover
    method default_alignment (line 564) | def default_alignment(self, value):  # pragma: no cover
    method default_padding (line 569) | def default_padding(self):  # pragma: no cover
    method default_padding (line 574) | def default_padding(self, value):  # pragma: no cover
    method column_widths (line 579) | def column_widths(self):  # pragma: no cover
    method column_widths (line 584) | def column_widths(self, value):  # pragma: no cover
    method column_headers (line 589) | def column_headers(self):  # pragma: no cover
    method column_headers (line 594) | def column_headers(self, value):  # pragma: no cover
    method column_alignments (line 599) | def column_alignments(self):  # pragma: no cover
    method column_alignments (line 604) | def column_alignments(self, value):  # pragma: no cover
    method left_padding_widths (line 609) | def left_padding_widths(self):  # pragma: no cover
    method left_padding_widths (line 614) | def left_padding_widths(self, value):  # pragma: no cover
    method right_padding_widths (line 619) | def right_padding_widths(self):  # pragma: no cover
    method right_padding_widths (line 624) | def right_padding_widths(self, value):  # pragma: no cover
    method __getitem__ (line 633) | def __getitem__(self, key):  # pragma: no cover
    method __setitem__ (line 642) | def __setitem__(self, key, value):  # pragma: no cover
    method __delitem__ (line 654) | def __delitem__(self, key):  # pragma: no cover
    method get_column (line 668) | def get_column(self, key):  # pragma: no cover
    method get_column_header (line 677) | def get_column_header(self, index):  # pragma: no cover
    method get_column_index (line 686) | def get_column_index(self, header):  # pragma: no cover
    method filter (line 690) | def filter(self, key):  # pragma: no cover
    method sort (line 694) | def sort(self, key, reverse=False):  # pragma: no cover
    method reverse (line 698) | def reverse(self, value):  # pragma: no cover
    method pop_row (line 702) | def pop_row(self, index=-1):  # pragma: no cover
    method insert_row (line 706) | def insert_row(self, index, row):  # pragma: no cover
    method append_row (line 710) | def append_row(self, value):  # pragma: no cover
    method update_row (line 714) | def update_row(self, key, value):  # pragma: no cover
    method pop_column (line 718) | def pop_column(self, index=-1):  # pragma: no cover
    method insert_column (line 722) | def insert_column(self, index, header, column):  # pragma: no cover
    method append_column (line 726) | def append_column(self, header, column):  # pragma: no cover
    method update_column (line 730) | def update_column(self, header, column):  # pragma: no cover
    method set_style (line 733) | def set_style(self, style):
    method _compute_width (line 784) | def _compute_width(self):
    method set_padding_widths (line 868) | def set_padding_widths(self, pad_width):  # pragma: no cover
    method copy (line 873) | def copy(self):
    method clear (line 877) | def clear(self, reset_columns=False, **kwargs):  # pragma: no cover
    method _get_horizontal_line (line 897) | def _get_horizontal_line(
    method _get_top_border (line 976) | def _get_top_border(self, *args, **kwargs):
    method _get_header_separator (line 986) | def _get_header_separator(self, *args, **kwargs):
    method _get_row_separator (line 996) | def _get_row_separator(self, *args, **kwargs):
    method _get_bottom_border (line 1006) | def _get_bottom_border(self, *args, **kwargs):
    method _width (line 1017) | def _width(self):
    method get_table_width (line 1036) | def get_table_width(self):  # pragma: no cover
    method _get_string (line 1039) | def _get_string(self, rows=None, append=False, recalculate_width=True):
    method stream (line 1125) | def stream(self, rows, append=False):
    method get_string (line 1150) | def get_string(self):
    method to_csv (line 1153) | def to_csv(self, file_name, *args, **kwargs):
    method from_csv (line 1175) | def from_csv(self, file_name, header=True, **kwargs):
    method to_df (line 1207) | def to_df(self):
    method from_df (line 1239) | def from_df(self, df):

FILE: beautifultable/enums.py
  class WidthExceedPolicy (line 20) | class WidthExceedPolicy(enum.Enum):
    method __repr__ (line 25) | def __repr__(self):
  class SignMode (line 29) | class SignMode(enum.Enum):
    method __repr__ (line 34) | def __repr__(self):
  class Alignment (line 38) | class Alignment(enum.Enum):
    method __repr__ (line 43) | def __repr__(self):
  class Style (line 47) | class Style(enum.Enum):
    method __repr__ (line 61) | def __repr__(self):

FILE: beautifultable/helpers.py
  class BTRowHeader (line 12) | class BTRowHeader(BTBaseColumn):
    method __init__ (line 13) | def __init__(self, table, value):
    method __setitem__ (line 18) | def __setitem__(self, key, value):
    method _validate_item (line 22) | def _validate_item(self, value):
  class BTColumnHeader (line 27) | class BTColumnHeader(BTBaseRow):
    method __init__ (line 28) | def __init__(self, table, value):
    method alignment (line 35) | def alignment(self):
    method alignment (line 47) | def alignment(self, value):
    method separator (line 56) | def separator(self):
    method separator (line 61) | def separator(self, value):
    method junction (line 65) | def junction(self):
    method junction (line 70) | def junction(self, value):
    method __setitem__ (line 73) | def __setitem__(self, key, value):
    method _validate_item (line 77) | def _validate_item(self, value):
  class BTRowData (line 82) | class BTRowData(BTBaseRow):
    method _get_padding (line 83) | def _get_padding(self):
    method _clamp_row (line 89) | def _clamp_row(self, row):
    method _clamp_string (line 146) | def _clamp_string(self, row_item, index, delimiter=""):
    method _get_string (line 182) | def _get_string(
    method __str__ (line 274) | def __str__(self):
    method aslist (line 277) | def aslist(self):
    method asdict (line 281) | def asdict(self):
  class BTColumnData (line 294) | class BTColumnData(BTBaseColumn):
    method aslist (line 295) | def aslist(self):
    method asdict (line 299) | def asdict(self):
  class BTRowCollection (line 306) | class BTRowCollection(object):
    method __init__ (line 307) | def __init__(self, table):
    method _table (line 312) | def _table(self):
    method _table (line 316) | def _table(self, value):
    method _reset_state (line 319) | def _reset_state(self, nrow):
    method header (line 327) | def header(self):
    method header (line 331) | def header(self, value):
    method separator (line 335) | def separator(self):
    method separator (line 340) | def separator(self, value):
    method _canonical_key (line 343) | def _canonical_key(self, key):
    method __len__ (line 352) | def __len__(self):
    method __getitem__ (line 355) | def __getitem__(self, key):
    method __delitem__ (line 387) | def __delitem__(self, key):
    method __setitem__ (line 414) | def __setitem__(self, key, value):
    method __contains__ (line 443) | def __contains__(self, key):
    method __iter__ (line 451) | def __iter__(self):
    method __repr__ (line 454) | def __repr__(self):
    method __str__ (line 457) | def __str__(self):
    method reverse (line 460) | def reverse(self):
    method pop (line 464) | def pop(self, index=-1):
    method insert (line 481) | def insert(self, index, row, header=None):
    method append (line 510) | def append(self, row, header=None):
    method update (line 524) | def update(self, key, value):
    method clear (line 552) | def clear(self):
    method sort (line 555) | def sort(self, key, reverse=False):
    method filter (line 580) | def filter(self, key):
  class BTCollectionIterator (line 596) | class BTCollectionIterator(object):
    method __init__ (line 597) | def __init__(self, collection):
    method __iter__ (line 601) | def __iter__(self):
    method __next__ (line 604) | def __next__(self):
  class BTColumnCollection (line 611) | class BTColumnCollection(object):
    method __init__ (line 612) | def __init__(self, table, default_alignment, default_padding):
    method _table (line 622) | def _table(self):
    method _table (line 626) | def _table(self, value):
    method padding (line 630) | def padding(self):
    method padding (line 637) | def padding(self, value):
    method _reset_state (line 641) | def _reset_state(self, ncol):
    method _canonical_key (line 663) | def _canonical_key(self, key):
    method header (line 673) | def header(self):
    method header (line 681) | def header(self, value):
    method alignment (line 685) | def alignment(self):
    method alignment (line 697) | def alignment(self, value):
    method width (line 703) | def width(self):
    method width (line 716) | def width(self, value):
    method padding_left (line 728) | def padding_left(self):
    method padding_left (line 737) | def padding_left(self, value):
    method padding_right (line 743) | def padding_right(self):
    method padding_right (line 752) | def padding_right(self, value):
    method width_exceed_policy (line 758) | def width_exceed_policy(self):
    method width_exceed_policy (line 779) | def width_exceed_policy(self, value):
    method default_alignment (line 791) | def default_alignment(self):
    method default_alignment (line 809) | def default_alignment(self, value):
    method default_padding (line 819) | def default_padding(self):
    method default_padding (line 824) | def default_padding(self, value):
    method separator (line 833) | def separator(self):
    method separator (line 838) | def separator(self, value):
    method __len__ (line 841) | def __len__(self):
    method __getitem__ (line 844) | def __getitem__(self, key):
    method __delitem__ (line 889) | def __delitem__(self, key):
    method __setitem__ (line 930) | def __setitem__(self, key, value):
    method __contains__ (line 956) | def __contains__(self, key):
    method __iter__ (line 965) | def __iter__(self):
    method __repr__ (line 968) | def __repr__(self):
    method __str__ (line 971) | def __str__(self):
    method clear (line 974) | def clear(self):
    method pop (line 977) | def pop(self, index=-1):
    method update (line 1017) | def update(self, key, value):
    method insert (line 1042) | def insert(
    method append (line 1141) | def append(

FILE: beautifultable/meta.py
  class MetaData (line 5) | class MetaData(BTBaseRow):
    method __init__ (line 6) | def __init__(self, table, row):
    method __setitem__ (line 11) | def __setitem__(self, key, value):
    method validate (line 15) | def validate(self, value):
  class AlignmentMetaData (line 19) | class AlignmentMetaData(MetaData):
    method validate (line 20) | def validate(self, value):
  class NonNegativeIntegerMetaData (line 32) | class NonNegativeIntegerMetaData(MetaData):
    method validate (line 33) | def validate(self, value):

FILE: beautifultable/styles.py
  class NoStyle (line 5) | class NoStyle:
  class DefaultStyle (line 27) | class DefaultStyle(NoStyle):
  class MySQLStyle (line 49) | class MySQLStyle(DefaultStyle):
  class SeparatedStyle (line 53) | class SeparatedStyle(DefaultStyle):
  class CompactStyle (line 58) | class CompactStyle(NoStyle):
  class DottedStyle (line 75) | class DottedStyle(NoStyle):
  class MarkdownStyle (line 84) | class MarkdownStyle(NoStyle):
  class RestructuredTextStyle (line 94) | class RestructuredTextStyle(CompactStyle):
  class BoxStyle (line 100) | class BoxStyle(NoStyle):
  class DoubledBoxStyle (line 122) | class DoubledBoxStyle(NoStyle):
  class RoundedStyle (line 144) | class RoundedStyle(BoxStyle):
  class GridStyle (line 151) | class GridStyle(BoxStyle):

FILE: beautifultable/utils.py
  function to_numeric (line 10) | def to_numeric(item):
  function ensure_type (line 39) | def ensure_type(value, *types, varname="value"):
  function pre_process (line 50) | def pre_process(item, detect_numerics, precision, sign_value):
  function termwidth (line 65) | def termwidth(item):
  function textwrap (line 71) | def textwrap(item, width):
  function deprecation_message (line 76) | def deprecation_message(
  function deprecated (line 82) | def deprecated(
  function deprecated_param (line 130) | def deprecated_param(

FILE: test.py
  class TableOperationsTestCase (line 21) | class TableOperationsTestCase(unittest.TestCase):
    method setUp (line 22) | def setUp(self):
    method create_table (line 25) | def create_table(self, maxwidth=80):
    method create_dataframe (line 39) | def create_dataframe(self):
    method compare_iterable (line 42) | def compare_iterable(self, iterable1, iterable2):
    method test_filter (line 48) | def test_filter(self):
    method test_sort_by_index (line 59) | def test_sort_by_index(self):
    method test_sort_by_index_reversed (line 71) | def test_sort_by_index_reversed(self):
    method test_sort_by_header (line 83) | def test_sort_by_header(self):
    method test_sort_by_callable (line 95) | def test_sort_by_callable(self):
    method test_sort_raises_exception (line 107) | def test_sort_raises_exception(self):
    method test_column_aslist (line 113) | def test_column_aslist(self):
    method test_column_asdict (line 123) | def test_column_asdict(self):
    method test_column_count (line 127) | def test_column_count(self):
    method test_access_column_by_header (line 130) | def test_access_column_by_header(self):
    method test_access_column_element_by_index (line 136) | def test_access_column_element_by_index(self):
    method test_access_column_element_by_header (line 139) | def test_access_column_element_by_header(self):
    method test_get_column_index (line 142) | def test_get_column_index(self):
    method test_get_column_header (line 147) | def test_get_column_header(self):
    method test_append_column (line 152) | def test_append_column(self):
    method test_append_column_empty_table (line 160) | def test_append_column_empty_table(self):
    method test_insert_column (line 180) | def test_insert_column(self):
    method test_pop_column_by_position (line 188) | def test_pop_column_by_position(self):
    method test_pop_column_by_header (line 196) | def test_pop_column_by_header(self):
    method test_update_column_by_index (line 203) | def test_update_column_by_index(self):
    method test_update_column_by_header (line 210) | def test_update_column_by_header(self):
    method test_update_column_slice (line 217) | def test_update_column_slice(self):
    method test_row_asdict (line 232) | def test_row_asdict(self):
    method test_row_aslist (line 244) | def test_row_aslist(self):
    method test_row_count (line 256) | def test_row_count(self):
    method test_access_row_by_index (line 259) | def test_access_row_by_index(self):
    method test_access_row_by_header (line 265) | def test_access_row_by_header(self):
    method test_access_row_element_by_index (line 271) | def test_access_row_element_by_index(self):
    method test_access_row_element_by_header (line 274) | def test_access_row_element_by_header(self):
    method test_append_row (line 277) | def test_append_row(self):
    method test_insert_row (line 283) | def test_insert_row(self):
    method test_pop_row_by_position (line 290) | def test_pop_row_by_position(self):
    method test_pop_row_by_header (line 295) | def test_pop_row_by_header(self):
    method test_update_row_by_index (line 300) | def test_update_row_by_index(self):
    method test_update_row_by_header (line 306) | def test_update_row_by_header(self):
    method test_update_row_slice (line 312) | def test_update_row_slice(self):
    method test_row_getitem_slice (line 321) | def test_row_getitem_slice(self):
    method test_row_delitem_int (line 326) | def test_row_delitem_int(self):
    method test_row_delitem_slice (line 330) | def test_row_delitem_slice(self):
    method test_row_delitem_str (line 334) | def test_row_delitem_str(self):
    method test_row_setitem_int (line 340) | def test_row_setitem_int(self):
    method test_row_setitem_slice (line 346) | def test_row_setitem_slice(self):
    method test_row_setitem_str (line 353) | def test_row_setitem_str(self):
    method test_row_contains (line 360) | def test_row_contains(self):
    method test_row_header_contains (line 364) | def test_row_header_contains(self):
    method test_column_getitem_slice (line 370) | def test_column_getitem_slice(self):
    method test_column_delitem_int (line 375) | def test_column_delitem_int(self):
    method test_column_delitem_slice (line 379) | def test_column_delitem_slice(self):
    method test_column_delitem_str (line 383) | def test_column_delitem_str(self):
    method test_column_setitem_int (line 389) | def test_column_setitem_int(self):
    method test_column_setitem_slice (line 395) | def test_column_setitem_slice(self):
    method test_column_setitem_str (line 408) | def test_column_setitem_str(self):
    method test_column_header_contains (line 415) | def test_column_header_contains(self):
    method test_column_contains (line 419) | def test_column_contains(self):
    method test_get_string (line 425) | def test_get_string(self):
    method test_stream (line 441) | def test_stream(self):
    method test_left_align (line 467) | def test_left_align(self):
    method test_right_align (line 484) | def test_right_align(self):
    method test_mixed_align (line 501) | def test_mixed_align(self):
    method test_align_all (line 522) | def test_align_all(self):
    method test_sign_plus (line 539) | def test_sign_plus(self):
    method test_wep_wrap (line 556) | def test_wep_wrap(self):
    method test_wep_strip (line 591) | def test_wep_strip(self):
    method test_wep_ellipsis (line 609) | def test_wep_ellipsis(self):
    method test_empty_header (line 627) | def test_empty_header(self):
    method test_eastasian_characters (line 642) | def test_eastasian_characters(self):
    method test_newline (line 661) | def test_newline(self):
    method test_newline_multiple_columns (line 670) | def test_newline_multiple_columns(self):
    method test_ansi_sequences (line 682) | def test_ansi_sequences(self):
    method test_ansi_wrap (line 690) | def test_ansi_wrap(self):
    method test_ansi_wrap_mb (line 700) | def test_ansi_wrap_mb(self):
    method test_ansi_ellipsis (line 710) | def test_ansi_ellipsis(self):
    method test_ansi_ellipsis_mb (line 720) | def test_ansi_ellipsis_mb(self):
    method test_ansi_strip (line 730) | def test_ansi_strip(self):
    method test_ansi_strip_mb (line 740) | def test_ansi_strip_mb(self):
    method test_empty_table_by_column (line 752) | def test_empty_table_by_column(self):
    method test_empty_table_by_row (line 758) | def test_empty_table_by_row(self):
    method test_table_width_zero (line 764) | def test_table_width_zero(self):
    method test_table_auto_width (line 769) | def test_table_auto_width(self):
    method test_csv_export (line 782) | def test_csv_export(self):
    method test_csv_import (line 798) | def test_csv_import(self):
    method test_df_export (line 819) | def test_df_export(self):
    method test_df_import (line 829) | def test_df_import(self):
    method test_df_export_scenario1 (line 841) | def test_df_export_scenario1(self):
    method test_df_export_scenario2 (line 852) | def test_df_export_scenario2(self):
Condensed preview — 37 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (280K chars).
[
  {
    "path": ".coveragerc",
    "chars": 220,
    "preview": "[run]\nbranch = True\nsource = beautifultable\n\n[report]\nexclude_lines =\n    if self.debug:\n    pragma: no cover\n    raise "
  },
  {
    "path": ".github/workflows/build.yml",
    "chars": 2332,
    "preview": "# This workflow will install Python dependencies, run tests and lint with a variety of Python versions\n# For more inform"
  },
  {
    "path": ".github/workflows/publish-test.yml",
    "chars": 1109,
    "preview": "# This workflow will upload a Python Package using Twine when a release is created\n# For more information see: https://h"
  },
  {
    "path": ".github/workflows/publish.yml",
    "chars": 1041,
    "preview": "# This workflow will upload a Python Package using Twine when a release is created\n# For more information see: https://h"
  },
  {
    "path": ".gitignore",
    "chars": 93,
    "preview": "__pycache__/\n*egg-info/\ndocs/_build/\ndist/\nbuild/\n.tox/\n*.pyc\n.coverage\n.vscode/\n.venv\n.idea\n"
  },
  {
    "path": "LICENSE.txt",
    "chars": 1069,
    "preview": "MIT License\n\nCopyright (c) 2022 Priyam Singh\n\nPermission is hereby granted, free of charge, to any person obtaining a co"
  },
  {
    "path": "MANIFEST.in",
    "chars": 74,
    "preview": "include LICENSE.txt\ninclude README.rst\ninclude docs/*.rst\ninclude test.py\n"
  },
  {
    "path": "README.rst",
    "chars": 12122,
    "preview": "##########################################################################\nbeautifultable\n##############################"
  },
  {
    "path": "beautifultable/__init__.py",
    "chars": 959,
    "preview": "from .__version__ import __title__, __description__, __url__, __version__\nfrom .__version__ import __copyright__, __auth"
  },
  {
    "path": "beautifultable/__version__.py",
    "chars": 305,
    "preview": "__title__ = \"beautifultable\"\n__description__ = \"Print text tables for terminals\"\n__url__ = \"https://github.com/pri22296/"
  },
  {
    "path": "beautifultable/ansi.py",
    "chars": 3525,
    "preview": "\"\"\"Module Containing class required for handling ANSI and east asian chars\"\"\"\n\nfrom __future__ import unicode_literals\n\n"
  },
  {
    "path": "beautifultable/base.py",
    "chars": 3409,
    "preview": "import abc\nimport weakref\n\n\nclass BTBaseList(metaclass=abc.ABCMeta):\n    def __init__(self, table, value):\n        self."
  },
  {
    "path": "beautifultable/beautifultable.py",
    "chars": 41487,
    "preview": "\"\"\"This module provides BeautifulTable class\n\nIt is intended for printing Tabular data to terminals.\n\nExample\n-------\n>>"
  },
  {
    "path": "beautifultable/compat.py",
    "chars": 143,
    "preview": "from itertools import zip_longest  # noqa: F401\nfrom collections.abc import Iterable  # noqa: F401\n\nto_unicode = str\nbas"
  },
  {
    "path": "beautifultable/enums.py",
    "chars": 1923,
    "preview": "from __future__ import unicode_literals\nimport enum\n\nfrom .styles import (\n    DefaultStyle,\n    NoStyle,\n    DottedStyl"
  },
  {
    "path": "beautifultable/helpers.py",
    "chars": 38489,
    "preview": "import copy\nimport weakref\nimport operator\n\nfrom . import enums\nfrom .base import BTBaseRow, BTBaseColumn\nfrom .utils im"
  },
  {
    "path": "beautifultable/meta.py",
    "chars": 1067,
    "preview": "from .base import BTBaseRow\nfrom .enums import Alignment\n\n\nclass MetaData(BTBaseRow):\n    def __init__(self, table, row)"
  },
  {
    "path": "beautifultable/styles.py",
    "chars": 4215,
    "preview": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\n\nclass NoStyle:\n    left_border_char = \"\"\n    right_bor"
  },
  {
    "path": "beautifultable/utils.py",
    "chars": 4448,
    "preview": "\"\"\"Module containing some utility methods\"\"\"\n\nimport warnings\nimport functools\n\nfrom .ansi import ANSIMultiByteString\nfr"
  },
  {
    "path": "coverage.xml",
    "chars": 63384,
    "preview": "<?xml version=\"1.0\" ?>\n<coverage branch-rate=\"0.7541\" branches-covered=\"368\" branches-valid=\"488\" complexity=\"0\" line-ra"
  },
  {
    "path": "docs/Makefile",
    "chars": 611,
    "preview": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHI"
  },
  {
    "path": "docs/badges.rst",
    "chars": 117,
    "preview": ".. include:: ../README.rst\n   :start-after: inclusion-marker-badges-start\n   :end-before: inclusion-marker-badges-end"
  },
  {
    "path": "docs/changelog.rst",
    "chars": 124,
    "preview": ".. include:: ../README.rst\n   :start-after: inclusion-marker-changelog-start\n   :end-before: inclusion-marker-changelog-"
  },
  {
    "path": "docs/conf.py",
    "chars": 5447,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n#\n# BeautifulTable documentation build configuration file, created by\n# s"
  },
  {
    "path": "docs/donation.rst",
    "chars": 121,
    "preview": ".. include:: ../README.rst\n   :start-after: inclusion-marker-donation-start\n   :end-before: inclusion-marker-donation-en"
  },
  {
    "path": "docs/index.rst",
    "chars": 634,
    "preview": ".. BeautifulTable documentation master file, created by\n   sphinx-quickstart on Sun Dec 18 15:59:32 2016.\n   You can ada"
  },
  {
    "path": "docs/install.rst",
    "chars": 119,
    "preview": ".. include:: ../README.rst\n   :start-after: inclusion-marker-install-start\n   :end-before: inclusion-marker-install-end"
  },
  {
    "path": "docs/introduction.rst",
    "chars": 129,
    "preview": ".. include:: ../README.rst\n   :start-after: inclusion-marker-introduction-start\n   :end-before: inclusion-marker-introdu"
  },
  {
    "path": "docs/links.rst",
    "chars": 115,
    "preview": ".. include:: ../README.rst\n   :start-after: inclusion-marker-links-start\n   :end-before: inclusion-marker-links-end"
  },
  {
    "path": "docs/make.bat",
    "chars": 780,
    "preview": "@ECHO OFF\n\npushd %~dp0\n\nREM Command file for Sphinx documentation\n\nif \"%SPHINXBUILD%\" == \"\" (\n\tset SPHINXBUILD=sphinx-bu"
  },
  {
    "path": "docs/quickstart.rst",
    "chars": 30528,
    "preview": "*************************************************************************\nQuickstart\n***********************************"
  },
  {
    "path": "docs/source/beautifultable.rst",
    "chars": 561,
    "preview": "beautifultable package\n======================\n\nModule contents\n---------------\n\n.. automodule:: beautifultable\n    :impo"
  },
  {
    "path": "docs/source/modules.rst",
    "chars": 79,
    "preview": "beautifultable\n==============\n\n.. toctree::\n   :maxdepth: 2\n\n   beautifultable\n"
  },
  {
    "path": "requirements.txt",
    "chars": 14,
    "preview": "wcwidth\npandas"
  },
  {
    "path": "setup.cfg",
    "chars": 26,
    "preview": "[bdist_wheel]\nuniversal=1\n"
  },
  {
    "path": "setup.py",
    "chars": 1708,
    "preview": "import os\nimport codecs\nimport itertools\n\nfrom setuptools import setup\n\ninstall_requires = [\"wcwidth\"]\n\nextras_require ="
  },
  {
    "path": "test.py",
    "chars": 29223,
    "preview": "# -*- coding: utf-8 -*-\n\n\nimport os\nimport unittest\nimport itertools\n\nfrom beautifultable import BeautifulTable\n\ntry:\n  "
  }
]

About this extraction

This page contains the full source code of the pri22296/beautifultable GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 37 files (245.8 KB), approximately 67.4k tokens, and a symbol index with 349 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!