diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/package.nix b/pkgs/by-name/ni/nixos-rebuild-ng/package.nix index 6cd7eca28691..df33313193fe 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/package.nix +++ b/pkgs/by-name/ni/nixos-rebuild-ng/package.nix @@ -35,10 +35,6 @@ python3Packages.buildPythonApplication rec { setuptools ]; - dependencies = with python3Packages; [ - tabulate - ]; - nativeBuildInputs = lib.optionals withShellFiles [ installShellFiles python3Packages.shtab @@ -94,9 +90,6 @@ python3Packages.buildPythonApplication rec { mypy pytest ruff - types-tabulate - # dependencies - tabulate ] ); in diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/__init__.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/__init__.py index b2f9cf22dfe5..dec1a5a252d9 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/__init__.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/__init__.py @@ -12,7 +12,7 @@ from . import nix from .constants import EXECUTABLE, WITH_NIX_2_18, WITH_REEXEC, WITH_SHELL_FILES from .models import Action, BuildAttr, Flake, NRError, Profile from .process import Remote, cleanup_ssh -from .utils import Args, LogFormatter +from .utils import Args, LogFormatter, tabulate logger = logging.getLogger() logger.setLevel(logging.INFO) @@ -448,8 +448,6 @@ def execute(argv: list[str]) -> None: if args.json: print(json.dumps(generations, indent=2)) else: - from tabulate import tabulate - headers = { "generation": "Generation", "date": "Build-date", @@ -459,17 +457,7 @@ def execute(argv: list[str]) -> None: "specialisations": "Specialisation", "current": "Current", } - # Not exactly the same format as legacy nixos-rebuild but close - # enough - table = tabulate( - generations, - headers=headers, - tablefmt="plain", - numalign="left", - stralign="left", - disable_numparse=True, - ) - print(table) + print(tabulate(generations, headers=headers)) case Action.REPL: if flake: nix.repl_flake("toplevel", flake, **flake_build_flags) diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/utils.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/utils.py index 87d3f21596a3..11a0d9188294 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/utils.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/utils.py @@ -1,5 +1,6 @@ import logging -from typing import TypeAlias, assert_never, override +from collections.abc import Mapping, Sequence +from typing import Any, TypeAlias, assert_never, override Args: TypeAlias = bool | str | list[str] | int | None @@ -18,7 +19,7 @@ class LogFormatter(logging.Formatter): return formatter.format(record) -def dict_to_flags(d: dict[str, Args]) -> list[str]: +def dict_to_flags(d: Mapping[str, Args]) -> list[str]: flags = [] for key, value in d.items(): flag = f"--{'-'.join(key.split('_'))}" @@ -44,3 +45,49 @@ def dict_to_flags(d: dict[str, Args]) -> list[str]: case _: assert_never(value) return flags + + +def remap_dicts( + dicts: Sequence[Mapping[str, Any]], + mappings: Mapping[str, str], +) -> list[dict[str, Any]]: + return [{mappings.get(k, k): v for k, v in d.items()} for d in dicts] + + +def tabulate( + data: Sequence[Mapping[str, Any]], + headers: Mapping[str, str] | None = None, +) -> str: + """Convert a sequence of mappings in a tabular-style format for terminal. + + It expects that all mappings (dicts) have the same keys as the first one, + otherwise it will misbehave. + """ + if not data: + return "" + + if headers: + data = remap_dicts(data, headers) + + data_headers = list(data[0].keys()) + + column_widths = [ + max( + len(str(header)), + *(len(str(row.get(header, ""))) for row in data), + ) + for header in data_headers + ] + + def format_row(row: Mapping[str, Any]) -> str: + s = (2 * " ").join( + f"{str(row[header]).ljust(width)}" + for header, width in zip(data_headers, column_widths) + ) + return s.strip() + + result = [format_row(dict(zip(data_headers, data_headers)))] + for row in data: + result.append(format_row(row)) + + return "\n".join(result) diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_utils.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_utils.py index 5f73c33acf98..b1cd52e0b121 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_utils.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_utils.py @@ -1,3 +1,5 @@ +import textwrap + import nixos_rebuild.utils as u @@ -28,3 +30,22 @@ def test_dict_to_flags() -> None: ] r2 = u.dict_to_flags({"verbose": 0, "empty_list": []}) assert r2 == [] + + +def test_remap_dicts() -> None: + assert u.remap_dicts( + [{"foo": 1, "bar": True}, {"qux": "keep"}], + {"foo": "Foo", "bar": "Bar"}, + ) == [{"Foo": 1, "Bar": True}, {"qux": "keep"}] + + +def test_tabulate() -> None: + assert u.tabulate([]) == "" + assert u.tabulate([{}]) == "\n" + assert u.tabulate( + [{"foo": 12345, "bar": ["abc", "cde"]}, {"foo": 345, "bar": 456}], + {"foo": "Foo", "bar": "Bar"}, + ) == textwrap.dedent("""\ + Foo Bar + 12345 ['abc', 'cde'] + 345 456""")