From c358bf408efd1c6b13b3b9bd5d609d81757d76f1 Mon Sep 17 00:00:00 2001 From: Roland Walker Date: Fri, 3 Apr 2026 13:04:09 -0400 Subject: [PATCH] move --list-dsn execution path out of main.py --- changelog.md | 1 + mycli/main.py | 16 +--- mycli/main_modes/list_dsn.py | 25 ++++++ test/pytests/test_main_modes_list_dsn.py | 104 +++++++++++++++++++++++ 4 files changed, 132 insertions(+), 14 deletions(-) create mode 100644 mycli/main_modes/list_dsn.py create mode 100644 test/pytests/test_main_modes_list_dsn.py diff --git a/changelog.md b/changelog.md index 37444d96..96602a63 100644 --- a/changelog.md +++ b/changelog.md @@ -37,6 +37,7 @@ Internal * Factor the `--batch` execution modes out of `main.py`. * Move `--checkup` logic to the new `main_modes` with `--batch`. * Move `--execute` logic to the new `main_modes` with `--batch`. +* Move `--list-dsn` logic to the new `main_modes` with `--batch`. * Sort coverage report in tox suite. * Skip more tests when a database connection is not present. diff --git a/mycli/main.py b/mycli/main.py index 5847c018..7d377a62 100755 --- a/mycli/main.py +++ b/mycli/main.py @@ -87,6 +87,7 @@ ) from mycli.main_modes.checkup import main_checkup from mycli.main_modes.execute import main_execute_from_cli +from mycli.main_modes.list_dsn import main_list_dsn from mycli.packages import special from mycli.packages.filepaths import dir_path_exists, guess_socket_location from mycli.packages.hybrid_redirection import get_redirect_components, is_redirect_command @@ -2312,20 +2313,7 @@ def get_password_from_file(password_file: str | None) -> str | None: ) if cli_args.list_dsn: - try: - alias_dsn = mycli.config["alias_dsn"] - except KeyError: - click.secho("Invalid DSNs found in the config file. Please check the \"[alias_dsn]\" section in myclirc.", err=True, fg="red") - sys.exit(1) - except Exception as e: - click.secho(str(e), err=True, fg="red") - sys.exit(1) - for alias, value in alias_dsn.items(): - if cli_args.verbose: - click.secho(f"{alias} : {value}") - else: - click.secho(alias) - sys.exit(0) + sys.exit(main_list_dsn(mycli, cli_args)) if cli_args.list_ssh_config: ssh_config = read_ssh_config(cli_args.ssh_config_path) diff --git a/mycli/main_modes/list_dsn.py b/mycli/main_modes/list_dsn.py new file mode 100644 index 00000000..39ce4584 --- /dev/null +++ b/mycli/main_modes/list_dsn.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +import click + +if TYPE_CHECKING: + from mycli.main import CliArgs, MyCli + + +def main_list_dsn(mycli: 'MyCli', cli_args: 'CliArgs') -> int: + try: + alias_dsn = mycli.config['alias_dsn'] + except KeyError: + click.secho('Invalid DSNs found in the config file. Please check the "[alias_dsn]" section in myclirc.', err=True, fg='red') + return 1 + except Exception as e: + click.secho(str(e), err=True, fg='red') + return 1 + for alias, value in alias_dsn.items(): + if cli_args.verbose: + click.secho(f'{alias} : {value}') + else: + click.secho(alias) + return 0 diff --git a/test/pytests/test_main_modes_list_dsn.py b/test/pytests/test_main_modes_list_dsn.py new file mode 100644 index 00000000..a622015a --- /dev/null +++ b/test/pytests/test_main_modes_list_dsn.py @@ -0,0 +1,104 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, cast + +import mycli.main_modes.list_dsn as list_dsn_mode + + +@dataclass +class DummyCliArgs: + verbose: bool = False + + +class DummyConfig: + def __init__(self, value: dict[str, str] | Exception) -> None: + self.value = value + + def __getitem__(self, key: str) -> dict[str, str]: + assert key == 'alias_dsn' + if isinstance(self.value, Exception): + raise self.value + return self.value + + +class DummyMyCli: + def __init__(self, config: Any) -> None: + self.config = config + + +def main_list_dsn(mycli: DummyMyCli, cli_args: DummyCliArgs) -> int: + return list_dsn_mode.main_list_dsn(cast(Any, mycli), cast(Any, cli_args)) + + +def test_main_list_dsn_lists_aliases_without_values(monkeypatch) -> None: + secho_calls: list[tuple[str, bool | None, str | None]] = [] + mycli = DummyMyCli(DummyConfig({'prod': 'mysql://u:p@h/db', 'staging': 'mysql://u2:p2@h2/db2'})) + + monkeypatch.setattr( + list_dsn_mode.click, + 'secho', + lambda message, err=None, fg=None: secho_calls.append((message, err, fg)), + ) + + result = main_list_dsn(mycli, DummyCliArgs(verbose=False)) + + assert result == 0 + assert secho_calls == [ + ('prod', None, None), + ('staging', None, None), + ] + + +def test_main_list_dsn_lists_aliases_with_values_in_verbose_mode(monkeypatch) -> None: + secho_calls: list[tuple[str, bool | None, str | None]] = [] + mycli = DummyMyCli(DummyConfig({'prod': 'mysql://u:p@h/db'})) + + monkeypatch.setattr( + list_dsn_mode.click, + 'secho', + lambda message, err=None, fg=None: secho_calls.append((message, err, fg)), + ) + + result = main_list_dsn(mycli, DummyCliArgs(verbose=True)) + + assert result == 0 + assert secho_calls == [('prod : mysql://u:p@h/db', None, None)] + + +def test_main_list_dsn_reports_invalid_alias_section(monkeypatch) -> None: + secho_calls: list[tuple[str, bool | None, str | None]] = [] + mycli = DummyMyCli(DummyConfig(KeyError('alias_dsn'))) + + monkeypatch.setattr( + list_dsn_mode.click, + 'secho', + lambda message, err=None, fg=None: secho_calls.append((message, err, fg)), + ) + + result = main_list_dsn(mycli, DummyCliArgs()) + + assert result == 1 + assert secho_calls == [ + ( + 'Invalid DSNs found in the config file. Please check the "[alias_dsn]" section in myclirc.', + True, + 'red', + ) + ] + + +def test_main_list_dsn_reports_other_config_errors(monkeypatch) -> None: + secho_calls: list[tuple[str, bool | None, str | None]] = [] + mycli = DummyMyCli(DummyConfig(RuntimeError('boom'))) + + monkeypatch.setattr( + list_dsn_mode.click, + 'secho', + lambda message, err=None, fg=None: secho_calls.append((message, err, fg)), + ) + + result = main_list_dsn(mycli, DummyCliArgs()) + + assert result == 1 + assert secho_calls == [('boom', True, 'red')]