diff --git a/MANIFEST.in b/MANIFEST.in index e1cdca0..e0d4513 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,7 @@ include *.txt *.rst *.py include AUTHORS CHANGELOG LICENSE include tox.ini +recursive-include cli_helpers *.pyi py.typed recursive-include docs *.py recursive-include docs *.rst recursive-include docs Makefile diff --git a/cli_helpers/compat.pyi b/cli_helpers/compat.pyi new file mode 100644 index 0000000..c5d7236 --- /dev/null +++ b/cli_helpers/compat.pyi @@ -0,0 +1,11 @@ +from decimal import Decimal + +PY2: bool +WIN: bool +MAC: bool +text_type: type[str] +binary_type: type[bytes] +long_type: type[int] +int_types: tuple[type[int]] +HAS_PYGMENTS: bool +float_types: tuple[type[float], type[Decimal]] diff --git a/cli_helpers/py.typed b/cli_helpers/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/cli_helpers/tabular_output/delimited_output_adapter.pyi b/cli_helpers/tabular_output/delimited_output_adapter.pyi new file mode 100644 index 0000000..92f6fe6 --- /dev/null +++ b/cli_helpers/tabular_output/delimited_output_adapter.pyi @@ -0,0 +1,46 @@ +import sys +from collections.abc import Iterable, Iterator, Sequence +from typing import Literal, Protocol + +if sys.version_info >= (3, 12): + _Quoting = Literal[0, 1, 2, 3, 4, 5] +else: + _Quoting = Literal[0, 1, 2, 3] + +class _Preprocessor(Protocol): + def __call__( + self, + data: Iterable[Sequence[object]], + headers: Sequence[object], + **kwargs: object + ) -> tuple[Iterable[Sequence[object]], Sequence[object]]: ... + +supported_formats: tuple[ + Literal["csv"], + Literal["csv-tab"], + Literal["csv-noheader"], + Literal["csv-tab-noheader"], +] +preprocessors: tuple[_Preprocessor, _Preprocessor] + +class linewriter: + line: str | None + def __init__(self) -> None: ... + def reset(self) -> None: ... + def write(self, d: str) -> None: ... + +def adapter( + data: Iterable[Sequence[object]], + headers: Sequence[object], + table_format: Literal["csv", "csv-tab", "csv-noheader", "csv-tab-noheader"] = ..., + *, + dialect: object = ..., + delimiter: str = ..., + doublequote: bool = ..., + escapechar: str | None = ..., + quotechar: str | None = ..., + quoting: _Quoting = ..., + skipinitialspace: bool = ..., + strict: bool = ..., + **kwargs: object +) -> Iterator[str]: ... diff --git a/cli_helpers/tabular_output/json_output_adapter.pyi b/cli_helpers/tabular_output/json_output_adapter.pyi new file mode 100644 index 0000000..7f5a7f6 --- /dev/null +++ b/cli_helpers/tabular_output/json_output_adapter.pyi @@ -0,0 +1,24 @@ +import json +from collections.abc import Iterable, Iterator, Sequence +from typing import Literal, Protocol + +class _Preprocessor(Protocol): + def __call__( + self, + data: Iterable[Sequence[object]], + headers: Sequence[object], + **kwargs: object + ) -> tuple[Iterable[Sequence[object]], Sequence[object]]: ... + +supported_formats: tuple[Literal["jsonl"], Literal["jsonl_escaped"]] +preprocessors: tuple[_Preprocessor,] + +class CustomEncoder(json.JSONEncoder): + def default(self, o: object) -> float: ... + +def adapter( + data: Iterable[Sequence[object]], + headers: Iterable[str | int | float | bool | None], + table_format: Literal["jsonl", "jsonl_escaped"] = ..., + **_kwargs: object +) -> Iterator[str]: ... diff --git a/cli_helpers/tabular_output/preprocessors.pyi b/cli_helpers/tabular_output/preprocessors.pyi new file mode 100644 index 0000000..bf18e28 --- /dev/null +++ b/cli_helpers/tabular_output/preprocessors.pyi @@ -0,0 +1,98 @@ +from decimal import Decimal +from collections.abc import Iterable, Iterator, Mapping, Sequence +from typing import TypeVar, overload + +from pygments.style import StyleMeta +from pygments.token import _TokenType + +_T = TypeVar("_T") +_H = TypeVar("_H") +_ColumnType = ( + type[None] + | type[bool] + | type[int] + | type[float] + | type[Decimal] + | type[bytes] + | type[str] +) + +def truncate_string( + data: Iterable[Sequence[_T]], + headers: Sequence[_H], + max_field_width: int | None = ..., + skip_multiline_string: bool = ..., + **_: object +) -> tuple[Iterator[list[_T]], list[_H]]: ... +def convert_to_string( + data: Iterable[Sequence[object]], headers: Sequence[object], **_: object +) -> tuple[Iterator[list[str]], list[str]]: ... +def convert_to_undecoded_string( + data: Iterable[Sequence[object]], headers: Sequence[object], **_: object +) -> tuple[Iterator[list[str | None]], list[str | None]]: ... +def override_missing_value( + data: Iterable[Sequence[_T]], + headers: Sequence[_H], + style: str | StyleMeta | None = ..., + missing_value_token: _TokenType | None = ..., + missing_value: str = ..., + **_: object +) -> tuple[Iterator[list[_T | str]], Sequence[_H]]: ... +def override_tab_value( + data: Iterable[Sequence[_T]], + headers: Sequence[_H], + new_value: str = ..., + **_: object +) -> tuple[Iterator[list[_T]], Sequence[_H]]: ... +def escape_newlines( + data: Iterable[Sequence[_T]], headers: Sequence[_H], **_: object +) -> tuple[Iterator[list[_T]], Sequence[_H]]: ... +def bytes_to_string( + data: Iterable[Sequence[_T]], headers: Sequence[_H], **_: object +) -> tuple[Iterator[list[_T | str]], list[_H | str]]: ... +def align_decimals( + data: Iterable[Sequence[_T]], + headers: Sequence[_H], + column_types: Sequence[_ColumnType] = ..., + **_: object +) -> tuple[Iterator[list[_T | str]], Sequence[_H]]: ... +def quote_whitespaces( + data: Iterable[Sequence[object]], + headers: Sequence[_H], + quotestyle: str = ..., + **_: object +) -> tuple[Iterator[list[str]], Sequence[_H]]: ... +@overload +def style_output( + data: Iterable[Sequence[_T]], + headers: Sequence[_H], + style: None = ..., + header_token: _TokenType | None = ..., + odd_row_token: _TokenType | None = ..., + even_row_token: _TokenType | None = ..., + **_: object +) -> tuple[Iterator[Sequence[_T]], Sequence[_H]]: ... +@overload +def style_output( + data: Iterable[Sequence[str]], + headers: Sequence[str], + style: str | StyleMeta, + header_token: _TokenType | None = ..., + odd_row_token: _TokenType | None = ..., + even_row_token: _TokenType | None = ..., + **_: object +) -> tuple[Iterator[Sequence[str]], Sequence[str]]: ... +def format_numbers( + data: Iterable[Sequence[_T]], + headers: Sequence[_H], + column_types: Sequence[_ColumnType] = ..., + integer_format: str | None = ..., + float_format: str | None = ..., + **_: object +) -> tuple[Iterator[list[_T | str]], Sequence[_H]]: ... +def format_timestamps( + data: Iterable[Sequence[_T]], + headers: Sequence[_H], + column_date_formats: Mapping[_H, str] | None = ..., + **_: object +) -> tuple[Iterator[list[_T | str]], Sequence[_H]]: ... diff --git a/cli_helpers/tabular_output/tsv_output_adapter.pyi b/cli_helpers/tabular_output/tsv_output_adapter.pyi new file mode 100644 index 0000000..1f35e5f --- /dev/null +++ b/cli_helpers/tabular_output/tsv_output_adapter.pyi @@ -0,0 +1,20 @@ +from collections.abc import Iterable, Iterator, Sequence +from typing import Literal, Protocol + +class _Preprocessor(Protocol): + def __call__( + self, + data: Iterable[Sequence[object]], + headers: Sequence[object], + **kwargs: object + ) -> tuple[Iterable[Sequence[object]], Sequence[object]]: ... + +supported_formats: tuple[Literal["tsv"], Literal["tsv_noheader"]] +preprocessors: tuple[_Preprocessor, _Preprocessor, _Preprocessor] + +def adapter( + data: Iterable[Sequence[str]], + headers: Sequence[str], + table_format: Literal["tsv", "tsv_noheader"] = ..., + **kwargs: object +) -> Iterator[str]: ... diff --git a/cli_helpers/tabular_output/vertical_table_adapter.pyi b/cli_helpers/tabular_output/vertical_table_adapter.pyi new file mode 100644 index 0000000..6faa5de --- /dev/null +++ b/cli_helpers/tabular_output/vertical_table_adapter.pyi @@ -0,0 +1,30 @@ +from collections.abc import Iterable, Iterator, Sequence +from typing import Literal, Protocol + +class _Preprocessor(Protocol): + def __call__( + self, + data: Iterable[Sequence[object]], + headers: Sequence[object], + **kwargs: object + ) -> tuple[Iterable[Sequence[object]], Sequence[object]]: ... + +supported_formats: tuple[Literal["vertical"]] +preprocessors: tuple[_Preprocessor, _Preprocessor, _Preprocessor] + +def vertical_table( + data: Iterable[Sequence[str]], + headers: Sequence[str], + sep_title: str = ..., + sep_character: str = ..., + sep_length: int | tuple[int, int] = ..., +) -> Iterator[str]: ... +def adapter( + data: Iterable[Sequence[str]], + headers: Sequence[str], + *, + sep_title: str = ..., + sep_character: str = ..., + sep_length: int | tuple[int, int] = ..., + **kwargs: object +) -> Iterator[str]: ... diff --git a/cli_helpers/utils.pyi b/cli_helpers/utils.pyi new file mode 100644 index 0000000..e347317 --- /dev/null +++ b/cli_helpers/utils.pyi @@ -0,0 +1,42 @@ +from collections.abc import Container, Hashable, Iterable, Mapping +from typing import TypeVar, overload + +from pygments.style import StyleMeta +from pygments.token import _TokenType + +_K = TypeVar("_K") +_V = TypeVar("_V") +_T = TypeVar("_T", bound=Hashable) + +@overload +def bytes_to_string(b: bytes) -> str: ... +@overload +def bytes_to_string(b: object) -> object: ... +@overload +def to_hex_if_bin(b: bytes) -> str: ... +@overload +def to_hex_if_bin(b: object) -> object: ... +def to_string(value: object) -> str: ... +def to_undecoded_string(value: object) -> str | None: ... +@overload +def truncate_string( + value: str, + max_width: int | None = ..., + skip_multiline_string: bool = ..., +) -> str: ... +@overload +def truncate_string( + value: object, + max_width: int | None = ..., + skip_multiline_string: bool = ..., +) -> object: ... +def intlen(n: str) -> int: ... +def filter_dict_by_key(d: Mapping[_K, _V], keys: Container[_K]) -> dict[_K, _V]: ... +def unique_items(seq: Iterable[_T]) -> list[_T]: ... +def strip_ansi(value: str) -> str: ... +def replace(s: str, replace: Iterable[tuple[str, str]]) -> str: ... +def style_field(token: _TokenType, field: str, style: str | StyleMeta) -> str: ... +def filter_style_table( + style: str | StyleMeta | None, *relevant_styles: _TokenType | None +) -> dict[_TokenType | None, str]: ... +def version_as_tuple(version: str) -> tuple[int, int, int]: ...