summaryrefslogtreecommitdiffstats
path: root/contrib/python/prettytable
diff options
context:
space:
mode:
authorrobot-piglet <[email protected]>2026-05-30 22:59:32 +0300
committerrobot-piglet <[email protected]>2026-05-30 23:21:30 +0300
commit7b185ac5cce1be8572664dafa59dc340a9b47187 (patch)
tree9651cbc8e56226df41ab6fccc936b5b5f84a58d1 /contrib/python/prettytable
parentf325ea8b5f12fce1cc7b7d19286875163956a945 (diff)
Intermediate changes
commit_hash:92c3a2039e8a959ce895f918b5bf3ec01a415885
Diffstat (limited to 'contrib/python/prettytable')
-rw-r--r--contrib/python/prettytable/py3/.dist-info/METADATA154
-rw-r--r--contrib/python/prettytable/py3/README.md148
-rw-r--r--contrib/python/prettytable/py3/prettytable/__init__.py2
-rw-r--r--contrib/python/prettytable/py3/prettytable/_version.py26
-rw-r--r--contrib/python/prettytable/py3/prettytable/prettytable.py388
-rw-r--r--contrib/python/prettytable/py3/tests/conftest.py89
-rw-r--r--contrib/python/prettytable/py3/tests/test_colortable.py41
-rw-r--r--contrib/python/prettytable/py3/tests/test_html.py585
-rw-r--r--contrib/python/prettytable/py3/tests/test_json.py80
-rw-r--r--contrib/python/prettytable/py3/tests/test_latex.py100
-rw-r--r--contrib/python/prettytable/py3/tests/test_mediawiki.py171
-rw-r--r--contrib/python/prettytable/py3/tests/test_prettytable.py2611
-rw-r--r--contrib/python/prettytable/py3/tests/test_sections.py55
-rw-r--r--contrib/python/prettytable/py3/tests/test_sorting.py119
-rw-r--r--contrib/python/prettytable/py3/tests/test_style.py591
-rw-r--r--contrib/python/prettytable/py3/ya.make2
16 files changed, 3042 insertions, 2120 deletions
diff --git a/contrib/python/prettytable/py3/.dist-info/METADATA b/contrib/python/prettytable/py3/.dist-info/METADATA
index 16e1235b677..6c57a35f2bc 100644
--- a/contrib/python/prettytable/py3/.dist-info/METADATA
+++ b/contrib/python/prettytable/py3/.dist-info/METADATA
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: prettytable
-Version: 3.14.0
+Version: 3.17.0
Summary: A simple Python library for easily displaying tabular data in a visually appealing ASCII table format
Project-URL: Changelog, https://github.com/prettytable/prettytable/releases
Project-URL: Funding, https://tidelift.com/subscription/pkg/pypi-prettytable?utm_source=pypi-prettytable&utm_medium=pypi
@@ -12,17 +12,17 @@ License-Expression: BSD-3-Clause
License-File: LICENSE
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3 :: Only
-Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
+Classifier: Programming Language :: Python :: 3.15
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Text Processing
Classifier: Typing :: Typed
-Requires-Python: >=3.9
+Requires-Python: >=3.10
Requires-Dist: wcwidth
Provides-Extra: tests
Requires-Dist: pytest; extra == 'tests'
@@ -36,7 +36,8 @@ Description-Content-Type: text/markdown
[![Supported Python versions](https://img.shields.io/pypi/pyversions/prettytable.svg?logo=python&logoColor=FFE873)](https://pypi.org/project/prettytable/)
[![PyPI downloads](https://img.shields.io/pypi/dm/prettytable.svg)](https://pypistats.org/packages/prettytable)
[![GitHub Actions status](https://github.com/prettytable/prettytable/workflows/Test/badge.svg)](https://github.com/prettytable/prettytable/actions)
-[![codecov](https://codecov.io/gh/prettytable/prettytable/branch/main/graph/badge.svg)](https://codecov.io/gh/prettytable/prettytable)
+[![Codecov](https://codecov.io/gh/prettytable/prettytable/branch/main/graph/badge.svg)](https://codecov.io/gh/prettytable/prettytable)
+[![Licence](https://img.shields.io/github/license/prettytable/prettytable.svg)](LICENSE)
[![Code style: Black](https://img.shields.io/badge/code%20style-Black-000000.svg)](https://github.com/psf/black)
[![Tidelift](https://tidelift.com/badges/package/pypi/prettytable)](https://tidelift.com/subscription/pkg/pypi-prettytable?utm_source=pypi-prettytable&utm_medium=badge)
@@ -244,9 +245,9 @@ This string is guaranteed to look exactly the same as what would be printed by d
your table to a file or insert it into a GUI.
The table can be displayed in several different formats using `get_formatted_string` by
-changing the `out_format=<text|html|json|csv|latex>`. This function passes through
-arguments to the functions that render the table, so additional arguments can be given.
-This provides a way to let a user choose the output formatting.
+changing the `out_format=<text|html|json|csv|latex|mediawiki>`. This function passes
+through arguments to the functions that render the table, so additional arguments can be
+given. This provides a way to let a user choose the output formatting.
```python
def my_cli_function(table_format: str = 'text'):
@@ -302,6 +303,36 @@ prints:
+-----------+------+------------+-----------------+
```
+#### Filtering your table
+
+You can make sure that your tables are filtered by giving `get_string` a `row_filter`
+keyword argument, which must be a function with one argument `row` returning a Boolean
+value. The `row` is the list of fields in a row.
+
+For example, to print the example table we built earlier of Australian capital city
+data, so that cities with a population of at least 1,000,000, we can do this:
+
+```python
+def filter_function(row: list[str]) -> bool:
+ return row[2] > 999999
+
+print(table.get_string(row_filter=filter_function))
+```
+
+to get:
+
+```
++-----------+------+------------+-----------------+
+| City name | Area | Population | Annual Rainfall |
++-----------+------+------------+-----------------+
+| Adelaide | 1295 | 1158259 | 600.5 |
+| Brisbane | 5905 | 1857594 | 1146.4 |
+| Sydney | 2058 | 4336374 | 1214.8 |
+| Melbourne | 1566 | 3806092 | 646.9 |
+| Perth | 5386 | 1554769 | 869.4 |
++-----------+------+------------+-----------------+
+```
+
#### Changing the alignment of columns
By default, all columns in a table are centre aligned.
@@ -413,28 +444,33 @@ table.sortby = None
```
If you want to specify a custom sorting function, you can use the `sort_key` keyword
-argument. Pass this a function which accepts two lists of values and returns a negative
-or positive value depending on whether the first list should appear before or after the
-second one. If your table has n columns, each list will have n+1 elements. Each list
-corresponds to one row of the table. The first element will be whatever data is in the
-relevant row, in the column specified by the `sort_by` argument. The remaining n
-elements are the data in each of the table's columns, in order, including a repeated
-instance of the data in the `sort_by` column.
+argument as Pythons `sorted()` `key` parameter. The value of the `sort_key` parameter
+should be a function (or other callable) that takes a single argument and returns a key
+to use for sorting purposes.
+
+If your table has n columns, each list will have n+1 elements. Each list corresponds to
+one row of the table. The first element will be whatever data is in the relevant row, in
+the column specified by the `sort_by` argument. The remaining n elements are the data in
+each of the table's columns, in order, including a repeated instance of the data in the
+`sort_by` column.
#### Adding sections to a table
You can divide your table into different sections using the `add_divider` method or
-`divider` argument . This will add a dividing line into the table under the row who has
-this field set. So we can set up a table like this:
+`divider` argument to `add_row()` or even to `add_rows()`. This will add a dividing line
+into the table under the row who has this field set. So we can set up a table like this:
```python
table = PrettyTable()
table.field_names = ["City name", "Area", "Population", "Annual Rainfall"]
table.add_row(["Adelaide", 1295, 1158259, 600.5])
table.add_divider()
-table.add_row(["Brisbane", 5905, 1857594, 1146.4])
-table.add_row(["Darwin", 112, 120900, 1714.7])
-table.add_row(["Hobart", 1357, 205556, 619.5], divider=True)
+table.add_row(["Brisbane", 5905, 1857594, 1146.4], divider=True)
+table.add_rows(
+ [["Darwin", 112, 120900, 1714.7],
+ ["Hobart", 1357, 205556, 619.5]],
+ divider=True
+)
table.add_row(["Melbourne", 1566, 3806092, 646.9])
table.add_row(["Perth", 5386, 1554769, 869.4])
table.add_row(["Sydney", 2058, 4336374, 1214.8])
@@ -449,6 +485,7 @@ to get a table like this:
| Adelaide | 1295 | 1158259 | 600.5 |
+-----------+------+------------+-----------------+
| Brisbane | 5905 | 1857594 | 1146.4 |
++-----------+------+------------+-----------------+
| Darwin | 112 | 120900 | 1714.7 |
| Hobart | 1357 | 205556 | 619.5 |
+-----------+------+------------+-----------------+
@@ -506,34 +543,53 @@ PrettyTable has a number of style options which control various aspects of how t
are displayed. You have the freedom to set each of these options individually to
whatever you prefer. The `set_style` method just does this automatically for you.
-The options are:
+Table-specific options are:
+
+| Option | Details |
+| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
+| `HEADER`, `ALL`, `NONE` | These are variables defined inside the `prettytable` module so make sure you import them or use `prettytable.FRAME` etc. |
+| `_horizontal_align_char` | Single character string used to indicate column alignment in horizontal lines. Default: `:` for Markdown, otherwise `None`. |
+| `border` | A Boolean option (must be `True` or `False`). Controls whether a border is drawn inside and around the table. |
+| `bottom_junction_char` | single character string used to draw bottom line junctions. Default: `junction_char`. |
+| `bottom_left_junction_char` | Single character string used to draw bottom-left line junctions. Default: `junction_char`. |
+| `bottom_right_junction_char` | Single character string used to draw bottom-right line junctions. Default: `junction_char`. |
+| `break_on_hyphens` | Whether long lines are wrapped on hyphens. Default: `True`. |
+| `header` | A Boolean option (must be `True` or `False`). Controls whether the first row of the table is a header showing the names of all the fields. |
+| `horizontal_char` | Single character string used to draw horizontal lines. Default: `-`. |
+| `hrules` | Controls printing of horizontal rules after rows. Allowed values: `FRAME`, `HEADER`, `ALL`, `NONE`. |
+| `junction_char` | Single character string used to draw line junctions. Default: `+`. |
+| `left_junction_char` | Single character string used to draw left line junctions. Default: `junction_char`. |
+| `left_padding_width` | Number of spaces on left-hand side of column data. |
+| `max_table_width` | Number of characters used for the maximum total table width. |
+| `min_table_width` | Number of characters used for the minimum total table width. |
+| `padding_width` | Number of spaces on either side of column data (only used if left and right paddings are `None`). |
+| `preserve_internal_border` | A Boolean option (must be `True` or `False`). Controls whether borders are still drawn within the table even when `border=False`. |
+| `right_junction_char` | Single character string used to draw right line junctions. Default: `junction_char`. |
+| `right_padding_width` | Number of spaces on right-hand side of column data. |
+| `top_junction_char` | Single character string used to draw top line junctions. Default: `junction_char`. |
+| `top_left_junction_char` | Single character string used to draw top-left line junctions. Default: `junction_char`. |
+| `top_right_junction_char` | Single character string used to draw top-right line junctions. Default: `junction_char`. |
+| `use_header_width` | A Boolean option (must be `True` or `False`). Controls whether the width of the header is used for computing column width. Default: `True`. |
+| `vertical_char` | Single character string used to draw vertical lines. Default: `\|`. |
+| `vrules` | Controls printing of vertical rules between columns. Allowed values: `FRAME`, `ALL`, `NONE`. |
-| Option | Details |
-| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| `border` | A Boolean option (must be `True` or `False`). Controls whether a border is drawn inside and around the table. |
-| `preserve_internal_border` | A Boolean option (must be `True` or `False`). Controls whether borders are still drawn within the table even when `border=False`. |
-| `header` | A Boolean option (must be `True` or `False`). Controls whether the first row of the table is a header showing the names of all the fields. |
-| `hrules` | Controls printing of horizontal rules after rows. Allowed values: `FRAME`, `HEADER`, `ALL`, `NONE`. |
-| `HEADER`, `ALL`, `NONE` | These are variables defined inside the `prettytable` module so make sure you import them or use `prettytable.FRAME` etc. |
-| `vrules` | Controls printing of vertical rules between columns. Allowed values: `FRAME`, `ALL`, `NONE`. |
-| `int_format` | A string which controls the way integer data is printed. This works like: `print("%<int_format>d" % data)`. |
-| `float_format` | A string which controls the way floating point data is printed. This works like: `print("%<float_format>f" % data)`. |
-| `custom_format` | A dictionary of field and callable. This allows you to set any format you want `pf.custom_format["my_col_int"] = lambda f, v: f"{v:,}"`. The type of the callable is `Callable[[str, Any], str]` |
-| `padding_width` | Number of spaces on either side of column data (only used if left and right paddings are `None`). |
-| `left_padding_width` | Number of spaces on left-hand side of column data. |
-| `right_padding_width` | Number of spaces on right-hand side of column data. |
-| `vertical_char` | Single character string used to draw vertical lines. Default: `\|`. |
-| `horizontal_char` | Single character string used to draw horizontal lines. Default: `-`. |
-| `_horizontal_align_char` | Single character string used to indicate column alignment in horizontal lines. Default: `:` for Markdown, otherwise `None`. |
-| `junction_char` | Single character string used to draw line junctions. Default: `+`. |
-| `top_junction_char` | Single character string used to draw top line junctions. Default: `junction_char`. |
-| `bottom_junction_char` | single character string used to draw bottom line junctions. Default: `junction_char`. |
-| `right_junction_char` | Single character string used to draw right line junctions. Default: `junction_char`. |
-| `left_junction_char` | Single character string used to draw left line junctions. Default: `junction_char`. |
-| `top_right_junction_char` | Single character string used to draw top-right line junctions. Default: `junction_char`. |
-| `top_left_junction_char` | Single character string used to draw top-left line junctions. Default: `junction_char`. |
-| `bottom_right_junction_char` | Single character string used to draw bottom-right line junctions. Default: `junction_char`. |
-| `bottom_left_junction_char` | Single character string used to draw bottom-left line junctions. Default: `junction_char`. |
+For options that can be set individually for each column (`align`, `valign`,
+`custom_format`, `max_width`, `min_width`, `int_format`, `float_format`, `none_format`)
+you can either set a value, that applies to all columns or set a dict with column names
+and individual values.
+
+Column-specific options are:
+
+| Option | Details |
+| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `align` | Controls alignment of fields, one of "l", "c", or "r" or a dictionary with column and value. |
+| `custom_format` | Set any format, by setting a function that gets the original value,the formatted representation and returns the new string. E.g. `pf.custom_format["my_col_int"] = lambda f, v: f"{v:,}"`. The type of the callable is `Callable[[str, Any], str]`. This also takes a dictionary with column and function. |
+| `float_format` | A string which controls the way floating point data is printed or a dictionary with column and value. This works like: `print("%<float_format>f" % data)`. |
+| `int_format` | A string which controls the way integer data is printed or a dictionary with column and value. This works like: `print("%<int_format>d" % data)`. |
+| `max_width` | Number of characters used for maximum width of a column or a dictionary with column and value. |
+| `min_width` | Number of characters used for minimum width of a column or a dictionary with column and value. |
+| `none_format` | Representation of None values or a dictionary with column and value. |
+| `valign` | Controls vertical alignment of fields, one of "t", "m", or "b" or a dictionary with column and value. |
You can set the style options to your own settings in two ways:
@@ -632,6 +688,12 @@ PrettyTable will also print your tables in JSON, as a list of fields and an arra
rows. Just like in ASCII form, you can actually get a string representation - just use
`get_json_string()`.
+### Displaying your table in MediaWiki markup
+
+PrettyTable can also print your tables in MediaWiki table markup, making it easy to
+format tables for wikis. Similar to the ASCII format, you can get a string
+representation using `get_mediawiki_string()`.
+
### Displaying your table in HTML form
PrettyTable will also print your tables in HTML form, as `<table>`s. Just like in ASCII
diff --git a/contrib/python/prettytable/py3/README.md b/contrib/python/prettytable/py3/README.md
index fb67d377942..a06e6c35cf6 100644
--- a/contrib/python/prettytable/py3/README.md
+++ b/contrib/python/prettytable/py3/README.md
@@ -4,7 +4,8 @@
[![Supported Python versions](https://img.shields.io/pypi/pyversions/prettytable.svg?logo=python&logoColor=FFE873)](https://pypi.org/project/prettytable/)
[![PyPI downloads](https://img.shields.io/pypi/dm/prettytable.svg)](https://pypistats.org/packages/prettytable)
[![GitHub Actions status](https://github.com/prettytable/prettytable/workflows/Test/badge.svg)](https://github.com/prettytable/prettytable/actions)
-[![codecov](https://codecov.io/gh/prettytable/prettytable/branch/main/graph/badge.svg)](https://codecov.io/gh/prettytable/prettytable)
+[![Codecov](https://codecov.io/gh/prettytable/prettytable/branch/main/graph/badge.svg)](https://codecov.io/gh/prettytable/prettytable)
+[![Licence](https://img.shields.io/github/license/prettytable/prettytable.svg)](LICENSE)
[![Code style: Black](https://img.shields.io/badge/code%20style-Black-000000.svg)](https://github.com/psf/black)
[![Tidelift](https://tidelift.com/badges/package/pypi/prettytable)](https://tidelift.com/subscription/pkg/pypi-prettytable?utm_source=pypi-prettytable&utm_medium=badge)
@@ -212,9 +213,9 @@ This string is guaranteed to look exactly the same as what would be printed by d
your table to a file or insert it into a GUI.
The table can be displayed in several different formats using `get_formatted_string` by
-changing the `out_format=<text|html|json|csv|latex>`. This function passes through
-arguments to the functions that render the table, so additional arguments can be given.
-This provides a way to let a user choose the output formatting.
+changing the `out_format=<text|html|json|csv|latex|mediawiki>`. This function passes
+through arguments to the functions that render the table, so additional arguments can be
+given. This provides a way to let a user choose the output formatting.
```python
def my_cli_function(table_format: str = 'text'):
@@ -270,6 +271,36 @@ prints:
+-----------+------+------------+-----------------+
```
+#### Filtering your table
+
+You can make sure that your tables are filtered by giving `get_string` a `row_filter`
+keyword argument, which must be a function with one argument `row` returning a Boolean
+value. The `row` is the list of fields in a row.
+
+For example, to print the example table we built earlier of Australian capital city
+data, so that cities with a population of at least 1,000,000, we can do this:
+
+```python
+def filter_function(row: list[str]) -> bool:
+ return row[2] > 999999
+
+print(table.get_string(row_filter=filter_function))
+```
+
+to get:
+
+```
++-----------+------+------------+-----------------+
+| City name | Area | Population | Annual Rainfall |
++-----------+------+------------+-----------------+
+| Adelaide | 1295 | 1158259 | 600.5 |
+| Brisbane | 5905 | 1857594 | 1146.4 |
+| Sydney | 2058 | 4336374 | 1214.8 |
+| Melbourne | 1566 | 3806092 | 646.9 |
+| Perth | 5386 | 1554769 | 869.4 |
++-----------+------+------------+-----------------+
+```
+
#### Changing the alignment of columns
By default, all columns in a table are centre aligned.
@@ -381,28 +412,33 @@ table.sortby = None
```
If you want to specify a custom sorting function, you can use the `sort_key` keyword
-argument. Pass this a function which accepts two lists of values and returns a negative
-or positive value depending on whether the first list should appear before or after the
-second one. If your table has n columns, each list will have n+1 elements. Each list
-corresponds to one row of the table. The first element will be whatever data is in the
-relevant row, in the column specified by the `sort_by` argument. The remaining n
-elements are the data in each of the table's columns, in order, including a repeated
-instance of the data in the `sort_by` column.
+argument as Pythons `sorted()` `key` parameter. The value of the `sort_key` parameter
+should be a function (or other callable) that takes a single argument and returns a key
+to use for sorting purposes.
+
+If your table has n columns, each list will have n+1 elements. Each list corresponds to
+one row of the table. The first element will be whatever data is in the relevant row, in
+the column specified by the `sort_by` argument. The remaining n elements are the data in
+each of the table's columns, in order, including a repeated instance of the data in the
+`sort_by` column.
#### Adding sections to a table
You can divide your table into different sections using the `add_divider` method or
-`divider` argument . This will add a dividing line into the table under the row who has
-this field set. So we can set up a table like this:
+`divider` argument to `add_row()` or even to `add_rows()`. This will add a dividing line
+into the table under the row who has this field set. So we can set up a table like this:
```python
table = PrettyTable()
table.field_names = ["City name", "Area", "Population", "Annual Rainfall"]
table.add_row(["Adelaide", 1295, 1158259, 600.5])
table.add_divider()
-table.add_row(["Brisbane", 5905, 1857594, 1146.4])
-table.add_row(["Darwin", 112, 120900, 1714.7])
-table.add_row(["Hobart", 1357, 205556, 619.5], divider=True)
+table.add_row(["Brisbane", 5905, 1857594, 1146.4], divider=True)
+table.add_rows(
+ [["Darwin", 112, 120900, 1714.7],
+ ["Hobart", 1357, 205556, 619.5]],
+ divider=True
+)
table.add_row(["Melbourne", 1566, 3806092, 646.9])
table.add_row(["Perth", 5386, 1554769, 869.4])
table.add_row(["Sydney", 2058, 4336374, 1214.8])
@@ -417,6 +453,7 @@ to get a table like this:
| Adelaide | 1295 | 1158259 | 600.5 |
+-----------+------+------------+-----------------+
| Brisbane | 5905 | 1857594 | 1146.4 |
++-----------+------+------------+-----------------+
| Darwin | 112 | 120900 | 1714.7 |
| Hobart | 1357 | 205556 | 619.5 |
+-----------+------+------------+-----------------+
@@ -474,34 +511,53 @@ PrettyTable has a number of style options which control various aspects of how t
are displayed. You have the freedom to set each of these options individually to
whatever you prefer. The `set_style` method just does this automatically for you.
-The options are:
+Table-specific options are:
+
+| Option | Details |
+| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
+| `HEADER`, `ALL`, `NONE` | These are variables defined inside the `prettytable` module so make sure you import them or use `prettytable.FRAME` etc. |
+| `_horizontal_align_char` | Single character string used to indicate column alignment in horizontal lines. Default: `:` for Markdown, otherwise `None`. |
+| `border` | A Boolean option (must be `True` or `False`). Controls whether a border is drawn inside and around the table. |
+| `bottom_junction_char` | single character string used to draw bottom line junctions. Default: `junction_char`. |
+| `bottom_left_junction_char` | Single character string used to draw bottom-left line junctions. Default: `junction_char`. |
+| `bottom_right_junction_char` | Single character string used to draw bottom-right line junctions. Default: `junction_char`. |
+| `break_on_hyphens` | Whether long lines are wrapped on hyphens. Default: `True`. |
+| `header` | A Boolean option (must be `True` or `False`). Controls whether the first row of the table is a header showing the names of all the fields. |
+| `horizontal_char` | Single character string used to draw horizontal lines. Default: `-`. |
+| `hrules` | Controls printing of horizontal rules after rows. Allowed values: `FRAME`, `HEADER`, `ALL`, `NONE`. |
+| `junction_char` | Single character string used to draw line junctions. Default: `+`. |
+| `left_junction_char` | Single character string used to draw left line junctions. Default: `junction_char`. |
+| `left_padding_width` | Number of spaces on left-hand side of column data. |
+| `max_table_width` | Number of characters used for the maximum total table width. |
+| `min_table_width` | Number of characters used for the minimum total table width. |
+| `padding_width` | Number of spaces on either side of column data (only used if left and right paddings are `None`). |
+| `preserve_internal_border` | A Boolean option (must be `True` or `False`). Controls whether borders are still drawn within the table even when `border=False`. |
+| `right_junction_char` | Single character string used to draw right line junctions. Default: `junction_char`. |
+| `right_padding_width` | Number of spaces on right-hand side of column data. |
+| `top_junction_char` | Single character string used to draw top line junctions. Default: `junction_char`. |
+| `top_left_junction_char` | Single character string used to draw top-left line junctions. Default: `junction_char`. |
+| `top_right_junction_char` | Single character string used to draw top-right line junctions. Default: `junction_char`. |
+| `use_header_width` | A Boolean option (must be `True` or `False`). Controls whether the width of the header is used for computing column width. Default: `True`. |
+| `vertical_char` | Single character string used to draw vertical lines. Default: `\|`. |
+| `vrules` | Controls printing of vertical rules between columns. Allowed values: `FRAME`, `ALL`, `NONE`. |
-| Option | Details |
-| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| `border` | A Boolean option (must be `True` or `False`). Controls whether a border is drawn inside and around the table. |
-| `preserve_internal_border` | A Boolean option (must be `True` or `False`). Controls whether borders are still drawn within the table even when `border=False`. |
-| `header` | A Boolean option (must be `True` or `False`). Controls whether the first row of the table is a header showing the names of all the fields. |
-| `hrules` | Controls printing of horizontal rules after rows. Allowed values: `FRAME`, `HEADER`, `ALL`, `NONE`. |
-| `HEADER`, `ALL`, `NONE` | These are variables defined inside the `prettytable` module so make sure you import them or use `prettytable.FRAME` etc. |
-| `vrules` | Controls printing of vertical rules between columns. Allowed values: `FRAME`, `ALL`, `NONE`. |
-| `int_format` | A string which controls the way integer data is printed. This works like: `print("%<int_format>d" % data)`. |
-| `float_format` | A string which controls the way floating point data is printed. This works like: `print("%<float_format>f" % data)`. |
-| `custom_format` | A dictionary of field and callable. This allows you to set any format you want `pf.custom_format["my_col_int"] = lambda f, v: f"{v:,}"`. The type of the callable is `Callable[[str, Any], str]` |
-| `padding_width` | Number of spaces on either side of column data (only used if left and right paddings are `None`). |
-| `left_padding_width` | Number of spaces on left-hand side of column data. |
-| `right_padding_width` | Number of spaces on right-hand side of column data. |
-| `vertical_char` | Single character string used to draw vertical lines. Default: `\|`. |
-| `horizontal_char` | Single character string used to draw horizontal lines. Default: `-`. |
-| `_horizontal_align_char` | Single character string used to indicate column alignment in horizontal lines. Default: `:` for Markdown, otherwise `None`. |
-| `junction_char` | Single character string used to draw line junctions. Default: `+`. |
-| `top_junction_char` | Single character string used to draw top line junctions. Default: `junction_char`. |
-| `bottom_junction_char` | single character string used to draw bottom line junctions. Default: `junction_char`. |
-| `right_junction_char` | Single character string used to draw right line junctions. Default: `junction_char`. |
-| `left_junction_char` | Single character string used to draw left line junctions. Default: `junction_char`. |
-| `top_right_junction_char` | Single character string used to draw top-right line junctions. Default: `junction_char`. |
-| `top_left_junction_char` | Single character string used to draw top-left line junctions. Default: `junction_char`. |
-| `bottom_right_junction_char` | Single character string used to draw bottom-right line junctions. Default: `junction_char`. |
-| `bottom_left_junction_char` | Single character string used to draw bottom-left line junctions. Default: `junction_char`. |
+For options that can be set individually for each column (`align`, `valign`,
+`custom_format`, `max_width`, `min_width`, `int_format`, `float_format`, `none_format`)
+you can either set a value, that applies to all columns or set a dict with column names
+and individual values.
+
+Column-specific options are:
+
+| Option | Details |
+| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `align` | Controls alignment of fields, one of "l", "c", or "r" or a dictionary with column and value. |
+| `custom_format` | Set any format, by setting a function that gets the original value,the formatted representation and returns the new string. E.g. `pf.custom_format["my_col_int"] = lambda f, v: f"{v:,}"`. The type of the callable is `Callable[[str, Any], str]`. This also takes a dictionary with column and function. |
+| `float_format` | A string which controls the way floating point data is printed or a dictionary with column and value. This works like: `print("%<float_format>f" % data)`. |
+| `int_format` | A string which controls the way integer data is printed or a dictionary with column and value. This works like: `print("%<int_format>d" % data)`. |
+| `max_width` | Number of characters used for maximum width of a column or a dictionary with column and value. |
+| `min_width` | Number of characters used for minimum width of a column or a dictionary with column and value. |
+| `none_format` | Representation of None values or a dictionary with column and value. |
+| `valign` | Controls vertical alignment of fields, one of "t", "m", or "b" or a dictionary with column and value. |
You can set the style options to your own settings in two ways:
@@ -600,6 +656,12 @@ PrettyTable will also print your tables in JSON, as a list of fields and an arra
rows. Just like in ASCII form, you can actually get a string representation - just use
`get_json_string()`.
+### Displaying your table in MediaWiki markup
+
+PrettyTable can also print your tables in MediaWiki table markup, making it easy to
+format tables for wikis. Similar to the ASCII format, you can get a string
+representation using `get_mediawiki_string()`.
+
### Displaying your table in HTML form
PrettyTable will also print your tables in HTML form, as `<table>`s. Just like in ASCII
diff --git a/contrib/python/prettytable/py3/prettytable/__init__.py b/contrib/python/prettytable/py3/prettytable/__init__.py
index 66be991cb8b..f28c03658ab 100644
--- a/contrib/python/prettytable/py3/prettytable/__init__.py
+++ b/contrib/python/prettytable/py3/prettytable/__init__.py
@@ -28,6 +28,7 @@ from .prettytable import ( # noqa: F401
from_html,
from_html_one,
from_json,
+ from_mediawiki,
)
__all__ = [
@@ -55,6 +56,7 @@ __all__ = [
"from_html",
"from_html_one",
"from_json",
+ "from_mediawiki",
]
diff --git a/contrib/python/prettytable/py3/prettytable/_version.py b/contrib/python/prettytable/py3/prettytable/_version.py
index 431bd6d8195..b6f2ce2e738 100644
--- a/contrib/python/prettytable/py3/prettytable/_version.py
+++ b/contrib/python/prettytable/py3/prettytable/_version.py
@@ -1,16 +1,34 @@
-# file generated by setuptools_scm
+# file generated by setuptools-scm
# don't change, don't track in version control
+
+__all__ = [
+ "__version__",
+ "__version_tuple__",
+ "version",
+ "version_tuple",
+ "__commit_id__",
+ "commit_id",
+]
+
TYPE_CHECKING = False
if TYPE_CHECKING:
- from typing import Tuple, Union
+ from typing import Tuple
+ from typing import Union
+
VERSION_TUPLE = Tuple[Union[int, str], ...]
+ COMMIT_ID = Union[str, None]
else:
VERSION_TUPLE = object
+ COMMIT_ID = object
version: str
__version__: str
__version_tuple__: VERSION_TUPLE
version_tuple: VERSION_TUPLE
+commit_id: COMMIT_ID
+__commit_id__: COMMIT_ID
+
+__version__ = version = '3.17.0'
+__version_tuple__ = version_tuple = (3, 17, 0)
-__version__ = version = '3.14.0'
-__version_tuple__ = version_tuple = (3, 14, 0)
+__commit_id__ = commit_id = None
diff --git a/contrib/python/prettytable/py3/prettytable/prettytable.py b/contrib/python/prettytable/py3/prettytable/prettytable.py
index 5b6c396b0d1..1b6c964652c 100644
--- a/contrib/python/prettytable/py3/prettytable/prettytable.py
+++ b/contrib/python/prettytable/py3/prettytable/prettytable.py
@@ -35,17 +35,19 @@ from __future__ import annotations
import io
import re
-import warnings
-from collections.abc import Callable, Iterable, Mapping, Sequence
from enum import IntEnum
+from functools import lru_cache
from html.parser import HTMLParser
-from typing import TYPE_CHECKING, Any, Final, Literal, TypedDict, cast
+from typing import Any, Literal, TypedDict, cast
+TYPE_CHECKING = False
if TYPE_CHECKING:
+ from collections.abc import Callable, Mapping, Sequence
from sqlite3 import Cursor
+ from typing import Final, TypeAlias
from _typeshed import SupportsRichComparison
- from typing_extensions import Self, TypeAlias
+ from typing_extensions import Self
class HRuleStyle(IntEnum):
@@ -92,7 +94,7 @@ BASE_ALIGN_VALUE: Final = "base_align_value"
RowType: TypeAlias = list[Any]
AlignType: TypeAlias = Literal["l", "c", "r"]
VAlignType: TypeAlias = Literal["t", "m", "b"]
-HeaderStyleType: TypeAlias = Literal["cap", "title", "upper", "lower", None]
+HeaderStyleType: TypeAlias = Literal["cap", "title", "upper", "lower"] | None
class OptionsType(TypedDict):
@@ -101,11 +103,13 @@ class OptionsType(TypedDict):
end: int | None
fields: Sequence[str | None] | None
header: bool
+ use_header_width: bool
border: bool
preserve_internal_border: bool
sortby: str | None
reversesort: bool
sort_key: Callable[[RowType], SupportsRichComparison]
+ row_filter: Callable[[RowType], bool]
attributes: dict[str, str]
format: bool
hrules: HRuleStyle
@@ -143,11 +147,16 @@ class OptionsType(TypedDict):
none_format: str | dict[str, str | None] | None
escape_header: bool
escape_data: bool
+ break_on_hyphens: bool
+# ANSI colour codes
_re = re.compile(r"\033\[[0-9;]*m|\033\(B")
+# OSC 8 hyperlinks
+_osc8_re = re.compile(r"\033\]8;;.*?\033\\(.*?)\033\]8;;\033\\")
+@lru_cache
def _get_size(text: str) -> tuple[int, int]:
lines = text.split("\n")
height = len(lines)
@@ -170,7 +179,9 @@ class PrettyTable:
_sortby: str | None
_reversesort: bool
_sort_key: Callable[[RowType], SupportsRichComparison]
+ _row_filter: Callable[[RowType], bool]
_header: bool
+ _use_header_width: bool
_header_style: HeaderStyleType
_border: bool
_preserve_internal_border: bool
@@ -204,6 +215,7 @@ class PrettyTable:
orgmode: bool
_widths: list[int]
_hrule: str
+ _break_on_hyphens: bool
def __init__(self, field_names: Sequence[str] | None = None, **kwargs) -> None:
"""Return a new PrettyTable instance
@@ -217,6 +229,7 @@ class PrettyTable:
start - index of first data row to include in output
end - index of last data row to include in output PLUS ONE (list slice style)
header - print a header showing field names (True or False)
+ use_header_width - reflect width of header (True or False)
header_style - stylisation to apply to field names in header
("cap", "title", "upper", "lower" or None)
border - print a border around the table (True or False)
@@ -256,11 +269,13 @@ class PrettyTable:
single character string used to draw bottom-left line junctions
sortby - name of field to sort rows by
sort_key - sorting key function, applied to data points before sorting
+ row_filter - filter function applied on rows
align - default align for each column (None, "l", "c" or "r")
valign - default valign for each row (None, "t", "m" or "b")
reversesort - True or False to sort in descending or ascending order
- oldsortslice - Slice rows before sorting in the "old style" """
-
+ oldsortslice - Slice rows before sorting in the "old style"
+ break_on_hyphens - Whether long lines are broken on hypens or not, default: True
+ """
self.encoding = kwargs.get("encoding", "UTF-8")
# Data
@@ -283,11 +298,13 @@ class PrettyTable:
"end",
"fields",
"header",
+ "use_header_width",
"border",
"preserve_internal_border",
"sortby",
"reversesort",
"sort_key",
+ "row_filter",
"attributes",
"format",
"hrules",
@@ -323,6 +340,7 @@ class PrettyTable:
"none_format",
"escape_header",
"escape_data",
+ "break_on_hyphens",
]
self._none_format: dict[str, str | None] = {}
@@ -349,6 +367,10 @@ class PrettyTable:
self._header = kwargs["header"]
else:
self._header = True
+ if kwargs["use_header_width"] in (True, False):
+ self._use_header_width = kwargs["use_header_width"]
+ else:
+ self._use_header_width = True
self._header_style = kwargs["header_style"] or None
if kwargs["border"] in (True, False):
self._border = kwargs["border"]
@@ -367,6 +389,7 @@ class PrettyTable:
else:
self._reversesort = False
self._sort_key = kwargs["sort_key"] or (lambda x: x)
+ self._row_filter = kwargs["row_filter"] or (lambda x: True)
if kwargs["escape_data"] in (True, False):
self._escape_data = kwargs["escape_data"]
@@ -412,8 +435,12 @@ class PrettyTable:
self._format = kwargs["format"] or False
self._xhtml = kwargs["xhtml"] or False
self._attributes = kwargs["attributes"] or {}
+ if kwargs["break_on_hyphens"] in (True, False):
+ self._break_on_hyphens = kwargs["break_on_hyphens"]
+ else:
+ self._break_on_hyphens = True
- def _column_specific_args(self):
+ def _column_specific_args(self) -> None:
# Column specific arguments, use property.setters
for attr in (
"align",
@@ -524,7 +551,7 @@ class PrettyTable:
self._validate_nonnegative_int(option, val)
elif option == "sortby":
self._validate_field_name(option, val)
- elif option == "sort_key":
+ elif option in ("sort_key", "row_filter"):
self._validate_function(option, val)
elif option == "hrules":
self._validate_hrules(option, val)
@@ -534,6 +561,7 @@ class PrettyTable:
self._validate_all_field_names(option, val)
elif option in (
"header",
+ "use_header_width",
"border",
"preserve_internal_border",
"reversesort",
@@ -543,6 +571,7 @@ class PrettyTable:
"oldsortslice",
"escape_header",
"escape_data",
+ "break_on_hyphens",
):
self._validate_true_or_false(option, val)
elif option == "header_style":
@@ -742,23 +771,35 @@ class PrettyTable:
self._xhtml = val
@property
- def none_format(self):
+ def none_format(self) -> dict[str, str | None]:
return self._none_format
@none_format.setter
- def none_format(self, val):
+ def none_format(self, val: str | dict[str, str | None] | None):
+ """Representation of None values:
+
+ Arguments:
+
+ val - The alternative representation to be used for None values
+ """
if not self._field_names:
self._none_format = {}
- elif val is None or (isinstance(val, dict) and len(val) == 0):
+ elif isinstance(val, str):
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
+ elif isinstance(val, dict) and val:
+ for field, fval in val.items():
+ self._validate_none_format(fval)
+ self._none_format[field] = fval
+ else:
+ for field in self._field_names:
+ self._none_format[field] = None
@property
- def field_names(self):
+ def field_names(self) -> list[str]:
"""List or tuple of field names
When setting field_names, if there are already field names the new list
@@ -767,8 +808,8 @@ class PrettyTable:
return self._field_names
@field_names.setter
- def field_names(self, val) -> None:
- val = [str(x) for x in val]
+ def field_names(self, val: Sequence[Any]) -> None:
+ val = cast("list[str]", [str(x) for x in val])
self._validate_option("field_names", val)
old_names = None
if self._field_names:
@@ -798,7 +839,7 @@ class PrettyTable:
self.valign = "t"
@property
- def align(self):
+ def align(self) -> dict[str, AlignType]:
"""Controls alignment of fields
Arguments:
@@ -806,23 +847,27 @@ class PrettyTable:
return self._align
@align.setter
- def align(self, val) -> None:
- if val is None or (isinstance(val, dict) and len(val) == 0):
+ def align(self, val: AlignType | dict[str, AlignType] | None) -> None:
+ if isinstance(val, str):
+ self._validate_align(val)
if not self._field_names:
- self._align = {BASE_ALIGN_VALUE: "c"}
+ self._align = {BASE_ALIGN_VALUE: val}
else:
for field in self._field_names:
- self._align[field] = "c"
+ self._align[field] = val
+ elif isinstance(val, dict) and val:
+ for field, fval in val.items():
+ self._validate_align(fval)
+ self._align[field] = fval
else:
- self._validate_align(val)
if not self._field_names:
- self._align = {BASE_ALIGN_VALUE: val}
+ self._align = {BASE_ALIGN_VALUE: "c"}
else:
for field in self._field_names:
- self._align[field] = val
+ self._align[field] = "c"
@property
- def valign(self):
+ def valign(self) -> dict[str, VAlignType]:
"""Controls vertical alignment of fields
Arguments:
@@ -830,19 +875,23 @@ class PrettyTable:
return self._valign
@valign.setter
- def valign(self, val) -> None:
+ def valign(self, val: VAlignType | dict[str, VAlignType] | None) -> None:
if not self._field_names:
self._valign = {}
- elif val is None or (isinstance(val, dict) and len(val) == 0):
- for field in self._field_names:
- self._valign[field] = "t"
- else:
+ if isinstance(val, str):
self._validate_valign(val)
for field in self._field_names:
self._valign[field] = val
+ elif isinstance(val, dict) and val:
+ for field, fval in val.items():
+ self._validate_valign(fval)
+ self._valign[field] = fval
+ else:
+ for field in self._field_names:
+ self._valign[field] = "t"
@property
- def max_width(self):
+ def max_width(self) -> dict[str, int]:
"""Controls maximum width of fields
Arguments:
@@ -850,16 +899,20 @@ class PrettyTable:
return self._max_width
@max_width.setter
- def max_width(self, val) -> None:
- if val is None or (isinstance(val, dict) and len(val) == 0):
- self._max_width = {}
- else:
+ def max_width(self, val: int | dict[str, int] | None) -> None:
+ if isinstance(val, int):
self._validate_option("max_width", val)
for field in self._field_names:
self._max_width[field] = val
+ elif isinstance(val, dict) and val:
+ for field, fval in val.items():
+ self._validate_option("max_width", fval)
+ self._max_width[field] = fval
+ else:
+ self._max_width = {}
@property
- def min_width(self):
+ def min_width(self) -> dict[str, int]:
"""Controls minimum width of fields
Arguments:
@@ -867,13 +920,17 @@ class PrettyTable:
return self._min_width
@min_width.setter
- def min_width(self, val) -> None:
- if val is None or (isinstance(val, dict) and len(val) == 0):
- self._min_width = {}
- else:
+ def min_width(self, val: int | dict[str, int] | None) -> None:
+ if isinstance(val, int):
self._validate_option("min_width", val)
for field in self._field_names:
self._min_width[field] = val
+ elif isinstance(val, dict) and val:
+ for field, fval in val.items():
+ self._validate_option("min_width", fval)
+ self._min_width[field] = fval
+ else:
+ self._min_width = {}
@property
def min_table_width(self) -> int | None:
@@ -989,6 +1046,20 @@ class PrettyTable:
self._sort_key = val
@property
+ def row_filter(self) -> Callable[[RowType], bool]:
+ """Filter function, applied to data points
+
+ Arguments:
+
+ row_filter - a function which takes one argument and returns a Boolean"""
+ return self._row_filter
+
+ @row_filter.setter
+ def row_filter(self, val: Callable[[RowType], bool]) -> None:
+ self._validate_option("row_filter", val)
+ self._row_filter = val
+
+ @property
def header(self) -> bool:
"""Controls printing of table header with field names
@@ -1003,6 +1074,22 @@ class PrettyTable:
self._header = val
@property
+ def use_header_width(self) -> bool:
+ """Controls whether header is included in computing width
+
+ Arguments:
+
+ use_header_width - respect width of fieldname in header to calculate column
+ width (True or False)
+ """
+ return self._use_header_width
+
+ @use_header_width.setter
+ def use_header_width(self, val: bool) -> None:
+ self._validate_option("use_header_width", val)
+ self._use_header_width = val
+
+ @property
def header_style(self) -> HeaderStyleType:
"""Controls stylisation applied to field names in header
@@ -1075,7 +1162,7 @@ class PrettyTable:
self._vrules = val
@property
- def int_format(self):
+ def int_format(self) -> dict[str, str]:
"""Controls formatting of integer data
Arguments:
@@ -1083,16 +1170,20 @@ class PrettyTable:
return self._int_format
@int_format.setter
- def int_format(self, val) -> None:
- if val is None or (isinstance(val, dict) and len(val) == 0):
- self._int_format = {}
- else:
+ def int_format(self, val: str | dict[str, str] | None) -> None:
+ if isinstance(val, str):
self._validate_option("int_format", val)
for field in self._field_names:
self._int_format[field] = val
+ elif isinstance(val, dict) and val:
+ for field, fval in val.items():
+ self._validate_option("int_format", fval)
+ self._int_format[field] = fval
+ else:
+ self._int_format = {}
@property
- def float_format(self):
+ def float_format(self) -> dict[str, str]:
"""Controls formatting of floating point data
Arguments:
@@ -1100,16 +1191,20 @@ class PrettyTable:
return self._float_format
@float_format.setter
- def float_format(self, val) -> None:
- if val is None or (isinstance(val, dict) and len(val) == 0):
- self._float_format = {}
- else:
+ def float_format(self, val: str | dict[str, str] | None) -> None:
+ if isinstance(val, str):
self._validate_option("float_format", val)
for field in self._field_names:
self._float_format[field] = val
+ elif isinstance(val, dict) and val:
+ for field, fval in val.items():
+ self._validate_option("float_format", fval)
+ self._float_format[field] = fval
+ else:
+ self._float_format = {}
@property
- def custom_format(self):
+ def custom_format(self) -> dict[str, Callable[[str, Any], str]]:
"""Controls formatting of any column using callable
Arguments:
@@ -1117,7 +1212,10 @@ class PrettyTable:
return self._custom_format
@custom_format.setter
- def custom_format(self, val):
+ def custom_format(
+ self,
+ val: Callable[[str, Any], str] | dict[str, Callable[[str, Any], str]] | None,
+ ):
if val is None:
self._custom_format = {}
elif isinstance(val, dict):
@@ -1438,6 +1536,16 @@ class PrettyTable:
self._validate_option("escape_data", val)
self._escape_data = val
+ @property
+ def break_on_hyphens(self) -> bool:
+ """Break longlines on hyphens (True or False)"""
+ return self._break_on_hyphens
+
+ @break_on_hyphens.setter
+ def break_on_hyphens(self, val: bool) -> None:
+ self._validate_option("break_on_hyphens", val)
+ self._break_on_hyphens = val
+
##############################
# OPTION MIXER #
##############################
@@ -1573,16 +1681,22 @@ class PrettyTable:
# DATA INPUT METHODS #
##############################
- def add_rows(self, rows: Iterable[RowType]) -> None:
+ def add_rows(self, rows: Sequence[RowType], *, divider: bool = False) -> None:
"""Add rows to the table
Arguments:
rows - rows of data, should be an iterable of lists, each list with as many
- elements as the table has fields"""
- for row in rows:
+ elements as the table has fields
+
+ divider - add row divider after the row block
+ """
+ for row in rows[:-1]:
self.add_row(row)
+ if len(rows) > 0:
+ self.add_row(rows[-1], divider=divider)
+
def add_row(self, row: RowType, *, divider: bool = False) -> None:
"""Add a row to the table
@@ -1598,7 +1712,7 @@ class PrettyTable:
)
raise ValueError(msg)
if not self._field_names:
- self.field_names = [f"Field {n + 1}" for n in range(0, len(row))]
+ self.field_names = [f"Field {n + 1}" for n in range(len(row))]
self._rows.append(list(row))
self._dividers.append(divider)
@@ -1648,7 +1762,7 @@ class PrettyTable:
self._field_names.append(fieldname)
self._align[fieldname] = align
self._valign[fieldname] = valign
- for i in range(0, len(column)):
+ for i in range(len(column)):
if len(self._rows) < i + 1:
self._rows.append([])
self._dividers.append(False)
@@ -1732,10 +1846,12 @@ class PrettyTable:
return self.get_csv_string(**kwargs)
if out_format == "latex":
return self.get_latex_string(**kwargs)
+ if out_format == "mediawiki":
+ return self.get_mediawiki_string(**kwargs)
msg = (
f"Invalid format {out_format}. "
- "Must be one of: text, html, json, csv, or latex"
+ "Must be one of: text, html, json, csv, latex or mediawiki"
)
raise ValueError(msg)
@@ -1768,7 +1884,7 @@ class PrettyTable:
return table_width
def _compute_widths(self, rows: list[list[str]], options: OptionsType) -> None:
- if options["header"]:
+ if options["header"] and options["use_header_width"]:
widths = [_get_size(field)[0] for field in self._field_names]
else:
widths = len(self.field_names) * [0]
@@ -1816,7 +1932,7 @@ class PrettyTable:
# Are we under min_table_width or title width?
if self._min_table_width or options["title"]:
if options["title"]:
- title_width = len(options["title"]) + per_col_padding
+ title_width = _str_block_width(options["title"]) + per_col_padding
if options["vrules"] in (VRuleStyle.FRAME, VRuleStyle.ALL):
title_width += 2
else:
@@ -1861,12 +1977,13 @@ class PrettyTable:
Arguments:
options - dictionary of option settings."""
- import copy
if options["oldsortslice"]:
- rows = copy.deepcopy(self._rows[options["start"] : options["end"]])
+ rows = self._rows[options["start"] : options["end"]]
else:
- rows = copy.deepcopy(self._rows)
+ rows = self._rows
+
+ rows = [row for row in rows if options["row_filter"](row)]
# Sort
if options["sortby"]:
@@ -1890,12 +2007,10 @@ class PrettyTable:
Arguments:
options - dictionary of option settings."""
- import copy
-
if options["oldsortslice"]:
- dividers = copy.deepcopy(self._dividers[options["start"] : options["end"]])
+ dividers = self._dividers[options["start"] : options["end"]]
else:
- dividers = copy.deepcopy(self._dividers)
+ dividers = self._dividers
if options["sortby"]:
dividers = [False for divider in dividers]
@@ -1925,6 +2040,7 @@ class PrettyTable:
end - index of last data row to include in output PLUS ONE (list slice style)
fields - names of fields (columns) to include
header - print a header showing field names (True or False)
+ use_header_width - reflect width of header (True or False)
border - print a border around the table (True or False)
preserve_internal_border - print a border inside the table even if
border is disabled (True or False)
@@ -1960,6 +2076,7 @@ class PrettyTable:
sortby - name of field to sort rows by
sort_key - sorting key function, applied to data points before sorting
reversesort - True or False to sort in descending or ascending order
+ row_filter - filter function applied on rows
print empty - if True, stringify just the header for an empty table,
if False return an empty string"""
@@ -2177,7 +2294,7 @@ class PrettyTable:
import textwrap
for index, field, value, width in zip(
- range(0, len(row)), self._field_names, row, self._widths
+ range(len(row)), self._field_names, row, self._widths
):
# Enforce max widths
lines = value.split("\n")
@@ -2189,7 +2306,9 @@ class PrettyTable:
):
line = none_val
if _str_block_width(line) > width:
- line = textwrap.fill(line, width)
+ line = textwrap.fill(
+ line, width, break_on_hyphens=options["break_on_hyphens"]
+ )
new_lines.append(line)
lines = new_lines
value = "\n".join(lines)
@@ -2203,7 +2322,7 @@ class PrettyTable:
bits: list[list[str]] = []
lpad, rpad = self._get_padding_widths(options)
- for y in range(0, row_height):
+ for y in range(row_height):
bits.append([])
if options["border"]:
if options["vrules"] in (VRuleStyle.ALL, VRuleStyle.FRAME):
@@ -2250,7 +2369,7 @@ class PrettyTable:
# If vrules is FRAME, then we just appended a space at the end
# of the last field, when we really want a vertical character
- for y in range(0, row_height):
+ for y in range(row_height):
if options["border"] and options["vrules"] == VRuleStyle.FRAME:
bits[y].pop()
bits[y].append(options["vertical_char"])
@@ -2392,12 +2511,15 @@ class PrettyTable:
right_padding_width - number of spaces on right hand side of column data
sortby - name of field to sort rows by
sort_key - sorting key function, applied to data points before sorting
+ row_filter - filter function applied on rows
attributes - dictionary of name/value pairs to include as HTML attributes in the
<table> tag
format - Controls whether or not HTML tables are formatted to match
styling options (True or False)
escape_data - escapes the text within a data field (True or False)
- xhtml - print <br/> tags if True, <br> tags if False"""
+ xhtml - print <br/> tags if True, <br> tags if False
+ break_on_hyphens - Whether long lines are broken on hypens or not, default: True
+ """
options = self._get_options(kwargs)
@@ -2597,6 +2719,7 @@ class PrettyTable:
float_format - controls formatting of floating point data
sortby - name of field to sort rows by
sort_key - sorting key function, applied to data points before sorting
+ row_filter - filter function applied on rows
format - Controls whether or not HTML tables are formatted to match
styling options (True or False)
"""
@@ -2644,7 +2767,6 @@ class PrettyTable:
def _get_formatted_latex_string(self, options: OptionsType) -> str:
lines: list[str] = []
- wanted_fields: list[str] = []
if options["fields"]:
wanted_fields = [
field for field in self._field_names if field in options["fields"]
@@ -2701,15 +2823,81 @@ class PrettyTable:
return "\r\n".join(lines)
+ ##############################
+ # MEDIAWIKI STRING METHODS #
+ ##############################
+
+ def get_mediawiki_string(self, **kwargs) -> str:
+ """
+ Return string representation of the table in MediaWiki table markup.
+ The generated markup follows simple MediaWiki syntax. For example:
+ {| class="wikitable"
+ |+ Optional caption
+ |-
+ ! Header1 !! Header2 !! Header3
+ |-
+ | Data1 || Data2 || Data3
+ |-
+ | Data4 || Data5 || Data6
+ |}
+ """
+
+ options = self._get_options(kwargs)
+ lines: list[str] = []
+
+ if (
+ options.get("attributes")
+ and isinstance(options["attributes"], dict)
+ and options["attributes"]
+ ):
+ attr_str = " ".join(f'{k}="{v}"' for k, v in options["attributes"].items())
+ lines.append("{| " + attr_str)
+ else:
+ lines.append('{| class="wikitable"')
+
+ caption = options.get("title", self._title)
+ if caption:
+ lines.append("|+ " + caption)
+
+ if options.get("header"):
+ lines.append("|-")
+ headers = []
+ fields_option = options.get("fields")
+ for field in self._field_names:
+ if fields_option is not None and field not in fields_option:
+ continue
+ headers.append(field)
+ if headers:
+ header_line = " !! ".join(headers)
+ lines.append("! " + header_line)
+
+ rows = self._get_rows(options)
+ formatted_rows = self._format_rows(rows)
+ for row in formatted_rows:
+ lines.append("|-")
+ cells = []
+ fields_option = options.get("fields")
+ for field, cell in zip(self._field_names, row):
+ if fields_option is not None and field not in fields_option:
+ continue
+ cells.append(cell)
+ if cells:
+ lines.append("| " + " || ".join(cells))
+
+ lines.append("|}")
+ return "\n".join(lines)
+
##############################
# UNICODE WIDTH FUNCTION #
##############################
+@lru_cache
def _str_block_width(val: str) -> int:
- import wcwidth # type: ignore[import-untyped]
+ import wcwidth
+ val = _osc8_re.sub(r"\1", val)
return wcwidth.wcswidth(_re.sub("", val))
@@ -2842,7 +3030,7 @@ class TableHandler(HTMLParser):
"""
iterates over the row and make each field unique
"""
- for i in range(0, len(fields)):
+ for i in range(len(fields)):
for j in range(i + 1, len(fields)):
if fields[i] == fields[j]:
fields[j] += "'"
@@ -2874,6 +3062,53 @@ def from_html_one(html_code: str, **kwargs) -> PrettyTable:
return tables[0]
+def from_mediawiki(wiki_text: str, **kwargs) -> PrettyTable:
+ """
+ Returns a PrettyTable instance from simple MediaWiki table markup.
+ Note that the table should have a header row.
+ Arguments:
+ wiki_text -- Multiline string containing MediaWiki table markup
+ (Enter within ''' ''')
+ """
+ lines = wiki_text.strip().split("\n")
+ table = PrettyTable(**kwargs)
+ header = None
+ rows = []
+ inside_table = False
+ for line in lines:
+ line = line.strip()
+ if line.startswith("{|"):
+ inside_table = True
+ continue
+ if line.startswith("|}"):
+ break
+ if not inside_table:
+ continue
+ if line.startswith("|-"):
+ continue
+ if line.startswith("|+"):
+ continue
+ if line.startswith("!"):
+ header = [cell.strip() for cell in re.split(r"\s*!!\s*", line[1:])]
+ table.field_names = header
+ continue
+ if line.startswith("|"):
+ row_data = [cell.strip() for cell in re.split(r"\s*\|\|\s*", line[1:])]
+ rows.append(row_data)
+ continue
+
+ if header:
+ for row in rows:
+ if len(row) != len(header):
+ error_message = "Row length mismatch between header and body."
+ raise ValueError(error_message)
+ table.add_row(row)
+ else:
+ msg = "No valid header found in the MediaWiki table."
+ raise ValueError(msg)
+ return table
+
+
def _warn_deprecation(name: str, module_globals: dict[str, Any]) -> Any:
if (val := module_globals.get(f"_DEPRECATED_{name}")) is None:
msg = f"module '{__name__}' has no attribute '{name}'"
@@ -2886,6 +3121,9 @@ def _warn_deprecation(name: str, module_globals: dict[str, Any]) -> Any:
)
else:
msg = f"the '{name}' constant is deprecated, use the 'TableStyle' enum instead"
+
+ import warnings
+
warnings.warn(msg, DeprecationWarning, stacklevel=3)
return val
diff --git a/contrib/python/prettytable/py3/tests/conftest.py b/contrib/python/prettytable/py3/tests/conftest.py
new file mode 100644
index 00000000000..22aa54c8b27
--- /dev/null
+++ b/contrib/python/prettytable/py3/tests/conftest.py
@@ -0,0 +1,89 @@
+from __future__ import annotations
+
+import io
+
+import pytest
+from test_prettytable import CITY_DATA, CITY_DATA_HEADER
+
+from prettytable import PrettyTable, from_csv, from_mediawiki
+
+
[email protected](scope="function")
+def col_prettytable() -> PrettyTable:
+ # Column by column...
+ table = PrettyTable()
+ for idx, colname in enumerate(CITY_DATA_HEADER):
+ table.add_column(colname, [row[idx] for row in CITY_DATA])
+ return table
+
+
[email protected](scope="function")
+def city_data() -> PrettyTable:
+ """Just build the Australian capital city data example table."""
+ table = PrettyTable(CITY_DATA_HEADER)
+ for row in CITY_DATA:
+ table.add_row(row)
+ return table
+
+
[email protected](scope="function")
+def city_data_from_csv() -> PrettyTable:
+ csv_string = ", ".join(CITY_DATA_HEADER)
+ for row in CITY_DATA:
+ csv_string += "\n" + ",".join(str(fld) for fld in row)
+ csv_fp = io.StringIO(csv_string)
+ return from_csv(csv_fp)
+
+
[email protected](scope="function")
+def city_data_from_mediawiki() -> PrettyTable:
+ wiki_text = '{| class="wikitable"\n'
+ wiki_text += "|-\n"
+ wiki_text += "! " + " !! ".join(CITY_DATA_HEADER) + "\n"
+ for row in CITY_DATA:
+ wiki_text += "|-\n"
+ wiki_text += "| " + " || ".join(str(item) for item in row) + "\n"
+ wiki_text += "|}"
+ return from_mediawiki(wiki_text)
+
+
+def mix_prettytable() -> PrettyTable:
+ # A mix of both!
+ table = PrettyTable()
+ table.field_names = [CITY_DATA_HEADER[0], CITY_DATA_HEADER[1]]
+ for row in CITY_DATA:
+ table.add_row([row[0], row[1]])
+ for idx, colname in enumerate(CITY_DATA_HEADER[2:]):
+ table.add_column(colname, [row[idx + 2] for row in CITY_DATA])
+ return table
+
+
[email protected](scope="function")
+def field_name_less_table() -> PrettyTable:
+ table = PrettyTable()
+ for row in CITY_DATA:
+ table.add_row(row)
+ return table
+
+
[email protected](scope="function")
+def row_prettytable(field_name_less_table: PrettyTable) -> PrettyTable:
+ # Row by row...
+ field_name_less_table.field_names = CITY_DATA_HEADER
+ return field_name_less_table
+
+
[email protected](scope="function")
+def empty_helper_table() -> PrettyTable:
+ return PrettyTable(["", "Field 1", "Field 2", "Field 3"])
+
+
[email protected](scope="function")
+def helper_table(empty_helper_table: PrettyTable) -> PrettyTable:
+ v = 1
+ for row in range(3):
+ # Some have spaces, some not, to help test padding columns of different widths
+ empty_helper_table.add_row([v, f"value {v}", f"value{v+1}", f"value{v+2}"])
+ v += 3
+ return empty_helper_table
diff --git a/contrib/python/prettytable/py3/tests/test_colortable.py b/contrib/python/prettytable/py3/tests/test_colortable.py
index 50556384383..40a287169a0 100644
--- a/contrib/python/prettytable/py3/tests/test_colortable.py
+++ b/contrib/python/prettytable/py3/tests/test_colortable.py
@@ -1,37 +1,21 @@
from __future__ import annotations
import pytest
+from test_prettytable import CITY_DATA, CITY_DATA_HEADER
-from prettytable import PrettyTable
from prettytable.colortable import RESET_CODE, ColorTable, Theme, Themes
-
-def row_prettytable() -> PrettyTable:
- # Row by row...
- table = PrettyTable()
- table.field_names = ["City name", "Area", "Population", "Annual Rainfall"]
- table.add_row(["Adelaide", 1295, 1158259, 600.5])
- table.add_row(["Brisbane", 5905, 1857594, 1146.4])
- table.add_row(["Darwin", 112, 120900, 1714.7])
- table.add_row(["Hobart", 1357, 205556, 619.5])
- table.add_row(["Sydney", 2058, 4336374, 1214.8])
- table.add_row(["Melbourne", 1566, 3806092, 646.9])
- table.add_row(["Perth", 5386, 1554769, 869.4])
- return table
+TYPE_CHECKING = False
+if TYPE_CHECKING:
+ from prettytable import PrettyTable
@pytest.fixture
-def row_colortable():
+def row_colortable() -> PrettyTable:
table = ColorTable()
- table.field_names = ["City name", "Area", "Population", "Annual Rainfall"]
- table.add_row(["Adelaide", 1295, 1158259, 600.5])
- table.add_row(["Brisbane", 5905, 1857594, 1146.4])
- table.add_row(["Darwin", 112, 120900, 1714.7])
- table.add_row(["Hobart", 1357, 205556, 619.5])
- table.add_row(["Sydney", 2058, 4336374, 1214.8])
- table.add_row(["Melbourne", 1566, 3806092, 646.9])
- table.add_row(["Perth", 5386, 1554769, 869.4])
+ table.field_names = CITY_DATA_HEADER
+ for row in CITY_DATA:
+ table.add_row(row)
return table
@@ -68,8 +52,9 @@ class TestColorTable:
dict2 = table2.__dict__
# So we don't compare functions
- del dict1["_sort_key"]
- del dict2["_sort_key"]
+ for func in ("_sort_key", "_row_filter"):
+ del dict1[func]
+ del dict2[func]
assert dict1 == dict2
@@ -127,6 +112,10 @@ class TestColorTableRendering:
minus = chars.get("-")
pipe = chars.get("|")
space = chars.get(" ")
+ assert isinstance(plus, str)
+ assert isinstance(minus, str)
+ assert isinstance(pipe, str)
+ assert isinstance(space, str)
# +-----------------------+
# | Efforts |
diff --git a/contrib/python/prettytable/py3/tests/test_html.py b/contrib/python/prettytable/py3/tests/test_html.py
new file mode 100644
index 00000000000..0c3ef03a21d
--- /dev/null
+++ b/contrib/python/prettytable/py3/tests/test_html.py
@@ -0,0 +1,585 @@
+from __future__ import annotations
+
+import pytest
+
+from prettytable import HRuleStyle, PrettyTable, from_html, from_html_one
+
+
+class TestHtmlConstructor:
+ def test_html_and_back(self, city_data: PrettyTable) -> None:
+ html_string = city_data.get_html_string()
+ new_table = from_html(html_string)[0]
+ assert new_table.get_string() == city_data.get_string()
+
+ def test_html_one_and_back(self, city_data: PrettyTable) -> None:
+ html_string = city_data.get_html_string()
+ new_table = from_html_one(html_string)
+ assert new_table.get_string() == city_data.get_string()
+
+ def test_html_one_fail_on_many(self, city_data: PrettyTable) -> None:
+ html_string = city_data.get_html_string()
+ html_string += city_data.get_html_string()
+ with pytest.raises(ValueError):
+ from_html_one(html_string)
+
+
+class TestHtmlOutput:
+ def test_html_output(self, helper_table: PrettyTable) -> None:
+ result = helper_table.get_html_string()
+ assert (
+ result.strip()
+ == """
+<table>
+ <thead>
+ <tr>
+ <th></th>
+ <th>Field 1</th>
+ <th>Field 2</th>
+ <th>Field 3</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>1</td>
+ <td>value 1</td>
+ <td>value2</td>
+ <td>value3</td>
+ </tr>
+ <tr>
+ <td>4</td>
+ <td>value 4</td>
+ <td>value5</td>
+ <td>value6</td>
+ </tr>
+ <tr>
+ <td>7</td>
+ <td>value 7</td>
+ <td>value8</td>
+ <td>value9</td>
+ </tr>
+ </tbody>
+</table>
+""".strip()
+ )
+
+ def test_html_output_formatted(self, helper_table: PrettyTable) -> None:
+ result = helper_table.get_html_string(format=True)
+ assert (
+ result.strip()
+ == """
+<table frame="box" rules="cols">
+ <thead>
+ <tr>
+ <th style="padding-left: 1em; padding-right: 1em; text-align: center"></th>
+ <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">1</td>
+ <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">4</td>
+ <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">7</td>
+ <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
+ )
+
+ def test_html_output_with_title(self, helper_table: PrettyTable) -> None:
+ helper_table.title = "Title & Title"
+ result = helper_table.get_html_string(
+ attributes={"bgcolor": "red", "a<b": "1<2"}
+ )
+ assert (
+ result.strip()
+ == """
+<table bgcolor="red" a&lt;b="1&lt;2">
+ <caption>Title &amp; Title</caption>
+ <thead>
+ <tr>
+ <th></th>
+ <th>Field 1</th>
+ <th>Field 2</th>
+ <th>Field 3</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>1</td>
+ <td>value 1</td>
+ <td>value2</td>
+ <td>value3</td>
+ </tr>
+ <tr>
+ <td>4</td>
+ <td>value 4</td>
+ <td>value5</td>
+ <td>value6</td>
+ </tr>
+ <tr>
+ <td>7</td>
+ <td>value 7</td>
+ <td>value8</td>
+ <td>value9</td>
+ </tr>
+ </tbody>
+</table>
+""".strip()
+ )
+
+ def test_html_output_formatted_with_title(self, helper_table: PrettyTable) -> None:
+ helper_table.title = "Title & Title"
+ result = helper_table.get_html_string(
+ attributes={"bgcolor": "red", "a<b": "1<2"}, format=True
+ )
+ assert (
+ result.strip()
+ == """
+<table frame="box" rules="cols" bgcolor="red" a&lt;b="1&lt;2">
+ <caption>Title &amp; Title</caption>
+ <thead>
+ <tr>
+ <th style="padding-left: 1em; padding-right: 1em; text-align: center"></th>
+ <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">1</td>
+ <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">4</td>
+ <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">7</td>
+ <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
+ )
+
+ def test_html_output_without_escaped_header(
+ self, empty_helper_table: PrettyTable
+ ) -> None:
+ empty_helper_table.field_names = [
+ "",
+ "Field 1",
+ "<em>Field 2</em>",
+ "<a href='#'>Field 3</a>",
+ ]
+ result = empty_helper_table.get_html_string(escape_header=False)
+ assert (
+ result.strip()
+ == """
+<table>
+ <thead>
+ <tr>
+ <th></th>
+ <th>Field 1</th>
+ <th><em>Field 2</em></th>
+ <th><a href='#'>Field 3</a></th>
+ </tr>
+ </thead>
+ <tbody>
+ </tbody>
+</table>
+""".strip()
+ )
+
+ def test_html_output_without_escaped_data(
+ self, empty_helper_table: PrettyTable
+ ) -> None:
+ empty_helper_table.add_row(
+ [
+ 1,
+ "<b>value 1</b>",
+ "<span style='text-decoration: underline;'>value2</span>",
+ "<a href='#'>value3</a>",
+ ]
+ )
+ result = empty_helper_table.get_html_string(escape_data=False)
+ assert (
+ result.strip()
+ == """
+<table>
+ <thead>
+ <tr>
+ <th></th>
+ <th>Field 1</th>
+ <th>Field 2</th>
+ <th>Field 3</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>1</td>
+ <td><b>value 1</b></td>
+ <td><span style='text-decoration: underline;'>value2</span></td>
+ <td><a href='#'>value3</a></td>
+ </tr>
+ </tbody>
+</table>
+""".strip()
+ )
+
+ def test_html_output_with_escaped_header(
+ self, empty_helper_table: PrettyTable
+ ) -> None:
+ empty_helper_table.field_names = [
+ "",
+ "Field 1",
+ "<em>Field 2</em>",
+ "<a href='#'>Field 3</a>",
+ ]
+ result = empty_helper_table.get_html_string(escape_header=True)
+ assert (
+ result.strip()
+ == """
+<table>
+ <thead>
+ <tr>
+ <th></th>
+ <th>Field 1</th>
+ <th>&lt;em&gt;Field 2&lt;/em&gt;</th>
+ <th>&lt;a href=&#x27;#&#x27;&gt;Field 3&lt;/a&gt;</th>
+ </tr>
+ </thead>
+ <tbody>
+ </tbody>
+</table>
+""".strip()
+ )
+
+ def test_html_output_with_escaped_data(
+ self, empty_helper_table: PrettyTable
+ ) -> None:
+ empty_helper_table.add_row(
+ [
+ 1,
+ "<b>value 1</b>",
+ "<span style='text-decoration: underline;'>value2</span>",
+ "<a href='#'>value3</a>",
+ ]
+ )
+ result = empty_helper_table.get_html_string(escape_data=True)
+ assert (
+ result.strip()
+ == """
+<table>
+ <thead>
+ <tr>
+ <th></th>
+ <th>Field 1</th>
+ <th>Field 2</th>
+ <th>Field 3</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>1</td>
+ <td>&lt;b&gt;value 1&lt;/b&gt;</td>
+ <td>&lt;span style=&#x27;text-decoration: underline;&#x27;&gt;value2&lt;/span&gt;</td>
+ <td>&lt;a href=&#x27;#&#x27;&gt;value3&lt;/a&gt;</td>
+ </tr>
+ </tbody>
+</table>
+""".strip() # noqa: E501
+ )
+
+ def test_html_output_formatted_without_escaped_header(
+ self, empty_helper_table: PrettyTable
+ ) -> None:
+ empty_helper_table.field_names = [
+ "",
+ "Field 1",
+ "<em>Field 2</em>",
+ "<a href='#'>Field 3</a>",
+ ]
+ result = empty_helper_table.get_html_string(escape_header=False, format=True)
+ assert (
+ result.strip()
+ == """
+<table frame="box" rules="cols">
+ <thead>
+ <tr>
+ <th style="padding-left: 1em; padding-right: 1em; text-align: center"></th>
+ <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"><em>Field 2</em></th>
+ <th style="padding-left: 1em; padding-right: 1em; text-align: center"><a href='#'>Field 3</a></th>
+ </tr>
+ </thead>
+ <tbody>
+ </tbody>
+</table>
+""".strip() # noqa: E501
+ )
+
+ def test_html_output_formatted_without_escaped_data(
+ self, empty_helper_table: PrettyTable
+ ) -> None:
+ empty_helper_table.add_row(
+ [
+ 1,
+ "<b>value 1</b>",
+ "<span style='text-decoration: underline;'>value2</span>",
+ "<a href='#'>value3</a>",
+ ]
+ )
+ result = empty_helper_table.get_html_string(escape_data=False, format=True)
+ assert (
+ result.strip()
+ == """
+<table frame="box" rules="cols">
+ <thead>
+ <tr>
+ <th style="padding-left: 1em; padding-right: 1em; text-align: center"></th>
+ <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">1</td>
+ <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top"><b>value 1</b></td>
+ <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top"><span style='text-decoration: underline;'>value2</span></td>
+ <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top"><a href='#'>value3</a></td>
+ </tr>
+ </tbody>
+</table>
+""".strip() # noqa: E501
+ )
+
+ def test_html_output_formatted_with_escaped_header(
+ self, empty_helper_table: PrettyTable
+ ) -> None:
+ empty_helper_table.field_names = [
+ "",
+ "Field 1",
+ "<em>Field 2</em>",
+ "<a href='#'>Field 3</a>",
+ ]
+ result = empty_helper_table.get_html_string(escape_header=True, format=True)
+ assert (
+ result.strip()
+ == """
+<table frame="box" rules="cols">
+ <thead>
+ <tr>
+ <th style="padding-left: 1em; padding-right: 1em; text-align: center"></th>
+ <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">&lt;em&gt;Field 2&lt;/em&gt;</th>
+ <th style="padding-left: 1em; padding-right: 1em; text-align: center">&lt;a href=&#x27;#&#x27;&gt;Field 3&lt;/a&gt;</th>
+ </tr>
+ </thead>
+ <tbody>
+ </tbody>
+</table>
+""".strip() # noqa: E501
+ )
+
+ def test_html_output_formatted_with_escaped_data(
+ self, empty_helper_table: PrettyTable
+ ) -> None:
+ empty_helper_table.add_row(
+ [
+ 1,
+ "<b>value 1</b>",
+ "<span style='text-decoration: underline;'>value2</span>",
+ "<a href='#'>value3</a>",
+ ]
+ )
+ result = empty_helper_table.get_html_string(escape_data=True, format=True)
+ assert (
+ result.strip()
+ == """
+<table frame="box" rules="cols">
+ <thead>
+ <tr>
+ <th style="padding-left: 1em; padding-right: 1em; text-align: center"></th>
+ <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">1</td>
+ <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">&lt;b&gt;value 1&lt;/b&gt;</td>
+ <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">&lt;span style=&#x27;text-decoration: underline;&#x27;&gt;value2&lt;/span&gt;</td>
+ <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">&lt;a href=&#x27;#&#x27;&gt;value3&lt;/a&gt;</td>
+ </tr>
+ </tbody>
+</table>
+""".strip() # noqa: E501
+ )
+
+ def test_table_formatted_html_autoindex(self) -> None:
+ """See also #199"""
+ table = PrettyTable(["Field 1", "Field 2", "Field 3"])
+ for row in range(1, 3 * 3, 3):
+ table.add_row(
+ [f"value {row*100}", f"value {row+1*100}", f"value {row+2*100}"]
+ )
+ table.format = True
+ table.add_autoindex("I")
+
+ assert (
+ table.get_html_string().strip()
+ == """
+<table frame="box" rules="cols">
+ <thead>
+ <tr>
+ <th style="padding-left: 1em; padding-right: 1em; text-align: center">I</th>
+ <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">1</td>
+ <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value 100</td>
+ <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value 101</td>
+ <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value 201</td>
+ </tr>
+ <tr>
+ <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">2</td>
+ <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value 400</td>
+ <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value 104</td>
+ <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value 204</td>
+ </tr>
+ <tr>
+ <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">3</td>
+ <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value 700</td>
+ <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value 107</td>
+ <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value 207</td>
+ </tr>
+ </tbody>
+</table>""".strip() # noqa: E501
+ )
+
+ def test_internal_border_preserved_html(self, helper_table: PrettyTable) -> None:
+ helper_table.format = True
+ helper_table.border = False
+ helper_table.preserve_internal_border = True
+
+ assert (
+ helper_table.get_html_string().strip()
+ == """
+<table rules="cols">
+ <thead>
+ <tr>
+ <th style="padding-left: 1em; padding-right: 1em; text-align: center"></th>
+ <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">1</td>
+ <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">4</td>
+ <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">7</td>
+ <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
+ )
+
+ def test_break_line_html(self) -> None:
+ table = PrettyTable(["Field 1", "Field 2"])
+ table.add_row(["value 1", "value2\nsecond line"])
+ table.add_row(["value 3", "value4"])
+ result = table.get_html_string(hrules=HRuleStyle.ALL)
+ assert (
+ result.strip()
+ == """
+<table>
+ <thead>
+ <tr>
+ <th>Field 1</th>
+ <th>Field 2</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>value 1</td>
+ <td>value2<br>second line</td>
+ </tr>
+ <tr>
+ <td>value 3</td>
+ <td>value4</td>
+ </tr>
+ </tbody>
+</table>
+""".strip()
+ )
+
+ def test_break_line_xhtml(self) -> None:
+ table = PrettyTable(["Field 1", "Field 2"])
+ table.add_row(["value 1", "value2\nsecond line"])
+ table.add_row(["value 3", "value4"])
+ result = table.get_html_string(hrules=HRuleStyle.ALL, xhtml=True)
+ assert (
+ result.strip()
+ == """
+<table>
+ <thead>
+ <tr>
+ <th>Field 1</th>
+ <th>Field 2</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>value 1</td>
+ <td>value2<br/>second line</td>
+ </tr>
+ <tr>
+ <td>value 3</td>
+ <td>value4</td>
+ </tr>
+ </tbody>
+</table>
+""".strip()
+ )
diff --git a/contrib/python/prettytable/py3/tests/test_json.py b/contrib/python/prettytable/py3/tests/test_json.py
new file mode 100644
index 00000000000..0e2b3659397
--- /dev/null
+++ b/contrib/python/prettytable/py3/tests/test_json.py
@@ -0,0 +1,80 @@
+from __future__ import annotations
+
+from prettytable import PrettyTable, from_json
+
+
+class TestJSONOutput:
+ def test_json_output(self, helper_table: PrettyTable) -> None:
+ result = helper_table.get_json_string()
+ assert (
+ result.strip()
+ == """
+[
+ [
+ "",
+ "Field 1",
+ "Field 2",
+ "Field 3"
+ ],
+ {
+ "": 1,
+ "Field 1": "value 1",
+ "Field 2": "value2",
+ "Field 3": "value3"
+ },
+ {
+ "": 4,
+ "Field 1": "value 4",
+ "Field 2": "value5",
+ "Field 3": "value6"
+ },
+ {
+ "": 7,
+ "Field 1": "value 7",
+ "Field 2": "value8",
+ "Field 3": "value9"
+ }
+]""".strip()
+ )
+ options = {"fields": ["Field 1", "Field 3"]}
+ result = helper_table.get_json_string(**options)
+ assert (
+ result.strip()
+ == """
+[
+ [
+ "Field 1",
+ "Field 3"
+ ],
+ {
+ "Field 1": "value 1",
+ "Field 3": "value3"
+ },
+ {
+ "Field 1": "value 4",
+ "Field 3": "value6"
+ },
+ {
+ "Field 1": "value 7",
+ "Field 3": "value9"
+ }
+]""".strip()
+ )
+
+ def test_json_output_options(self, helper_table: PrettyTable) -> None:
+ result = helper_table.get_json_string(
+ header=False, indent=None, separators=(",", ":")
+ )
+ assert (
+ result
+ == """[{"":1,"Field 1":"value 1","Field 2":"value2","Field 3":"value3"},"""
+ """{"":4,"Field 1":"value 4","Field 2":"value5","Field 3":"value6"},"""
+ """{"":7,"Field 1":"value 7","Field 2":"value8","Field 3":"value9"}]"""
+ )
+
+
+class TestJSONConstructor:
+ def test_json_and_back(self, city_data: PrettyTable) -> None:
+ json_string = city_data.get_json_string()
+ new_table = from_json(json_string)
+ assert new_table.get_string() == city_data.get_string()
diff --git a/contrib/python/prettytable/py3/tests/test_latex.py b/contrib/python/prettytable/py3/tests/test_latex.py
new file mode 100644
index 00000000000..7faa7c81921
--- /dev/null
+++ b/contrib/python/prettytable/py3/tests/test_latex.py
@@ -0,0 +1,100 @@
+from __future__ import annotations
+
+from prettytable import HRuleStyle, PrettyTable, VRuleStyle
+
+
+class TestLatexOutput:
+ def test_latex_output(self, helper_table: PrettyTable) -> None:
+ assert helper_table.get_latex_string() == (
+ "\\begin{tabular}{cccc}\r\n"
+ " & Field 1 & Field 2 & Field 3 \\\\\r\n"
+ "1 & value 1 & value2 & value3 \\\\\r\n"
+ "4 & value 4 & value5 & value6 \\\\\r\n"
+ "7 & value 7 & value8 & value9 \\\\\r\n"
+ "\\end{tabular}"
+ )
+ options = {"fields": ["Field 1", "Field 3"]}
+ assert helper_table.get_latex_string(**options) == (
+ "\\begin{tabular}{cc}\r\n"
+ "Field 1 & Field 3 \\\\\r\n"
+ "value 1 & value3 \\\\\r\n"
+ "value 4 & value6 \\\\\r\n"
+ "value 7 & value9 \\\\\r\n"
+ "\\end{tabular}"
+ )
+
+ def test_latex_output_formatted(self, helper_table: PrettyTable) -> None:
+ assert helper_table.get_latex_string(format=True) == (
+ "\\begin{tabular}{|c|c|c|c|}\r\n"
+ "\\hline\r\n"
+ " & Field 1 & Field 2 & Field 3 \\\\\r\n"
+ "1 & value 1 & value2 & value3 \\\\\r\n"
+ "4 & value 4 & value5 & value6 \\\\\r\n"
+ "7 & value 7 & value8 & value9 \\\\\r\n"
+ "\\hline\r\n"
+ "\\end{tabular}"
+ )
+
+ options = {"fields": ["Field 1", "Field 3"]}
+ assert helper_table.get_latex_string(format=True, **options) == (
+ "\\begin{tabular}{|c|c|}\r\n"
+ "\\hline\r\n"
+ "Field 1 & Field 3 \\\\\r\n"
+ "value 1 & value3 \\\\\r\n"
+ "value 4 & value6 \\\\\r\n"
+ "value 7 & value9 \\\\\r\n"
+ "\\hline\r\n"
+ "\\end{tabular}"
+ )
+
+ vrule_options: dict[str, VRuleStyle] = {"vrules": VRuleStyle.FRAME}
+ assert helper_table.get_latex_string(format=True, **vrule_options) == (
+ "\\begin{tabular}{|cccc|}\r\n"
+ "\\hline\r\n"
+ " & Field 1 & Field 2 & Field 3 \\\\\r\n"
+ "1 & value 1 & value2 & value3 \\\\\r\n"
+ "4 & value 4 & value5 & value6 \\\\\r\n"
+ "7 & value 7 & value8 & value9 \\\\\r\n"
+ "\\hline\r\n"
+ "\\end{tabular}"
+ )
+
+ hrule_options: dict[str, HRuleStyle] = {"hrules": HRuleStyle.ALL}
+ assert helper_table.get_latex_string(format=True, **hrule_options) == (
+ "\\begin{tabular}{|c|c|c|c|}\r\n"
+ "\\hline\r\n"
+ " & Field 1 & Field 2 & Field 3 \\\\\r\n"
+ "\\hline\r\n"
+ "1 & value 1 & value2 & value3 \\\\\r\n"
+ "\\hline\r\n"
+ "4 & value 4 & value5 & value6 \\\\\r\n"
+ "\\hline\r\n"
+ "7 & value 7 & value8 & value9 \\\\\r\n"
+ "\\hline\r\n"
+ "\\end{tabular}"
+ )
+
+ def test_latex_output_header(self, helper_table: PrettyTable) -> None:
+ assert helper_table.get_latex_string(format=True, hrules=HRuleStyle.HEADER) == (
+ "\\begin{tabular}{|c|c|c|c|}\r\n"
+ " & Field 1 & Field 2 & Field 3 \\\\\r\n"
+ "\\hline\r\n"
+ "1 & value 1 & value2 & value3 \\\\\r\n"
+ "4 & value 4 & value5 & value6 \\\\\r\n"
+ "7 & value 7 & value8 & value9 \\\\\r\n"
+ "\\end{tabular}"
+ )
+
+ def test_internal_border_preserved_latex(self, helper_table: PrettyTable) -> None:
+ helper_table.border = False
+ helper_table.format = True
+ helper_table.preserve_internal_border = True
+
+ assert helper_table.get_latex_string().strip() == (
+ "\\begin{tabular}{c|c|c|c}\r\n"
+ " & Field 1 & Field 2 & Field 3 \\\\\r\n"
+ "1 & value 1 & value2 & value3 \\\\\r\n"
+ "4 & value 4 & value5 & value6 \\\\\r\n"
+ "7 & value 7 & value8 & value9 \\\\\r\n"
+ "\\end{tabular}"
+ )
diff --git a/contrib/python/prettytable/py3/tests/test_mediawiki.py b/contrib/python/prettytable/py3/tests/test_mediawiki.py
new file mode 100644
index 00000000000..7684b617324
--- /dev/null
+++ b/contrib/python/prettytable/py3/tests/test_mediawiki.py
@@ -0,0 +1,171 @@
+from __future__ import annotations
+
+import pytest
+
+from prettytable import PrettyTable, from_mediawiki
+
+
+class TestMediaWikiOutput:
+ def test_mediawiki_output(self, helper_table: PrettyTable) -> None:
+ assert (
+ helper_table.get_mediawiki_string(header=True).strip()
+ == """
+{| class="wikitable"
+|-
+! !! Field 1 !! Field 2 !! Field 3
+|-
+| 1 || value 1 || value2 || value3
+|-
+| 4 || value 4 || value5 || value6
+|-
+| 7 || value 7 || value8 || value9
+|}
+""".strip()
+ )
+
+ def test_mediawiki_output_without_header(self, helper_table: PrettyTable) -> None:
+ assert (
+ helper_table.get_mediawiki_string(header=False).strip()
+ == """
+{| class="wikitable"
+|-
+| 1 || value 1 || value2 || value3
+|-
+| 4 || value 4 || value5 || value6
+|-
+| 7 || value 7 || value8 || value9
+|}
+""".strip()
+ )
+
+ def test_mediawiki_output_with_caption(self, helper_table: PrettyTable) -> None:
+ assert (
+ helper_table.get_mediawiki_string(
+ title="Optional caption", header=True
+ ).strip()
+ == """
+{| class="wikitable"
+|+ Optional caption
+|-
+! !! Field 1 !! Field 2 !! Field 3
+|-
+| 1 || value 1 || value2 || value3
+|-
+| 4 || value 4 || value5 || value6
+|-
+| 7 || value 7 || value8 || value9
+|}
+""".strip()
+ )
+
+ def test_mediawiki_output_with_attributes(self, helper_table: PrettyTable) -> None:
+ assert (
+ helper_table.get_mediawiki_string(
+ attributes={"class": "mytable", "id": "table1"}, header=True
+ ).strip()
+ == """
+{| class="mytable" id="table1"
+|-
+! !! Field 1 !! Field 2 !! Field 3
+|-
+| 1 || value 1 || value2 || value3
+|-
+| 4 || value 4 || value5 || value6
+|-
+| 7 || value 7 || value8 || value9
+|}
+ """.strip()
+ )
+
+ def test_mediawiki_output_with_fields_option(
+ self, helper_table: PrettyTable
+ ) -> None:
+ assert (
+ helper_table.get_mediawiki_string(
+ fields=["Field 1", "Field 3"], header=True
+ ).strip()
+ == """
+{| class="wikitable"
+|-
+! Field 1 !! Field 3
+|-
+| value 1 || value3
+|-
+| value 4 || value6
+|-
+| value 7 || value9
+|}
+ """.strip()
+ )
+
+
+class TestMediaWikiConstructor:
+ def test_mediawiki_and_back(self, city_data: PrettyTable) -> None:
+ mediawiki_string = city_data.get_mediawiki_string()
+ new_table = from_mediawiki(mediawiki_string)
+ assert new_table.get_string() == city_data.get_string()
+
+ def test_from_mediawiki_ignores_non_table_text(
+ self, city_data: PrettyTable
+ ) -> None:
+ wiki_table = city_data.get_mediawiki_string()
+ wiki_text = f"Before table text.\n{wiki_table}\nAfter table text."
+ table = from_mediawiki(wiki_text)
+ output = table.get_string()
+
+ for header in city_data.field_names:
+ assert header in output
+ for row in city_data._rows:
+ for cell in row:
+ assert str(cell) in output
+ assert "Before table text." not in output
+ assert "After table text." not in output
+
+ def test_from_mediawiki_ignores_caption(self) -> None:
+ wiki_text = """
+{| class="wikitable"
+|+ Optional caption
+|-
+! Field 1 !! Field 2
+|-
+| value 1 || value2
+|-
+| value 4 || value5
+|}
+ """
+ table = from_mediawiki(wiki_text)
+ output = table.get_string()
+ assert "Optional caption" not in output
+ assert "value 1" in output
+
+ def test_from_mediawiki_no_header(self) -> None:
+ wiki_text = """
+{| class="wikitable"
+|-
+| value 1 || value2
+|-
+| value 4 || value5
+|-
+| value 7 || value8
+|}
+ """
+ with pytest.raises(
+ ValueError, match="No valid header found in the MediaWiki table."
+ ):
+ from_mediawiki(wiki_text)
+
+ def test_from_mediawiki_row_length_mismatch(self) -> None:
+ wiki_text = """
+{| class="wikitable"
+|-
+! Field 1 !! Field 2
+|-
+| value 1 || value2 || value3
+|-
+| value 4 || value5 || value6
+|}
+ """
+ with pytest.raises(
+ ValueError, match="Row length mismatch between header and body."
+ ):
+ from_mediawiki(wiki_text)
diff --git a/contrib/python/prettytable/py3/tests/test_prettytable.py b/contrib/python/prettytable/py3/tests/test_prettytable.py
index e4777eb665e..7d85fad86f1 100644
--- a/contrib/python/prettytable/py3/tests/test_prettytable.py
+++ b/contrib/python/prettytable/py3/tests/test_prettytable.py
@@ -1,9 +1,8 @@
from __future__ import annotations
import datetime as dt
-import io
-import random
import sqlite3
+from collections.abc import Generator
from math import e, pi, sqrt
from typing import Any
@@ -14,13 +13,10 @@ import prettytable
from prettytable import (
HRuleStyle,
PrettyTable,
+ RowType,
TableStyle,
VRuleStyle,
- from_csv,
from_db_cursor,
- from_html,
- from_html_one,
- from_json,
)
@@ -31,68 +27,17 @@ def test_version() -> None:
assert prettytable.__version__[-1].isdigit()
-def helper_table(*, rows: int = 3) -> PrettyTable:
- table = PrettyTable(["", "Field 1", "Field 2", "Field 3"])
- v = 1
- for row in range(rows):
- # Some have spaces, some not, to help test padding columns of different widths
- table.add_row([v, f"value {v}", f"value{v+1}", f"value{v+2}"])
- v += 3
- return table
-
-
-def row_prettytable() -> PrettyTable:
- # Row by row...
- table = PrettyTable()
- table.field_names = ["City name", "Area", "Population", "Annual Rainfall"]
- table.add_row(["Adelaide", 1295, 1158259, 600.5])
- table.add_row(["Brisbane", 5905, 1857594, 1146.4])
- table.add_row(["Darwin", 112, 120900, 1714.7])
- table.add_row(["Hobart", 1357, 205556, 619.5])
- table.add_row(["Sydney", 2058, 4336374, 1214.8])
- table.add_row(["Melbourne", 1566, 3806092, 646.9])
- table.add_row(["Perth", 5386, 1554769, 869.4])
- return table
-
-
-def col_prettytable() -> PrettyTable:
- # Column by column...
- table = PrettyTable()
- table.add_column(
- "City name",
- ["Adelaide", "Brisbane", "Darwin", "Hobart", "Sydney", "Melbourne", "Perth"],
- )
- table.add_column("Area", [1295, 5905, 112, 1357, 2058, 1566, 5386])
- table.add_column(
- "Population", [1158259, 1857594, 120900, 205556, 4336374, 3806092, 1554769]
- )
- table.add_column(
- "Annual Rainfall", [600.5, 1146.4, 1714.7, 619.5, 1214.8, 646.9, 869.4]
- )
- return table
-
-
-def mix_prettytable() -> PrettyTable:
- # A mix of both!
- table = PrettyTable()
- table.field_names = ["City name", "Area"]
- table.add_row(["Adelaide", 1295])
- table.add_row(["Brisbane", 5905])
- table.add_row(["Darwin", 112])
- table.add_row(["Hobart", 1357])
- table.add_row(["Sydney", 2058])
- table.add_row(["Melbourne", 1566])
- table.add_row(["Perth", 5386])
- table.add_column(
- "Population", [1158259, 1857594, 120900, 205556, 4336374, 3806092, 1554769]
- )
- table.add_column(
- "Annual Rainfall", [600.5, 1146.4, 1714.7, 619.5, 1214.8, 646.9, 869.4]
- )
- return table
+# Australian capital city data example table
+CITY_DATA_HEADER = ["City name", "Area", "Population", "Annual Rainfall"]
+CITY_DATA = [
+ ["Adelaide", 1295, 1158259, 600.5],
+ ["Brisbane", 5905, 1857594, 1146.4],
+ ["Darwin", 112, 120900, 1714.7],
+ ["Hobart", 1357, 205556, 619.5],
+ ["Sydney", 2058, 4336374, 1214.8],
+ ["Melbourne", 1566, 3806092, 646.9],
+ ["Perth", 5386, 1554769, 869.4],
+]
class TestNoneOption:
@@ -133,16 +78,18 @@ class TestNoneOption:
)
def test_replace_none_all(self) -> None:
- table = PrettyTable(["Field 1", "Field 2", "Field 3"], none_format="N/A")
- table.add_row(["value 1", None, "None"])
+ table = PrettyTable(
+ ["Field 1", "Field 2", "Field 3", "Field 4"], none_format="N/A"
+ )
+ table.add_row(["value 1", None, "None", ""])
assert (
table.get_string().strip()
== """
-+---------+---------+---------+
-| Field 1 | Field 2 | Field 3 |
-+---------+---------+---------+
-| value 1 | N/A | N/A |
-+---------+---------+---------+
++---------+---------+---------+---------+
+| Field 1 | Field 2 | Field 3 | Field 4 |
++---------+---------+---------+---------+
+| value 1 | N/A | N/A | |
++---------+---------+---------+---------+
""".strip()
)
@@ -266,40 +213,70 @@ class TestBuildEquivalence:
) -> None:
assert left_hand.get_latex_string() == right_hand.get_latex_string()
+ @pytest.mark.parametrize(
+ ["left_hand", "right_hand"],
+ [
+ (
+ lf("row_prettytable"),
+ lf("col_prettytable"),
+ ),
+ (
+ lf("row_prettytable"),
+ lf("mix_prettytable"),
+ ),
+ ],
+ )
+ def test_equivalence_mediawiki(
+ self, left_hand: PrettyTable, right_hand: PrettyTable
+ ) -> None:
+ assert left_hand.get_mediawiki_string() == right_hand.get_mediawiki_string()
-class TestDeleteColumn:
- def test_delete_column(self) -> None:
- table = PrettyTable()
- table.add_column("City name", ["Adelaide", "Brisbane", "Darwin"])
- table.add_column("Area", [1295, 5905, 112])
- table.add_column("Population", [1158259, 1857594, 120900])
- table.del_column("Area")
-
- without_row = PrettyTable()
- without_row.add_column("City name", ["Adelaide", "Brisbane", "Darwin"])
- without_row.add_column("Population", [1158259, 1857594, 120900])
- assert table.get_string() == without_row.get_string()
+class TestDelete:
+ def test_delete_column(self, col_prettytable: PrettyTable) -> None:
+ col_prettytable.del_column("Area")
- def test_delete_illegal_column_raises_error(self) -> None:
- table = PrettyTable()
- table.add_column("City name", ["Adelaide", "Brisbane", "Darwin"])
+ assert (
+ col_prettytable.get_string()
+ == """+-----------+------------+-----------------+
+| City name | Population | Annual Rainfall |
++-----------+------------+-----------------+
+| Adelaide | 1158259 | 600.5 |
+| Brisbane | 1857594 | 1146.4 |
+| Darwin | 120900 | 1714.7 |
+| Hobart | 205556 | 619.5 |
+| Sydney | 4336374 | 1214.8 |
+| Melbourne | 3806092 | 646.9 |
+| Perth | 1554769 | 869.4 |
++-----------+------------+-----------------+"""
+ )
+ def test_delete_illegal_column_raises_error(
+ self, col_prettytable: PrettyTable
+ ) -> None:
with pytest.raises(ValueError):
- table.del_column("City not-a-name")
+ col_prettytable.del_column("City not-a-name")
+ def test_delete_row(self, city_data: PrettyTable) -> None:
+ city_data.del_row(2)
[email protected](scope="function")
-def field_name_less_table() -> PrettyTable:
- table = PrettyTable()
- table.add_row(["Adelaide", 1295, 1158259, 600.5])
- table.add_row(["Brisbane", 5905, 1857594, 1146.4])
- table.add_row(["Darwin", 112, 120900, 1714.7])
- table.add_row(["Hobart", 1357, 205556, 619.5])
- table.add_row(["Sydney", 2058, 4336374, 1214.8])
- table.add_row(["Melbourne", 1566, 3806092, 646.9])
- table.add_row(["Perth", 5386, 1554769, 869.4])
- return table
+ assert (
+ city_data.get_string()
+ == """+-----------+------+------------+-----------------+
+| City name | Area | Population | Annual Rainfall |
++-----------+------+------------+-----------------+
+| Adelaide | 1295 | 1158259 | 600.5 |
+| Brisbane | 5905 | 1857594 | 1146.4 |
+| Hobart | 1357 | 205556 | 619.5 |
+| Sydney | 2058 | 4336374 | 1214.8 |
+| Melbourne | 1566 | 3806092 | 646.9 |
+| Perth | 5386 | 1554769 | 869.4 |
++-----------+------+------------+-----------------+"""
+ )
+
+ def test_delete_row_unavailable(self, city_data: PrettyTable) -> None:
+ with pytest.raises(IndexError):
+ city_data.del_row(10)
class TestFieldNameLessTable:
@@ -320,13 +297,13 @@ class TestFieldNameLessTable:
assert "Field 1 & Field 2 & Field 3 & Field 4 \\\\" in output
assert "Adelaide & 1295 & 1158259 & 600.5 \\\\" in output
+ def test_can_string_mediawiki(self, field_name_less_table: PrettyTable) -> None:
+ output = field_name_less_table.get_mediawiki_string(header=True)
+ assert "! Field 1 !! Field 2 !! Field 3 !! Field 4" in output
+ assert "| Adelaide || 1295 || 1158259 || 600.5" in output
+
def test_add_field_names_later(self, field_name_less_table: PrettyTable) -> None:
- field_name_less_table.field_names = [
- "City name",
- "Area",
- "Population",
- "Annual Rainfall",
- ]
+ field_name_less_table.field_names = CITY_DATA_HEADER
assert (
"City name | Area | Population | Annual Rainfall"
in field_name_less_table.get_string()
@@ -337,28 +314,18 @@ class TestFieldNameLessTable:
def aligned_before_table() -> PrettyTable:
table = PrettyTable()
table.align = "r"
- table.field_names = ["City name", "Area", "Population", "Annual Rainfall"]
- table.add_row(["Adelaide", 1295, 1158259, 600.5])
- table.add_row(["Brisbane", 5905, 1857594, 1146.4])
- table.add_row(["Darwin", 112, 120900, 1714.7])
- table.add_row(["Hobart", 1357, 205556, 619.5])
- table.add_row(["Sydney", 2058, 4336374, 1214.8])
- table.add_row(["Melbourne", 1566, 3806092, 646.9])
- table.add_row(["Perth", 5386, 1554769, 869.4])
+ table.field_names = CITY_DATA_HEADER
+ for row in CITY_DATA:
+ table.add_row(row)
return table
@pytest.fixture(scope="function")
def aligned_after_table() -> PrettyTable:
table = PrettyTable()
- table.field_names = ["City name", "Area", "Population", "Annual Rainfall"]
- table.add_row(["Adelaide", 1295, 1158259, 600.5])
- table.add_row(["Brisbane", 5905, 1857594, 1146.4])
- table.add_row(["Darwin", 112, 120900, 1714.7])
- table.add_row(["Hobart", 1357, 205556, 619.5])
- table.add_row(["Sydney", 2058, 4336374, 1214.8])
- table.add_row(["Melbourne", 1566, 3806092, 646.9])
- table.add_row(["Perth", 5386, 1554769, 869.4])
+ table.field_names = CITY_DATA_HEADER
+ for row in CITY_DATA:
+ table.add_row(row)
table.align = "r"
return table
@@ -369,75 +336,46 @@ class TestAlignment:
def test_aligned_ascii(
self, aligned_before_table: PrettyTable, aligned_after_table: PrettyTable
) -> None:
- before = aligned_before_table.get_string()
- after = aligned_after_table.get_string()
- assert before == after
+ assert aligned_before_table.get_string() == aligned_after_table.get_string()
def test_aligned_html(
self, aligned_before_table: PrettyTable, aligned_after_table: PrettyTable
) -> None:
- before = aligned_before_table.get_html_string()
- after = aligned_after_table.get_html_string()
- assert before == after
+ assert (
+ aligned_before_table.get_html_string()
+ == aligned_after_table.get_html_string()
+ )
def test_aligned_latex(
self, aligned_before_table: PrettyTable, aligned_after_table: PrettyTable
) -> None:
- before = aligned_before_table.get_latex_string()
- after = aligned_after_table.get_latex_string()
- assert before == after
-
-
[email protected](scope="function")
-def city_data_prettytable() -> PrettyTable:
- """Just build the Australian capital city data example table."""
- table = PrettyTable(["City name", "Area", "Population", "Annual Rainfall"])
- table.add_row(["Adelaide", 1295, 1158259, 600.5])
- table.add_row(["Brisbane", 5905, 1857594, 1146.4])
- table.add_row(["Darwin", 112, 120900, 1714.7])
- table.add_row(["Hobart", 1357, 205556, 619.5])
- table.add_row(["Sydney", 2058, 4336374, 1214.8])
- table.add_row(["Melbourne", 1566, 3806092, 646.9])
- table.add_row(["Perth", 5386, 1554769, 869.4])
- return table
-
+ assert (
+ aligned_before_table.get_latex_string()
+ == aligned_after_table.get_latex_string()
+ )
[email protected](scope="function")
-def city_data_from_csv() -> PrettyTable:
- csv_string = """City name, Area, Population, Annual Rainfall
- Sydney, 2058, 4336374, 1214.8
- Melbourne, 1566, 3806092, 646.9
- Brisbane, 5905, 1857594, 1146.4
- Perth, 5386, 1554769, 869.4
- Adelaide, 1295, 1158259, 600.5
- Hobart, 1357, 205556, 619.5
- Darwin, 0112, 120900, 1714.7"""
- csv_fp = io.StringIO(csv_string)
- return from_csv(csv_fp)
+ def test_aligned_mediawiki(
+ self, aligned_before_table: PrettyTable, aligned_after_table: PrettyTable
+ ) -> None:
+ assert aligned_before_table.get_mediawiki_string(
+ header=True
+ ) == aligned_after_table.get_mediawiki_string(header=True)
class TestOptionOverride:
"""Make sure all options are properly overwritten by get_string."""
- def test_border(self, city_data_prettytable: PrettyTable) -> None:
- default = city_data_prettytable.get_string()
- override = city_data_prettytable.get_string(border=False)
- assert default != override
+ def test_border(self, city_data: PrettyTable) -> None:
+ assert city_data.get_string() != city_data.get_string(border=False)
- def test_header(self, city_data_prettytable) -> None:
- default = city_data_prettytable.get_string()
- override = city_data_prettytable.get_string(header=False)
- assert default != override
+ def test_header(self, city_data: PrettyTable) -> None:
+ assert city_data.get_string() != city_data.get_string(header=False)
- def test_hrules_all(self, city_data_prettytable) -> None:
- default = city_data_prettytable.get_string()
- override = city_data_prettytable.get_string(hrules=HRuleStyle.ALL)
- assert default != override
+ def test_hrules_all(self, city_data: PrettyTable) -> None:
+ assert city_data.get_string() != city_data.get_string(hrules=HRuleStyle.ALL)
- def test_hrules_none(self, city_data_prettytable) -> None:
- default = city_data_prettytable.get_string()
- override = city_data_prettytable.get_string(hrules=HRuleStyle.NONE)
- assert default != override
+ def test_hrules_none(self, city_data: PrettyTable) -> None:
+ assert city_data.get_string() != city_data.get_string(hrules=HRuleStyle.NONE)
class TestOptionAttribute:
@@ -445,55 +383,66 @@ class TestOptionAttribute:
Also make sure option settings are copied correctly when a table is cloned by
slicing."""
- def test_set_for_all_columns(self, city_data_prettytable) -> None:
- city_data_prettytable.field_names = sorted(city_data_prettytable.field_names)
- city_data_prettytable.align = "l"
- city_data_prettytable.max_width = 10
- city_data_prettytable.start = 2
- city_data_prettytable.end = 4
- city_data_prettytable.sortby = "Area"
- city_data_prettytable.reversesort = True
- city_data_prettytable.header = True
- city_data_prettytable.border = False
- city_data_prettytable.hrules = True
- city_data_prettytable.int_format = "4"
- city_data_prettytable.float_format = "2.2"
- city_data_prettytable.padding_width = 2
- city_data_prettytable.left_padding_width = 2
- city_data_prettytable.right_padding_width = 2
- city_data_prettytable.vertical_char = "!"
- city_data_prettytable.horizontal_char = "~"
- city_data_prettytable.junction_char = "*"
- city_data_prettytable.top_junction_char = "@"
- city_data_prettytable.bottom_junction_char = "#"
- city_data_prettytable.right_junction_char = "$"
- city_data_prettytable.left_junction_char = "%"
- city_data_prettytable.top_right_junction_char = "^"
- city_data_prettytable.top_left_junction_char = "&"
- city_data_prettytable.bottom_right_junction_char = "("
- city_data_prettytable.bottom_left_junction_char = ")"
- city_data_prettytable.format = True
- city_data_prettytable.attributes = {"class": "prettytable"}
- assert (
- city_data_prettytable.get_string() == city_data_prettytable[:].get_string()
- )
+ def test_set_for_all_columns(self, city_data: PrettyTable) -> None:
+ city_data.field_names = sorted(city_data.field_names)
+ city_data.align = "l"
+ city_data.max_width = 10
+ city_data.start = 2
+ city_data.end = 4
+ city_data.sortby = "Area"
+ city_data.reversesort = True
+ city_data.header = True
+ city_data.border = False
+ city_data.hrules = HRuleStyle.ALL
+ city_data.int_format = "4"
+ city_data.float_format = "2.2"
+ city_data.padding_width = 2
+ city_data.left_padding_width = 2
+ city_data.right_padding_width = 2
+ city_data.vertical_char = "!"
+ city_data.horizontal_char = "~"
+ city_data.junction_char = "*"
+ city_data.top_junction_char = "@"
+ city_data.bottom_junction_char = "#"
+ city_data.right_junction_char = "$"
+ city_data.left_junction_char = "%"
+ city_data.top_right_junction_char = "^"
+ city_data.top_left_junction_char = "&"
+ city_data.bottom_right_junction_char = "("
+ city_data.bottom_left_junction_char = ")"
+ city_data.format = True
+ city_data.attributes = {"class": "prettytable"}
+ assert city_data.get_string() == city_data[:].get_string()
- def test_set_for_one_column(self, city_data_prettytable) -> None:
- city_data_prettytable.align["Rainfall"] = "l"
- city_data_prettytable.max_width["Name"] = 10
- city_data_prettytable.int_format["Population"] = "4"
- city_data_prettytable.float_format["Area"] = "2.2"
- assert (
- city_data_prettytable.get_string() == city_data_prettytable[:].get_string()
- )
+ def test_set_for_one_column(self, city_data: PrettyTable) -> None:
+ city_data.align["Rainfall"] = "l"
+ city_data.max_width["Name"] = 10
+ city_data.int_format["Population"] = "4"
+ city_data.float_format["Area"] = "2.2"
+ assert city_data.get_string() == city_data[:].get_string()
def test_preserve_internal_border(self) -> None:
table = PrettyTable(preserve_internal_border=True)
assert table.preserve_internal_border is True
+ def test_internal_border_preserved(self, helper_table: PrettyTable) -> None:
+ helper_table.border = False
+ helper_table.preserve_internal_border = True
+
+ assert (
+ helper_table.get_string().strip()
+ == """
+ | Field 1 | Field 2 | Field 3
+---+---------+---------+---------
+ 1 | value 1 | value2 | value3
+ 4 | value 4 | value5 | value6
+ 7 | value 7 | value8 | value9
+""".strip() # noqa: W291
+ )
+
@pytest.fixture(scope="module")
-def db_cursor():
+def db_cursor() -> Generator[sqlite3.Cursor]:
conn = sqlite3.connect(":memory:")
cur = conn.cursor()
yield cur
@@ -502,18 +451,13 @@ def db_cursor():
@pytest.fixture(scope="module")
-def init_db(db_cursor):
+def init_db(db_cursor: sqlite3.Cursor) -> Generator[Any]:
db_cursor.execute(
"CREATE TABLE cities "
"(name TEXT, area INTEGER, population INTEGER, rainfall REAL)"
)
- db_cursor.execute('INSERT INTO cities VALUES ("Adelaide", 1295, 1158259, 600.5)')
- db_cursor.execute('INSERT INTO cities VALUES ("Brisbane", 5905, 1857594, 1146.4)')
- db_cursor.execute('INSERT INTO cities VALUES ("Darwin", 112, 120900, 1714.7)')
- db_cursor.execute('INSERT INTO cities VALUES ("Hobart", 1357, 205556, 619.5)')
- db_cursor.execute('INSERT INTO cities VALUES ("Sydney", 2058, 4336374, 1214.8)')
- db_cursor.execute('INSERT INTO cities VALUES ("Melbourne", 1566, 3806092, 646.9)')
- db_cursor.execute('INSERT INTO cities VALUES ("Perth", 5386, 1554769, 869.4)')
+ for row in CITY_DATA:
+ db_cursor.execute(f"INSERT INTO cities VALUES {tuple(row)}")
yield
db_cursor.execute("DROP TABLE cities")
@@ -521,10 +465,21 @@ def init_db(db_cursor):
class TestBasic:
"""Some very basic tests."""
- def test_table_rows(self, city_data_prettytable: PrettyTable) -> None:
- rows = city_data_prettytable.rows
+ def test_table_rows(self, city_data: PrettyTable) -> None:
+ rows = city_data.rows
assert len(rows) == 7
- assert rows[0] == ["Adelaide", 1295, 1158259, 600.5]
+ assert rows[0] == CITY_DATA[0]
+
+ def test_add_rows(self, city_data: PrettyTable) -> None:
+ """A table created with multiple add_row calls is the same as one created
+ with a single add_rows
+ """
+ table = PrettyTable(CITY_DATA_HEADER)
+ table.add_rows(CITY_DATA)
+ assert str(city_data) == str(table)
+
+ table.add_rows([])
+ assert str(city_data) == str(table)
def _test_no_blank_lines(self, table: PrettyTable) -> None:
string = table.get_string()
@@ -534,136 +489,101 @@ class TestBasic:
def _test_all_length_equal(self, table: PrettyTable) -> None:
string = table.get_string()
lines = string.split("\n")
- lengths = [len(line) for line in lines]
- lengths = set(lengths)
+ lengths = {len(line) for line in lines}
assert len(lengths) == 1
- def test_no_blank_lines(self, city_data_prettytable) -> None:
+ def test_no_blank_lines(self, city_data: PrettyTable) -> None:
"""No table should ever have blank lines in it."""
- self._test_no_blank_lines(city_data_prettytable)
+ self._test_no_blank_lines(city_data)
- def test_all_lengths_equal(self, city_data_prettytable) -> None:
+ def test_all_lengths_equal(self, city_data: PrettyTable) -> None:
"""All lines in a table should be of the same length."""
- self._test_all_length_equal(city_data_prettytable)
+ self._test_all_length_equal(city_data)
- def test_no_blank_lines_with_title(
- self, city_data_prettytable: PrettyTable
- ) -> None:
+ def test_no_blank_lines_with_title(self, city_data: PrettyTable) -> None:
"""No table should ever have blank lines in it."""
- city_data_prettytable.title = "My table"
- self._test_no_blank_lines(city_data_prettytable)
+ city_data.title = "My table"
+ self._test_no_blank_lines(city_data)
- def test_all_lengths_equal_with_title(
- self, city_data_prettytable: PrettyTable
- ) -> None:
+ def test_all_lengths_equal_with_title(self, city_data: PrettyTable) -> None:
"""All lines in a table should be of the same length."""
- city_data_prettytable.title = "My table"
- self._test_all_length_equal(city_data_prettytable)
+ city_data.title = "My table"
+ self._test_all_length_equal(city_data)
- def test_all_lengths_equal_with_long_title(
- self, city_data_prettytable: PrettyTable
- ) -> None:
+ def test_all_lengths_equal_with_long_title(self, city_data: PrettyTable) -> None:
"""All lines in a table should be of the same length, even with a long title."""
- city_data_prettytable.title = "My table (75 characters wide) " + "=" * 45
- self._test_all_length_equal(city_data_prettytable)
+ city_data.title = "My table (75 characters wide) " + "=" * 45
+ self._test_all_length_equal(city_data)
- def test_no_blank_lines_without_border(
- self, city_data_prettytable: PrettyTable
- ) -> None:
+ def test_no_blank_lines_without_border(self, city_data: PrettyTable) -> None:
"""No table should ever have blank lines in it."""
- city_data_prettytable.border = False
- self._test_no_blank_lines(city_data_prettytable)
+ city_data.border = False
+ self._test_no_blank_lines(city_data)
- def test_all_lengths_equal_without_border(
- self, city_data_prettytable: PrettyTable
- ) -> None:
+ def test_all_lengths_equal_without_border(self, city_data: PrettyTable) -> None:
"""All lines in a table should be of the same length."""
- city_data_prettytable.border = False
- self._test_all_length_equal(city_data_prettytable)
+ city_data.border = False
+ self._test_all_length_equal(city_data)
- def test_no_blank_lines_without_header(
- self, city_data_prettytable: PrettyTable
- ) -> None:
+ def test_no_blank_lines_without_header(self, city_data: PrettyTable) -> None:
"""No table should ever have blank lines in it."""
- city_data_prettytable.header = False
- self._test_no_blank_lines(city_data_prettytable)
+ city_data.header = False
+ self._test_no_blank_lines(city_data)
- def test_all_lengths_equal_without_header(
- self, city_data_prettytable: PrettyTable
- ) -> None:
+ def test_all_lengths_equal_without_header(self, city_data: PrettyTable) -> None:
"""All lines in a table should be of the same length."""
- city_data_prettytable.header = False
- self._test_all_length_equal(city_data_prettytable)
+ city_data.header = False
+ self._test_all_length_equal(city_data)
- def test_no_blank_lines_with_hrules_none(
- self, city_data_prettytable: PrettyTable
- ) -> None:
+ def test_no_blank_lines_with_hrules_none(self, city_data: PrettyTable) -> None:
"""No table should ever have blank lines in it."""
- city_data_prettytable.hrules = HRuleStyle.NONE
- self._test_no_blank_lines(city_data_prettytable)
+ city_data.hrules = HRuleStyle.NONE
+ self._test_no_blank_lines(city_data)
- def test_all_lengths_equal_with_hrules_none(
- self, city_data_prettytable: PrettyTable
- ) -> None:
+ def test_all_lengths_equal_with_hrules_none(self, city_data: PrettyTable) -> None:
"""All lines in a table should be of the same length."""
- city_data_prettytable.hrules = HRuleStyle.NONE
- self._test_all_length_equal(city_data_prettytable)
+ city_data.hrules = HRuleStyle.NONE
+ self._test_all_length_equal(city_data)
- def test_no_blank_lines_with_hrules_all(
- self, city_data_prettytable: PrettyTable
- ) -> None:
+ def test_no_blank_lines_with_hrules_all(self, city_data: PrettyTable) -> None:
"""No table should ever have blank lines in it."""
- city_data_prettytable.hrules = HRuleStyle.ALL
- self._test_no_blank_lines(city_data_prettytable)
+ city_data.hrules = HRuleStyle.ALL
+ self._test_no_blank_lines(city_data)
- def test_all_lengths_equal_with_hrules_all(
- self, city_data_prettytable: PrettyTable
- ) -> None:
+ def test_all_lengths_equal_with_hrules_all(self, city_data: PrettyTable) -> None:
"""All lines in a table should be of the same length."""
- city_data_prettytable.hrules = HRuleStyle.ALL
- self._test_all_length_equal(city_data_prettytable)
+ city_data.hrules = HRuleStyle.ALL
+ self._test_all_length_equal(city_data)
- def test_no_blank_lines_with_style_msword(
- self, city_data_prettytable: PrettyTable
- ) -> None:
+ def test_no_blank_lines_with_style_msword(self, city_data: PrettyTable) -> None:
"""No table should ever have blank lines in it."""
- city_data_prettytable.set_style(TableStyle.MSWORD_FRIENDLY)
- self._test_no_blank_lines(city_data_prettytable)
+ city_data.set_style(TableStyle.MSWORD_FRIENDLY)
+ self._test_no_blank_lines(city_data)
- def test_all_lengths_equal_with_style_msword(
- self, city_data_prettytable: PrettyTable
- ) -> None:
+ def test_all_lengths_equal_with_style_msword(self, city_data: PrettyTable) -> None:
"""All lines in a table should be of the same length."""
- city_data_prettytable.set_style(TableStyle.MSWORD_FRIENDLY)
- self._test_all_length_equal(city_data_prettytable)
+ city_data.set_style(TableStyle.MSWORD_FRIENDLY)
+ self._test_all_length_equal(city_data)
- def test_no_blank_lines_with_int_format(
- self, city_data_prettytable: PrettyTable
- ) -> None:
+ def test_no_blank_lines_with_int_format(self, city_data: PrettyTable) -> None:
"""No table should ever have blank lines in it."""
- city_data_prettytable.int_format = "04"
- self._test_no_blank_lines(city_data_prettytable)
+ city_data.int_format = "04"
+ self._test_no_blank_lines(city_data)
- def test_all_lengths_equal_with_int_format(
- self, city_data_prettytable: PrettyTable
- ) -> None:
+ def test_all_lengths_equal_with_int_format(self, city_data: PrettyTable) -> None:
"""All lines in a table should be of the same length."""
- city_data_prettytable.int_format = "04"
- self._test_all_length_equal(city_data_prettytable)
+ city_data.int_format = "04"
+ self._test_all_length_equal(city_data)
- def test_no_blank_lines_with_float_format(
- self, city_data_prettytable: PrettyTable
- ) -> None:
+ def test_no_blank_lines_with_float_format(self, city_data: PrettyTable) -> None:
"""No table should ever have blank lines in it."""
- city_data_prettytable.float_format = "6.2f"
- self._test_no_blank_lines(city_data_prettytable)
+ city_data.float_format = "6.2f"
+ self._test_no_blank_lines(city_data)
- def test_all_lengths_equal_with_float_format(
- self, city_data_prettytable: PrettyTable
- ) -> None:
+ def test_all_lengths_equal_with_float_format(self, city_data: PrettyTable) -> None:
"""All lines in a table should be of the same length."""
- city_data_prettytable.float_format = "6.2f"
- self._test_all_length_equal(city_data_prettytable)
+ city_data.float_format = "6.2f"
+ self._test_all_length_equal(city_data)
def test_no_blank_lines_from_csv(self, city_data_from_csv: PrettyTable) -> None:
"""No table should ever have blank lines in it."""
@@ -673,197 +593,143 @@ class TestBasic:
"""All lines in a table should be of the same length."""
self._test_all_length_equal(city_data_from_csv)
+ def test_no_blank_lines_from_mediawiki(
+ self, city_data_from_mediawiki: PrettyTable
+ ) -> None:
+ """No table should ever have blank lines in it."""
+ self._test_no_blank_lines(city_data_from_mediawiki)
+
+ def test_all_lengths_equal_from_mediawiki(
+ self, city_data_from_mediawiki: PrettyTable
+ ) -> None:
+ """All lines in a table should be of the same length."""
+ self._test_all_length_equal(city_data_from_mediawiki)
+
+ def test_rowcount(self, city_data: PrettyTable) -> None:
+ assert city_data.rowcount == 7
+
+ def test_colcount(self, city_data: PrettyTable) -> None:
+ assert city_data.colcount == 4
+
+ def test_getitem(self, city_data: PrettyTable) -> None:
+ assert (
+ city_data[1].get_string()
+ == """+-----------+------+------------+-----------------+
+| City name | Area | Population | Annual Rainfall |
++-----------+------+------------+-----------------+
+| Brisbane | 5905 | 1857594 | 1146.4 |
++-----------+------+------------+-----------------+"""
+ )
+
+ def test_invalid_getitem(self, city_data: PrettyTable) -> None:
+ with pytest.raises(IndexError):
+ assert city_data[10]
+
@pytest.mark.usefixtures("init_db")
- def test_no_blank_lines_from_db(self, db_cursor) -> None:
+ def test_no_blank_lines_from_db(self, db_cursor: sqlite3.Cursor) -> None:
"""No table should ever have blank lines in it."""
db_cursor.execute("SELECT * FROM cities")
- pt = from_db_cursor(db_cursor)
- self._test_no_blank_lines(pt)
+ table = from_db_cursor(db_cursor)
+ assert table is not None
+ self._test_no_blank_lines(table)
@pytest.mark.usefixtures("init_db")
- def test_all_lengths_equal_from_db(self, db_cursor) -> None:
+ def test_all_lengths_equal_from_db(self, db_cursor: sqlite3.Cursor) -> None:
"""No table should ever have blank lines in it."""
db_cursor.execute("SELECT * FROM cities")
- pt = from_db_cursor(db_cursor)
- self._test_all_length_equal(pt)
+ table = from_db_cursor(db_cursor)
+ assert table is not None
+ self._test_all_length_equal(table)
class TestEmptyTable:
"""Make sure the print_empty option works"""
- def test_print_empty_true(self, city_data_prettytable: PrettyTable) -> None:
+ def test_print_empty_true(self, city_data: PrettyTable) -> None:
table = PrettyTable()
- table.field_names = ["City name", "Area", "Population", "Annual Rainfall"]
+ table.field_names = CITY_DATA_HEADER
assert table.get_string(print_empty=True) != ""
- assert table.get_string(print_empty=True) != city_data_prettytable.get_string(
+ assert table.get_string(print_empty=True) != city_data.get_string(
print_empty=True
)
- def test_print_empty_false(self, city_data_prettytable: PrettyTable) -> None:
+ def test_print_empty_false(self, city_data: PrettyTable) -> None:
table = PrettyTable()
- table.field_names = ["City name", "Area", "Population", "Annual Rainfall"]
+ table.field_names = CITY_DATA_HEADER
assert table.get_string(print_empty=False) == ""
- assert table.get_string(print_empty=False) != city_data_prettytable.get_string(
+ assert table.get_string(print_empty=False) != city_data.get_string(
print_empty=False
)
def test_interaction_with_border(self) -> None:
table = PrettyTable()
- table.field_names = ["City name", "Area", "Population", "Annual Rainfall"]
+ table.field_names = CITY_DATA_HEADER
assert table.get_string(border=False, print_empty=True) == ""
class TestSlicing:
- def test_slice_all(self, city_data_prettytable: PrettyTable) -> None:
- table = city_data_prettytable[:]
- assert city_data_prettytable.get_string() == table.get_string()
+ def test_slice_all(self, city_data: PrettyTable) -> None:
+ table = city_data[:]
+ assert city_data.get_string() == table.get_string()
- def test_slice_first_two_rows(self, city_data_prettytable: PrettyTable) -> None:
- table = city_data_prettytable[0:2]
+ def test_slice_first_two_rows(self, city_data: PrettyTable) -> None:
+ table = city_data[0:2]
string = table.get_string()
assert len(string.split("\n")) == 6
- assert "Adelaide" in string
- assert "Brisbane" in string
- assert "Melbourne" not in string
- assert "Perth" not in string
+ for row_index in (0, 1):
+ city = CITY_DATA[row_index][0]
+ assert isinstance(city, str)
+ assert city in string
+ for row_index in (2, 3, 4, 5, 6):
+ city = CITY_DATA[row_index][0]
+ assert isinstance(city, str)
+ assert city not in string
- def test_slice_last_two_rows(self, city_data_prettytable: PrettyTable) -> None:
- table = city_data_prettytable[-2:]
+ def test_slice_last_two_rows(self, city_data: PrettyTable) -> None:
+ table = city_data[-2:]
string = table.get_string()
assert len(string.split("\n")) == 6
- assert "Adelaide" not in string
- assert "Brisbane" not in string
- assert "Melbourne" in string
- assert "Perth" in string
-
-
-class TestSorting:
- def test_sort_by_different_per_columns(
- self, city_data_prettytable: PrettyTable
- ) -> None:
- city_data_prettytable.sortby = city_data_prettytable.field_names[0]
- old = city_data_prettytable.get_string()
- for field in city_data_prettytable.field_names[1:]:
- city_data_prettytable.sortby = field
- new = city_data_prettytable.get_string()
- assert new != old
-
- def test_reverse_sort(self, city_data_prettytable: PrettyTable) -> None:
- for field in city_data_prettytable.field_names:
- city_data_prettytable.sortby = field
- city_data_prettytable.reversesort = False
- forward = city_data_prettytable.get_string()
- city_data_prettytable.reversesort = True
- backward = city_data_prettytable.get_string()
- forward_lines = forward.split("\n")[2:] # Discard header lines
- backward_lines = backward.split("\n")[2:]
- backward_lines.reverse()
- assert forward_lines == backward_lines
+ for row_index in (0, 1, 2, 3, 4):
+ city = CITY_DATA[row_index][0]
+ assert isinstance(city, str)
+ assert city not in string
+ for row_index in (5, 6):
+ city = CITY_DATA[row_index][0]
+ assert isinstance(city, str)
+ assert city in string
- def test_sort_key(self, city_data_prettytable: PrettyTable) -> None:
- # Test sorting by length of city name
- def key(vals):
- vals[0] = len(vals[0])
- return vals
- city_data_prettytable.sortby = "City name"
- city_data_prettytable.sort_key = key
- assert (
- city_data_prettytable.get_string().strip()
- == """
-+-----------+------+------------+-----------------+
+class TestRowFilter:
+ EXPECTED_RESULT = """+-----------+------+------------+-----------------+
| City name | Area | Population | Annual Rainfall |
+-----------+------+------------+-----------------+
-| Perth | 5386 | 1554769 | 869.4 |
-| Darwin | 112 | 120900 | 1714.7 |
-| Hobart | 1357 | 205556 | 619.5 |
-| Sydney | 2058 | 4336374 | 1214.8 |
| Adelaide | 1295 | 1158259 | 600.5 |
| Brisbane | 5905 | 1857594 | 1146.4 |
-| Melbourne | 1566 | 3806092 | 646.9 |
-+-----------+------+------------+-----------------+
-""".strip()
- )
-
- def test_sort_key_at_class_declaration(self) -> None:
- # Test sorting by length of city name
- def key(vals):
- vals[0] = len(vals[0])
- return vals
-
- table = PrettyTable(
- field_names=["City name", "Area", "Population", "Annual Rainfall"],
- sortby="City name",
- sort_key=key,
- )
- assert table.sort_key == key
- table.add_row(["Adelaide", 1295, 1158259, 600.5])
- table.add_row(["Brisbane", 5905, 1857594, 1146.4])
- table.add_row(["Darwin", 112, 120900, 1714.7])
- table.add_row(["Hobart", 1357, 205556, 619.5])
- table.add_row(["Sydney", 2058, 4336374, 1214.8])
- table.add_row(["Melbourne", 1566, 3806092, 646.9])
- table.add_row(["Perth", 5386, 1554769, 869.4])
- assert (
- """+-----------+------+------------+-----------------+
-| City name | Area | Population | Annual Rainfall |
-+-----------+------+------------+-----------------+
-| Perth | 5386 | 1554769 | 869.4 |
-| Darwin | 112 | 120900 | 1714.7 |
-| Hobart | 1357 | 205556 | 619.5 |
| Sydney | 2058 | 4336374 | 1214.8 |
-| Adelaide | 1295 | 1158259 | 600.5 |
-| Brisbane | 5905 | 1857594 | 1146.4 |
| Melbourne | 1566 | 3806092 | 646.9 |
+| Perth | 5386 | 1554769 | 869.4 |
+-----------+------+------------+-----------------+"""
- == table.get_string().strip()
- )
- def test_sort_slice(self) -> None:
- """Make sure sorting and slicing interact in the expected way"""
- table = PrettyTable(["Foo"])
- for i in range(20, 0, -1):
- table.add_row([i])
- new_style = table.get_string(sortby="Foo", end=10)
- assert "10" in new_style
- assert "20" not in new_style
- oldstyle = table.get_string(sortby="Foo", end=10, oldsortslice=True)
- assert "10" not in oldstyle
- assert "20" in oldstyle
+ def filter_function(self, vals: RowType) -> bool:
+ return vals[2] > 999999
- def test_sortby_at_class_declaration(self) -> None:
- """
- Fix #354 where initialization of a table with sortby fails
- """
+ def test_row_filter(self, city_data: PrettyTable) -> None:
+ city_data.row_filter = self.filter_function
+ assert city_data.row_filter == self.filter_function
+ assert self.EXPECTED_RESULT == city_data.get_string()
+
+ def test_row_filter_at_class_declaration(self) -> None:
table = PrettyTable(
- field_names=["City name", "Area", "Population", "Annual Rainfall"],
- sortby="Area",
- )
- assert table.sortby == "Area"
- table.add_row(["Adelaide", 1295, 1158259, 600.5])
- table.add_row(["Brisbane", 5905, 1857594, 1146.4])
- table.add_row(["Darwin", 112, 120900, 1714.7])
- table.add_row(["Hobart", 1357, 205556, 619.5])
- table.add_row(["Sydney", 2058, 4336374, 1214.8])
- table.add_row(["Melbourne", 1566, 3806092, 646.9])
- table.add_row(["Perth", 5386, 1554769, 869.4])
- assert (
- """+-----------+------+------------+-----------------+
-| City name | Area | Population | Annual Rainfall |
-+-----------+------+------------+-----------------+
-| Darwin | 112 | 120900 | 1714.7 |
-| Adelaide | 1295 | 1158259 | 600.5 |
-| Hobart | 1357 | 205556 | 619.5 |
-| Melbourne | 1566 | 3806092 | 646.9 |
-| Sydney | 2058 | 4336374 | 1214.8 |
-| Perth | 5386 | 1554769 | 869.4 |
-| Brisbane | 5905 | 1857594 | 1146.4 |
-+-----------+------+------------+-----------------+"""
- == table.get_string().strip()
+ field_names=CITY_DATA_HEADER,
+ row_filter=self.filter_function,
)
+ for row in CITY_DATA:
+ table.add_row(row)
+ assert table.row_filter == self.filter_function
+ assert self.EXPECTED_RESULT == table.get_string().strip()
@pytest.fixture(scope="function")
@@ -878,7 +744,6 @@ def float_pt() -> PrettyTable:
class TestFloatFormat:
def test_no_decimals(self, float_pt: PrettyTable) -> None:
float_pt.float_format = ".0f"
- float_pt.caching = False
assert "." not in float_pt.get_string()
def test_round_to_5dp(self, float_pt: PrettyTable) -> None:
@@ -900,6 +765,194 @@ class TestFloatFormat:
assert "001.41" in string
+class TestColumnFormattingfromDict:
+ def test_set_align_format(self, city_data: PrettyTable) -> None:
+ city_data.align = {"Annual Rainfall": "r"}
+ assert (
+ city_data.get_string()
+ == """
++-----------+------+------------+-----------------+
+| City name | Area | Population | Annual Rainfall |
++-----------+------+------------+-----------------+
+| Adelaide | 1295 | 1158259 | 600.5 |
+| Brisbane | 5905 | 1857594 | 1146.4 |
+| Darwin | 112 | 120900 | 1714.7 |
+| Hobart | 1357 | 205556 | 619.5 |
+| Sydney | 2058 | 4336374 | 1214.8 |
+| Melbourne | 1566 | 3806092 | 646.9 |
+| Perth | 5386 | 1554769 | 869.4 |
++-----------+------+------------+-----------------+
+""".strip()
+ )
+
+ def test_set_valign_format(self, city_data: PrettyTable) -> None:
+ table = PrettyTable(
+ ["Field 1", "Field 2", "Field 3", "Field 4", "Field 5", "Field 6"],
+ )
+ table.valign = {"Field 1": "m"}
+ table.max_width = {"Field 2": 20, "Field 4": 10, "Field 6": 10}
+ table.add_row(
+ [
+ "Lorem",
+ "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam ",
+ "ipsum",
+ "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam ",
+ "dolor",
+ "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam ",
+ ]
+ )
+
+ assert (
+ table.get_string()
+ == """
++---------+----------------------+---------+------------+---------+------------+
+| Field 1 | Field 2 | Field 3 | Field 4 | Field 5 | Field 6 |
++---------+----------------------+---------+------------+---------+------------+
+| | Lorem ipsum dolor | ipsum | Lorem | dolor | Lorem |
+| | sit amet, consetetur | | ipsum | | ipsum |
+| | sadipscing elitr, | | dolor sit | | dolor sit |
+| Lorem | sed diam | | amet, | | amet, |
+| | | | consetetur | | consetetur |
+| | | | sadipscing | | sadipscing |
+| | | | elitr, sed | | elitr, sed |
+| | | | diam | | diam |
++---------+----------------------+---------+------------+---------+------------+
+""".strip()
+ )
+
+ def test_max_width(
+ self,
+ ) -> None:
+ table = PrettyTable(
+ ["Field 1", "Field 2", "Field 3", "Field 4", "Field 5", "Field 6"],
+ )
+ table.max_width = {"Field 2": 20, "Field 4": 10, "Field 6": 10}
+ table.add_row(
+ [
+ "Lorem",
+ "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam ",
+ "ipsum",
+ "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam ",
+ "dolor",
+ "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam ",
+ ]
+ )
+
+ assert (
+ table.get_string()
+ == """
++---------+----------------------+---------+------------+---------+------------+
+| Field 1 | Field 2 | Field 3 | Field 4 | Field 5 | Field 6 |
++---------+----------------------+---------+------------+---------+------------+
+| Lorem | Lorem ipsum dolor | ipsum | Lorem | dolor | Lorem |
+| | sit amet, consetetur | | ipsum | | ipsum |
+| | sadipscing elitr, | | dolor sit | | dolor sit |
+| | sed diam | | amet, | | amet, |
+| | | | consetetur | | consetetur |
+| | | | sadipscing | | sadipscing |
+| | | | elitr, sed | | elitr, sed |
+| | | | diam | | diam |
++---------+----------------------+---------+------------+---------+------------+
+""".strip()
+ )
+
+ def test_min_width(self, city_data: PrettyTable) -> None:
+ city_data.min_width = {
+ "City name": 20,
+ "Area": 10,
+ "Population": 20,
+ "Annual Rainfall": 20,
+ }
+ assert (
+ city_data.get_string()
+ == """
++----------------------+------------+----------------------+----------------------+
+| City name | Area | Population | Annual Rainfall |
++----------------------+------------+----------------------+----------------------+
+| Adelaide | 1295 | 1158259 | 600.5 |
+| Brisbane | 5905 | 1857594 | 1146.4 |
+| Darwin | 112 | 120900 | 1714.7 |
+| Hobart | 1357 | 205556 | 619.5 |
+| Sydney | 2058 | 4336374 | 1214.8 |
+| Melbourne | 1566 | 3806092 | 646.9 |
+| Perth | 5386 | 1554769 | 869.4 |
++----------------------+------------+----------------------+----------------------+
+""".strip()
+ )
+
+ def test_set_int_format(self, city_data: PrettyTable) -> None:
+ city_data.int_format = {"Population": "20"}
+ assert (
+ city_data.get_string()
+ == """
++-----------+------+----------------------+-----------------+
+| City name | Area | Population | Annual Rainfall |
++-----------+------+----------------------+-----------------+
+| Adelaide | 1295 | 1158259 | 600.5 |
+| Brisbane | 5905 | 1857594 | 1146.4 |
+| Darwin | 112 | 120900 | 1714.7 |
+| Hobart | 1357 | 205556 | 619.5 |
+| Sydney | 2058 | 4336374 | 1214.8 |
+| Melbourne | 1566 | 3806092 | 646.9 |
+| Perth | 5386 | 1554769 | 869.4 |
++-----------+------+----------------------+-----------------+
+""".strip()
+ )
+
+ def test_set_float_format(self, city_data: PrettyTable) -> None:
+ city_data.float_format = {"Annual Rainfall": "4.2"}
+ assert (
+ city_data.get_string()
+ == """
++-----------+------+------------+-----------------+
+| City name | Area | Population | Annual Rainfall |
++-----------+------+------------+-----------------+
+| Adelaide | 1295 | 1158259 | 600.50 |
+| Brisbane | 5905 | 1857594 | 1146.40 |
+| Darwin | 112 | 120900 | 1714.70 |
+| Hobart | 1357 | 205556 | 619.50 |
+| Sydney | 2058 | 4336374 | 1214.80 |
+| Melbourne | 1566 | 3806092 | 646.90 |
+| Perth | 5386 | 1554769 | 869.40 |
++-----------+------+------------+-----------------+
+""".strip()
+ )
+
+ def test_set_custom_format(self, city_data: PrettyTable) -> None:
+ city_data.custom_format = {"Annual Rainfall": lambda f, v: f"{v:.2f}"}
+ assert (
+ city_data.get_string()
+ == """
++-----------+------+------------+-----------------+
+| City name | Area | Population | Annual Rainfall |
++-----------+------+------------+-----------------+
+| Adelaide | 1295 | 1158259 | 600.50 |
+| Brisbane | 5905 | 1857594 | 1146.40 |
+| Darwin | 112 | 120900 | 1714.70 |
+| Hobart | 1357 | 205556 | 619.50 |
+| Sydney | 2058 | 4336374 | 1214.80 |
+| Melbourne | 1566 | 3806092 | 646.90 |
+| Perth | 5386 | 1554769 | 869.40 |
++-----------+------+------------+-----------------+
+""".strip()
+ )
+
+ def test_set_none_format(self, city_data: PrettyTable) -> None:
+ city_data.clear_rows()
+ city_data.add_row([None, None, None, None])
+ city_data.none_format = {"Annual Rainfall": "N/A"}
+ assert (
+ city_data.get_string()
+ == """
++-----------+------+------------+-----------------+
+| City name | Area | Population | Annual Rainfall |
++-----------+------+------------+-----------------+
+| None | None | None | N/A |
++-----------+------+------------+-----------------+
+""".strip()
+ )
+
+
class TestBreakLine:
@pytest.mark.parametrize(
["rows", "hrule", "expected_result"],
@@ -968,996 +1021,29 @@ class TestBreakLine:
result = table.get_string(hrules=hrule)
assert result.strip() == expected_result.strip()
- def test_break_line_html(self) -> None:
- table = PrettyTable(["Field 1", "Field 2"])
- table.add_row(["value 1", "value2\nsecond line"])
- table.add_row(["value 3", "value4"])
- result = table.get_html_string(hrules=HRuleStyle.ALL)
- assert (
- result.strip()
- == """
-<table>
- <thead>
- <tr>
- <th>Field 1</th>
- <th>Field 2</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>value 1</td>
- <td>value2<br>second line</td>
- </tr>
- <tr>
- <td>value 3</td>
- <td>value4</td>
- </tr>
- </tbody>
-</table>
-""".strip()
- )
-
-
-class TestAnsiWidth:
- colored = "\033[31mC\033[32mO\033[31mL\033[32mO\033[31mR\033[32mE\033[31mD\033[0m"
-
- def test_color(self) -> None:
- table = PrettyTable(["Field 1", "Field 2"])
- table.add_row([self.colored, self.colored])
- table.add_row(["nothing", "neither"])
- result = table.get_string()
- assert (
- result.strip()
- == f"""
-+---------+---------+
-| Field 1 | Field 2 |
-+---------+---------+
-| {self.colored} | {self.colored} |
-| nothing | neither |
-+---------+---------+
-""".strip()
- )
-
- def test_reset(self) -> None:
- table = PrettyTable(["Field 1", "Field 2"])
- table.add_row(["abc def\033(B", "\033[31mabc def\033[m"])
- table.add_row(["nothing", "neither"])
- result = table.get_string()
- assert (
- result.strip()
- == """
-+---------+---------+
-| Field 1 | Field 2 |
-+---------+---------+
-| abc def\033(B | \033[31mabc def\033[m |
-| nothing | neither |
-+---------+---------+
-""".strip()
- )
-
class TestFromDB:
@pytest.mark.usefixtures("init_db")
- def test_non_select_cursor(self, db_cursor) -> None:
- db_cursor.execute(
- 'INSERT INTO cities VALUES ("Adelaide", 1295, 1158259, 600.5)'
- )
+ def test_non_select_cursor(self, db_cursor: sqlite3.Cursor) -> None:
+ db_cursor.execute(f"INSERT INTO cities VALUES {tuple(CITY_DATA[0])}")
assert from_db_cursor(db_cursor) is None
-class TestJSONOutput:
- def test_json_output(self) -> None:
- t = helper_table()
- result = t.get_json_string()
- assert (
- result.strip()
- == """
-[
- [
- "",
- "Field 1",
- "Field 2",
- "Field 3"
- ],
- {
- "": 1,
- "Field 1": "value 1",
- "Field 2": "value2",
- "Field 3": "value3"
- },
- {
- "": 4,
- "Field 1": "value 4",
- "Field 2": "value5",
- "Field 3": "value6"
- },
- {
- "": 7,
- "Field 1": "value 7",
- "Field 2": "value8",
- "Field 3": "value9"
- }
-]""".strip()
- )
- options = {"fields": ["Field 1", "Field 3"]}
- result = t.get_json_string(**options)
- assert (
- result.strip()
- == """
-[
- [
- "Field 1",
- "Field 3"
- ],
- {
- "Field 1": "value 1",
- "Field 3": "value3"
- },
- {
- "Field 1": "value 4",
- "Field 3": "value6"
- },
- {
- "Field 1": "value 7",
- "Field 3": "value9"
- }
-]""".strip()
- )
-
- def test_json_output_options(self) -> None:
- t = helper_table()
- result = t.get_json_string(header=False, indent=None, separators=(",", ":"))
- assert (
- result
- == """[{"":1,"Field 1":"value 1","Field 2":"value2","Field 3":"value3"},"""
- """{"":4,"Field 1":"value 4","Field 2":"value5","Field 3":"value6"},"""
- """{"":7,"Field 1":"value 7","Field 2":"value8","Field 3":"value9"}]"""
- )
-
-
-class TestHtmlOutput:
- def test_html_output(self) -> None:
- t = helper_table()
- result = t.get_html_string()
- assert (
- result.strip()
- == """
-<table>
- <thead>
- <tr>
- <th></th>
- <th>Field 1</th>
- <th>Field 2</th>
- <th>Field 3</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>1</td>
- <td>value 1</td>
- <td>value2</td>
- <td>value3</td>
- </tr>
- <tr>
- <td>4</td>
- <td>value 4</td>
- <td>value5</td>
- <td>value6</td>
- </tr>
- <tr>
- <td>7</td>
- <td>value 7</td>
- <td>value8</td>
- <td>value9</td>
- </tr>
- </tbody>
-</table>
-""".strip()
- )
-
- def test_html_output_formatted(self) -> None:
- t = helper_table()
- result = t.get_html_string(format=True)
- assert (
- result.strip()
- == """
-<table frame="box" rules="cols">
- <thead>
- <tr>
- <th style="padding-left: 1em; padding-right: 1em; text-align: center"></th>
- <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">1</td>
- <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">4</td>
- <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">7</td>
- <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
- )
-
- def test_html_output_with_title(self) -> None:
- t = helper_table()
- t.title = "Title & Title"
- result = t.get_html_string(attributes={"bgcolor": "red", "a<b": "1<2"})
- assert (
- result.strip()
- == """
-<table bgcolor="red" a&lt;b="1&lt;2">
- <caption>Title &amp; Title</caption>
- <thead>
- <tr>
- <th></th>
- <th>Field 1</th>
- <th>Field 2</th>
- <th>Field 3</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>1</td>
- <td>value 1</td>
- <td>value2</td>
- <td>value3</td>
- </tr>
- <tr>
- <td>4</td>
- <td>value 4</td>
- <td>value5</td>
- <td>value6</td>
- </tr>
- <tr>
- <td>7</td>
- <td>value 7</td>
- <td>value8</td>
- <td>value9</td>
- </tr>
- </tbody>
-</table>
-""".strip()
- )
-
- def test_html_output_formatted_with_title(self) -> None:
- t = helper_table()
- t.title = "Title & Title"
- result = t.get_html_string(
- attributes={"bgcolor": "red", "a<b": "1<2"}, format=True
- )
- assert (
- result.strip()
- == """
-<table frame="box" rules="cols" bgcolor="red" a&lt;b="1&lt;2">
- <caption>Title &amp; Title</caption>
- <thead>
- <tr>
- <th style="padding-left: 1em; padding-right: 1em; text-align: center"></th>
- <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">1</td>
- <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">4</td>
- <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">7</td>
- <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
- )
-
- def test_html_output_without_escaped_header(self) -> None:
- t = helper_table(rows=0)
- t.field_names = ["", "Field 1", "<em>Field 2</em>", "<a href='#'>Field 3</a>"]
- result = t.get_html_string(escape_header=False)
- assert (
- result.strip()
- == """
-<table>
- <thead>
- <tr>
- <th></th>
- <th>Field 1</th>
- <th><em>Field 2</em></th>
- <th><a href='#'>Field 3</a></th>
- </tr>
- </thead>
- <tbody>
- </tbody>
-</table>
-""".strip()
- )
-
- def test_html_output_without_escaped_data(self) -> None:
- t = helper_table(rows=0)
- t.add_row(
- [
- 1,
- "<b>value 1</b>",
- "<span style='text-decoration: underline;'>value2</span>",
- "<a href='#'>value3</a>",
- ]
- )
- result = t.get_html_string(escape_data=False)
- assert (
- result.strip()
- == """
-<table>
- <thead>
- <tr>
- <th></th>
- <th>Field 1</th>
- <th>Field 2</th>
- <th>Field 3</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>1</td>
- <td><b>value 1</b></td>
- <td><span style='text-decoration: underline;'>value2</span></td>
- <td><a href='#'>value3</a></td>
- </tr>
- </tbody>
-</table>
-""".strip()
- )
-
- def test_html_output_with_escaped_header(self) -> None:
- t = helper_table(rows=0)
- t.field_names = ["", "Field 1", "<em>Field 2</em>", "<a href='#'>Field 3</a>"]
- result = t.get_html_string(escape_header=True)
- assert (
- result.strip()
- == """
-<table>
- <thead>
- <tr>
- <th></th>
- <th>Field 1</th>
- <th>&lt;em&gt;Field 2&lt;/em&gt;</th>
- <th>&lt;a href=&#x27;#&#x27;&gt;Field 3&lt;/a&gt;</th>
- </tr>
- </thead>
- <tbody>
- </tbody>
-</table>
-""".strip()
- )
-
- def test_html_output_with_escaped_data(self) -> None:
- t = helper_table(rows=0)
- t.add_row(
- [
- 1,
- "<b>value 1</b>",
- "<span style='text-decoration: underline;'>value2</span>",
- "<a href='#'>value3</a>",
- ]
- )
- result = t.get_html_string(escape_data=True)
- assert (
- result.strip()
- == """
-<table>
- <thead>
- <tr>
- <th></th>
- <th>Field 1</th>
- <th>Field 2</th>
- <th>Field 3</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>1</td>
- <td>&lt;b&gt;value 1&lt;/b&gt;</td>
- <td>&lt;span style=&#x27;text-decoration: underline;&#x27;&gt;value2&lt;/span&gt;</td>
- <td>&lt;a href=&#x27;#&#x27;&gt;value3&lt;/a&gt;</td>
- </tr>
- </tbody>
-</table>
-""".strip() # noqa: E501
- )
-
- def test_html_output_formatted_without_escaped_header(self) -> None:
- t = helper_table(rows=0)
- t.field_names = ["", "Field 1", "<em>Field 2</em>", "<a href='#'>Field 3</a>"]
- result = t.get_html_string(escape_header=False, format=True)
- assert (
- result.strip()
- == """
-<table frame="box" rules="cols">
- <thead>
- <tr>
- <th style="padding-left: 1em; padding-right: 1em; text-align: center"></th>
- <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"><em>Field 2</em></th>
- <th style="padding-left: 1em; padding-right: 1em; text-align: center"><a href='#'>Field 3</a></th>
- </tr>
- </thead>
- <tbody>
- </tbody>
-</table>
-""".strip() # noqa: E501
- )
-
- def test_html_output_formatted_without_escaped_data(self) -> None:
- t = helper_table(rows=0)
- t.add_row(
- [
- 1,
- "<b>value 1</b>",
- "<span style='text-decoration: underline;'>value2</span>",
- "<a href='#'>value3</a>",
- ]
- )
- result = t.get_html_string(escape_data=False, format=True)
- assert (
- result.strip()
- == """
-<table frame="box" rules="cols">
- <thead>
- <tr>
- <th style="padding-left: 1em; padding-right: 1em; text-align: center"></th>
- <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">1</td>
- <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top"><b>value 1</b></td>
- <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top"><span style='text-decoration: underline;'>value2</span></td>
- <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top"><a href='#'>value3</a></td>
- </tr>
- </tbody>
-</table>
-""".strip() # noqa: E501
- )
-
- def test_html_output_formatted_with_escaped_header(self) -> None:
- t = helper_table(rows=0)
- t.field_names = ["", "Field 1", "<em>Field 2</em>", "<a href='#'>Field 3</a>"]
- result = t.get_html_string(escape_header=True, format=True)
- assert (
- result.strip()
- == """
-<table frame="box" rules="cols">
- <thead>
- <tr>
- <th style="padding-left: 1em; padding-right: 1em; text-align: center"></th>
- <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">&lt;em&gt;Field 2&lt;/em&gt;</th>
- <th style="padding-left: 1em; padding-right: 1em; text-align: center">&lt;a href=&#x27;#&#x27;&gt;Field 3&lt;/a&gt;</th>
- </tr>
- </thead>
- <tbody>
- </tbody>
-</table>
-""".strip() # noqa: E501
- )
-
- def test_html_output_formatted_with_escaped_data(self) -> None:
- t = helper_table(rows=0)
- t.add_row(
- [
- 1,
- "<b>value 1</b>",
- "<span style='text-decoration: underline;'>value2</span>",
- "<a href='#'>value3</a>",
- ]
- )
- result = t.get_html_string(escape_data=True, format=True)
- assert (
- result.strip()
- == """
-<table frame="box" rules="cols">
- <thead>
- <tr>
- <th style="padding-left: 1em; padding-right: 1em; text-align: center"></th>
- <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">1</td>
- <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">&lt;b&gt;value 1&lt;/b&gt;</td>
- <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">&lt;span style=&#x27;text-decoration: underline;&#x27;&gt;value2&lt;/span&gt;</td>
- <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">&lt;a href=&#x27;#&#x27;&gt;value3&lt;/a&gt;</td>
- </tr>
- </tbody>
-</table>
-""".strip() # noqa: E501
- )
-
-
-class TestPositionalJunctions:
- """Verify different cases for positional-junction characters"""
-
- def test_default(self, city_data_prettytable: PrettyTable) -> None:
- city_data_prettytable.set_style(TableStyle.DOUBLE_BORDER)
-
- assert (
- city_data_prettytable.get_string().strip()
- == """
-╔═══════════╦══════╦════════════╦═════════════════╗
-║ City name ║ Area ║ Population ║ Annual Rainfall ║
-╠═══════════╬══════╬════════════╬═════════════════╣
-║ Adelaide ║ 1295 ║ 1158259 ║ 600.5 ║
-║ Brisbane ║ 5905 ║ 1857594 ║ 1146.4 ║
-║ Darwin ║ 112 ║ 120900 ║ 1714.7 ║
-║ Hobart ║ 1357 ║ 205556 ║ 619.5 ║
-║ Sydney ║ 2058 ║ 4336374 ║ 1214.8 ║
-║ Melbourne ║ 1566 ║ 3806092 ║ 646.9 ║
-║ Perth ║ 5386 ║ 1554769 ║ 869.4 ║
-╚═══════════╩══════╩════════════╩═════════════════╝""".strip()
- )
-
- def test_no_header(self, city_data_prettytable: PrettyTable) -> None:
- city_data_prettytable.set_style(TableStyle.DOUBLE_BORDER)
- city_data_prettytable.header = False
-
- assert (
- city_data_prettytable.get_string().strip()
- == """
-╔═══════════╦══════╦═════════╦════════╗
-║ Adelaide ║ 1295 ║ 1158259 ║ 600.5 ║
-║ Brisbane ║ 5905 ║ 1857594 ║ 1146.4 ║
-║ Darwin ║ 112 ║ 120900 ║ 1714.7 ║
-║ Hobart ║ 1357 ║ 205556 ║ 619.5 ║
-║ Sydney ║ 2058 ║ 4336374 ║ 1214.8 ║
-║ Melbourne ║ 1566 ║ 3806092 ║ 646.9 ║
-║ Perth ║ 5386 ║ 1554769 ║ 869.4 ║
-╚═══════════╩══════╩═════════╩════════╝""".strip()
- )
-
- def test_with_title(self, city_data_prettytable: PrettyTable) -> None:
- city_data_prettytable.set_style(TableStyle.DOUBLE_BORDER)
- city_data_prettytable.title = "Title"
-
- assert (
- city_data_prettytable.get_string().strip()
- == """
-╔═════════════════════════════════════════════════╗
-║ Title ║
-╠═══════════╦══════╦════════════╦═════════════════╣
-║ City name ║ Area ║ Population ║ Annual Rainfall ║
-╠═══════════╬══════╬════════════╬═════════════════╣
-║ Adelaide ║ 1295 ║ 1158259 ║ 600.5 ║
-║ Brisbane ║ 5905 ║ 1857594 ║ 1146.4 ║
-║ Darwin ║ 112 ║ 120900 ║ 1714.7 ║
-║ Hobart ║ 1357 ║ 205556 ║ 619.5 ║
-║ Sydney ║ 2058 ║ 4336374 ║ 1214.8 ║
-║ Melbourne ║ 1566 ║ 3806092 ║ 646.9 ║
-║ Perth ║ 5386 ║ 1554769 ║ 869.4 ║
-╚═══════════╩══════╩════════════╩═════════════════╝""".strip()
- )
-
- def test_with_title_no_header(self, city_data_prettytable: PrettyTable) -> None:
- city_data_prettytable.set_style(TableStyle.DOUBLE_BORDER)
- city_data_prettytable.title = "Title"
- city_data_prettytable.header = False
- assert (
- city_data_prettytable.get_string().strip()
- == """
-╔═════════════════════════════════════╗
-║ Title ║
-╠═══════════╦══════╦═════════╦════════╣
-║ Adelaide ║ 1295 ║ 1158259 ║ 600.5 ║
-║ Brisbane ║ 5905 ║ 1857594 ║ 1146.4 ║
-║ Darwin ║ 112 ║ 120900 ║ 1714.7 ║
-║ Hobart ║ 1357 ║ 205556 ║ 619.5 ║
-║ Sydney ║ 2058 ║ 4336374 ║ 1214.8 ║
-║ Melbourne ║ 1566 ║ 3806092 ║ 646.9 ║
-║ Perth ║ 5386 ║ 1554769 ║ 869.4 ║
-╚═══════════╩══════╩═════════╩════════╝""".strip()
- )
-
- def test_hrule_all(self, city_data_prettytable: PrettyTable) -> None:
- city_data_prettytable.set_style(TableStyle.DOUBLE_BORDER)
- city_data_prettytable.title = "Title"
- city_data_prettytable.hrules = HRuleStyle.ALL
- assert (
- city_data_prettytable.get_string().strip()
- == """
-╔═════════════════════════════════════════════════╗
-║ Title ║
-╠═══════════╦══════╦════════════╦═════════════════╣
-║ City name ║ Area ║ Population ║ Annual Rainfall ║
-╠═══════════╬══════╬════════════╬═════════════════╣
-║ Adelaide ║ 1295 ║ 1158259 ║ 600.5 ║
-╠═══════════╬══════╬════════════╬═════════════════╣
-║ Brisbane ║ 5905 ║ 1857594 ║ 1146.4 ║
-╠═══════════╬══════╬════════════╬═════════════════╣
-║ Darwin ║ 112 ║ 120900 ║ 1714.7 ║
-╠═══════════╬══════╬════════════╬═════════════════╣
-║ Hobart ║ 1357 ║ 205556 ║ 619.5 ║
-╠═══════════╬══════╬════════════╬═════════════════╣
-║ Sydney ║ 2058 ║ 4336374 ║ 1214.8 ║
-╠═══════════╬══════╬════════════╬═════════════════╣
-║ Melbourne ║ 1566 ║ 3806092 ║ 646.9 ║
-╠═══════════╬══════╬════════════╬═════════════════╣
-║ Perth ║ 5386 ║ 1554769 ║ 869.4 ║
-╚═══════════╩══════╩════════════╩═════════════════╝""".strip()
- )
-
- def test_vrules_none(self, city_data_prettytable: PrettyTable) -> None:
- city_data_prettytable.set_style(TableStyle.DOUBLE_BORDER)
- city_data_prettytable.vrules = VRuleStyle.NONE
- assert (
- city_data_prettytable.get_string().strip()
- == "═══════════════════════════════════════════════════\n"
- " City name Area Population Annual Rainfall \n"
- "═══════════════════════════════════════════════════\n"
- " Adelaide 1295 1158259 600.5 \n"
- " Brisbane 5905 1857594 1146.4 \n"
- " Darwin 112 120900 1714.7 \n"
- " Hobart 1357 205556 619.5 \n"
- " Sydney 2058 4336374 1214.8 \n"
- " Melbourne 1566 3806092 646.9 \n"
- " Perth 5386 1554769 869.4 \n"
- "═══════════════════════════════════════════════════".strip()
- )
-
- def test_vrules_frame_with_title(self, city_data_prettytable: PrettyTable) -> None:
- city_data_prettytable.set_style(TableStyle.DOUBLE_BORDER)
- city_data_prettytable.vrules = VRuleStyle.FRAME
- city_data_prettytable.title = "Title"
- assert (
- city_data_prettytable.get_string().strip()
- == """
-╔═════════════════════════════════════════════════╗
-║ Title ║
-╠═════════════════════════════════════════════════╣
-║ City name Area Population Annual Rainfall ║
-╠═════════════════════════════════════════════════╣
-║ Adelaide 1295 1158259 600.5 ║
-║ Brisbane 5905 1857594 1146.4 ║
-║ Darwin 112 120900 1714.7 ║
-║ Hobart 1357 205556 619.5 ║
-║ Sydney 2058 4336374 1214.8 ║
-║ Melbourne 1566 3806092 646.9 ║
-║ Perth 5386 1554769 869.4 ║
-╚═════════════════════════════════════════════════╝""".strip()
- )
-
-
-class TestStyle:
- @pytest.mark.parametrize(
- "style, expected",
- [
- pytest.param(
- TableStyle.DEFAULT,
- """
-+---+---------+---------+---------+
-| | Field 1 | Field 2 | Field 3 |
-+---+---------+---------+---------+
-| 1 | value 1 | value2 | value3 |
-| 4 | value 4 | value5 | value6 |
-| 7 | value 7 | value8 | value9 |
-+---+---------+---------+---------+
-""",
- id="DEFAULT",
- ),
- pytest.param(
- TableStyle.MARKDOWN, # TODO fix
- """
-| | Field 1 | Field 2 | Field 3 |
-| :-: | :-----: | :-----: | :-----: |
-| 1 | value 1 | value2 | value3 |
-| 4 | value 4 | value5 | value6 |
-| 7 | value 7 | value8 | value9 |
-""",
- id="MARKDOWN",
- ),
- pytest.param(
- TableStyle.MSWORD_FRIENDLY,
- """
-| | Field 1 | Field 2 | Field 3 |
-| 1 | value 1 | value2 | value3 |
-| 4 | value 4 | value5 | value6 |
-| 7 | value 7 | value8 | value9 |
-""",
- id="MSWORD_FRIENDLY",
- ),
- pytest.param(
- TableStyle.ORGMODE,
- """
-|---+---------+---------+---------|
-| | Field 1 | Field 2 | Field 3 |
-|---+---------+---------+---------|
-| 1 | value 1 | value2 | value3 |
-| 4 | value 4 | value5 | value6 |
-| 7 | value 7 | value8 | value9 |
-|---+---------+---------+---------|
-""",
- id="ORGMODE",
- ),
- pytest.param(
- TableStyle.PLAIN_COLUMNS,
- """
- Field 1 Field 2 Field 3
-1 value 1 value2 value3
-4 value 4 value5 value6
-7 value 7 value8 value9
-""", # noqa: W291
- id="PLAIN_COLUMNS",
- ),
- pytest.param(
- TableStyle.RANDOM,
- """
-'^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^'
-% 1 value 1 value2 value3%
-% 4 value 4 value5 value6%
-% 7 value 7 value8 value9%
-'^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^'
-""",
- id="RANDOM",
- ),
- pytest.param(
- TableStyle.DOUBLE_BORDER,
- """
-╔═══╦═════════╦═════════╦═════════╗
-║ ║ Field 1 ║ Field 2 ║ Field 3 ║
-╠═══╬═════════╬═════════╬═════════╣
-║ 1 ║ value 1 ║ value2 ║ value3 ║
-║ 4 ║ value 4 ║ value5 ║ value6 ║
-║ 7 ║ value 7 ║ value8 ║ value9 ║
-╚═══╩═════════╩═════════╩═════════╝
-""",
- ),
- pytest.param(
- TableStyle.SINGLE_BORDER,
- """
-┌───┬─────────┬─────────┬─────────┐
-│ │ Field 1 │ Field 2 │ Field 3 │
-├───┼─────────┼─────────┼─────────┤
-│ 1 │ value 1 │ value2 │ value3 │
-│ 4 │ value 4 │ value5 │ value6 │
-│ 7 │ value 7 │ value8 │ value9 │
-└───┴─────────┴─────────┴─────────┘
-""",
- ),
- ],
- )
- def test_style(self, style, expected) -> None:
- # Arrange
- t = helper_table()
- random.seed(1234)
-
- # Act
- t.set_style(style)
-
- # Assert
- result = t.get_string()
- assert result.strip() == expected.strip()
-
- def test_style_invalid(self) -> None:
- # Arrange
- t = helper_table()
-
- # Act / Assert
- # This is an hrule style, not a table style
- with pytest.raises(ValueError):
- t.set_style(HRuleStyle.ALL) # type: ignore[arg-type]
-
- @pytest.mark.parametrize(
- "original_style,style, expected",
- [
- pytest.param(
- TableStyle.MARKDOWN,
- TableStyle.DEFAULT,
- """
-+---+---------+---------+---------+
-| | Field 1 | Field 2 | Field 3 |
-+---+---------+---------+---------+
-| 1 | value 1 | value2 | value3 |
-| 4 | value 4 | value5 | value6 |
-| 7 | value 7 | value8 | value9 |
-+---+---------+---------+---------+
-""",
- id="DEFAULT",
- ),
- pytest.param(
- TableStyle.MSWORD_FRIENDLY,
- TableStyle.MARKDOWN,
- """
-| | Field 1 | Field 2 | Field 3 |
-| :-: | :-----: | :-----: | :-----: |
-| 1 | value 1 | value2 | value3 |
-| 4 | value 4 | value5 | value6 |
-| 7 | value 7 | value8 | value9 |
-""",
- id="MARKDOWN",
- ),
- pytest.param(
- TableStyle.MARKDOWN,
- TableStyle.MSWORD_FRIENDLY,
- """
-| | Field 1 | Field 2 | Field 3 |
-| 1 | value 1 | value2 | value3 |
-| 4 | value 4 | value5 | value6 |
-| 7 | value 7 | value8 | value9 |
-""",
- id="MSWORD_FRIENDLY",
- ),
- pytest.param(
- TableStyle.MARKDOWN,
- TableStyle.ORGMODE,
- """
-|---+---------+---------+---------|
-| | Field 1 | Field 2 | Field 3 |
-|---+---------+---------+---------|
-| 1 | value 1 | value2 | value3 |
-| 4 | value 4 | value5 | value6 |
-| 7 | value 7 | value8 | value9 |
-|---+---------+---------+---------|
-""",
- id="ORGMODE",
- ),
- pytest.param(
- TableStyle.MARKDOWN,
- TableStyle.PLAIN_COLUMNS,
- """
- Field 1 Field 2 Field 3
-1 value 1 value2 value3
-4 value 4 value5 value6
-7 value 7 value8 value9
-""", # noqa: W291
- id="PLAIN_COLUMNS",
- ),
- pytest.param(
- TableStyle.MARKDOWN,
- TableStyle.RANDOM,
- """
-'^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^'
-% 1 value 1 value2 value3%
-% 4 value 4 value5 value6%
-% 7 value 7 value8 value9%
-'^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^'
-""",
- id="RANDOM",
- ),
- pytest.param(
- TableStyle.MARKDOWN,
- TableStyle.DOUBLE_BORDER,
- """
-╔═══╦═════════╦═════════╦═════════╗
-║ ║ Field 1 ║ Field 2 ║ Field 3 ║
-╠═══╬═════════╬═════════╬═════════╣
-║ 1 ║ value 1 ║ value2 ║ value3 ║
-║ 4 ║ value 4 ║ value5 ║ value6 ║
-║ 7 ║ value 7 ║ value8 ║ value9 ║
-╚═══╩═════════╩═════════╩═════════╝
-""",
- id="DOUBLE_BORDER",
- ),
- pytest.param(
- TableStyle.MARKDOWN,
- TableStyle.SINGLE_BORDER,
- """
-┌───┬─────────┬─────────┬─────────┐
-│ │ Field 1 │ Field 2 │ Field 3 │
-├───┼─────────┼─────────┼─────────┤
-│ 1 │ value 1 │ value2 │ value3 │
-│ 4 │ value 4 │ value5 │ value6 │
-│ 7 │ value 7 │ value8 │ value9 │
-└───┴─────────┴─────────┴─────────┘
-""",
- id="SINGLE_BORDER",
- ),
- ],
- )
- def test_style_reset(self, original_style, style, expected) -> None:
- """
- Testing to ensure that default styling is reset between changes
- of styles on a PrettyTable
-
- Args:
- style (str): Style to be used (Default, markdown, etc)
- expected (str): The expected format of style as a string representation
- """
- # Arrange
- t = helper_table()
- random.seed(1234)
-
- # Act
- t.set_style(original_style)
- t.set_style(style)
-
- # Assert
- result = t.get_string()
- assert result.strip() == expected.strip()
-
- @pytest.mark.parametrize(
- "style, expected",
- [
- pytest.param(
- TableStyle.MARKDOWN,
- """
-| l | c | r | Align left | Align centre | Align right |
-| :-| :-: |-: | :----------| :----------: |-----------: |
-| 1 | 2 | 3 | value 1 | value2 | value3 |
-| 4 | 5 | 6 | value 4 | value5 | value6 |
-| 7 | 8 | 9 | value 7 | value8 | value9 |
-""",
- id="MARKDOWN",
- ),
- ],
- )
- def test_style_align(self, style, expected) -> None:
- # Arrange
- t = PrettyTable(["l", "c", "r", "Align left", "Align centre", "Align right"])
- v = 1
- for row in range(3):
- # Some have spaces, some not, to help test padding columns of
- # different widths
- t.add_row([v, v + 1, v + 2, f"value {v}", f"value{v + 1}", f"value{v + 2}"])
- v += 3
-
- # Act
- t.set_style(style)
- t.align["l"] = t.align["Align left"] = "l"
- t.align["c"] = t.align["Align centre"] = "c"
- t.align["r"] = t.align["Align right"] = "r"
-
- # Assert
- result = t.get_string()
- assert result.strip() == expected.strip()
-
-
class TestCsvOutput:
- def test_csv_output(self) -> None:
- t = helper_table()
- assert t.get_csv_string(delimiter="\t", header=False) == (
+ def test_csv_output(self, helper_table: PrettyTable) -> None:
+ assert helper_table.get_csv_string(delimiter="\t", header=False) == (
"1\tvalue 1\tvalue2\tvalue3\r\n"
"4\tvalue 4\tvalue5\tvalue6\r\n"
"7\tvalue 7\tvalue8\tvalue9\r\n"
)
- assert t.get_csv_string() == (
+ assert helper_table.get_csv_string() == (
",Field 1,Field 2,Field 3\r\n"
"1,value 1,value2,value3\r\n"
"4,value 4,value5,value6\r\n"
"7,value 7,value8,value9\r\n"
)
options = {"fields": ["Field 1", "Field 3"]}
- assert t.get_csv_string(**options) == (
+ assert helper_table.get_csv_string(**options) == (
"Field 1,Field 3\r\n"
"value 1,value3\r\n"
"value 4,value6\r\n"
@@ -1965,162 +1051,8 @@ class TestCsvOutput:
)
-class TestLatexOutput:
- def test_latex_output(self) -> None:
- t = helper_table()
- assert t.get_latex_string() == (
- "\\begin{tabular}{cccc}\r\n"
- " & Field 1 & Field 2 & Field 3 \\\\\r\n"
- "1 & value 1 & value2 & value3 \\\\\r\n"
- "4 & value 4 & value5 & value6 \\\\\r\n"
- "7 & value 7 & value8 & value9 \\\\\r\n"
- "\\end{tabular}"
- )
- options = {"fields": ["Field 1", "Field 3"]}
- assert t.get_latex_string(**options) == (
- "\\begin{tabular}{cc}\r\n"
- "Field 1 & Field 3 \\\\\r\n"
- "value 1 & value3 \\\\\r\n"
- "value 4 & value6 \\\\\r\n"
- "value 7 & value9 \\\\\r\n"
- "\\end{tabular}"
- )
-
- def test_latex_output_formatted(self) -> None:
- t = helper_table()
- assert t.get_latex_string(format=True) == (
- "\\begin{tabular}{|c|c|c|c|}\r\n"
- "\\hline\r\n"
- " & Field 1 & Field 2 & Field 3 \\\\\r\n"
- "1 & value 1 & value2 & value3 \\\\\r\n"
- "4 & value 4 & value5 & value6 \\\\\r\n"
- "7 & value 7 & value8 & value9 \\\\\r\n"
- "\\hline\r\n"
- "\\end{tabular}"
- )
-
- options = {"fields": ["Field 1", "Field 3"]}
- assert t.get_latex_string(format=True, **options) == (
- "\\begin{tabular}{|c|c|}\r\n"
- "\\hline\r\n"
- "Field 1 & Field 3 \\\\\r\n"
- "value 1 & value3 \\\\\r\n"
- "value 4 & value6 \\\\\r\n"
- "value 7 & value9 \\\\\r\n"
- "\\hline\r\n"
- "\\end{tabular}"
- )
-
- options = {"vrules": VRuleStyle.FRAME}
- assert t.get_latex_string(format=True, **options) == (
- "\\begin{tabular}{|cccc|}\r\n"
- "\\hline\r\n"
- " & Field 1 & Field 2 & Field 3 \\\\\r\n"
- "1 & value 1 & value2 & value3 \\\\\r\n"
- "4 & value 4 & value5 & value6 \\\\\r\n"
- "7 & value 7 & value8 & value9 \\\\\r\n"
- "\\hline\r\n"
- "\\end{tabular}"
- )
-
- options = {"hrules": HRuleStyle.ALL}
- assert t.get_latex_string(format=True, **options) == (
- "\\begin{tabular}{|c|c|c|c|}\r\n"
- "\\hline\r\n"
- " & Field 1 & Field 2 & Field 3 \\\\\r\n"
- "\\hline\r\n"
- "1 & value 1 & value2 & value3 \\\\\r\n"
- "\\hline\r\n"
- "4 & value 4 & value5 & value6 \\\\\r\n"
- "\\hline\r\n"
- "7 & value 7 & value8 & value9 \\\\\r\n"
- "\\hline\r\n"
- "\\end{tabular}"
- )
-
- def test_latex_output_header(self) -> None:
- t = helper_table()
- assert t.get_latex_string(format=True, hrules=HRuleStyle.HEADER) == (
- "\\begin{tabular}{|c|c|c|c|}\r\n"
- " & Field 1 & Field 2 & Field 3 \\\\\r\n"
- "\\hline\r\n"
- "1 & value 1 & value2 & value3 \\\\\r\n"
- "4 & value 4 & value5 & value6 \\\\\r\n"
- "7 & value 7 & value8 & value9 \\\\\r\n"
- "\\end{tabular}"
- )
-
-
-class TestJSONConstructor:
- def test_json_and_back(self, city_data_prettytable: PrettyTable) -> None:
- json_string = city_data_prettytable.get_json_string()
- new_table = from_json(json_string)
- assert new_table.get_string() == city_data_prettytable.get_string()
-
-
-class TestHtmlConstructor:
- def test_html_and_back(self, city_data_prettytable: PrettyTable) -> None:
- html_string = city_data_prettytable.get_html_string()
- new_table = from_html(html_string)[0]
- assert new_table.get_string() == city_data_prettytable.get_string()
-
- def test_html_one_and_back(self, city_data_prettytable: PrettyTable) -> None:
- html_string = city_data_prettytable.get_html_string()
- new_table = from_html_one(html_string)
- assert new_table.get_string() == city_data_prettytable.get_string()
-
- def test_html_one_fail_on_many(self, city_data_prettytable: PrettyTable) -> None:
- html_string = city_data_prettytable.get_html_string()
- html_string += city_data_prettytable.get_html_string()
- with pytest.raises(ValueError):
- from_html_one(html_string)
-
-
-def japanese_pretty_table() -> PrettyTable:
- table = PrettyTable(["Kanji", "Hiragana", "English"])
- table.add_row(["神戸", "こうべ", "Kobe"])
- table.add_row(["京都", "きょうと", "Kyoto"])
- table.add_row(["長崎", "ながさき", "Nagasaki"])
- table.add_row(["名古屋", "なごや", "Nagoya"])
- table.add_row(["大阪", "おおさか", "Osaka"])
- table.add_row(["札幌", "さっぽろ", "Sapporo"])
- table.add_row(["東京", "とうきょう", "Tokyo"])
- table.add_row(["横浜", "よこはま", "Yokohama"])
- return table
-
-
-def emoji_pretty_table() -> PrettyTable:
- thunder1 = [
- '\033[38;5;226m _`/""\033[38;5;250m.-. \033[0m',
- "\033[38;5;226m ,\\_\033[38;5;250m( ). \033[0m",
- "\033[38;5;226m /\033[38;5;250m(___(__) \033[0m",
- "\033[38;5;228;5m ⚡\033[38;5;111;25mʻ ʻ\033[38;5;228;5m"
- "⚡\033[38;5;111;25mʻ ʻ \033[0m",
- "\033[38;5;111m ʻ ʻ ʻ ʻ \033[0m",
- ]
- thunder2 = [
- "\033[38;5;240;1m .-. \033[0m",
- "\033[38;5;240;1m ( ). \033[0m",
- "\033[38;5;240;1m (___(__) \033[0m",
- "\033[38;5;21;1m ‚ʻ\033[38;5;228;5m⚡\033[38;5;21;25mʻ‚\033[38;5;228;5m"
- "⚡\033[38;5;21;25m‚ʻ \033[0m",
- "\033[38;5;21;1m ‚ʻ‚ʻ\033[38;5;228;5m⚡\033[38;5;21;25mʻ‚ʻ \033[0m",
- ]
- table = PrettyTable(["Thunderbolt", "Lightning"])
- for i in range(len(thunder1)):
- table.add_row([thunder1[i], thunder2[i]])
- return table
-
-
-class TestMultiPattern:
- @pytest.mark.parametrize(
- ["pt", "expected_output", "test_type"],
- [
- (
- lf("city_data_prettytable"),
- """
+def test_paginate(city_data: PrettyTable) -> None:
+ expected_page_1 = """
+-----------+------+------------+-----------------+
| City name | Area | Population | Annual Rainfall |
+-----------+------+------------+-----------------+
@@ -2128,154 +1060,48 @@ class TestMultiPattern:
| Brisbane | 5905 | 1857594 | 1146.4 |
| Darwin | 112 | 120900 | 1714.7 |
| Hobart | 1357 | 205556 | 619.5 |
++-----------+------+------------+-----------------+""".strip()
+ expected_page_2 = """
++-----------+------+------------+-----------------+
+| City name | Area | Population | Annual Rainfall |
++-----------+------+------------+-----------------+
| Sydney | 2058 | 4336374 | 1214.8 |
| Melbourne | 1566 | 3806092 | 646.9 |
| Perth | 5386 | 1554769 | 869.4 |
-+-----------+------+------------+-----------------+
-""",
- "English Table",
- ),
- (
- lf("japanese_pretty_table"),
- """
-+--------+------------+----------+
-| Kanji | Hiragana | English |
-+--------+------------+----------+
-| 神戸 | こうべ | Kobe |
-| 京都 | きょうと | Kyoto |
-| 長崎 | ながさき | Nagasaki |
-| 名古屋 | なごや | Nagoya |
-| 大阪 | おおさか | Osaka |
-| 札幌 | さっぽろ | Sapporo |
-| 東京 | とうきょう | Tokyo |
-| 横浜 | よこはま | Yokohama |
-+--------+------------+----------+
-
-""",
- "Japanese table",
- ),
- (
- lf("emoji_pretty_table"),
- """
-+-----------------+-----------------+
-| Thunderbolt | Lightning |
-+-----------------+-----------------+
-| \x1b[38;5;226m _`/""\x1b[38;5;250m.-. \x1b[0m | \x1b[38;5;240;1m .-. \x1b[0m |
-| \x1b[38;5;226m ,\\_\x1b[38;5;250m( ). \x1b[0m | \x1b[38;5;240;1m ( ). \x1b[0m |
-| \x1b[38;5;226m /\x1b[38;5;250m(___(__) \x1b[0m | \x1b[38;5;240;1m (___(__) \x1b[0m |
-| \x1b[38;5;228;5m ⚡\x1b[38;5;111;25mʻ ʻ\x1b[38;5;228;5m⚡\x1b[38;5;111;25mʻ ʻ \x1b[0m | \x1b[38;5;21;1m ‚ʻ\x1b[38;5;228;5m⚡\x1b[38;5;21;25mʻ‚\x1b[38;5;228;5m⚡\x1b[38;5;21;25m‚ʻ \x1b[0m |
-| \x1b[38;5;111m ʻ ʻ ʻ ʻ \x1b[0m | \x1b[38;5;21;1m ‚ʻ‚ʻ\x1b[38;5;228;5m⚡\x1b[38;5;21;25mʻ‚ʻ \x1b[0m |
-+-----------------+-----------------+
- """, # noqa: E501
- "Emoji table",
- ),
- ],
- )
- def test_multi_pattern_outputs(
- self, pt: PrettyTable, expected_output: str, test_type: str
- ) -> None:
- printed_table = pt.get_string()
- assert (
- printed_table.strip() == expected_output.strip()
- ), f"Error output for test output of type {test_type}"
-
++-----------+------+------------+-----------------+""".strip()
-def test_paginate() -> None:
- # Arrange
- t = helper_table(rows=7)
- expected_page_1 = """
-+----+----------+---------+---------+
-| | Field 1 | Field 2 | Field 3 |
-+----+----------+---------+---------+
-| 1 | value 1 | value2 | value3 |
-| 4 | value 4 | value5 | value6 |
-| 7 | value 7 | value8 | value9 |
-| 10 | value 10 | value11 | value12 |
-+----+----------+---------+---------+
- """.strip()
- expected_page_2 = """
-+----+----------+---------+---------+
-| | Field 1 | Field 2 | Field 3 |
-+----+----------+---------+---------+
-| 13 | value 13 | value14 | value15 |
-| 16 | value 16 | value17 | value18 |
-| 19 | value 19 | value20 | value21 |
-+----+----------+---------+---------+
-""".strip()
-
- # Act
- paginated = t.paginate(page_length=4)
-
- # Assert
- paginated = paginated.strip()
+ paginated = city_data.paginate(page_length=4).strip()
assert paginated.startswith(expected_page_1)
assert "\f" in paginated
assert paginated.endswith(expected_page_2)
- # Act
- paginated = t.paginate(page_length=4, line_break="\n")
-
- # Assert
+ paginated = city_data.paginate(page_length=4, line_break="\n")
assert "\f" not in paginated
assert "\n" in paginated
-def test_add_rows() -> None:
- """A table created with multiple add_row calls
- is the same as one created with a single add_rows
- """
- # Arrange
- table1 = PrettyTable(["A", "B", "C"])
- table2 = PrettyTable(["A", "B", "C"])
- table1.add_row([1, 2, 3])
- table1.add_row([4, 5, 6])
- rows = [
- [1, 2, 3],
- [4, 5, 6],
- ]
-
- # Act
- table2.add_rows(rows)
-
- # Assert
- assert str(table1) == str(table2)
-
-
-def test_autoindex() -> None:
+def test_autoindex(city_data: PrettyTable) -> None:
"""Testing that a table with a custom index row is
equal to the one produced by the function
.add_autoindex()
"""
- table1 = PrettyTable()
- table1.field_names = ["City name", "Area", "Population", "Annual Rainfall"]
- table1.add_row(["Adelaide", 1295, 1158259, 600.5])
- table1.add_row(["Brisbane", 5905, 1857594, 1146.4])
- table1.add_row(["Darwin", 112, 120900, 1714.7])
- table1.add_row(["Hobart", 1357, 205556, 619.5])
- table1.add_row(["Sydney", 2058, 4336374, 1214.8])
- table1.add_row(["Melbourne", 1566, 3806092, 646.9])
- table1.add_row(["Perth", 5386, 1554769, 869.4])
- table1.add_autoindex(fieldname="Test")
+ city_data.field_names = CITY_DATA_HEADER
+ city_data.add_autoindex(fieldname="Test")
table2 = PrettyTable()
- table2.field_names = ["Test", "City name", "Area", "Population", "Annual Rainfall"]
- table2.add_row([1, "Adelaide", 1295, 1158259, 600.5])
- table2.add_row([2, "Brisbane", 5905, 1857594, 1146.4])
- table2.add_row([3, "Darwin", 112, 120900, 1714.7])
- table2.add_row([4, "Hobart", 1357, 205556, 619.5])
- table2.add_row([5, "Sydney", 2058, 4336374, 1214.8])
- table2.add_row([6, "Melbourne", 1566, 3806092, 646.9])
- table2.add_row([7, "Perth", 5386, 1554769, 869.4])
+ table2.field_names = ["Test"] + CITY_DATA_HEADER
+ for idx, row in enumerate(CITY_DATA):
+ table2.add_row([idx + 1] + row)
- assert str(table1) == str(table2)
+ assert str(city_data) == str(table2)
@pytest.fixture(scope="function")
def unpadded_pt() -> PrettyTable:
table = PrettyTable(header=False, padding_width=0)
- table.add_row("abc")
- table.add_row("def")
- table.add_row("g..")
+ table.add_row(list("abc"))
+ table.add_row(list("def"))
+ table.add_row(list("g.."))
return table
@@ -2330,23 +1156,20 @@ class TestCustomFormatter:
def test_set_custom_format_to_none_set_empty_dict(self) -> None:
table = PrettyTable()
table.custom_format = None
- assert len(table.custom_format) == 0
assert isinstance(table.custom_format, dict)
def test_set_custom_format_invalid_type_throw_error(self) -> None:
table = PrettyTable()
with pytest.raises(TypeError) as e:
- table.custom_format = "Some String"
+ table.custom_format = "Some String" # type: ignore[assignment]
assert "The custom_format property need to be a dictionary or callable" in str(
e.value
)
- def test_use_custom_formatter_for_int(
- self, city_data_prettytable: PrettyTable
- ) -> None:
- city_data_prettytable.custom_format["Annual Rainfall"] = lambda n, v: f"{v:.2f}"
+ def test_use_custom_formatter_for_int(self, city_data: PrettyTable) -> None:
+ city_data.custom_format["Annual Rainfall"] = lambda n, v: f"{v:.2f}"
assert (
- city_data_prettytable.get_string().strip()
+ city_data.get_string().strip()
== """
+-----------+------+------------+-----------------+
| City name | Area | Population | Annual Rainfall |
@@ -2417,7 +1240,84 @@ class TestRepr:
assert row_prettytable._repr_html_() == row_prettytable.get_html_string()
-class TestMinTableWidth:
+class TestBreakOnHyphens:
+ row = [
+ "bluedevil breeze breeze-gtk eos-bash-shared glib2 "
+ "kactivitymanagerd kde-cli-tools kde-gtk-config kdecoration"
+ ]
+ EXPECTED_TRUE = """+------------------------------------------+
+| Field 1 |
++------------------------------------------+
+| bluedevil breeze breeze-gtk eos-bash- |
+| shared glib2 kactivitymanagerd kde-cli- |
+| tools kde-gtk-config kdecoration |
++------------------------------------------+"""
+ EXPECTED_FALSE = """+------------------------------------------+
+| Field 1 |
++------------------------------------------+
+| bluedevil breeze breeze-gtk |
+| eos-bash-shared glib2 kactivitymanagerd |
+| kde-cli-tools kde-gtk-config kdecoration |
++------------------------------------------+"""
+
+ def test_break_on_hyphens(self) -> None:
+ table = PrettyTable(max_width=40)
+ table.break_on_hyphens = False
+ assert not table.break_on_hyphens
+ table.add_row(self.row)
+ assert table.get_string().strip() == self.EXPECTED_FALSE
+
+ def test_break_on_hyphens_on_init(self) -> None:
+ table = PrettyTable(max_width=40, break_on_hyphens=False)
+ assert not table._break_on_hyphens
+ assert not table.break_on_hyphens
+ table.add_row(self.row)
+ assert table.get_string().strip() == self.EXPECTED_FALSE
+
+ def test_break_on_hyphens_default(self) -> None:
+ table = PrettyTable(max_width=40)
+ assert table.break_on_hyphens
+ table.add_row(self.row)
+ assert table.get_string().strip() == self.EXPECTED_TRUE
+
+
+class TestWidth:
+ colored = "\033[31mC\033[32mO\033[31mL\033[32mO\033[31mR\033[32mE\033[31mD\033[0m"
+
+ def test_color(self) -> None:
+ table = PrettyTable(["Field 1", "Field 2"])
+ table.add_row([self.colored, self.colored])
+ table.add_row(["nothing", "neither"])
+ result = table.get_string()
+ assert (
+ result.strip()
+ == f"""
++---------+---------+
+| Field 1 | Field 2 |
++---------+---------+
+| {self.colored} | {self.colored} |
+| nothing | neither |
++---------+---------+
+""".strip()
+ )
+
+ def test_reset(self) -> None:
+ table = PrettyTable(["Field 1", "Field 2"])
+ table.add_row(["abc def\033(B", "\033[31mabc def\033[m"])
+ table.add_row(["nothing", "neither"])
+ result = table.get_string()
+ assert (
+ result.strip()
+ == """
++---------+---------+
+| Field 1 | Field 2 |
++---------+---------+
+| abc def\033(B | \033[31mabc def\033[m |
+| nothing | neither |
++---------+---------+
+""".strip()
+ )
+
@pytest.mark.parametrize(
"loops, fields, desired_width, border, internal_border",
[
@@ -2442,7 +1342,12 @@ class TestMinTableWidth:
],
)
def test_min_table_width(
- self, loops, fields, desired_width, border, internal_border
+ self,
+ loops: int,
+ fields: list[str],
+ desired_width: int,
+ border: bool,
+ internal_border: bool,
) -> None:
for col_width in range(loops):
x = prettytable.PrettyTable()
@@ -2469,8 +1374,6 @@ class TestMinTableWidth:
desired_width,
]
-
-class TestMaxTableWidth:
def test_max_table_width(self) -> None:
table = PrettyTable()
table.max_table_width = 5
@@ -2547,6 +1450,38 @@ class TestMaxTableWidth:
+---+-----------------+---+-----------------+---+-----------------+""".strip()
)
+ @pytest.mark.parametrize("set_width_parameter", [True, False])
+ def test_table_max_width_wo_header_width(self, set_width_parameter: bool) -> None:
+ headers = [
+ "A Field Name",
+ "B Field Name",
+ "D Field Name",
+ "E Field Name",
+ "F Field Name",
+ "G Field Name",
+ "H Field Name",
+ "I Field Name",
+ "J Field Name",
+ "K Field Name",
+ "L Field Name",
+ "M Field Name",
+ ]
+ row = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
+ expected = """+---+---+---+---+---+---+---+---+---+---+----+----+
+| A | B | D | E | F | G | H | I | J | K | L | M |
++---+---+---+---+---+---+---+---+---+---+----+----+
+| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
++---+---+---+---+---+---+---+---+---+---+----+----+"""
+
+ if set_width_parameter:
+ table = PrettyTable(headers, use_header_width=False)
+ else:
+ table = PrettyTable(headers)
+ table.use_header_width = False
+ table.add_row(row)
+
+ assert table.get_string() == expected
+
def test_table_width_on_init_wo_columns(self) -> None:
"""See also #272"""
table = PrettyTable(max_width=10)
@@ -2665,51 +1600,6 @@ class TestMaxTableWidth:
+--------+--------------+------------+""".strip()
)
- def test_table_formatted_html_autoindex(self) -> None:
- """See also #199"""
- table = PrettyTable(["Field 1", "Field 2", "Field 3"])
- for row in range(1, 3 * 3, 3):
- table.add_row(
- [f"value {row*100}", f"value {row+1*100}", f"value {row+2*100}"]
- )
- table.format = True
- table.add_autoindex("I")
-
- assert (
- table.get_html_string().strip()
- == """
-<table frame="box" rules="cols">
- <thead>
- <tr>
- <th style="padding-left: 1em; padding-right: 1em; text-align: center">I</th>
- <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">1</td>
- <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value 100</td>
- <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value 101</td>
- <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value 201</td>
- </tr>
- <tr>
- <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">2</td>
- <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value 400</td>
- <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value 104</td>
- <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value 204</td>
- </tr>
- <tr>
- <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">3</td>
- <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value 700</td>
- <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value 107</td>
- <td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value 207</td>
- </tr>
- </tbody>
-</table>""".strip() # noqa: E501
- )
-
def test_max_table_width_wide_vrules_frame(self) -> None:
table = PrettyTable()
table.max_table_width = 52
@@ -2778,16 +1668,11 @@ class TestMaxTableWidth:
class TestFields:
def test_fields_at_class_declaration(self) -> None:
table = PrettyTable(
- field_names=["City name", "Area", "Population", "Annual Rainfall"],
+ field_names=CITY_DATA_HEADER,
fields=["City name", "Annual Rainfall"],
)
- table.add_row(["Adelaide", 1295, 1158259, 600.5])
- table.add_row(["Brisbane", 5905, 1857594, 1146.4])
- table.add_row(["Darwin", 112, 120900, 1714.7])
- table.add_row(["Hobart", 1357, 205556, 619.5])
- table.add_row(["Sydney", 2058, 4336374, 1214.8])
- table.add_row(["Melbourne", 1566, 3806092, 646.9])
- table.add_row(["Perth", 5386, 1554769, 869.4])
+ for row in CITY_DATA:
+ table.add_row(row)
assert (
"""+-----------+-----------------+
| City name | Annual Rainfall |
@@ -2805,15 +1690,10 @@ class TestFields:
def test_fields(self) -> None:
table = PrettyTable()
- table.field_names = ["City name", "Area", "Population", "Annual Rainfall"]
+ table.field_names = CITY_DATA_HEADER
table.fields = ["City name", "Annual Rainfall"]
- table.add_row(["Adelaide", 1295, 1158259, 600.5])
- table.add_row(["Brisbane", 5905, 1857594, 1146.4])
- table.add_row(["Darwin", 112, 120900, 1714.7])
- table.add_row(["Hobart", 1357, 205556, 619.5])
- table.add_row(["Sydney", 2058, 4336374, 1214.8])
- table.add_row(["Melbourne", 1566, 3806092, 646.9])
- table.add_row(["Perth", 5386, 1554769, 869.4])
+ for row in CITY_DATA:
+ table.add_row(row)
assert (
"""+-----------+-----------------+
| City name | Annual Rainfall |
@@ -2830,136 +1710,63 @@ class TestFields:
)
-class TestPreservingInternalBorders:
- def test_internal_border_preserved(self) -> None:
- pt = helper_table()
- pt.border = False
- pt.preserve_internal_border = True
-
- assert (
- pt.get_string().strip()
- == """
- | Field 1 | Field 2 | Field 3
----+---------+---------+---------
- 1 | value 1 | value2 | value3
- 4 | value 4 | value5 | value6
- 7 | value 7 | value8 | value9
-""".strip() # noqa: W291
- )
-
- def test_internal_border_preserved_latex(self) -> None:
- pt = helper_table()
- pt.border = False
- pt.format = True
- pt.preserve_internal_border = True
-
- assert pt.get_latex_string().strip() == (
- "\\begin{tabular}{c|c|c|c}\r\n"
- " & Field 1 & Field 2 & Field 3 \\\\\r\n"
- "1 & value 1 & value2 & value3 \\\\\r\n"
- "4 & value 4 & value5 & value6 \\\\\r\n"
- "7 & value 7 & value8 & value9 \\\\\r\n"
- "\\end{tabular}"
- )
-
- def test_internal_border_preserved_html(self) -> None:
- pt = helper_table()
- pt.format = True
- pt.border = False
- pt.preserve_internal_border = True
-
- assert (
- pt.get_html_string().strip()
- == """
-<table rules="cols">
- <thead>
- <tr>
- <th style="padding-left: 1em; padding-right: 1em; text-align: center"></th>
- <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">1</td>
- <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">4</td>
- <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">7</td>
- <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 TestGeneralOutput:
- def test_copy(self) -> None:
- # Arrange
- t = helper_table()
+ def test_copy(self, helper_table: PrettyTable) -> None:
+ t_copy = helper_table.copy()
+ assert helper_table.get_string() == t_copy.get_string()
- # Act
- t_copy = t.copy()
-
- # Assert
- assert t.get_string() == t_copy.get_string()
-
- def test_text(self) -> None:
- t = helper_table()
- assert t.get_formatted_string("text") == t.get_string()
+ def test_text(self, helper_table: PrettyTable) -> None:
+ assert helper_table.get_formatted_string("text") == helper_table.get_string()
# test with default arg, too
- assert t.get_formatted_string() == t.get_string()
+ assert helper_table.get_formatted_string() == helper_table.get_string()
# args passed through
- assert t.get_formatted_string(border=False) == t.get_string(border=False)
-
- def test_csv(self) -> None:
- t = helper_table()
- assert t.get_formatted_string("csv") == t.get_csv_string()
- # args passed through
- assert t.get_formatted_string("csv", border=False) == t.get_csv_string(
+ assert helper_table.get_formatted_string(
border=False
- )
+ ) == helper_table.get_string(border=False)
- def test_json(self) -> None:
- t = helper_table()
- assert t.get_formatted_string("json") == t.get_json_string()
+ def test_csv(self, helper_table: PrettyTable) -> None:
+ assert helper_table.get_formatted_string("csv") == helper_table.get_csv_string()
# args passed through
- assert t.get_formatted_string("json", border=False) == t.get_json_string(
- border=False
- )
+ assert helper_table.get_formatted_string(
+ "csv", border=False
+ ) == helper_table.get_csv_string(border=False)
- def test_html(self) -> None:
- t = helper_table()
- assert t.get_formatted_string("html") == t.get_html_string()
- # args passed through
- assert t.get_formatted_string("html", border=False) == t.get_html_string(
- border=False
+ def test_json(self, helper_table: PrettyTable) -> None:
+ assert (
+ helper_table.get_formatted_string("json") == helper_table.get_json_string()
)
+ # args passed through
+ assert helper_table.get_formatted_string(
+ "json", border=False
+ ) == helper_table.get_json_string(border=False)
- def test_latex(self) -> None:
- t = helper_table()
- assert t.get_formatted_string("latex") == t.get_latex_string()
+ def test_html(self, helper_table: PrettyTable) -> None:
+ assert (
+ helper_table.get_formatted_string("html") == helper_table.get_html_string()
+ )
# args passed through
- assert t.get_formatted_string("latex", border=False) == t.get_latex_string(
- border=False
+ assert helper_table.get_formatted_string(
+ "html", border=False
+ ) == helper_table.get_html_string(border=False)
+
+ def test_latex(self, helper_table: PrettyTable) -> None:
+ assert (
+ helper_table.get_formatted_string("latex")
+ == helper_table.get_latex_string()
)
+ # args passed through
+ assert helper_table.get_formatted_string(
+ "latex", border=False
+ ) == helper_table.get_latex_string(border=False)
+
+ def test_mediawiki(self, helper_table: PrettyTable) -> None:
+ assert helper_table.get_formatted_string(
+ "mediawiki", border=False
+ ) == helper_table.get_mediawiki_string(border=False)
- def test_invalid(self) -> None:
- t = helper_table()
+ def test_invalid(self, helper_table: PrettyTable) -> None:
with pytest.raises(ValueError):
- t.get_formatted_string("pdf")
+ helper_table.get_formatted_string("pdf")
class TestDeprecations:
diff --git a/contrib/python/prettytable/py3/tests/test_sections.py b/contrib/python/prettytable/py3/tests/test_sections.py
index 12c56d0021b..55b7065bb0b 100644
--- a/contrib/python/prettytable/py3/tests/test_sections.py
+++ b/contrib/python/prettytable/py3/tests/test_sections.py
@@ -1,7 +1,5 @@
from __future__ import annotations
-from test_prettytable import helper_table
-
from prettytable import PrettyTable, TableStyle
@@ -17,39 +15,50 @@ class TestRowEndSection:
└──────────┴──────────┴──────────┘
""".strip()
+ TEST_ROWS = [
+ ["value 4", "value 5", "value 6"],
+ ["value 7", "value 8", "value 9"],
+ ["value 10", "value 11", "value 12"],
+ ]
+
def test_row_end_section_via_argument(self) -> None:
table = PrettyTable()
table.set_style(TableStyle.SINGLE_BORDER)
- table.add_row(["value 4", "value 5", "value 6"])
- table.add_row(["value 7", "value 8", "value 9"], divider=True)
- table.add_row(["value 10", "value 11", "value 12"])
-
+ table.add_row(self.TEST_ROWS[0])
+ table.add_row(self.TEST_ROWS[1], divider=True)
+ table.add_row(self.TEST_ROWS[2])
assert table.get_string().strip() == self.EXPECTED_RESULT
def test_row_end_section_via_method(self) -> None:
table = PrettyTable()
table.set_style(TableStyle.SINGLE_BORDER)
- table.add_row(["value 4", "value 5", "value 6"])
- table.add_row(["value 7", "value 8", "value 9"])
+ table.add_row(self.TEST_ROWS[0])
+ table.add_row(self.TEST_ROWS[1])
table.add_divider()
- table.add_row(["value 10", "value 11", "value 12"])
+ table.add_row(self.TEST_ROWS[2])
+ assert table.get_string().strip() == self.EXPECTED_RESULT
+ def test_add_rows_divider(self) -> None:
+ """A table created with two add_rows calls, one with divider=True has a
+ divider"""
+ table = PrettyTable()
+ table.set_style(TableStyle.SINGLE_BORDER)
+ table.add_rows(self.TEST_ROWS[0:2], divider=True)
+ table.add_rows(self.TEST_ROWS[2:])
assert table.get_string().strip() == self.EXPECTED_RESULT
class TestClearing:
- def test_clear_rows(self) -> None:
- t = helper_table()
- t.add_row([0, "a", "b", "c"], divider=True)
- t.clear_rows()
- assert t.rows == []
- assert t.dividers == []
- assert t.field_names == ["", "Field 1", "Field 2", "Field 3"]
+ def test_clear_rows(self, helper_table: PrettyTable) -> None:
+ helper_table.add_row([0, "a", "b", "c"], divider=True)
+ helper_table.clear_rows()
+ assert helper_table.rows == []
+ assert helper_table.dividers == []
+ assert helper_table.field_names == ["", "Field 1", "Field 2", "Field 3"]
- def test_clear(self) -> None:
- t = helper_table()
- t.add_row([0, "a", "b", "c"], divider=True)
- t.clear()
- assert t.rows == []
- assert t.dividers == []
- assert t.field_names == []
+ def test_clear(self, helper_table: PrettyTable) -> None:
+ helper_table.add_row([0, "a", "b", "c"], divider=True)
+ helper_table.clear()
+ assert helper_table.rows == []
+ assert helper_table.dividers == []
+ assert helper_table.field_names == []
diff --git a/contrib/python/prettytable/py3/tests/test_sorting.py b/contrib/python/prettytable/py3/tests/test_sorting.py
new file mode 100644
index 00000000000..1968c718f40
--- /dev/null
+++ b/contrib/python/prettytable/py3/tests/test_sorting.py
@@ -0,0 +1,119 @@
+from __future__ import annotations
+
+from test_prettytable import CITY_DATA, CITY_DATA_HEADER
+
+from prettytable import PrettyTable, RowType
+
+
+class TestSorting:
+ def test_sort_by_different_per_columns(self, city_data: PrettyTable) -> None:
+ city_data.sortby = city_data.field_names[0]
+ old = city_data.get_string()
+ for field in city_data.field_names[1:]:
+ city_data.sortby = field
+ new = city_data.get_string()
+ assert new != old
+
+ def test_reverse_sort(self, city_data: PrettyTable) -> None:
+ for field in city_data.field_names:
+ city_data.sortby = field
+ city_data.reversesort = False
+ forward = city_data.get_string()
+ city_data.reversesort = True
+ backward = city_data.get_string()
+ forward_lines = forward.split("\n")[2:] # Discard header lines
+ backward_lines = backward.split("\n")[2:]
+ backward_lines.reverse()
+ assert forward_lines == backward_lines
+
+ def test_sort_key(self, city_data: PrettyTable) -> None:
+ # Test sorting by length of city name
+ def key(vals: RowType) -> list[int]:
+ vals[0] = len(vals[0])
+ return vals
+
+ city_data.sortby = "City name"
+ city_data.sort_key = key
+ assert (
+ city_data.get_string().strip()
+ == """
++-----------+------+------------+-----------------+
+| City name | Area | Population | Annual Rainfall |
++-----------+------+------------+-----------------+
+| Perth | 5386 | 1554769 | 869.4 |
+| Darwin | 112 | 120900 | 1714.7 |
+| Hobart | 1357 | 205556 | 619.5 |
+| Sydney | 2058 | 4336374 | 1214.8 |
+| Adelaide | 1295 | 1158259 | 600.5 |
+| Brisbane | 5905 | 1857594 | 1146.4 |
+| Melbourne | 1566 | 3806092 | 646.9 |
++-----------+------+------------+-----------------+
+""".strip()
+ )
+
+ def test_sort_key_at_class_declaration(self) -> None:
+ # Test sorting by length of city name
+ def key(vals: RowType) -> list[int]:
+ vals[0] = len(vals[0])
+ return vals
+
+ table = PrettyTable(
+ field_names=CITY_DATA_HEADER,
+ sortby="City name",
+ sort_key=key,
+ )
+ assert table.sort_key == key
+ for row in CITY_DATA:
+ table.add_row(row)
+ assert (
+ """+-----------+------+------------+-----------------+
+| City name | Area | Population | Annual Rainfall |
++-----------+------+------------+-----------------+
+| Perth | 5386 | 1554769 | 869.4 |
+| Darwin | 112 | 120900 | 1714.7 |
+| Hobart | 1357 | 205556 | 619.5 |
+| Sydney | 2058 | 4336374 | 1214.8 |
+| Adelaide | 1295 | 1158259 | 600.5 |
+| Brisbane | 5905 | 1857594 | 1146.4 |
+| Melbourne | 1566 | 3806092 | 646.9 |
++-----------+------+------------+-----------------+"""
+ == table.get_string().strip()
+ )
+
+ def test_sort_slice(self) -> None:
+ """Make sure sorting and slicing interact in the expected way"""
+ table = PrettyTable(["Foo"])
+ for i in range(20, 0, -1):
+ table.add_row([i])
+ new_style = table.get_string(sortby="Foo", end=10)
+ assert "10" in new_style
+ assert "20" not in new_style
+ oldstyle = table.get_string(sortby="Foo", end=10, oldsortslice=True)
+ assert "10" not in oldstyle
+ assert "20" in oldstyle
+
+ def test_sortby_at_class_declaration(self) -> None:
+ """
+ Fix #354 where initialization of a table with sortby fails
+ """
+ table = PrettyTable(
+ field_names=CITY_DATA_HEADER,
+ sortby="Area",
+ )
+ assert table.sortby == "Area"
+ for row in CITY_DATA:
+ table.add_row(row)
+ assert (
+ """+-----------+------+------------+-----------------+
+| City name | Area | Population | Annual Rainfall |
++-----------+------+------------+-----------------+
+| Darwin | 112 | 120900 | 1714.7 |
+| Adelaide | 1295 | 1158259 | 600.5 |
+| Hobart | 1357 | 205556 | 619.5 |
+| Melbourne | 1566 | 3806092 | 646.9 |
+| Sydney | 2058 | 4336374 | 1214.8 |
+| Perth | 5386 | 1554769 | 869.4 |
+| Brisbane | 5905 | 1857594 | 1146.4 |
++-----------+------+------------+-----------------+"""
+ == table.get_string().strip()
+ )
diff --git a/contrib/python/prettytable/py3/tests/test_style.py b/contrib/python/prettytable/py3/tests/test_style.py
new file mode 100644
index 00000000000..2ae696e4bc3
--- /dev/null
+++ b/contrib/python/prettytable/py3/tests/test_style.py
@@ -0,0 +1,591 @@
+from __future__ import annotations
+
+import random
+
+import pytest
+from pytest_lazy_fixtures import lf
+
+from prettytable import HRuleStyle, PrettyTable, TableStyle, VRuleStyle
+from prettytable.prettytable import _str_block_width
+
+
+class TestPositionalJunctions:
+ """Verify different cases for positional-junction characters"""
+
+ def test_default(self, city_data: PrettyTable) -> None:
+ city_data.set_style(TableStyle.DOUBLE_BORDER)
+
+ assert (
+ city_data.get_string().strip()
+ == """
+╔═══════════╦══════╦════════════╦═════════════════╗
+║ City name ║ Area ║ Population ║ Annual Rainfall ║
+╠═══════════╬══════╬════════════╬═════════════════╣
+║ Adelaide ║ 1295 ║ 1158259 ║ 600.5 ║
+║ Brisbane ║ 5905 ║ 1857594 ║ 1146.4 ║
+║ Darwin ║ 112 ║ 120900 ║ 1714.7 ║
+║ Hobart ║ 1357 ║ 205556 ║ 619.5 ║
+║ Sydney ║ 2058 ║ 4336374 ║ 1214.8 ║
+║ Melbourne ║ 1566 ║ 3806092 ║ 646.9 ║
+║ Perth ║ 5386 ║ 1554769 ║ 869.4 ║
+╚═══════════╩══════╩════════════╩═════════════════╝""".strip()
+ )
+
+ def test_no_header(self, city_data: PrettyTable) -> None:
+ city_data.set_style(TableStyle.DOUBLE_BORDER)
+ city_data.header = False
+
+ assert (
+ city_data.get_string().strip()
+ == """
+╔═══════════╦══════╦═════════╦════════╗
+║ Adelaide ║ 1295 ║ 1158259 ║ 600.5 ║
+║ Brisbane ║ 5905 ║ 1857594 ║ 1146.4 ║
+║ Darwin ║ 112 ║ 120900 ║ 1714.7 ║
+║ Hobart ║ 1357 ║ 205556 ║ 619.5 ║
+║ Sydney ║ 2058 ║ 4336374 ║ 1214.8 ║
+║ Melbourne ║ 1566 ║ 3806092 ║ 646.9 ║
+║ Perth ║ 5386 ║ 1554769 ║ 869.4 ║
+╚═══════════╩══════╩═════════╩════════╝""".strip()
+ )
+
+ def test_with_title(self, city_data: PrettyTable) -> None:
+ city_data.set_style(TableStyle.DOUBLE_BORDER)
+ city_data.title = "Title"
+
+ assert (
+ city_data.get_string().strip()
+ == """
+╔═════════════════════════════════════════════════╗
+║ Title ║
+╠═══════════╦══════╦════════════╦═════════════════╣
+║ City name ║ Area ║ Population ║ Annual Rainfall ║
+╠═══════════╬══════╬════════════╬═════════════════╣
+║ Adelaide ║ 1295 ║ 1158259 ║ 600.5 ║
+║ Brisbane ║ 5905 ║ 1857594 ║ 1146.4 ║
+║ Darwin ║ 112 ║ 120900 ║ 1714.7 ║
+║ Hobart ║ 1357 ║ 205556 ║ 619.5 ║
+║ Sydney ║ 2058 ║ 4336374 ║ 1214.8 ║
+║ Melbourne ║ 1566 ║ 3806092 ║ 646.9 ║
+║ Perth ║ 5386 ║ 1554769 ║ 869.4 ║
+╚═══════════╩══════╩════════════╩═════════════════╝""".strip()
+ )
+
+ def test_with_title_no_header(self, city_data: PrettyTable) -> None:
+ city_data.set_style(TableStyle.DOUBLE_BORDER)
+ city_data.title = "Title"
+ city_data.header = False
+ assert (
+ city_data.get_string().strip()
+ == """
+╔═════════════════════════════════════╗
+║ Title ║
+╠═══════════╦══════╦═════════╦════════╣
+║ Adelaide ║ 1295 ║ 1158259 ║ 600.5 ║
+║ Brisbane ║ 5905 ║ 1857594 ║ 1146.4 ║
+║ Darwin ║ 112 ║ 120900 ║ 1714.7 ║
+║ Hobart ║ 1357 ║ 205556 ║ 619.5 ║
+║ Sydney ║ 2058 ║ 4336374 ║ 1214.8 ║
+║ Melbourne ║ 1566 ║ 3806092 ║ 646.9 ║
+║ Perth ║ 5386 ║ 1554769 ║ 869.4 ║
+╚═══════════╩══════╩═════════╩════════╝""".strip()
+ )
+
+ def test_hrule_all(self, city_data: PrettyTable) -> None:
+ city_data.set_style(TableStyle.DOUBLE_BORDER)
+ city_data.title = "Title"
+ city_data.hrules = HRuleStyle.ALL
+ assert (
+ city_data.get_string().strip()
+ == """
+╔═════════════════════════════════════════════════╗
+║ Title ║
+╠═══════════╦══════╦════════════╦═════════════════╣
+║ City name ║ Area ║ Population ║ Annual Rainfall ║
+╠═══════════╬══════╬════════════╬═════════════════╣
+║ Adelaide ║ 1295 ║ 1158259 ║ 600.5 ║
+╠═══════════╬══════╬════════════╬═════════════════╣
+║ Brisbane ║ 5905 ║ 1857594 ║ 1146.4 ║
+╠═══════════╬══════╬════════════╬═════════════════╣
+║ Darwin ║ 112 ║ 120900 ║ 1714.7 ║
+╠═══════════╬══════╬════════════╬═════════════════╣
+║ Hobart ║ 1357 ║ 205556 ║ 619.5 ║
+╠═══════════╬══════╬════════════╬═════════════════╣
+║ Sydney ║ 2058 ║ 4336374 ║ 1214.8 ║
+╠═══════════╬══════╬════════════╬═════════════════╣
+║ Melbourne ║ 1566 ║ 3806092 ║ 646.9 ║
+╠═══════════╬══════╬════════════╬═════════════════╣
+║ Perth ║ 5386 ║ 1554769 ║ 869.4 ║
+╚═══════════╩══════╩════════════╩═════════════════╝""".strip()
+ )
+
+ def test_vrules_none(self, city_data: PrettyTable) -> None:
+ city_data.set_style(TableStyle.DOUBLE_BORDER)
+ city_data.vrules = VRuleStyle.NONE
+ assert (
+ city_data.get_string().strip()
+ == "═══════════════════════════════════════════════════\n"
+ " City name Area Population Annual Rainfall \n"
+ "═══════════════════════════════════════════════════\n"
+ " Adelaide 1295 1158259 600.5 \n"
+ " Brisbane 5905 1857594 1146.4 \n"
+ " Darwin 112 120900 1714.7 \n"
+ " Hobart 1357 205556 619.5 \n"
+ " Sydney 2058 4336374 1214.8 \n"
+ " Melbourne 1566 3806092 646.9 \n"
+ " Perth 5386 1554769 869.4 \n"
+ "═══════════════════════════════════════════════════".strip()
+ )
+
+ def test_vrules_frame_with_title(self, city_data: PrettyTable) -> None:
+ city_data.set_style(TableStyle.DOUBLE_BORDER)
+ city_data.vrules = VRuleStyle.FRAME
+ city_data.title = "Title"
+ assert (
+ city_data.get_string().strip()
+ == """
+╔═════════════════════════════════════════════════╗
+║ Title ║
+╠═════════════════════════════════════════════════╣
+║ City name Area Population Annual Rainfall ║
+╠═════════════════════════════════════════════════╣
+║ Adelaide 1295 1158259 600.5 ║
+║ Brisbane 5905 1857594 1146.4 ║
+║ Darwin 112 120900 1714.7 ║
+║ Hobart 1357 205556 619.5 ║
+║ Sydney 2058 4336374 1214.8 ║
+║ Melbourne 1566 3806092 646.9 ║
+║ Perth 5386 1554769 869.4 ║
+╚═════════════════════════════════════════════════╝""".strip()
+ )
+
+
+class TestStyle:
+ @pytest.mark.parametrize(
+ "style, expected",
+ [
+ pytest.param(
+ TableStyle.DEFAULT,
+ """
++---+---------+---------+---------+
+| | Field 1 | Field 2 | Field 3 |
++---+---------+---------+---------+
+| 1 | value 1 | value2 | value3 |
+| 4 | value 4 | value5 | value6 |
+| 7 | value 7 | value8 | value9 |
++---+---------+---------+---------+
+""",
+ id="DEFAULT",
+ ),
+ pytest.param(
+ TableStyle.MARKDOWN, # TODO fix
+ """
+| | Field 1 | Field 2 | Field 3 |
+| :-: | :-----: | :-----: | :-----: |
+| 1 | value 1 | value2 | value3 |
+| 4 | value 4 | value5 | value6 |
+| 7 | value 7 | value8 | value9 |
+""",
+ id="MARKDOWN",
+ ),
+ pytest.param(
+ TableStyle.MSWORD_FRIENDLY,
+ """
+| | Field 1 | Field 2 | Field 3 |
+| 1 | value 1 | value2 | value3 |
+| 4 | value 4 | value5 | value6 |
+| 7 | value 7 | value8 | value9 |
+""",
+ id="MSWORD_FRIENDLY",
+ ),
+ pytest.param(
+ TableStyle.ORGMODE,
+ """
+|---+---------+---------+---------|
+| | Field 1 | Field 2 | Field 3 |
+|---+---------+---------+---------|
+| 1 | value 1 | value2 | value3 |
+| 4 | value 4 | value5 | value6 |
+| 7 | value 7 | value8 | value9 |
+|---+---------+---------+---------|
+""",
+ id="ORGMODE",
+ ),
+ pytest.param(
+ TableStyle.PLAIN_COLUMNS,
+ """
+ Field 1 Field 2 Field 3
+1 value 1 value2 value3
+4 value 4 value5 value6
+7 value 7 value8 value9
+""", # noqa: W291
+ id="PLAIN_COLUMNS",
+ ),
+ pytest.param(
+ TableStyle.RANDOM,
+ """
+'^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^'
+% 1 value 1 value2 value3%
+% 4 value 4 value5 value6%
+% 7 value 7 value8 value9%
+'^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^'
+""",
+ id="RANDOM",
+ ),
+ pytest.param(
+ TableStyle.DOUBLE_BORDER,
+ """
+╔═══╦═════════╦═════════╦═════════╗
+║ ║ Field 1 ║ Field 2 ║ Field 3 ║
+╠═══╬═════════╬═════════╬═════════╣
+║ 1 ║ value 1 ║ value2 ║ value3 ║
+║ 4 ║ value 4 ║ value5 ║ value6 ║
+║ 7 ║ value 7 ║ value8 ║ value9 ║
+╚═══╩═════════╩═════════╩═════════╝
+""",
+ ),
+ pytest.param(
+ TableStyle.SINGLE_BORDER,
+ """
+┌───┬─────────┬─────────┬─────────┐
+│ │ Field 1 │ Field 2 │ Field 3 │
+├───┼─────────┼─────────┼─────────┤
+│ 1 │ value 1 │ value2 │ value3 │
+│ 4 │ value 4 │ value5 │ value6 │
+│ 7 │ value 7 │ value8 │ value9 │
+└───┴─────────┴─────────┴─────────┘
+""",
+ ),
+ ],
+ )
+ def test_style(
+ self, helper_table: PrettyTable, style: TableStyle, expected: str
+ ) -> None:
+ random.seed(1234)
+ helper_table.set_style(style)
+ assert helper_table.get_string().strip() == expected.strip()
+
+ def test_style_invalid(self, helper_table: PrettyTable) -> None:
+ # This is an hrule style, not a table style
+ with pytest.raises(ValueError):
+ helper_table.set_style(HRuleStyle.ALL) # type: ignore[arg-type]
+
+ @pytest.mark.parametrize(
+ "original_style,style, expected",
+ [
+ pytest.param(
+ TableStyle.MARKDOWN,
+ TableStyle.DEFAULT,
+ """
++---+---------+---------+---------+
+| | Field 1 | Field 2 | Field 3 |
++---+---------+---------+---------+
+| 1 | value 1 | value2 | value3 |
+| 4 | value 4 | value5 | value6 |
+| 7 | value 7 | value8 | value9 |
++---+---------+---------+---------+
+""",
+ id="DEFAULT",
+ ),
+ pytest.param(
+ TableStyle.MSWORD_FRIENDLY,
+ TableStyle.MARKDOWN,
+ """
+| | Field 1 | Field 2 | Field 3 |
+| :-: | :-----: | :-----: | :-----: |
+| 1 | value 1 | value2 | value3 |
+| 4 | value 4 | value5 | value6 |
+| 7 | value 7 | value8 | value9 |
+""",
+ id="MARKDOWN",
+ ),
+ pytest.param(
+ TableStyle.MARKDOWN,
+ TableStyle.MSWORD_FRIENDLY,
+ """
+| | Field 1 | Field 2 | Field 3 |
+| 1 | value 1 | value2 | value3 |
+| 4 | value 4 | value5 | value6 |
+| 7 | value 7 | value8 | value9 |
+""",
+ id="MSWORD_FRIENDLY",
+ ),
+ pytest.param(
+ TableStyle.MARKDOWN,
+ TableStyle.ORGMODE,
+ """
+|---+---------+---------+---------|
+| | Field 1 | Field 2 | Field 3 |
+|---+---------+---------+---------|
+| 1 | value 1 | value2 | value3 |
+| 4 | value 4 | value5 | value6 |
+| 7 | value 7 | value8 | value9 |
+|---+---------+---------+---------|
+""",
+ id="ORGMODE",
+ ),
+ pytest.param(
+ TableStyle.MARKDOWN,
+ TableStyle.PLAIN_COLUMNS,
+ """
+ Field 1 Field 2 Field 3
+1 value 1 value2 value3
+4 value 4 value5 value6
+7 value 7 value8 value9
+""", # noqa: W291
+ id="PLAIN_COLUMNS",
+ ),
+ pytest.param(
+ TableStyle.MARKDOWN,
+ TableStyle.RANDOM,
+ """
+'^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^'
+% 1 value 1 value2 value3%
+% 4 value 4 value5 value6%
+% 7 value 7 value8 value9%
+'^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^'
+""",
+ id="RANDOM",
+ ),
+ pytest.param(
+ TableStyle.MARKDOWN,
+ TableStyle.DOUBLE_BORDER,
+ """
+╔═══╦═════════╦═════════╦═════════╗
+║ ║ Field 1 ║ Field 2 ║ Field 3 ║
+╠═══╬═════════╬═════════╬═════════╣
+║ 1 ║ value 1 ║ value2 ║ value3 ║
+║ 4 ║ value 4 ║ value5 ║ value6 ║
+║ 7 ║ value 7 ║ value8 ║ value9 ║
+╚═══╩═════════╩═════════╩═════════╝
+""",
+ id="DOUBLE_BORDER",
+ ),
+ pytest.param(
+ TableStyle.MARKDOWN,
+ TableStyle.SINGLE_BORDER,
+ """
+┌───┬─────────┬─────────┬─────────┐
+│ │ Field 1 │ Field 2 │ Field 3 │
+├───┼─────────┼─────────┼─────────┤
+│ 1 │ value 1 │ value2 │ value3 │
+│ 4 │ value 4 │ value5 │ value6 │
+│ 7 │ value 7 │ value8 │ value9 │
+└───┴─────────┴─────────┴─────────┘
+""",
+ id="SINGLE_BORDER",
+ ),
+ ],
+ )
+ def test_style_reset(
+ self,
+ helper_table: PrettyTable,
+ original_style: TableStyle,
+ style: TableStyle,
+ expected: str,
+ ) -> None:
+ """
+ Testing to ensure that default styling is reset between changes
+ of styles on a PrettyTable
+
+ Args:
+ style (str): Style to be used (Default, markdown, etc)
+ expected (str): The expected format of style as a string representation
+ """
+ random.seed(1234)
+ helper_table.set_style(original_style)
+ helper_table.set_style(style)
+ assert helper_table.get_string().strip() == expected.strip()
+
+ @pytest.mark.parametrize(
+ "style, expected",
+ [
+ pytest.param(
+ TableStyle.MARKDOWN,
+ """
+| l | c | r | Align left | Align centre | Align right |
+| :-| :-: |-: | :----------| :----------: |-----------: |
+| 1 | 2 | 3 | value 1 | value2 | value3 |
+| 4 | 5 | 6 | value 4 | value5 | value6 |
+| 7 | 8 | 9 | value 7 | value8 | value9 |
+""",
+ id="MARKDOWN",
+ ),
+ ],
+ )
+ def test_style_align(self, style: TableStyle, expected: str) -> None:
+ table = PrettyTable(
+ ["l", "c", "r", "Align left", "Align centre", "Align right"]
+ )
+ v = 1
+ for row in range(3):
+ # Some have spaces, some not, to help test padding columns of
+ # different widths
+ table.add_row(
+ [v, v + 1, v + 2, f"value {v}", f"value{v + 1}", f"value{v + 2}"]
+ )
+ v += 3
+
+ table.set_style(style)
+ table.align["l"] = table.align["Align left"] = "l"
+ table.align["c"] = table.align["Align centre"] = "c"
+ table.align["r"] = table.align["Align right"] = "r"
+ assert table.get_string().strip() == expected.strip()
+
+
+def japanese_pretty_table() -> PrettyTable:
+ table = PrettyTable(["Kanji", "Hiragana", "English"])
+ table.add_row(["神戸", "こうべ", "Kobe"])
+ table.add_row(["京都", "きょうと", "Kyoto"])
+ table.add_row(["長崎", "ながさき", "Nagasaki"])
+ table.add_row(["名古屋", "なごや", "Nagoya"])
+ table.add_row(["大阪", "おおさか", "Osaka"])
+ table.add_row(["札幌", "さっぽろ", "Sapporo"])
+ table.add_row(["東京", "とうきょう", "Tokyo"])
+ table.add_row(["横浜", "よこはま", "Yokohama"])
+ return table
+
+
+def emoji_pretty_table() -> PrettyTable:
+ thunder1 = [
+ '\033[38;5;226m _`/""\033[38;5;250m.-. \033[0m',
+ "\033[38;5;226m ,\\_\033[38;5;250m( ). \033[0m",
+ "\033[38;5;226m /\033[38;5;250m(___(__) \033[0m",
+ "\033[38;5;228;5m ⚡\033[38;5;111;25mʻ ʻ\033[38;5;228;5m"
+ "⚡\033[38;5;111;25mʻ ʻ \033[0m",
+ "\033[38;5;111m ʻ ʻ ʻ ʻ \033[0m",
+ ]
+ thunder2 = [
+ "\033[38;5;240;1m .-. \033[0m",
+ "\033[38;5;240;1m ( ). \033[0m",
+ "\033[38;5;240;1m (___(__) \033[0m",
+ "\033[38;5;21;1m ‚ʻ\033[38;5;228;5m⚡\033[38;5;21;25mʻ‚\033[38;5;228;5m"
+ "⚡\033[38;5;21;25m‚ʻ \033[0m",
+ "\033[38;5;21;1m ‚ʻ‚ʻ\033[38;5;228;5m⚡\033[38;5;21;25mʻ‚ʻ \033[0m",
+ ]
+ table = PrettyTable(["Thunderbolt", "Lightning"])
+ for i, t1 in enumerate(thunder1):
+ table.add_row([t1, thunder2[i]])
+ return table
+
+
+class TestMultiPattern:
+ @pytest.mark.parametrize(
+ ["pt", "expected_output", "test_type"],
+ [
+ (
+ lf("city_data"),
+ """
++-----------+------+------------+-----------------+
+| City name | Area | Population | Annual Rainfall |
++-----------+------+------------+-----------------+
+| Adelaide | 1295 | 1158259 | 600.5 |
+| Brisbane | 5905 | 1857594 | 1146.4 |
+| Darwin | 112 | 120900 | 1714.7 |
+| Hobart | 1357 | 205556 | 619.5 |
+| Sydney | 2058 | 4336374 | 1214.8 |
+| Melbourne | 1566 | 3806092 | 646.9 |
+| Perth | 5386 | 1554769 | 869.4 |
++-----------+------+------------+-----------------+
+""",
+ "English Table",
+ ),
+ (
+ lf("japanese_pretty_table"),
+ """
++--------+------------+----------+
+| Kanji | Hiragana | English |
++--------+------------+----------+
+| 神戸 | こうべ | Kobe |
+| 京都 | きょうと | Kyoto |
+| 長崎 | ながさき | Nagasaki |
+| 名古屋 | なごや | Nagoya |
+| 大阪 | おおさか | Osaka |
+| 札幌 | さっぽろ | Sapporo |
+| 東京 | とうきょう | Tokyo |
+| 横浜 | よこはま | Yokohama |
++--------+------------+----------+
+
+""",
+ "Japanese table",
+ ),
+ (
+ lf("emoji_pretty_table"),
+ """
++-----------------+-----------------+
+| Thunderbolt | Lightning |
++-----------------+-----------------+
+| \x1b[38;5;226m _`/""\x1b[38;5;250m.-. \x1b[0m | \x1b[38;5;240;1m .-. \x1b[0m |
+| \x1b[38;5;226m ,\\_\x1b[38;5;250m( ). \x1b[0m | \x1b[38;5;240;1m ( ). \x1b[0m |
+| \x1b[38;5;226m /\x1b[38;5;250m(___(__) \x1b[0m | \x1b[38;5;240;1m (___(__) \x1b[0m |
+| \x1b[38;5;228;5m ⚡\x1b[38;5;111;25mʻ ʻ\x1b[38;5;228;5m⚡\x1b[38;5;111;25mʻ ʻ \x1b[0m | \x1b[38;5;21;1m ‚ʻ\x1b[38;5;228;5m⚡\x1b[38;5;21;25mʻ‚\x1b[38;5;228;5m⚡\x1b[38;5;21;25m‚ʻ \x1b[0m |
+| \x1b[38;5;111m ʻ ʻ ʻ ʻ \x1b[0m | \x1b[38;5;21;1m ‚ʻ‚ʻ\x1b[38;5;228;5m⚡\x1b[38;5;21;25mʻ‚ʻ \x1b[0m |
++-----------------+-----------------+
+ """, # noqa: E501
+ "Emoji table",
+ ),
+ ],
+ )
+ def test_multi_pattern_outputs(
+ self, pt: PrettyTable, expected_output: str, test_type: str
+ ) -> None:
+ assert (
+ pt.get_string().strip() == expected_output.strip()
+ ), f"Error output for test output of type {test_type}"
+
+
+def test_colored_table() -> None:
+ table = PrettyTable(field_names=["Namespace", "Count"])
+ table.title = "\x1b[34mHere be Table caption\x1b[39m"
+ assert (
+ table.get_string()
+ == """+-----------------------+
+| \x1b[34mHere be Table caption\x1b[39m |
++-------------+---------+
+| Namespace | Count |
++-------------+---------+
++-------------+---------+"""
+ )
+
+
+def test_link_and_color() -> None:
+ table = PrettyTable(["Link", "Count"])
+ # Add link
+ text = "Click here"
+ table.add_row([f"\033]8;;https://example.com\033\\{text}\033]8;;\033\\", "1"])
+ table.add_row(["No link", "2"])
+ # Add link with colour
+ text = "Click \x1b[34mhere\x1b[39m"
+ table.add_row([f"\033]8;;https://example.com\033\\{text}\033]8;;\033\\", "3"])
+
+ assert (
+ table.get_string()
+ == """\
++------------+-------+
+| Link | Count |
++------------+-------+
+| \033]8;;https://example.com\033\\Click here\033]8;;\033\\ | 1 |
+| No link | 2 |
+| \033]8;;https://example.com\033\\Click \x1b[34mhere\x1b[39m\033]8;;\033\\ | 3 |
++------------+-------+"""
+ )
+
+
+ ["test_input", "expected"],
+ [
+ ("a", 1),
+ ("abc", 3),
+ ("abc def", 7),
+ ("\x1b[34mblue\x1b[39m", 4),
+ ("\033]8;;https://example.com\033\\link\033]8;;\033\\", 4),
+ # colour inside link
+ ("\033]8;;https://example.com\033\\\x1b[34mblue link\x1b[39m\033]8;;\033\\", 9),
+ # link inside colour
+ ("\x1b[34m\033]8;;https://example.com\033\\blue link\033]8;;\033\\\x1b[39m", 9),
+ ],
+)
+def test__str_block_width(test_input: str, expected: int) -> None:
+ assert _str_block_width(test_input) == expected
diff --git a/contrib/python/prettytable/py3/ya.make b/contrib/python/prettytable/py3/ya.make
index 0ed2577e62c..487fd84e582 100644
--- a/contrib/python/prettytable/py3/ya.make
+++ b/contrib/python/prettytable/py3/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(3.14.0)
+VERSION(3.17.0)
LICENSE(BSD-3-Clause)