Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-prettytable for 
openSUSE:Factory checked in at 2022-04-02 18:20:02
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-prettytable (Old)
 and      /work/SRC/openSUSE:Factory/.python-prettytable.new.1900 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-prettytable"

Sat Apr  2 18:20:02 2022 rev:3 rq:966024 version:3.2.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-prettytable/python-prettytable.changes    
2022-01-05 13:39:37.393519475 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-prettytable.new.1900/python-prettytable.changes
  2022-04-02 18:20:06.614564916 +0200
@@ -1,0 +2,15 @@
+Wed Mar 30 09:21:10 UTC 2022 - Ben Greiner <c...@bnavigator.de>
+
+- Update to 3.2.0
+  * Drop support for EOL Python 3.6 (#152) @hugovk
+  * Use <caption> tags to print html table titles (#160) @daibhid
+  * Add colorful tables and themes (#140) @BD103
+  * Convert None to empty cell or custom value (#164) @av-guy
+  * Resolve "KeyError" issue on _stringify_row (#167)
+    @michal-jagiello-tmpl
+  * Use concrete built-in exceptions instead of Exception base
+    class (#169) @hugovk
+  * Fix width for custom none_format (#174) @av-guy
+  * Enforce max widths for field names (#171) @OlafvdSpek
+
+-------------------------------------------------------------------

Old:
----
  prettytable-2.5.0.tar.gz

New:
----
  prettytable-3.2.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-prettytable.spec ++++++
--- /var/tmp/diff_new_pack.lrieLG/_old  2022-04-02 18:20:07.102559438 +0200
+++ /var/tmp/diff_new_pack.lrieLG/_new  2022-04-02 18:20:07.110559348 +0200
@@ -17,21 +17,24 @@
 #
 
 
-%{?!python_module:%define python_module() python-%{**} python3-%{**}}
+%{?!python_module:%define python_module() python3-%{**}}
 %define skip_python2 1
 Name:           python-prettytable
-Version:        2.5.0
+Version:        3.2.0
 Release:        0
 Summary:        Library for displaying tabular data in formatted fashion
 License:        BSD-2-Clause
 URL:            https://github.com/jazzband/prettytable
 Source0:        
https://files.pythonhosted.org/packages/source/p/prettytable/prettytable-%{version}.tar.gz
-BuildRequires:  %{python_module dbm}
+BuildRequires:  %{python_module base >= 3.7}
 BuildRequires:  %{python_module importlib-metadata if %python-base < 3.8}
 BuildRequires:  %{python_module pytest-lazy-fixture}
 BuildRequires:  %{python_module pytest}
 BuildRequires:  %{python_module setuptools_scm}
 BuildRequires:  %{python_module setuptools}
+BuildRequires:  %{python_module wcwidth}
+BuildRequires:  %{pythons}
+BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
 BuildArch:      noarch
 Requires:       python-wcwidth
@@ -51,12 +54,14 @@
 
 %prep
 %setup -q -n prettytable-%{version}
+sed -i '1 {/env python/d}' src/prettytable/prettytable.py
 
 %build
 %python_build
 
 %install
 %python_install
+%python_expand %fdupes %{buildroot}%{$python_sitelib}
 
 %check
 export LANG=en_US.UTF-8
@@ -65,6 +70,7 @@
 %files %{python_files}
 %license COPYING
 %doc CHANGELOG.md README.md
-%{python_sitelib}/prettytable*
+%{python_sitelib}/prettytable
+%{python_sitelib}/prettytable-%{version}*-info
 
 %changelog

++++++ prettytable-2.5.0.tar.gz -> prettytable-3.2.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prettytable-2.5.0/.github/workflows/labels.yml 
new/prettytable-3.2.0/.github/workflows/labels.yml
--- old/prettytable-2.5.0/.github/workflows/labels.yml  2021-12-20 
16:34:13.000000000 +0100
+++ new/prettytable-3.2.0/.github/workflows/labels.yml  2022-03-07 
08:13:49.000000000 +0100
@@ -1,12 +1,15 @@
 name: Sync labels
+
 on:
   push:
     branches:
       - master
     paths:
       - .github/labels.yml
+  workflow_dispatch:
+
 jobs:
-  build:
+  sync:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prettytable-2.5.0/.github/workflows/lint.yml 
new/prettytable-3.2.0/.github/workflows/lint.yml
--- old/prettytable-2.5.0/.github/workflows/lint.yml    2021-12-20 
16:34:13.000000000 +0100
+++ new/prettytable-3.2.0/.github/workflows/lint.yml    2022-03-07 
08:13:49.000000000 +0100
@@ -1,6 +1,6 @@
 name: Lint
 
-on: [push, pull_request]
+on: [push, pull_request, workflow_dispatch]
 
 jobs:
   lint:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/prettytable-2.5.0/.github/workflows/release-drafter.yml 
new/prettytable-3.2.0/.github/workflows/release-drafter.yml
--- old/prettytable-2.5.0/.github/workflows/release-drafter.yml 2021-12-20 
16:34:13.000000000 +0100
+++ new/prettytable-3.2.0/.github/workflows/release-drafter.yml 2022-03-07 
08:13:49.000000000 +0100
@@ -5,13 +5,14 @@
     # branches to consider in the event; optional, defaults to all
     branches:
       - master
+  workflow_dispatch:
 
 jobs:
   update_release_draft:
     permissions:
       contents: write
       pull-requests: read
-    if: github.repository == 'jazzband/prettytable'
+    if: github.repository_owner == 'jazzband'
     runs-on: ubuntu-latest
     steps:
       # Drafts your next release notes as pull requests are merged into 
"master"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prettytable-2.5.0/.github/workflows/release.yml 
new/prettytable-3.2.0/.github/workflows/release.yml
--- old/prettytable-2.5.0/.github/workflows/release.yml 2021-12-20 
16:34:13.000000000 +0100
+++ new/prettytable-3.2.0/.github/workflows/release.yml 2022-03-07 
08:13:49.000000000 +0100
@@ -7,10 +7,11 @@
   release:
     types:
       - published
+  workflow_dispatch:
 
 jobs:
   build:
-    if: github.repository == 'jazzband/prettytable'
+    if: github.repository_owner == 'jazzband'
     runs-on: ubuntu-latest
 
     steps:
@@ -21,31 +22,20 @@
       - name: Set up Python
         uses: actions/setup-python@v2
         with:
-          python-version: 3.8
-
-      - name: Get pip cache dir
-        id: pip-cache
-        run: |
-          echo "::set-output name=dir::$(pip cache dir)"
-
-      - name: Cache
-        uses: actions/cache@v2
-        with:
-          path: ${{ steps.pip-cache.outputs.dir }}
-          key: release-${{ hashFiles('**/setup.py') }}
-          restore-keys: |
-            release-
+          python-version: "3.10"
+          cache: pip
+          cache-dependency-path: "setup.py"
 
       - name: Install dependencies
         run: |
           python -m pip install -U pip
-          python -m pip install -U setuptools twine wheel
+          python -m pip install -U build twine wheel
 
       - name: Build package
         run: |
           python setup.py --version
-          python setup.py sdist --format=gztar bdist_wheel
-          twine check dist/*
+          python -m build
+          twine check --strict dist/*
 
       - name: Upload packages to Jazzband
         if: github.event.action == 'published'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prettytable-2.5.0/.github/workflows/test.yml 
new/prettytable-3.2.0/.github/workflows/test.yml
--- old/prettytable-2.5.0/.github/workflows/test.yml    2021-12-20 
16:34:13.000000000 +0100
+++ new/prettytable-3.2.0/.github/workflows/test.yml    2022-03-07 
08:13:49.000000000 +0100
@@ -1,17 +1,17 @@
 name: Test
 
-on: [push, pull_request]
+on: [push, pull_request, workflow_dispatch]
 
 env:
   FORCE_COLOR: 1
 
 jobs:
-  build:
+  test:
     runs-on: ${{ matrix.os }}
     strategy:
       fail-fast: false
       matrix:
-        python-version: ["pypy-3.8", "3.6", "3.7", "3.8", "3.9", "3.10"]
+        python-version: ["pypy-3.8", "3.7", "3.8", "3.9", "3.10"]
         os: [ubuntu-latest, macos-latest, windows-latest]
         include:
           # Include new variables for Codecov
@@ -26,21 +26,8 @@
         uses: actions/setup-python@v2
         with:
           python-version: ${{ matrix.python-version }}
-
-      - name: Get pip cache dir
-        id: pip-cache
-        run: |
-          echo "::set-output name=dir::$(pip cache dir)"
-
-      - name: Cache
-        uses: actions/cache@v2
-        with:
-          path: ${{ steps.pip-cache.outputs.dir }}
-          key:
-            ${{ matrix.os }}-${{ matrix.python-version }}-v1-${{
-            hashFiles('**/setup.py') }}
-          restore-keys: |
-            ${{ matrix.os }}-${{ matrix.python-version }}-v1-
+          cache: pip
+          cache-dependency-path: "setup.py"
 
       - name: Install dependencies
         run: |
@@ -52,7 +39,7 @@
           tox -e py
 
       - name: Upload coverage
-        uses: codecov/codecov-action@v1
+        uses: codecov/codecov-action@v2
         with:
           flags: ${{ matrix.codecov-flag }}
           name: ${{ matrix.os }} Python ${{ matrix.python-version }}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prettytable-2.5.0/.pre-commit-config.yaml 
new/prettytable-3.2.0/.pre-commit-config.yaml
--- old/prettytable-2.5.0/.pre-commit-config.yaml       2021-12-20 
16:34:13.000000000 +0100
+++ new/prettytable-3.2.0/.pre-commit-config.yaml       2022-03-07 
08:13:49.000000000 +0100
@@ -1,18 +1,18 @@
 repos:
   - repo: https://github.com/asottile/pyupgrade
-    rev: v2.29.0
+    rev: v2.31.0
     hooks:
       - id: pyupgrade
-        args: ["--py36-plus"]
+        args: [--py37-plus]
 
   - repo: https://github.com/psf/black
-    rev: 21.9b0
+    rev: 21.12b0
     hooks:
       - id: black
-        args: ["--target-version", "py36"]
+        args: [--target-version=py37]
 
   - repo: https://github.com/PyCQA/isort
-    rev: 5.9.3
+    rev: 5.10.1
     hooks:
       - id: isort
 
@@ -28,24 +28,23 @@
       - id: python-check-blanket-noqa
 
   - repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v4.0.1
+    rev: v4.1.0
     hooks:
       - id: check-merge-conflict
       - id: check-yaml
 
   - repo: https://github.com/asottile/setup-cfg-fmt
-    rev: v1.18.0
+    rev: v1.20.0
     hooks:
       - id: setup-cfg-fmt
-        args: ["--max-py-version=3.10"]
 
   - repo: https://github.com/tox-dev/tox-ini-fmt
-    rev: 0.5.1
+    rev: 0.5.2
     hooks:
       - id: tox-ini-fmt
 
   - repo: https://github.com/pre-commit/mirrors-prettier
-    rev: v2.4.1
+    rev: v2.5.1
     hooks:
       - id: prettier
         args: [--prose-wrap=always, --print-width=88]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prettytable-2.5.0/PKG-INFO 
new/prettytable-3.2.0/PKG-INFO
--- old/prettytable-2.5.0/PKG-INFO      2021-12-20 16:34:33.773119000 +0100
+++ new/prettytable-3.2.0/PKG-INFO      2022-03-07 08:14:08.130133000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: prettytable
-Version: 2.5.0
+Version: 3.2.0
 Summary: A simple Python library for easily displaying tabular data in a 
visually appealing ASCII table format
 Home-page: https://github.com/jazzband/prettytable
 Author: Luke Maurits
@@ -13,7 +13,6 @@
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: 3 :: Only
-Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: 3.7
 Classifier: Programming Language :: Python :: 3.8
 Classifier: Programming Language :: Python :: 3.9
@@ -21,7 +20,7 @@
 Classifier: Programming Language :: Python :: Implementation :: CPython
 Classifier: Programming Language :: Python :: Implementation :: PyPy
 Classifier: Topic :: Text Processing
-Requires-Python: >=3.6
+Requires-Python: >=3.7
 Description-Content-Type: text/markdown
 Provides-Extra: tests
 License-File: COPYING
@@ -523,6 +522,46 @@
 print(x)
 ```
 
+### Changing the appearance of your table - with _colors_!
+
+PrettyTable has the functionality of printing your table with ANSI color 
codes. This
+includes support for most Windows versions through
+[Colorama](https://pypi.org/project/colorama/). To get started, import the 
`ColorTable`
+class instead of `PrettyTable`.
+
+```diff
+-from prettytable import PrettyTable
++from prettytable.colortable import ColorTable
+```
+
+The `ColorTable` class can be used the same as `PrettyTable`, but it adds an 
extra
+property. You can now specify a custom _theme_ that will format your table 
with colors.
+
+```python
+from prettytable.colortable import ColorTable, Themes
+
+x = ColorTable(theme=Themes.OCEAN)
+
+print(x)
+```
+
+#### Creating a custom theme
+
+The `Theme` class allows you to customize both the characters and colors used 
in your
+table.
+
+| Argument                                                   | Description     
                                          |
+| ---------------------------------------------------------- | 
--------------------------------------------------------- |
+| `default_color`                                            | The color to 
use as default                               |
+| `vertical_char`, `horizontal_char`, and `junction_char`    | The characters 
used for creating the outline of the table |
+| `vertical_color`, `horizontal_color`, and `junction_color` | The colors used 
to style each character.                  |
+
+> **Note:** Colors are formatted with the `Theme.format_code(s: str)` 
function. It
+> accepts a string. If the string starts with an escape code (like `\x1b`) 
then it will
+> return the given string. If the string is just whitespace, it will return 
`""`. If the
+> string is a number (like `"34"`), it will automatically format it into an 
escape code.
+> I recommend you look into the source code for more information.
+
 ### Displaying your table in JSON
 
 PrettyTable will also print your tables in JSON, as a list of fields and an 
array of
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prettytable-2.5.0/README.md 
new/prettytable-3.2.0/README.md
--- old/prettytable-2.5.0/README.md     2021-12-20 16:34:13.000000000 +0100
+++ new/prettytable-3.2.0/README.md     2022-03-07 08:13:49.000000000 +0100
@@ -495,6 +495,46 @@
 print(x)
 ```
 
+### Changing the appearance of your table - with _colors_!
+
+PrettyTable has the functionality of printing your table with ANSI color 
codes. This
+includes support for most Windows versions through
+[Colorama](https://pypi.org/project/colorama/). To get started, import the 
`ColorTable`
+class instead of `PrettyTable`.
+
+```diff
+-from prettytable import PrettyTable
++from prettytable.colortable import ColorTable
+```
+
+The `ColorTable` class can be used the same as `PrettyTable`, but it adds an 
extra
+property. You can now specify a custom _theme_ that will format your table 
with colors.
+
+```python
+from prettytable.colortable import ColorTable, Themes
+
+x = ColorTable(theme=Themes.OCEAN)
+
+print(x)
+```
+
+#### Creating a custom theme
+
+The `Theme` class allows you to customize both the characters and colors used 
in your
+table.
+
+| Argument                                                   | Description     
                                          |
+| ---------------------------------------------------------- | 
--------------------------------------------------------- |
+| `default_color`                                            | The color to 
use as default                               |
+| `vertical_char`, `horizontal_char`, and `junction_char`    | The characters 
used for creating the outline of the table |
+| `vertical_color`, `horizontal_color`, and `junction_color` | The colors used 
to style each character.                  |
+
+> **Note:** Colors are formatted with the `Theme.format_code(s: str)` 
function. It
+> accepts a string. If the string starts with an escape code (like `\x1b`) 
then it will
+> return the given string. If the string is just whitespace, it will return 
`""`. If the
+> string is a number (like `"34"`), it will automatically format it into an 
escape code.
+> I recommend you look into the source code for more information.
+
 ### Displaying your table in JSON
 
 PrettyTable will also print your tables in JSON, as a list of fields and an 
array of
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prettytable-2.5.0/setup.cfg 
new/prettytable-3.2.0/setup.cfg
--- old/prettytable-2.5.0/setup.cfg     2021-12-20 16:34:33.773119000 +0100
+++ new/prettytable-3.2.0/setup.cfg     2022-03-07 08:14:08.130133000 +0100
@@ -13,7 +13,6 @@
        Programming Language :: Python
        Programming Language :: Python :: 3
        Programming Language :: Python :: 3 :: Only
-       Programming Language :: Python :: 3.6
        Programming Language :: Python :: 3.7
        Programming Language :: Python :: 3.8
        Programming Language :: Python :: 3.9
@@ -29,10 +28,10 @@
 install_requires = 
        wcwidth
        importlib-metadata;python_version < '3.8'
-python_requires = >=3.6
+python_requires = >=3.7
 package_dir = =src
 setup_requires = 
-       setuptools_scm
+       setuptools-scm
 
 [options.packages.find]
 where = src
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prettytable-2.5.0/src/prettytable/colortable.py 
new/prettytable-3.2.0/src/prettytable/colortable.py
--- old/prettytable-2.5.0/src/prettytable/colortable.py 1970-01-01 
01:00:00.000000000 +0100
+++ new/prettytable-3.2.0/src/prettytable/colortable.py 2022-03-07 
08:13:49.000000000 +0100
@@ -0,0 +1,96 @@
+from .prettytable import PrettyTable
+
+try:
+    from colorama import init
+except ImportError:
+    # Do nothing if not installed
+    def init():
+        pass
+
+
+init()
+
+RESET_CODE = "\x1b[0m"
+
+
+class Theme:
+    def __init__(
+        self,
+        default_color: str = "",
+        vertical_char: str = "|",
+        vertical_color: str = "",
+        horizontal_char: str = "-",
+        horizontal_color: str = "",
+        junction_char: str = "+",
+        junction_color: str = "",
+    ):
+        self.default_color = Theme.format_code(default_color)
+        self.vertical_char = vertical_char
+        self.vertical_color = Theme.format_code(vertical_color)
+        self.horizontal_char = horizontal_char
+        self.horizontal_color = Theme.format_code(horizontal_color)
+        self.junction_char = junction_char
+        self.junction_color = Theme.format_code(junction_color)
+
+    def format_code(s: str) -> str:
+        """Takes string and intelligently puts it into an ANSI escape 
sequence"""
+        if s.strip() == "":
+            return ""
+        elif s.startswith("\x1b["):
+            return s
+        else:
+            return f"\x1b[{s}m"
+
+
+class Themes:
+    DEFAULT = Theme()
+    OCEAN = Theme(
+        default_color="96",
+        vertical_color="34",
+        horizontal_color="34",
+        junction_color="36",
+    )
+
+
+class ColorTable(PrettyTable):
+    def __init__(self, field_names=None, **kwargs):
+        super().__init__(field_names=field_names, **kwargs)
+        # TODO: Validate option
+
+        self.theme = kwargs.get("theme") or Themes.DEFAULT
+
+    @property
+    def theme(self) -> Theme:
+        return self._theme
+
+    @theme.setter
+    def theme(self, value: Theme):
+        self._theme = value
+        self.update_theme()
+
+    def update_theme(self):
+        theme = self._theme
+
+        self._vertical_char = (
+            theme.vertical_color
+            + theme.vertical_char
+            + RESET_CODE
+            + theme.default_color
+        )
+
+        self._horizontal_char = (
+            theme.horizontal_color
+            + theme.horizontal_char
+            + RESET_CODE
+            + theme.default_color
+        )
+
+        self._junction_char = (
+            theme.junction_color
+            + theme.junction_char
+            + RESET_CODE
+            + theme.default_color
+        )
+
+    def get_string(self, **kwargs):
+        return super().get_string(**kwargs) + RESET_CODE
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prettytable-2.5.0/src/prettytable/prettytable.py 
new/prettytable-3.2.0/src/prettytable/prettytable.py
--- old/prettytable-2.5.0/src/prettytable/prettytable.py        2021-12-20 
16:34:13.000000000 +0100
+++ new/prettytable-3.2.0/src/prettytable/prettytable.py        2022-03-07 
08:13:49.000000000 +0100
@@ -72,7 +72,6 @@
 
 class PrettyTable:
     def __init__(self, field_names=None, **kwargs):
-
         """Return a new PrettyTable instance
 
         Arguments:
@@ -188,6 +187,7 @@
             "valign",
             "max_width",
             "min_width",
+            "none_format",
         ]
         for option in self._options:
             if option in kwargs:
@@ -199,6 +199,7 @@
         self._start = kwargs["start"] or 0
         self._end = kwargs["end"] or None
         self._fields = kwargs["fields"] or None
+        self._none_format = {}
 
         if kwargs["header"] in (True, False):
             self._header = kwargs["header"]
@@ -227,6 +228,7 @@
         self.int_format = kwargs["int_format"] or {}
         self.float_format = kwargs["float_format"] or {}
         self.custom_format = kwargs["custom_format"] or {}
+        self.none_format = kwargs["none_format"] or {}
 
         self._min_table_width = kwargs["min_table_width"] or None
         self._max_table_width = kwargs["max_table_width"] or None
@@ -310,7 +312,7 @@
         elif isinstance(index, int):
             new.add_row(self._rows[index])
         else:
-            raise Exception(f"Index {index} is invalid, must be an integer or 
slice")
+            raise IndexError(f"Index {index} is invalid, must be an integer or 
slice")
         return new
 
     def __str__(self):
@@ -342,6 +344,8 @@
     def _validate_option(self, option, val):
         if option == "field_names":
             self._validate_field_names(val)
+        elif option == "none_format":
+            self._validate_none_format(val)
         elif option in (
             "start",
             "end",
@@ -407,7 +411,7 @@
             try:
                 assert len(val) == len(self._field_names)
             except AssertionError:
-                raise Exception(
+                raise ValueError(
                     "Field name list has incorrect number of values, "
                     f"(actual) {len(val)}!={len(self._field_names)} (expected)"
                 )
@@ -415,7 +419,7 @@
             try:
                 assert len(val) == len(self._rows[0])
             except AssertionError:
-                raise Exception(
+                raise ValueError(
                     "Field name list has incorrect number of values, "
                     f"(actual) {len(val)}!={len(self._rows[0])} (expected)"
                 )
@@ -423,13 +427,22 @@
         try:
             assert len(val) == len(set(val))
         except AssertionError:
-            raise Exception("Field names must be unique")
+            raise ValueError("Field names must be unique")
+
+    def _validate_none_format(self, val):
+        try:
+            if val is not None:
+                assert isinstance(val, str)
+        except AssertionError:
+            raise TypeError(
+                "Replacement for None value must be a string if being 
supplied."
+            )
 
     def _validate_header_style(self, val):
         try:
             assert val in ("cap", "title", "upper", "lower", None)
         except AssertionError:
-            raise Exception(
+            raise ValueError(
                 "Invalid header style, use cap, title, upper, lower or None"
             )
 
@@ -437,25 +450,25 @@
         try:
             assert val in ["l", "c", "r"]
         except AssertionError:
-            raise Exception(f"Alignment {val} is invalid, use l, c or r")
+            raise ValueError(f"Alignment {val} is invalid, use l, c or r")
 
     def _validate_valign(self, val):
         try:
             assert val in ["t", "m", "b", None]
         except AssertionError:
-            raise Exception(f"Alignment {val} is invalid, use t, m, b or None")
+            raise ValueError(f"Alignment {val} is invalid, use t, m, b or 
None")
 
     def _validate_nonnegative_int(self, name, val):
         try:
             assert int(val) >= 0
         except AssertionError:
-            raise Exception(f"Invalid value for {name}: {val}")
+            raise ValueError(f"Invalid value for {name}: {val}")
 
     def _validate_true_or_false(self, name, val):
         try:
             assert val in (True, False)
         except AssertionError:
-            raise Exception(f"Invalid value for {name}. Must be True or 
False.")
+            raise ValueError(f"Invalid value for {name}. Must be True or 
False.")
 
     def _validate_int_format(self, name, val):
         if val == "":
@@ -464,7 +477,7 @@
             assert isinstance(val, str)
             assert val.isdigit()
         except AssertionError:
-            raise Exception(
+            raise ValueError(
                 f"Invalid value for {name}. Must be an integer format string."
             )
 
@@ -483,19 +496,21 @@
                 or (bits[1][-1] == "f" and bits[1].rstrip("f").isdigit())
             )
         except AssertionError:
-            raise Exception(f"Invalid value for {name}. Must be a float format 
string.")
+            raise ValueError(
+                f"Invalid value for {name}. Must be a float format string."
+            )
 
     def _validate_function(self, name, val):
         try:
             assert hasattr(val, "__call__")
         except AssertionError:
-            raise Exception(f"Invalid value for {name}. Must be a function.")
+            raise ValueError(f"Invalid value for {name}. Must be a function.")
 
     def _validate_hrules(self, name, val):
         try:
             assert val in (ALL, FRAME, HEADER, NONE)
         except AssertionError:
-            raise Exception(
+            raise ValueError(
                 f"Invalid value for {name}. Must be ALL, FRAME, HEADER or 
NONE."
             )
 
@@ -503,32 +518,32 @@
         try:
             assert val in (ALL, FRAME, NONE)
         except AssertionError:
-            raise Exception(f"Invalid value for {name}. Must be ALL, FRAME, or 
NONE.")
+            raise ValueError(f"Invalid value for {name}. Must be ALL, FRAME, 
or NONE.")
 
     def _validate_field_name(self, name, val):
         try:
             assert (val in self._field_names) or (val is None)
         except AssertionError:
-            raise Exception(f"Invalid field name: {val}")
+            raise ValueError(f"Invalid field name: {val}")
 
     def _validate_all_field_names(self, name, val):
         try:
             for x in val:
                 self._validate_field_name(name, x)
         except AssertionError:
-            raise Exception("Fields must be a sequence of field names")
+            raise ValueError("Fields must be a sequence of field names")
 
     def _validate_single_char(self, name, val):
         try:
             assert _str_block_width(val) == 1
         except AssertionError:
-            raise Exception(f"Invalid value for {name}. Must be a string of 
length 1.")
+            raise ValueError(f"Invalid value for {name}. Must be a string of 
length 1.")
 
     def _validate_attributes(self, name, val):
         try:
             assert isinstance(val, dict)
         except AssertionError:
-            raise Exception("Attributes must be a dictionary of name/value 
pairs")
+            raise TypeError("Attributes must be a dictionary of name/value 
pairs")
 
     ##############################
     # ATTRIBUTE MANAGEMENT       #
@@ -548,6 +563,22 @@
         self._xhtml = val
 
     @property
+    def none_format(self):
+        return self._none_format
+
+    @none_format.setter
+    def none_format(self, val):
+        if not self._field_names:
+            self._none_format = {}
+        elif val is None or (isinstance(val, dict) and len(val) == 0):
+            for field in self._field_names:
+                self._none_format[field] = None
+        else:
+            self._validate_none_format(val)
+            for field in self._field_names:
+                self._none_format[field] = val
+
+    @property
     def field_names(self):
         """List or tuple of field names
 
@@ -894,7 +925,7 @@
             for field in self._field_names:
                 self._custom_format[field] = val
         else:
-            raise Exception(
+            raise TypeError(
                 "The custom_format property need to be a dictionary or 
callable"
             )
 
@@ -1221,7 +1252,7 @@
         elif style == RANDOM:
             self._set_random_style()
         else:
-            raise Exception("Invalid pre-set style")
+            raise ValueError("Invalid pre-set style")
 
     def _set_orgmode_style(self):
         self._set_default_style()
@@ -1342,7 +1373,7 @@
         has fields"""
 
         if self._field_names and len(row) != len(self._field_names):
-            raise Exception(
+            raise ValueError(
                 "Row has incorrect number of values, "
                 f"(actual) {len(row)}!={len(self._field_names)} (expected)"
             )
@@ -1359,7 +1390,7 @@
         row_index - The index of the row you want to delete.  Indexing starts 
at 0."""
 
         if row_index > len(self._rows) - 1:
-            raise Exception(
+            raise IndexError(
                 f"Can't delete row at index {row_index}, "
                 f"table only has {len(self._rows)} rows"
             )
@@ -1390,7 +1421,7 @@
                     self._rows.append([])
                 self._rows[i].append(column[i])
         else:
-            raise Exception(
+            raise ValueError(
                 f"Column length {len(column)} does not match number of rows "
                 f"{len(self._rows)}"
             )
@@ -1414,7 +1445,7 @@
         fieldname - The field name of the column you want to delete."""
 
         if fieldname not in self._field_names:
-            raise Exception(
+            raise ValueError(
                 "Can't delete column %r which is not a field name of this 
table."
                 " Field names are: %s"
                 % (fieldname, ", ".join(map(repr, self._field_names)))
@@ -1479,6 +1510,9 @@
         for row in rows:
             for index, value in enumerate(row):
                 fieldname = self.field_names[index]
+                if self.none_format.get(fieldname) is not None:
+                    if value == "None" or value is None:
+                        value = self.none_format.get(fieldname)
                 if fieldname in self.max_width:
                     widths[index] = max(
                         widths[index],
@@ -1772,6 +1806,8 @@
                 fieldname = field.lower()
             else:
                 fieldname = field
+            if _str_block_width(fieldname) > width:
+                fieldname = fieldname[:width]
             bits.append(
                 " " * lpad
                 + self._justify(fieldname, width, self._align[field])
@@ -1801,6 +1837,8 @@
             lines = value.split("\n")
             new_lines = []
             for line in lines:
+                if line == "None" and self.none_format.get(field) is not None:
+                    line = self.none_format[field]
                 if _str_block_width(line) > width:
                     line = textwrap.fill(line, width)
                 new_lines.append(line)
@@ -2005,12 +2043,7 @@
         # Title
         title = options["title"] or self._title
         if title:
-            cols = (
-                len(options["fields"]) if options["fields"] else 
len(self.field_names)
-            )
-            lines.append("    <tr>")
-            lines.append(f"        <td colspan={cols}>{title}</td>")
-            lines.append("    </tr>")
+            lines.append(f"    <caption>{title}</caption>")
 
         # Headers
         if options["header"]:
@@ -2077,12 +2110,7 @@
         # Title
         title = options["title"] or self._title
         if title:
-            cols = (
-                len(options["fields"]) if options["fields"] else 
len(self.field_names)
-            )
-            lines.append("    <tr>")
-            lines.append(f"        <td colspan={cols}>{title}</td>")
-            lines.append("    </tr>")
+            lines.append(f"    <caption>{title}</caption>")
 
         # Headers
         if options["header"]:
@@ -2413,7 +2441,7 @@
     try:
         assert len(tables) == 1
     except AssertionError:
-        raise Exception(
+        raise ValueError(
             "More than one <table> in provided HTML code. Use from_html 
instead."
         )
     return tables[0]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prettytable-2.5.0/src/prettytable.egg-info/PKG-INFO 
new/prettytable-3.2.0/src/prettytable.egg-info/PKG-INFO
--- old/prettytable-2.5.0/src/prettytable.egg-info/PKG-INFO     2021-12-20 
16:34:33.000000000 +0100
+++ new/prettytable-3.2.0/src/prettytable.egg-info/PKG-INFO     2022-03-07 
08:14:07.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: prettytable
-Version: 2.5.0
+Version: 3.2.0
 Summary: A simple Python library for easily displaying tabular data in a 
visually appealing ASCII table format
 Home-page: https://github.com/jazzband/prettytable
 Author: Luke Maurits
@@ -13,7 +13,6 @@
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: 3 :: Only
-Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: 3.7
 Classifier: Programming Language :: Python :: 3.8
 Classifier: Programming Language :: Python :: 3.9
@@ -21,7 +20,7 @@
 Classifier: Programming Language :: Python :: Implementation :: CPython
 Classifier: Programming Language :: Python :: Implementation :: PyPy
 Classifier: Topic :: Text Processing
-Requires-Python: >=3.6
+Requires-Python: >=3.7
 Description-Content-Type: text/markdown
 Provides-Extra: tests
 License-File: COPYING
@@ -523,6 +522,46 @@
 print(x)
 ```
 
+### Changing the appearance of your table - with _colors_!
+
+PrettyTable has the functionality of printing your table with ANSI color 
codes. This
+includes support for most Windows versions through
+[Colorama](https://pypi.org/project/colorama/). To get started, import the 
`ColorTable`
+class instead of `PrettyTable`.
+
+```diff
+-from prettytable import PrettyTable
++from prettytable.colortable import ColorTable
+```
+
+The `ColorTable` class can be used the same as `PrettyTable`, but it adds an 
extra
+property. You can now specify a custom _theme_ that will format your table 
with colors.
+
+```python
+from prettytable.colortable import ColorTable, Themes
+
+x = ColorTable(theme=Themes.OCEAN)
+
+print(x)
+```
+
+#### Creating a custom theme
+
+The `Theme` class allows you to customize both the characters and colors used 
in your
+table.
+
+| Argument                                                   | Description     
                                          |
+| ---------------------------------------------------------- | 
--------------------------------------------------------- |
+| `default_color`                                            | The color to 
use as default                               |
+| `vertical_char`, `horizontal_char`, and `junction_char`    | The characters 
used for creating the outline of the table |
+| `vertical_color`, `horizontal_color`, and `junction_color` | The colors used 
to style each character.                  |
+
+> **Note:** Colors are formatted with the `Theme.format_code(s: str)` 
function. It
+> accepts a string. If the string starts with an escape code (like `\x1b`) 
then it will
+> return the given string. If the string is just whitespace, it will return 
`""`. If the
+> string is a number (like `"34"`), it will automatically format it into an 
escape code.
+> I recommend you look into the source code for more information.
+
 ### Displaying your table in JSON
 
 PrettyTable will also print your tables in JSON, as a list of fields and an 
array of
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/prettytable-2.5.0/src/prettytable.egg-info/SOURCES.txt 
new/prettytable-3.2.0/src/prettytable.egg-info/SOURCES.txt
--- old/prettytable-2.5.0/src/prettytable.egg-info/SOURCES.txt  2021-12-20 
16:34:33.000000000 +0100
+++ new/prettytable-3.2.0/src/prettytable.egg-info/SOURCES.txt  2022-03-07 
08:14:08.000000000 +0100
@@ -20,10 +20,12 @@
 .github/workflows/release.yml
 .github/workflows/test.yml
 src/prettytable/__init__.py
+src/prettytable/colortable.py
 src/prettytable/prettytable.py
 src/prettytable.egg-info/PKG-INFO
 src/prettytable.egg-info/SOURCES.txt
 src/prettytable.egg-info/dependency_links.txt
 src/prettytable.egg-info/requires.txt
 src/prettytable.egg-info/top_level.txt
+tests/test_colortable.py
 tests/test_prettytable.py
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prettytable-2.5.0/tests/test_colortable.py 
new/prettytable-3.2.0/tests/test_colortable.py
--- old/prettytable-2.5.0/tests/test_colortable.py      1970-01-01 
01:00:00.000000000 +0100
+++ new/prettytable-3.2.0/tests/test_colortable.py      2022-03-07 
08:13:49.000000000 +0100
@@ -0,0 +1,92 @@
+import pytest
+
+from prettytable import PrettyTable
+from prettytable.colortable import RESET_CODE, ColorTable, Theme
+
+
+@pytest.fixture
+def row_prettytable():
+    # Row by row...
+    row = PrettyTable()
+    row.field_names = ["City name", "Area", "Population", "Annual Rainfall"]
+    row.add_row(["Adelaide", 1295, 1158259, 600.5])
+    row.add_row(["Brisbane", 5905, 1857594, 1146.4])
+    row.add_row(["Darwin", 112, 120900, 1714.7])
+    row.add_row(["Hobart", 1357, 205556, 619.5])
+    row.add_row(["Sydney", 2058, 4336374, 1214.8])
+    row.add_row(["Melbourne", 1566, 3806092, 646.9])
+    row.add_row(["Perth", 5386, 1554769, 869.4])
+    return row
+
+
+@pytest.fixture
+def row_colortable():
+    row = ColorTable()
+    row.field_names = ["City name", "Area", "Population", "Annual Rainfall"]
+    row.add_row(["Adelaide", 1295, 1158259, 600.5])
+    row.add_row(["Brisbane", 5905, 1857594, 1146.4])
+    row.add_row(["Darwin", 112, 120900, 1714.7])
+    row.add_row(["Hobart", 1357, 205556, 619.5])
+    row.add_row(["Sydney", 2058, 4336374, 1214.8])
+    row.add_row(["Melbourne", 1566, 3806092, 646.9])
+    row.add_row(["Perth", 5386, 1554769, 869.4])
+    return row
+
+
+@pytest.fixture
+def color_theme():
+    return Theme(
+        default_color="31",
+        vertical_color="32",
+        horizontal_color="33",
+        junction_color="34",
+    )
+
+
+class TestColorTable:
+    def test_themeless(self, row_prettytable, row_colortable):
+        # Not worth the logic customizing the reset code
+        # For now we'll just get rid of it
+        assert (
+            row_colortable.get_string().replace(RESET_CODE, "")
+            == row_prettytable.get_string()
+        )
+
+    def test_theme_setter(self, color_theme):
+        table1 = ColorTable(theme=color_theme)
+
+        table2 = ColorTable()
+        table2.theme = color_theme
+
+        assert table1.theme == table2.theme
+
+        dict1 = table1.__dict__
+        dict2 = table2.__dict__
+
+        # So we don't compare functions
+        del dict1["_sort_key"]
+        del dict2["_sort_key"]
+
+        assert dict1 == dict2
+
+
+class TestFormatCode:
+    def test_basic(self):
+        assert Theme.format_code("31") == "\x1b[31m"
+
+    def test_prefix(self):
+        assert Theme.format_code("\x1b[35m") == "\x1b[35m"
+
+    def test_escapes(self):
+        assert Theme.format_code("\033[41m") == "\x1b[41m"
+        assert Theme.format_code("\u001b[41m") == "\x1b[41m"
+
+    def test_empty(self):
+        assert Theme.format_code("") == ""
+
+    def test_stripped(self):
+        assert Theme.format_code("\t\t     \t") == ""
+
+    def test_multiple(self):
+        assert Theme.format_code("30;42") == "\x1b[30;42m"
+        assert Theme.format_code("\x1b[30;42m") == "\x1b[30;42m"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prettytable-2.5.0/tests/test_prettytable.py 
new/prettytable-3.2.0/tests/test_prettytable.py
--- old/prettytable-2.5.0/tests/test_prettytable.py     2021-12-20 
16:34:13.000000000 +0100
+++ new/prettytable-3.2.0/tests/test_prettytable.py     2022-03-07 
08:13:49.000000000 +0100
@@ -94,6 +94,119 @@
     return mix
 
 
+class TestNoneOption:
+    def test_none_char_valid_option(self):
+        PrettyTable(["Field 1", "Field 2", "Field 3"], none_format="")
+
+    def test_none_char_invalid_option(self):
+        with pytest.raises(TypeError) as exc:
+            PrettyTable(["Field 1", "Field 2", "Field 3"], none_format=2)
+            assert "must be a string." in exc.value
+
+    def test_no_value_replace_none(self):
+        t = PrettyTable(["Field 1", "Field 2", "Field 3"])
+        t.add_row(["value 1", None, "value 2"])
+        assert (
+            t.get_string().strip()
+            == """
++---------+---------+---------+
+| Field 1 | Field 2 | Field 3 |
++---------+---------+---------+
+| value 1 |   None  | value 2 |
++---------+---------+---------+
+""".strip()
+        )
+
+    def test_no_value_replace_none_with_default_field_names(self):
+        t = PrettyTable()
+        t.add_row(["value 1", "None", "value 2"])
+        assert (
+            t.get_string().strip()
+            == """
++---------+---------+---------+
+| Field 1 | Field 2 | Field 3 |
++---------+---------+---------+
+| value 1 |   None  | value 2 |
++---------+---------+---------+
+""".strip()
+        )
+
+    def test_replace_none_all(self):
+        t = PrettyTable(["Field 1", "Field 2", "Field 3"], none_format="N/A")
+        t.add_row(["value 1", None, "None"])
+        assert (
+            t.get_string().strip()
+            == """
++---------+---------+---------+
+| Field 1 | Field 2 | Field 3 |
++---------+---------+---------+
+| value 1 |   N/A   |   N/A   |
++---------+---------+---------+
+""".strip()
+        )
+
+    def test_replace_none_by_col(self):
+        t = PrettyTable(["Field 1", "Field 2", "Field 3"])
+        t.none_format["Field 2"] = "N/A"
+        t.none_format["Field 3"] = ""
+        t.add_row(["value 1", None, None])
+        assert (
+            t.get_string().strip()
+            == """
++---------+---------+---------+
+| Field 1 | Field 2 | Field 3 |
++---------+---------+---------+
+| value 1 |   N/A   |         |
++---------+---------+---------+
+""".strip()
+        )
+
+    def test_replace_none_recompute_width(self):
+        t = PrettyTable()
+        t.add_row([None])
+        t.none_format = "0123456789"
+        assert (
+            t.get_string().strip()
+            == """
++------------+
+|  Field 1   |
++------------+
+| 0123456789 |
++------------+
+""".strip()
+        )
+
+    def test_replace_none_maintain_width_on_recompute(self):
+        t = PrettyTable()
+        t.add_row(["Hello"])
+        t.none_format = "0123456789"
+        assert (
+            t.get_string().strip()
+            == """
++---------+
+| Field 1 |
++---------+
+|  Hello  |
++---------+
+""".strip()
+        )
+
+    def test_replace_none_recompute_width_multi_column(self):
+        t = PrettyTable()
+        t.add_row(["Hello", None, "World"])
+        t.none_format = "0123456789"
+        assert (
+            t.get_string().strip()
+            == """
++---------+------------+---------+
+| Field 1 |  Field 2   | Field 3 |
++---------+------------+---------+
+|  Hello  | 0123456789 |  World  |
++---------+------------+---------+
+""".strip()
+        )
+
+
 class TestBuildEquivalence:
     """Make sure that building a table row-by-row and column-by-column yield 
the same
     results"""
@@ -161,11 +274,11 @@
 
         assert with_del.get_string() == without_row.get_string()
 
-    def test_delete_illegal_column_raises_exception(self):
+    def test_delete_illegal_column_raises_error(self):
         table = PrettyTable()
         table.add_column("City name", ["Adelaide", "Brisbane", "Darwin"])
 
-        with pytest.raises(Exception):
+        with pytest.raises(ValueError):
             table.del_column("City not-a-name")
 
 
@@ -886,6 +999,80 @@
 """.strip()  # noqa: E501
         )
 
+    def test_HtmlOutputWithTitle(self):
+        t = helper_table()
+        t.title = "Title"
+        result = t.get_html_string()
+        assert (
+            result.strip()
+            == """
+<table>
+    <caption>Title</caption>
+    <thead>
+        <tr>
+            <th>Field 1</th>
+            <th>Field 2</th>
+            <th>Field 3</th>
+        </tr>
+    </thead>
+    <tbody>
+        <tr>
+            <td>value 1</td>
+            <td>value2</td>
+            <td>value3</td>
+        </tr>
+        <tr>
+            <td>value 4</td>
+            <td>value5</td>
+            <td>value6</td>
+        </tr>
+        <tr>
+            <td>value 7</td>
+            <td>value8</td>
+            <td>value9</td>
+        </tr>
+    </tbody>
+</table>
+""".strip()
+        )
+
+    def test_HtmlOutputFormattedWithTitle(self):
+        t = helper_table()
+        t.title = "Title"
+        result = t.get_html_string(format=True)
+        assert (
+            result.strip()
+            == """
+<table frame="box" rules="cols">
+    <caption>Title</caption>
+    <thead>
+        <tr>
+            <th style="padding-left: 1em; padding-right: 1em; text-align: 
center">Field 1</th>
+            <th style="padding-left: 1em; padding-right: 1em; text-align: 
center">Field 2</th>
+            <th style="padding-left: 1em; padding-right: 1em; text-align: 
center">Field 3</th>
+        </tr>
+    </thead>
+    <tbody>
+        <tr>
+            <td style="padding-left: 1em; padding-right: 1em; text-align: 
center; vertical-align: top">value 1</td>
+            <td style="padding-left: 1em; padding-right: 1em; text-align: 
center; vertical-align: top">value2</td>
+            <td style="padding-left: 1em; padding-right: 1em; text-align: 
center; vertical-align: top">value3</td>
+        </tr>
+        <tr>
+            <td style="padding-left: 1em; padding-right: 1em; text-align: 
center; vertical-align: top">value 4</td>
+            <td style="padding-left: 1em; padding-right: 1em; text-align: 
center; vertical-align: top">value5</td>
+            <td style="padding-left: 1em; padding-right: 1em; text-align: 
center; vertical-align: top">value6</td>
+        </tr>
+        <tr>
+            <td style="padding-left: 1em; padding-right: 1em; text-align: 
center; vertical-align: top">value 7</td>
+            <td style="padding-left: 1em; padding-right: 1em; text-align: 
center; vertical-align: top">value8</td>
+            <td style="padding-left: 1em; padding-right: 1em; text-align: 
center; vertical-align: top">value9</td>
+        </tr>
+    </tbody>
+</table>
+""".strip()  # noqa: E501
+        )
+
 
 class TestPositionalJunctions:
     """Verify different cases for positional-junction characters"""
@@ -1091,12 +1278,10 @@
             ),
             pytest.param(
                 PLAIN_COLUMNS,
-                """
-Field 1        Field 2        Field 3        
-value 1         value2         value3        
-value 4         value5         value6        
-value 7         value8         value9
-""",  # noqa: W291
+                "Field 1        Field 2        Field 3        \n"
+                "value 1         value2         value3        \n"
+                "value 4         value5         value6        \n"
+                "value 7         value8         value9",
                 id="PLAIN_COLUMNS",
             ),
             pytest.param(
@@ -1156,7 +1341,7 @@
 
         # Act / Assert
         # This is an hrule style, not a table style
-        with pytest.raises(Exception):
+        with pytest.raises(ValueError):
             t.set_style(ALL)
 
     @pytest.mark.parametrize(
@@ -1314,7 +1499,7 @@
     def test_HtmlOneFailOnMany(self, city_data_prettytable: PrettyTable):
         html_string = city_data_prettytable.get_html_string()
         html_string += city_data_prettytable.get_html_string()
-        with pytest.raises(Exception):
+        with pytest.raises(ValueError):
             from_html_one(html_string)
 
 
@@ -1551,7 +1736,7 @@
         assert len(pt.custom_format) == 1
 
     def test_init_custom_format_throw_error_is_not_callable(self):
-        with pytest.raises(Exception) as e:
+        with pytest.raises(ValueError) as e:
             PrettyTable(custom_format={"col1": "{:.2}"})
 
         assert "Invalid value for custom_format.col1. Must be a function." in 
str(
@@ -1571,7 +1756,7 @@
 
     def test_set_custom_format_invalid_type_throw_error(self):
         pt = PrettyTable()
-        with pytest.raises(Exception) as e:
+        with pytest.raises(TypeError) as e:
             pt.custom_format = "Some String"
         assert "The custom_format property need to be a dictionary or 
callable" in str(
             e.value
@@ -1649,3 +1834,21 @@
 
     def test_jupyter_repr(self, row_prettytable: PrettyTable):
         assert row_prettytable._repr_html_() == 
row_prettytable.get_html_string()
+
+
+class TestMaxTableWidth:
+    def test_max_table_width(self):
+        pt = PrettyTable()
+        pt.max_table_width = 5
+        pt.add_row([0])
+
+        assert (
+            pt.get_string().strip()
+            == """
++-----+
+| Fie |
++-----+
+|  0  |
++-----+
+""".strip()
+        )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prettytable-2.5.0/tox.ini 
new/prettytable-3.2.0/tox.ini
--- old/prettytable-2.5.0/tox.ini       2021-12-20 16:34:13.000000000 +0100
+++ new/prettytable-3.2.0/tox.ini       2022-03-07 08:13:49.000000000 +0100
@@ -1,7 +1,7 @@
 [tox]
 envlist =
     lint
-    py{py3, 310, 39, 38, 37, 36}
+    py{py3, 310, 39, 38, 37}
 
 [testenv]
 passenv =

Reply via email to