table2ascii 1.1.0__tar.gz → 1.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. {table2ascii-1.1.0/table2ascii.egg-info → table2ascii-1.1.2}/PKG-INFO +1 -1
  2. {table2ascii-1.1.0 → table2ascii-1.1.2}/pyproject.toml +3 -3
  3. table2ascii-1.1.2/setup.py +51 -0
  4. table2ascii-1.1.2/table2ascii/__init__.py +56 -0
  5. {table2ascii-1.1.0 → table2ascii-1.1.2}/table2ascii/annotations.py +2 -1
  6. {table2ascii-1.1.0 → table2ascii-1.1.2}/table2ascii/exceptions.py +23 -18
  7. table2ascii-1.1.2/table2ascii/py.typed +1 -0
  8. {table2ascii-1.1.0 → table2ascii-1.1.2}/table2ascii/table_style.py +1 -1
  9. {table2ascii-1.1.0 → table2ascii-1.1.2}/table2ascii/table_to_ascii.py +10 -6
  10. {table2ascii-1.1.0 → table2ascii-1.1.2/table2ascii.egg-info}/PKG-INFO +1 -1
  11. {table2ascii-1.1.0 → table2ascii-1.1.2}/table2ascii.egg-info/SOURCES.txt +9 -1
  12. {table2ascii-1.1.0 → table2ascii-1.1.2}/table2ascii.egg-info/requires.txt +2 -2
  13. table2ascii-1.1.2/tests/test_alignments.py +192 -0
  14. table2ascii-1.1.2/tests/test_cell_padding.py +83 -0
  15. table2ascii-1.1.2/tests/test_column_widths.py +110 -0
  16. table2ascii-1.1.2/tests/test_convert.py +307 -0
  17. table2ascii-1.1.2/tests/test_heading_cols.py +113 -0
  18. table2ascii-1.1.2/tests/test_merge.py +175 -0
  19. table2ascii-1.1.2/tests/test_styles.py +113 -0
  20. table2ascii-1.1.0/setup.py +0 -4
  21. table2ascii-1.1.0/table2ascii/__init__.py +0 -26
  22. {table2ascii-1.1.0 → table2ascii-1.1.2}/LICENSE +0 -0
  23. {table2ascii-1.1.0 → table2ascii-1.1.2}/README.md +0 -0
  24. {table2ascii-1.1.0 → table2ascii-1.1.2}/setup.cfg +0 -0
  25. {table2ascii-1.1.0 → table2ascii-1.1.2}/table2ascii/alignment.py +0 -0
  26. {table2ascii-1.1.0 → table2ascii-1.1.2}/table2ascii/merge.py +0 -0
  27. {table2ascii-1.1.0 → table2ascii-1.1.2}/table2ascii/options.py +0 -0
  28. {table2ascii-1.1.0 → table2ascii-1.1.2}/table2ascii/preset_style.py +0 -0
  29. {table2ascii-1.1.0 → table2ascii-1.1.2}/table2ascii.egg-info/dependency_links.txt +0 -0
  30. {table2ascii-1.1.0 → table2ascii-1.1.2}/table2ascii.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: table2ascii
3
- Version: 1.1.0
3
+ Version: 1.1.2
4
4
  Summary: Convert 2D Python lists into Unicode/ASCII tables
5
5
  Author-email: Jonah Lawrence <jonah@freshidea.com>
6
6
  License: MIT License
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "table2ascii"
8
- version = "1.1.0"
8
+ version = "1.1.2"
9
9
  authors = [{name = "Jonah Lawrence", email = "jonah@freshidea.com"}]
10
10
  description = "Convert 2D Python lists into Unicode/ASCII tables"
11
11
  readme = "README.md"
@@ -54,8 +54,8 @@ docs = [
54
54
  "sphinx-book-theme==0.3.3",
55
55
  ]
56
56
  dev = [
57
- "mypy>=0.982,<1",
58
- "pre-commit>=2.0.0,<3",
57
+ "mypy>=0.982,<2",
58
+ "pre-commit>=2.0.0,<4",
59
59
  "pyright>=1.0.0,<2",
60
60
  "pytest>=6.0.0,<8",
61
61
  "slotscheck>=0.1.0,<1",
@@ -0,0 +1,51 @@
1
+ # /usr/bin/env python
2
+ import re
3
+
4
+ from setuptools import setup
5
+
6
+
7
+ def get_name():
8
+ name = ""
9
+ with open("pyproject.toml") as f:
10
+ name = re.search(r'^name = ["\']([^"\']*)["\']', f.read(), re.M)
11
+ if not name:
12
+ raise RuntimeError("name is not set")
13
+ return name.group(1)
14
+
15
+
16
+ def get_version():
17
+ version = ""
18
+ with open("pyproject.toml") as f:
19
+ version = re.search(r'^version = ["\']([^"\']*)["\']', f.read(), re.M)
20
+ if not version:
21
+ raise RuntimeError("version is not set")
22
+ return version.group(1)
23
+
24
+
25
+ def get_dependencies():
26
+ with open("pyproject.toml") as f:
27
+ dependency_match = re.search(r"^dependencies = \[([\s\S]*?)\]", f.read(), re.M)
28
+ if not dependency_match or not dependency_match.group(1):
29
+ return []
30
+ return [
31
+ dependency.strip().strip(",").strip('"')
32
+ for dependency in dependency_match.group(1).split("\n")
33
+ if dependency
34
+ ]
35
+
36
+
37
+ try:
38
+ # check if pyproject.toml can be used to install dependencies and set the version
39
+ setup(
40
+ packages=[get_name()],
41
+ package_data={get_name(): ["py.typed"]},
42
+ )
43
+ except Exception:
44
+ # fallback for old versions of pip/setuptools
45
+ setup(
46
+ name=get_name(),
47
+ packages=[get_name()],
48
+ package_data={get_name(): ["py.typed"]},
49
+ version=get_version(),
50
+ install_requires=get_dependencies(),
51
+ )
@@ -0,0 +1,56 @@
1
+ """
2
+ table2ascii - Library for converting 2D Python lists to fancy ASCII/Unicode tables
3
+ """
4
+ import sys
5
+ from typing import TYPE_CHECKING
6
+
7
+ from .alignment import Alignment
8
+ from .annotations import SupportsStr
9
+ from .exceptions import (
10
+ AlignmentCountMismatchError,
11
+ BodyColumnCountMismatchError,
12
+ ColumnCountMismatchError,
13
+ ColumnWidthsCountMismatchError,
14
+ ColumnWidthTooSmallError,
15
+ FooterColumnCountMismatchError,
16
+ InvalidAlignmentError,
17
+ InvalidCellPaddingError,
18
+ InvalidColumnWidthError,
19
+ Table2AsciiError,
20
+ TableOptionError,
21
+ TableStyleTooLongError,
22
+ TableStyleTooShortWarning,
23
+ )
24
+ from .merge import Merge
25
+ from .preset_style import PresetStyle
26
+ from .table_style import TableStyle
27
+ from .table_to_ascii import table2ascii
28
+
29
+ if TYPE_CHECKING or sys.version_info >= (3, 8):
30
+ from importlib import metadata
31
+ else:
32
+ import importlib_metadata as metadata
33
+
34
+ __version__ = metadata.version(__name__)
35
+
36
+ __all__ = [
37
+ "Alignment",
38
+ "Merge",
39
+ "PresetStyle",
40
+ "TableStyle",
41
+ "table2ascii",
42
+ "AlignmentCountMismatchError",
43
+ "BodyColumnCountMismatchError",
44
+ "ColumnCountMismatchError",
45
+ "ColumnWidthsCountMismatchError",
46
+ "ColumnWidthTooSmallError",
47
+ "FooterColumnCountMismatchError",
48
+ "InvalidAlignmentError",
49
+ "InvalidCellPaddingError",
50
+ "InvalidColumnWidthError",
51
+ "Table2AsciiError",
52
+ "TableOptionError",
53
+ "TableStyleTooLongError",
54
+ "TableStyleTooShortWarning",
55
+ "SupportsStr",
56
+ ]
@@ -10,8 +10,9 @@ else:
10
10
 
11
11
  @runtime_checkable
12
12
  class SupportsStr(Protocol):
13
- """An ABC with one abstract method __str__."""
13
+ """An abstract base class (ABC) with one abstract method :meth:`__str__`"""
14
14
 
15
15
  @abstractmethod
16
16
  def __str__(self) -> str:
17
+ """Return a string representation of the object"""
17
18
  pass
@@ -40,8 +40,9 @@ class FooterColumnCountMismatchError(ColumnCountMismatchError):
40
40
  This class is a subclass of :class:`ColumnCountMismatchError`.
41
41
 
42
42
  Attributes:
43
- footer (Sequence[SupportsStr]): The footer that caused the error
44
- expected_columns (int): The number of columns that were expected
43
+ footer (:class:`Sequence <collections.abc.Sequence>`\ [:class:`SupportsStr`]):
44
+ The footer that caused the error
45
+ expected_columns (:class:`int`): The number of columns that were expected
45
46
  """
46
47
 
47
48
  def __init__(self, footer: Sequence[SupportsStr], expected_columns: int):
@@ -63,9 +64,11 @@ class BodyColumnCountMismatchError(ColumnCountMismatchError):
63
64
  This class is a subclass of :class:`ColumnCountMismatchError`.
64
65
 
65
66
  Attributes:
66
- body (Sequence[Sequence[SupportsStr]]): The body that caused the error
67
- expected_columns (int): The number of columns that were expected
68
- first_invalid_row (Sequence[SupportsStr]): The first row with an invalid column count
67
+ body (:class:`Sequence <collections.abc.Sequence>`\ [\ :class:`Sequence <collections.abc.Sequence>`\ [:class:`SupportsStr`]]):
68
+ The body that caused the error
69
+ expected_columns (:class:`int`): The number of columns that were expected
70
+ first_invalid_row (:class:`Sequence <collections.abc.Sequence>`\ [:class:`SupportsStr`]):
71
+ The first row with an invalid column count
69
72
  """
70
73
 
71
74
  def __init__(self, body: Sequence[Sequence[SupportsStr]], expected_columns: int):
@@ -90,8 +93,9 @@ class AlignmentCountMismatchError(ColumnCountMismatchError):
90
93
  This class is a subclass of :class:`ColumnCountMismatchError`.
91
94
 
92
95
  Attributes:
93
- alignments (Sequence[Alignment]): The alignments that caused the error
94
- expected_columns (int): The number of columns that were expected
96
+ alignments (:class:`Sequence <collections.abc.Sequence>`\ [:class:`Alignment`]):
97
+ The alignments that caused the error
98
+ expected_columns (:class:`int`): The number of columns that were expected
95
99
  """
96
100
 
97
101
  def __init__(self, alignments: Sequence[Alignment], expected_columns: int):
@@ -113,8 +117,9 @@ class ColumnWidthsCountMismatchError(ColumnCountMismatchError):
113
117
  This class is a subclass of :class:`ColumnCountMismatchError`.
114
118
 
115
119
  Attributes:
116
- column_widths (Sequence[Optional[int]]): The column widths that caused the error
117
- expected_columns (int): The number of columns that were expected
120
+ column_widths (:class:`Sequence <collections.abc.Sequence>`\ [:data:`Optional <typing.Optional>`\ [:class:`int`]]):
121
+ The column widths that caused the error
122
+ expected_columns (:class:`int`): The number of columns that were expected
118
123
  """
119
124
 
120
125
  def __init__(self, column_widths: Sequence[int | None], expected_columns: int):
@@ -148,7 +153,7 @@ class InvalidCellPaddingError(TableOptionError):
148
153
  This class is a subclass of :class:`TableOptionError`.
149
154
 
150
155
  Attributes:
151
- padding (int): The padding that caused the error
156
+ padding (:class:`int`): The padding that caused the error
152
157
  """
153
158
 
154
159
  def __init__(self, padding: int):
@@ -169,9 +174,9 @@ class ColumnWidthTooSmallError(TableOptionError):
169
174
  This class is a subclass of :class:`TableOptionError`.
170
175
 
171
176
  Attributes:
172
- column_index (int): The index of the column that caused the error
173
- column_width (int): The column width that caused the error
174
- min_width (int): The minimum width that is allowed
177
+ column_index (:class:`int`): The index of the column that caused the error
178
+ column_width (:class:`int`): The column width that caused the error
179
+ min_width (:class:`int`): The minimum width that is allowed
175
180
  """
176
181
 
177
182
  def __init__(self, column_index: int, column_width: int, min_width: int | None = None):
@@ -208,7 +213,7 @@ class InvalidAlignmentError(TableOptionError):
208
213
  This class is a subclass of :class:`TableOptionError`.
209
214
 
210
215
  Attributes:
211
- alignment (Any): The alignment value that caused the error
216
+ alignment (:data:`Any <typing.Any>`): The alignment value that caused the error
212
217
  """
213
218
 
214
219
  def __init__(self, alignment: Any):
@@ -230,8 +235,8 @@ class TableStyleTooLongError(Table2AsciiError, ValueError):
230
235
  This class is a subclass of :class:`Table2AsciiError` and :class:`ValueError`.
231
236
 
232
237
  Attributes:
233
- string (str): The string that caused the error
234
- max_characters (int): The maximum number of characters that are allowed
238
+ string (:class:`str`): The string that caused the error
239
+ max_characters (:class:`int`): The maximum number of characters that are allowed
235
240
  """
236
241
 
237
242
  def __init__(self, string: str, max_characters: int):
@@ -256,8 +261,8 @@ class TableStyleTooShortWarning(UserWarning):
256
261
  It can be silenced using :func:`warnings.filterwarnings`.
257
262
 
258
263
  Attributes:
259
- string (str): The string that caused the warning
260
- max_characters (int): The number of characters that :class:`TableStyle` accepts
264
+ string (:class:`str`): The string that caused the warning
265
+ max_characters (:class:`int`): The number of characters that :class:`TableStyle` accepts
261
266
  """
262
267
 
263
268
  def __init__(self, string: str, max_characters: int):
@@ -0,0 +1 @@
1
+ # Marker file for PEP 561. The table2ascii package uses inline types.
@@ -145,7 +145,7 @@ class TableStyle:
145
145
 
146
146
  Example::
147
147
 
148
- TableStyle().set(top_left_corner="", top_and_bottom_edge="")
148
+ TableStyle.from_string("~" * 30).set(left_and_right_edge="", col_sep="")
149
149
  """
150
150
  for key, value in kwargs.items():
151
151
  setattr(self, key, value)
@@ -677,23 +677,27 @@ def table2ascii(
677
677
  """Convert a 2D Python table to ASCII text
678
678
 
679
679
  Args:
680
- header: List of column values in the table's header row. All values should be :class:`str`
680
+ header (:data:`Optional <typing.Optional>`\ [:class:`Sequence <collections.abc.Sequence>`\ [:class:`SupportsStr`]]):
681
+ List of column values in the table's header row. All values should be :class:`str`
681
682
  or support :class:`str` conversion. If not specified, the table will not have a header row.
682
- body: 2-dimensional list of values in the table's body. All values should be :class:`str`
683
+ body (:data:`Optional <typing.Optional>`\ [:class:`Sequence <collections.abc.Sequence>`\ [:class:`Sequence <collections.abc.Sequence>`\ [:class:`SupportsStr`]]]):
684
+ 2-dimensional list of values in the table's body. All values should be :class:`str`
683
685
  or support :class:`str` conversion. If not specified, the table will not have a body.
684
- footer: List of column values in the table's footer row. All values should be :class:`str`
686
+ footer (:data:`Optional <typing.Optional>`\ [:class:`Sequence <collections.abc.Sequence>`\ [:class:`SupportsStr`]]):
687
+ List of column values in the table's footer row. All values should be :class:`str`
685
688
  or support :class:`str` conversion. If not specified, the table will not have a footer row.
686
689
  first_col_heading: Whether to add a header column separator after the first column.
687
690
  Defaults to :py:obj:`False`.
688
691
  last_col_heading: Whether to add a header column separator before the last column.
689
692
  Defaults to :py:obj:`False`.
690
- column_widths: List of widths in characters for each column. Any value of :py:obj:`None`
693
+ column_widths (:data:`Optional <typing.Optional>`\ [:class:`Sequence <collections.abc.Sequence>`\ [:data:`Optional <typing.Optional>`\ [:class:`int`]]]):
694
+ List of widths in characters for each column. Any value of :py:obj:`None`
691
695
  indicates that the column width should be determined automatically. If :py:obj:`None`
692
696
  is passed instead of a :class:`~collections.abc.Sequence`, all columns will be automatically
693
697
  sized. Defaults to :py:obj:`None`.
694
698
  alignments: List of alignments for each column
695
- (ex. ``[Alignment.LEFT, Alignment.CENTER, Alignment.RIGHT, Alignment.DECIMAL]``)
696
- or a single alignment to apply to all columns (ex. ``Alignment.LEFT``).
699
+ (ex. [:attr:`Alignment.LEFT`, :attr:`Alignment.CENTER`, :attr:`Alignment.RIGHT`, :attr:`Alignment.DECIMAL`])
700
+ or a single alignment to apply to all columns (ex. :attr:`Alignment.LEFT`).
697
701
  If not specified or set to :py:obj:`None`, all columns will be center-aligned.
698
702
  Defaults to :py:obj:`None`.
699
703
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: table2ascii
3
- Version: 1.1.0
3
+ Version: 1.1.2
4
4
  Summary: Convert 2D Python lists into Unicode/ASCII tables
5
5
  Author-email: Jonah Lawrence <jonah@freshidea.com>
6
6
  License: MIT License
@@ -9,10 +9,18 @@ table2ascii/exceptions.py
9
9
  table2ascii/merge.py
10
10
  table2ascii/options.py
11
11
  table2ascii/preset_style.py
12
+ table2ascii/py.typed
12
13
  table2ascii/table_style.py
13
14
  table2ascii/table_to_ascii.py
14
15
  table2ascii.egg-info/PKG-INFO
15
16
  table2ascii.egg-info/SOURCES.txt
16
17
  table2ascii.egg-info/dependency_links.txt
17
18
  table2ascii.egg-info/requires.txt
18
- table2ascii.egg-info/top_level.txt
19
+ table2ascii.egg-info/top_level.txt
20
+ tests/test_alignments.py
21
+ tests/test_cell_padding.py
22
+ tests/test_column_widths.py
23
+ tests/test_convert.py
24
+ tests/test_heading_cols.py
25
+ tests/test_merge.py
26
+ tests/test_styles.py
@@ -5,8 +5,8 @@ typing-extensions>=3.7.4
5
5
  importlib-metadata<5,>=1
6
6
 
7
7
  [dev]
8
- mypy<1,>=0.982
9
- pre-commit<3,>=2.0.0
8
+ mypy<2,>=0.982
9
+ pre-commit<4,>=2.0.0
10
10
  pyright<2,>=1.0.0
11
11
  pytest<8,>=6.0.0
12
12
  slotscheck<1,>=0.1.0
@@ -0,0 +1,192 @@
1
+ import pytest
2
+
3
+ from table2ascii import Alignment, PresetStyle, table2ascii as t2a
4
+ from table2ascii.exceptions import AlignmentCountMismatchError, InvalidAlignmentError
5
+
6
+
7
+ def test_first_left_four_right():
8
+ text = t2a(
9
+ header=["#", "G", "H", "R", "S"],
10
+ body=[["1", "30", "40", "35", "30"], ["2", "30", "40", "35", "30"]],
11
+ footer=["SUM", "130", "140", "135", "130"],
12
+ first_col_heading=True,
13
+ alignments=[Alignment.LEFT] + [Alignment.RIGHT] * 4,
14
+ )
15
+ expected = (
16
+ "╔═════╦═══════════════════════╗\n"
17
+ "║ # ║ G H R S ║\n"
18
+ "╟─────╫───────────────────────╢\n"
19
+ "║ 1 ║ 30 40 35 30 ║\n"
20
+ "║ 2 ║ 30 40 35 30 ║\n"
21
+ "╟─────╫───────────────────────╢\n"
22
+ "║ SUM ║ 130 140 135 130 ║\n"
23
+ "╚═════╩═══════════════════════╝"
24
+ )
25
+ assert text == expected
26
+
27
+
28
+ def test_wrong_number_of_alignments():
29
+ with pytest.raises(AlignmentCountMismatchError):
30
+ t2a(
31
+ header=["#", "G", "H", "R", "S"],
32
+ body=[["1", "30", "40", "35", "30"], ["2", "30", "40", "35", "30"]],
33
+ footer=["SUM", "130", "140", "135", "130"],
34
+ first_col_heading=True,
35
+ alignments=[Alignment.LEFT, Alignment.CENTER, Alignment.RIGHT],
36
+ )
37
+
38
+
39
+ def test_invalid_alignments():
40
+ with pytest.raises(InvalidAlignmentError):
41
+ t2a(
42
+ header=["#", "G", "H", "R", "S"],
43
+ body=[["1", "30", "40", "35", "30"], ["2", "30", "40", "35", "30"]],
44
+ footer=["SUM", "130", "140", "135", "130"],
45
+ first_col_heading=True,
46
+ alignments=[9999, -1, Alignment.RIGHT, Alignment.CENTER, Alignment.RIGHT], # type: ignore
47
+ )
48
+
49
+
50
+ def test_alignment_numeric_data():
51
+ text = t2a(
52
+ header=[1, "G", "H", "R", "S"],
53
+ body=[[1, 2, 3, 4, 5]],
54
+ footer=["A", "B", 1, 2, 3],
55
+ column_widths=[4, 5, 5, 4, 5],
56
+ alignments=[Alignment.RIGHT] + [Alignment.CENTER] * 4,
57
+ first_col_heading=True,
58
+ )
59
+ expected = (
60
+ "╔════╦══════════════════════╗\n"
61
+ "║ 1 ║ G H R S ║\n"
62
+ "╟────╫──────────────────────╢\n"
63
+ "║ 1 ║ 2 3 4 5 ║\n"
64
+ "╟────╫──────────────────────╢\n"
65
+ "║ A ║ B 1 2 3 ║\n"
66
+ "╚════╩══════════════════════╝"
67
+ )
68
+ assert text == expected
69
+
70
+
71
+ def test_alignments_multiline_data():
72
+ text = t2a(
73
+ header=["Multiline\nHeader\nCell", "G", "Two\nLines", "R", "S"],
74
+ body=[[1, "Alpha\nBeta\nGamma", 3, 4, "One\nTwo"]],
75
+ footer=["A", "Footer\nBreak", 1, "Second\nCell\nBroken", 3],
76
+ alignments=[
77
+ Alignment.LEFT,
78
+ Alignment.RIGHT,
79
+ Alignment.CENTER,
80
+ Alignment.LEFT,
81
+ Alignment.CENTER,
82
+ ],
83
+ )
84
+ expected = (
85
+ "╔═══════════════════════════════════════════╗\n"
86
+ "║ Multiline G Two R S ║\n"
87
+ "║ Header Lines ║\n"
88
+ "║ Cell ║\n"
89
+ "╟───────────────────────────────────────────╢\n"
90
+ "║ 1 Alpha 3 4 One ║\n"
91
+ "║ Beta Two ║\n"
92
+ "║ Gamma ║\n"
93
+ "╟───────────────────────────────────────────╢\n"
94
+ "║ A Footer 1 Second 3 ║\n"
95
+ "║ Break Cell ║\n"
96
+ "║ Broken ║\n"
97
+ "╚═══════════════════════════════════════════╝"
98
+ )
99
+ assert text == expected
100
+
101
+
102
+ def test_decimal_alignment():
103
+ text = t2a(
104
+ header=["1.1.1", "G", "Long Header", "H", "R", "3.8"],
105
+ body=[[100.00001, 2, 3.14, 33, "AB", "1.5"], [10.0001, 22.0, 2.718, 3, "CD", "3.03"]],
106
+ footer=[10000.01, "123", 10.0, 0, "E", "A"],
107
+ alignments=[Alignment.DECIMAL] * 6,
108
+ first_col_heading=True,
109
+ style=PresetStyle.double_thin_box,
110
+ )
111
+ expected = (
112
+ "╔═════════════╦═══════╤═════════════╤════╤════╤═════════╗\n"
113
+ "║ 1.1.1 ║ G │ Long Header │ H │ R │ 3.8 ║\n"
114
+ "╠═════════════╬═══════╪═════════════╪════╪════╪═════════╣\n"
115
+ "║ 100.00001 ║ 2 │ 3.14 │ 33 │ AB │ 1.5 ║\n"
116
+ "╟─────────────╫───────┼─────────────┼────┼────┼─────────╢\n"
117
+ "║ 10.0001 ║ 22.0 │ 2.718 │ 3 │ CD │ 3.03 ║\n"
118
+ "╠═════════════╬═══════╪═════════════╪════╪════╪═════════╣\n"
119
+ "║ 10000.01 ║ 123 │ 10.0 │ 0 │ E │ A ║\n"
120
+ "╚═════════════╩═══════╧═════════════╧════╧════╧═════════╝"
121
+ )
122
+ assert text == expected
123
+
124
+
125
+ def test_single_decimal_alignment():
126
+ text = t2a(
127
+ header=["1.1.1", "G", "Long Header"],
128
+ body=[[100.00001, 2, 3.14], [10.0001, 22.0, 2.718]],
129
+ alignments=Alignment.DECIMAL,
130
+ )
131
+ expected = (
132
+ "╔════════════════════════════════╗\n"
133
+ "║ 1.1.1 G Long Header ║\n"
134
+ "╟────────────────────────────────╢\n"
135
+ "║ 100.00001 2 3.14 ║\n"
136
+ "║ 10.0001 22.0 2.718 ║\n"
137
+ "╚════════════════════════════════╝"
138
+ )
139
+ assert text == expected
140
+
141
+
142
+ def test_single_left_alignment():
143
+ text = t2a(
144
+ header=["1.1.1", "G", "Long Header"],
145
+ body=[[100.00001, 2, 3.14], [10.0001, 22.0, 2.718]],
146
+ alignments=Alignment.LEFT,
147
+ )
148
+ expected = (
149
+ "╔════════════════════════════════╗\n"
150
+ "║ 1.1.1 G Long Header ║\n"
151
+ "╟────────────────────────────────╢\n"
152
+ "║ 100.00001 2 3.14 ║\n"
153
+ "║ 10.0001 22.0 2.718 ║\n"
154
+ "╚════════════════════════════════╝"
155
+ )
156
+ assert text == expected
157
+
158
+
159
+ def test_number_alignments():
160
+ text = t2a(
161
+ header=["1.1.1", "G", "Long Header", "Another Long Header"],
162
+ body=[[100.00001, 2, 3.14, 6.28], [10.0001, 22.0, 2.718, 1.618]],
163
+ alignments=[Alignment.LEFT, Alignment.RIGHT, Alignment.CENTER, Alignment.RIGHT],
164
+ number_alignments=[Alignment.DECIMAL, Alignment.LEFT, Alignment.RIGHT, Alignment.DECIMAL],
165
+ )
166
+ expected = (
167
+ "╔══════════════════════════════════════════════════════╗\n"
168
+ "║ 1.1.1 G Long Header Another Long Header ║\n"
169
+ "╟──────────────────────────────────────────────────────╢\n"
170
+ "║ 100.00001 2 3.14 6.28 ║\n"
171
+ "║ 10.0001 22.0 2.718 1.618 ║\n"
172
+ "╚══════════════════════════════════════════════════════╝"
173
+ )
174
+ assert text == expected
175
+
176
+
177
+ def test_single_number_alignments():
178
+ text = t2a(
179
+ header=["1.1.1", "G", "Long Header", "S"],
180
+ body=[[100.00001, 2, 3.14, 6.28], [10.0001, 22.0, 2.718, 1.618]],
181
+ alignments=[Alignment.LEFT, Alignment.CENTER, Alignment.CENTER, Alignment.RIGHT],
182
+ number_alignments=Alignment.RIGHT,
183
+ )
184
+ expected = (
185
+ "╔════════════════════════════════════════╗\n"
186
+ "║ 1.1.1 G Long Header S ║\n"
187
+ "╟────────────────────────────────────────╢\n"
188
+ "║ 100.00001 2 3.14 6.28 ║\n"
189
+ "║ 10.0001 22.0 2.718 1.618 ║\n"
190
+ "╚════════════════════════════════════════╝"
191
+ )
192
+ assert text == expected
@@ -0,0 +1,83 @@
1
+ import pytest
2
+
3
+ from table2ascii import Alignment, table2ascii as t2a
4
+ from table2ascii.exceptions import InvalidCellPaddingError
5
+
6
+
7
+ def test_without_cell_padding():
8
+ text = t2a(
9
+ header=["#", "G", "H", "R", "S"],
10
+ body=[[1, 2, 3, 4, 5]],
11
+ footer=["A", "B", 1, 2, 3],
12
+ first_col_heading=True,
13
+ cell_padding=0,
14
+ )
15
+ expected = (
16
+ "╔═╦═══════╗\n"
17
+ "║#║G H R S║\n"
18
+ "╟─╫───────╢\n"
19
+ "║1║2 3 4 5║\n"
20
+ "╟─╫───────╢\n"
21
+ "║A║B 1 2 3║\n"
22
+ "╚═╩═══════╝"
23
+ )
24
+ assert text == expected
25
+
26
+
27
+ def test_column_width_and_alignment_without_cell_padding():
28
+ text = t2a(
29
+ header=["#", "G", "H", "R", "S"],
30
+ body=[[1, 2, 3, 4, 5]],
31
+ footer=["A", "B", 1, 2, 3],
32
+ column_widths=[4, 8, 5, 4, 5],
33
+ alignments=[
34
+ Alignment.LEFT,
35
+ Alignment.CENTER,
36
+ Alignment.RIGHT,
37
+ Alignment.LEFT,
38
+ Alignment.RIGHT,
39
+ ],
40
+ first_col_heading=True,
41
+ cell_padding=0,
42
+ )
43
+ expected = (
44
+ "╔════╦═════════════════════════╗\n"
45
+ "║# ║ G H R S║\n"
46
+ "╟────╫─────────────────────────╢\n"
47
+ "║1 ║ 2 3 4 5║\n"
48
+ "╟────╫─────────────────────────╢\n"
49
+ "║A ║ B 1 2 3║\n"
50
+ "╚════╩═════════════════════════╝"
51
+ )
52
+ assert text == expected
53
+
54
+
55
+ def test_cell_padding_more_than_one():
56
+ text = t2a(
57
+ header=["#", "G", "H", "R", "S"],
58
+ body=[[1, 2, 3, 4, 5]],
59
+ footer=["A", "B", 1, 2, 3],
60
+ first_col_heading=True,
61
+ cell_padding=2,
62
+ )
63
+ expected = (
64
+ "╔═════╦═══════════════════════╗\n"
65
+ "║ # ║ G H R S ║\n"
66
+ "╟─────╫───────────────────────╢\n"
67
+ "║ 1 ║ 2 3 4 5 ║\n"
68
+ "╟─────╫───────────────────────╢\n"
69
+ "║ A ║ B 1 2 3 ║\n"
70
+ "╚═════╩═══════════════════════╝"
71
+ )
72
+ assert text == expected
73
+
74
+
75
+ def test_negative_cell_padding():
76
+ with pytest.raises(InvalidCellPaddingError):
77
+ t2a(
78
+ header=["#", "G", "H", "R", "S"],
79
+ body=[[1, 2, 3, 4, 5]],
80
+ footer=["A", "B", 1, 2, 3],
81
+ first_col_heading=True,
82
+ cell_padding=-1,
83
+ )