Skip to content

Missing data error, Sequence instead of List, Fix TableStyle warning #78

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 16 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,20 +195,22 @@ print(output)

## ⚙️ Options

All parameters are optional.

| Option | Type | Default | Description |
| :-----------------: | :-------------------: | :-------------------: | :-------------------------------------------------------------------------------: |
| `header` | `List[Any]` | `None` | First table row seperated by header row separator. Values should support `str()` |
| `body` | `List[List[Any]]` | `None` | List of rows for the main section of the table. Values should support `str()` |
| `footer` | `List[Any]` | `None` | Last table row seperated by header row separator. Values should support `str()` |
| `column_widths` | `List[Optional[int]]` | `None` (automatic) | List of column widths in characters for each column |
| `alignments` | `List[Alignment]` | `None` (all centered) | Column alignments<br/>(ex. `[Alignment.LEFT, Alignment.CENTER, Alignment.RIGHT]`) |
| `style` | `TableStyle` | `double_thin_compact` | Table style to use for the table\* |
| `first_col_heading` | `bool` | `False` | Whether to add a heading column separator after the first column |
| `last_col_heading` | `bool` | `False` | Whether to add a heading column separator before the last column |
| `cell_padding` | `int` | `1` | The minimum number of spaces to add between the cell content and the cell border |
| `use_wcwidth` | `bool` | `True` | Whether to use [wcwidth][wcwidth] instead of `len()` to calculate cell width |
All parameters are optional. At least one of `header`, `body`, and `footer` must be provided.

Refer to the [documentation](https://table2ascii.readthedocs.io/en/stable/api.html#table2ascii) for more information.

| Option | Type | Default | Description |
| :-----------------: | :----------------------------: | :-------------------: | :-------------------------------------------------------------------------------: |
| `header` | `Sequence[SupportsStr]` | `None` | First table row seperated by header row separator. Values should support `str()` |
| `body` | `Sequence[Sequence[Sequence]]` | `None` | 2D List of rows for the main section of the table. Values should support `str()` |
| `footer` | `Sequence[Sequence]` | `None` | Last table row seperated by header row separator. Values should support `str()` |
| `column_widths` | `Sequence[Optional[int]]` | `None` (automatic) | List of column widths in characters for each column |
| `alignments` | `Sequence[Alignment]` | `None` (all centered) | Column alignments<br/>(ex. `[Alignment.LEFT, Alignment.CENTER, Alignment.RIGHT]`) |
| `style` | `TableStyle` | `double_thin_compact` | Table style to use for the table\* |
| `first_col_heading` | `bool` | `False` | Whether to add a heading column separator after the first column |
| `last_col_heading` | `bool` | `False` | Whether to add a heading column separator before the last column |
| `cell_padding` | `int` | `1` | The minimum number of spaces to add between the cell content and the cell border |
| `use_wcwidth` | `bool` | `True` | Whether to use [wcwidth][wcwidth] instead of `len()` to calculate cell width |

[wcwidth]: https://pypi.org/project/wcwidth/

Expand Down
6 changes: 3 additions & 3 deletions docs/source/generate_style_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
from table2ascii import PresetStyle, table2ascii


def indent_all_lines(text, number_of_spaces=3):
def indent_all_lines(text: str, number_of_spaces: int = 3) -> str:
"""Indent all lines in a string by a certain number of spaces"""
return "\n".join(number_of_spaces * " " + line for line in text.split("\n"))


def generate_style_list():
def generate_style_list() -> str:
"""Generate README.rst the style list"""
# get attributes in PresetStyle
attribute_names = [attr for attr in dir(PresetStyle) if not attr.startswith("__")]
Expand Down Expand Up @@ -43,7 +43,7 @@ def generate_style_list():
return f"{heading}\n\n{table_of_contents}\n{style_list}"


def write_to_file(filename, content):
def write_to_file(filename: str, content: str) -> None:
"""Write content to filename"""
with open(filename, "w") as f:
f.write(content)
Expand Down
54 changes: 34 additions & 20 deletions table2ascii/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
from __future__ import annotations

from collections.abc import Sequence
from typing import Any

from .alignment import Alignment

from .annotations import SupportsStr


class Table2AsciiError(Exception):
"""Base class for all table2ascii exceptions"""

def _message(self):
def _message(self) -> str:
"""Return the error message"""
raise NotImplementedError

Expand Down Expand Up @@ -39,16 +40,16 @@ class FooterColumnCountMismatchError(ColumnCountMismatchError):
This class is a subclass of :class:`ColumnCountMismatchError`.

Attributes:
footer (list[SupportsStr]): The footer that caused the error
footer (Sequence[SupportsStr]): The footer that caused the error
expected_columns (int): The number of columns that were expected
"""

def __init__(self, footer: list[SupportsStr], expected_columns: int):
def __init__(self, footer: Sequence[SupportsStr], expected_columns: int):
self.footer = footer
self.expected_columns = expected_columns
super().__init__(self._message())

def _message(self):
def _message(self) -> str:
return (
f"Footer column count mismatch: {len(self.footer)} columns "
f"found, expected {self.expected_columns}."
Expand All @@ -62,20 +63,20 @@ class BodyColumnCountMismatchError(ColumnCountMismatchError):
This class is a subclass of :class:`ColumnCountMismatchError`.

Attributes:
body (list[list[SupportsStr]]): The body that caused the error
body (Sequence[Sequence[SupportsStr]]): The body that caused the error
expected_columns (int): The number of columns that were expected
first_invalid_row (list[SupportsStr]): The first row with an invalid column count
first_invalid_row (Sequence[SupportsStr]): The first row with an invalid column count
"""

def __init__(self, body: list[list[SupportsStr]], expected_columns: int):
def __init__(self, body: Sequence[Sequence[SupportsStr]], expected_columns: int):
self.body = body
self.expected_columns = expected_columns
self.first_invalid_row = next(
(row for row in self.body if len(row) != self.expected_columns)
)
super().__init__(self._message())

def _message(self):
def _message(self) -> str:
return (
f"Body column count mismatch: A row with {len(self.first_invalid_row)} "
f"columns was found, expected {self.expected_columns}."
Expand All @@ -89,16 +90,16 @@ class AlignmentCountMismatchError(ColumnCountMismatchError):
This class is a subclass of :class:`ColumnCountMismatchError`.

Attributes:
alignments (list[Alignment]): The alignments that caused the error
alignments (Sequence[Alignment]): The alignments that caused the error
expected_columns (int): The number of columns that were expected
"""

def __init__(self, alignments: list[Alignment], expected_columns: int):
def __init__(self, alignments: Sequence[Alignment], expected_columns: int):
self.alignments = alignments
self.expected_columns = expected_columns
super().__init__(self._message())

def _message(self):
def _message(self) -> str:
return (
f"Alignment count mismatch: {len(self.alignments)} alignments "
f"found, expected {self.expected_columns}."
Expand All @@ -112,22 +113,35 @@ class ColumnWidthsCountMismatchError(ColumnCountMismatchError):
This class is a subclass of :class:`ColumnCountMismatchError`.

Attributes:
column_widths (list[Optional[int]]): The column widths that caused the error
column_widths (Sequence[Optional[int]]): The column widths that caused the error
expected_columns (int): The number of columns that were expected
"""

def __init__(self, column_widths: list[int | None], expected_columns: int):
def __init__(self, column_widths: Sequence[int | None], expected_columns: int):
self.column_widths = column_widths
self.expected_columns = expected_columns
super().__init__(self._message())

def _message(self):
def _message(self) -> str:
return (
f"Column widths count mismatch: {len(self.column_widths)} column widths "
f"found, expected {self.expected_columns}."
)


class NoHeaderBodyOrFooterError(TableOptionError):
"""Exception raised when no header, body or footer is provided

This class is a subclass of :class:`TableOptionError`.
"""

def __init__(self):
super().__init__(self._message())

def _message(self) -> str:
return "At least one of header, body or footer must be provided."


class InvalidCellPaddingError(TableOptionError):
"""Exception raised when the cell padding is invalid

Expand All @@ -141,7 +155,7 @@ def __init__(self, padding: int):
self.padding = padding
super().__init__(self._message())

def _message(self):
def _message(self) -> str:
return f"Invalid cell padding: {self.padding} is not a positive integer."


Expand All @@ -163,7 +177,7 @@ def __init__(self, column_index: int, column_width: int, min_width: int):
self.min_width = min_width
super().__init__(self._message())

def _message(self):
def _message(self) -> str:
return (
f"Column width too small: The column width for column index {self.column_index} "
f" of `column_widths` is {self.column_width}, but the minimum width "
Expand All @@ -184,7 +198,7 @@ def __init__(self, alignment: Any):
self.alignment = alignment
super().__init__(self._message())

def _message(self):
def _message(self) -> str:
return (
f"Invalid alignment: {self.alignment!r} is not a valid alignment. "
f"Valid alignments are: {', '.join(a.__repr__() for a in Alignment)}"
Expand All @@ -208,7 +222,7 @@ def __init__(self, string: str, max_characters: int):
self.max_characters = max_characters
super().__init__(self._message())

def _message(self):
def _message(self) -> str:
return (
f"Too many characters for table style: {len(self.string)} characters "
f"found, but the maximum number of characters allowed is {self.max_characters}."
Expand All @@ -234,7 +248,7 @@ def __init__(self, string: str, max_characters: int):
self.max_characters = max_characters
super().__init__(self._message())

def _message(self):
def _message(self) -> str:
return (
f"Too few characters for table style: {len(self.string)} characters "
f"found, but table styles can accept {self.max_characters} characters. "
Expand Down
2 changes: 1 addition & 1 deletion table2ascii/merge.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,5 @@ class Merge(Enum):

LEFT = 0

def __str__(self):
def __str__(self) -> str:
return ""
5 changes: 3 additions & 2 deletions table2ascii/options.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

from collections.abc import Sequence
from dataclasses import dataclass

from .alignment import Alignment
Expand All @@ -17,8 +18,8 @@ class Options:

first_col_heading: bool
last_col_heading: bool
column_widths: list[int | None] | None
alignments: list[Alignment] | None
column_widths: Sequence[int | None] | None
alignments: Sequence[Alignment] | None
cell_padding: int
style: TableStyle
use_wcwidth: bool
4 changes: 2 additions & 2 deletions table2ascii/table_style.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,11 @@ def from_string(cls, string: str) -> "TableStyle":
raise TableStyleTooLongError(string, num_params)
# if the string is too short, show a warning and pad it with spaces
elif len(string) < num_params:
string += " " * (num_params - len(string))
warnings.warn(TableStyleTooShortWarning(string, num_params), stacklevel=2)
string += " " * (num_params - len(string))
return cls(*string)

def set(self, **kwargs) -> "TableStyle":
def set(self, **kwargs: str) -> "TableStyle":
"""Set attributes of the TableStyle

Args:
Expand Down
Loading