diff --git a/changelog.md b/changelog.md index c8839328..b961d394 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,11 @@ +TBD +============== + +Features +-------- +* Right-align numeric columns, and make the behavior configurable. + + 1.47.0 (2026/01/24) ============== diff --git a/mycli/main.py b/mycli/main.py index 006c7f69..9514f613 100755 --- a/mycli/main.py +++ b/mycli/main.py @@ -1,6 +1,7 @@ from __future__ import annotations from collections import defaultdict, namedtuple +from decimal import Decimal from io import TextIOWrapper import logging import os @@ -160,6 +161,7 @@ def __init__( self.login_path_as_host = c["main"].as_bool("login_path_as_host") self.post_redirect_command = c['main'].get('post_redirect_command') self.null_string = c['main'].get('null_string') + self.numeric_alignment = c['main'].get('numeric_alignment', 'right') # set ssl_mode if a valid option is provided in a config file, otherwise None ssl_mode = c["main"].get("ssl_mode", None) @@ -831,6 +833,7 @@ def output_res(results: Generator[SQLResult], start: float) -> None: special.is_expanded_output(), special.is_redirected(), self.null_string, + self.numeric_alignment, max_width, ) @@ -868,6 +871,7 @@ def output_res(results: Generator[SQLResult], start: float) -> None: special.is_expanded_output(), special.is_redirected(), self.null_string, + self.numeric_alignment, max_width, ) self.echo("") @@ -1345,6 +1349,7 @@ def run_query( special.is_expanded_output(), special.is_redirected(), self.null_string, + self.numeric_alignment, ) for line in output: self.log_output(line) @@ -1364,6 +1369,7 @@ def run_query( special.is_expanded_output(), special.is_redirected(), self.null_string, + self.numeric_alignment, ) for line in output: click.echo(line, nl=new_line) @@ -1379,6 +1385,7 @@ def format_output( expanded: bool = False, is_redirected: bool = False, null_string: str | None = None, + numeric_alignment: str = 'right', max_width: int | None = None, ) -> itertools.chain[str]: if is_redirected: @@ -1408,6 +1415,7 @@ def format_output( if headers or (cur and title): column_types = None + colalign = None if isinstance(cur, Cursor): def get_col_type(col) -> type: @@ -1415,6 +1423,7 @@ def get_col_type(col) -> type: return col_type if type(col_type) is type else str column_types = [get_col_type(tup) for tup in cur.description] + colalign = [numeric_alignment if x in (int, float, Decimal) else 'left' for x in column_types] if max_width is not None and isinstance(cur, Cursor): cur = list(cur) @@ -1424,6 +1433,7 @@ def get_col_type(col) -> type: headers, format_name="vertical" if expanded else None, column_types=column_types, + colalign=colalign, **output_kwargs, ) diff --git a/mycli/myclirc b/mycli/myclirc index 66ac242d..91d92294 100644 --- a/mycli/myclirc +++ b/mycli/myclirc @@ -73,6 +73,9 @@ redirect_format = csv # empty string, and JSON formats use native nulls. null_string = +# How to align numeric data in tabular output: right or left. +numeric_alignment = right + # A command to run after a successful output redirect, with {} to be replaced # with the escaped filename. Mac example: echo {} | pbcopy. Escaping is not # reliable/safe on Windows. diff --git a/pyproject.toml b/pyproject.toml index 1f4b6e55..8beb9cd1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ dependencies = [ "sqlparse>=0.3.0,<0.6.0", "sqlglot[rs] == 27.*", "configobj >= 5.0.5", - "cli_helpers[styles] >= 2.7.0", + "cli_helpers[styles] >= 2.8.0", "pyperclip >= 1.8.1", "pycryptodomex", "pyfzf >= 0.3.1", diff --git a/test/myclirc b/test/myclirc index d4061fa5..870ef552 100644 --- a/test/myclirc +++ b/test/myclirc @@ -71,6 +71,9 @@ redirect_format = csv # empty string, and JSON formats use native nulls. null_string = +# How to align numeric data in tabular output: right or left. +numeric_alignment = right + # A command to run after a successful output redirect, with {} to be replaced # with the escaped filename. Mac example: echo {} | pbcopy. Escaping is not # reliable/safe on Windows. diff --git a/test/test_main.py b/test/test_main.py index 66a2ef85..22ab2c99 100644 --- a/test/test_main.py +++ b/test/test_main.py @@ -438,7 +438,7 @@ def test_batch_mode_table(executor): +----------+ | count(*) | +----------+ - | 3 | + | 3 | +----------+ +-----+ | a |