diff --git a/changelog.md b/changelog.md index 2a2242a8..05cd19c7 100644 --- a/changelog.md +++ b/changelog.md @@ -23,6 +23,7 @@ Internal -------- * Update `cli_helpers` to v2.15.2, improving vertical output. * Update `ruff` to v0.17.0. +* Complete moving `MyCli` to `client.py`. v2.29.6 (2026/10/15) diff --git a/mycli/cli_runner.py b/mycli/cli_runner.py index f10139f4..8a3729d6 100644 --- a/mycli/cli_runner.py +++ b/mycli/cli_runner.py @@ -153,7 +153,10 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory | None sys.exit(main_list_dsn(config, verbosity)) if client_factory is None: - client_factory = main_module.MyCli + import mycli.client as mycli_client + + client_factory = mycli_client.MyCli + mycli = client_factory( prompt=cli_args.prompt, toolbar_format=cli_args.toolbar, diff --git a/mycli/main.py b/mycli/main.py index 1bbd54e0..d7616416 100755 --- a/mycli/main.py +++ b/mycli/main.py @@ -23,7 +23,6 @@ import mycli as mycli_package from mycli.cli_runner import run_from_cli_args -from mycli.client import MyCli as MyCli from mycli.constants import ( DEFAULT_PROMPT, EMPTY_PASSWORD_FLAG_SENTINEL, diff --git a/mycli_test/pytests/test_client_query.py b/mycli_test/pytests/test_client_query.py index e7130e02..fdd307ac 100644 --- a/mycli_test/pytests/test_client_query.py +++ b/mycli_test/pytests/test_client_query.py @@ -3,7 +3,8 @@ import pytest -from mycli import client_query, main +from mycli import client_query +import mycli.client as mycli_client from mycli.packages.sql_result.sql_result import SQLResult from mycli.types import Query from mycli_test.utils import ( # type: ignore[attr-defined] @@ -51,7 +52,7 @@ def make_refresh_cli() -> tuple[Any, dict[str, Any]]: def test_refresh_completions_stops_prefetch() -> None: cli, state = make_refresh_cli() - main.MyCli.refresh_completions(cli) + mycli_client.MyCli.refresh_completions(cli) assert state['stopped'] == [True] @@ -59,7 +60,7 @@ def test_refresh_completions_stops_prefetch() -> None: def test_refresh_completions_returns_started_status() -> None: cli, _state = make_refresh_cli() - result: list[SQLResult] = main.MyCli.refresh_completions(cli) + result: list[SQLResult] = mycli_client.MyCli.refresh_completions(cli) assert result == [SQLResult(status='Auto-completion refresh started in the background.')] @@ -67,7 +68,7 @@ def test_refresh_completions_returns_started_status() -> None: def test_refresh_completions_passes_options_to_refresher() -> None: cli, state = make_refresh_cli() - main.MyCli.refresh_completions(cli) + mycli_client.MyCli.refresh_completions(cli) assert state['refresh_calls'] == [ ( @@ -94,7 +95,7 @@ def test_refresh_completions_passes_options_to_refresher() -> None: def test_refresh_completions_does_not_update_dbname_without_reset() -> None: cli, state = make_refresh_cli() - main.MyCli.refresh_completions(cli) + mycli_client.MyCli.refresh_completions(cli) assert state['set_dbname_calls'] == [] assert state['completion_stopped'] == [] @@ -103,7 +104,7 @@ def test_refresh_completions_does_not_update_dbname_without_reset() -> None: def test_refresh_completions_stops_completion_worker_when_reset() -> None: cli, state = make_refresh_cli() - main.MyCli.refresh_completions(cli, reset=True) + mycli_client.MyCli.refresh_completions(cli, reset=True) assert state['completion_stopped'] == [True] @@ -129,7 +130,7 @@ def test_refresh_completions_updates_dbname_when_reset() -> None: cli.main_formatter = SimpleNamespace(supported_formats=['table']) cli.completion_refresher = SimpleNamespace(stop=lambda: None, refresh=lambda executor, callbacks, options: None) - main.MyCli.refresh_completions(cli, reset=True) + mycli_client.MyCli.refresh_completions(cli, reset=True) assert set_dbname_calls == ['next_db'] @@ -156,7 +157,7 @@ def test_refresh_completions_uses_lock_when_reset() -> None: cli.main_formatter = SimpleNamespace(supported_formats=['table']) cli.completion_refresher = SimpleNamespace(stop=lambda: None, refresh=lambda executor, callbacks, options: None) - main.MyCli.refresh_completions(cli, reset=True) + mycli_client.MyCli.refresh_completions(cli, reset=True) assert entered_lock == {'count': 1} @@ -187,7 +188,7 @@ def make_refreshed_cli() -> tuple[Any, Any, Any, dict[str, Any]]: def test_on_completions_refreshed_swaps_completer() -> None: cli, _old_completer, new_completer, _state = make_refreshed_cli() - main.MyCli._on_completions_refreshed(cli, new_completer) + mycli_client.MyCli._on_completions_refreshed(cli, new_completer) assert cli.completer is new_completer @@ -195,7 +196,7 @@ def test_on_completions_refreshed_swaps_completer() -> None: def test_on_completions_refreshed_copies_other_schemas() -> None: cli, old_completer, new_completer, state = make_refreshed_cli() - main.MyCli._on_completions_refreshed(cli, new_completer) + mycli_client.MyCli._on_completions_refreshed(cli, new_completer) assert state['copy_calls'] == [(old_completer, 'current')] @@ -203,7 +204,7 @@ def test_on_completions_refreshed_copies_other_schemas() -> None: def test_on_completions_refreshed_uses_lock() -> None: cli, _old_completer, new_completer, state = make_refreshed_cli() - main.MyCli._on_completions_refreshed(cli, new_completer) + mycli_client.MyCli._on_completions_refreshed(cli, new_completer) assert state['entered_lock'] == {'count': 1} @@ -211,7 +212,7 @@ def test_on_completions_refreshed_uses_lock() -> None: def test_on_completions_refreshed_invalidates_prompt() -> None: cli, _old_completer, new_completer, state = make_refreshed_cli() - main.MyCli._on_completions_refreshed(cli, new_completer) + mycli_client.MyCli._on_completions_refreshed(cli, new_completer) assert state['invalidated'] == [True] @@ -219,7 +220,7 @@ def test_on_completions_refreshed_invalidates_prompt() -> None: def test_on_completions_refreshed_starts_schema_prefetch() -> None: cli, _old_completer, new_completer, state = make_refreshed_cli() - main.MyCli._on_completions_refreshed(cli, new_completer) + mycli_client.MyCli._on_completions_refreshed(cli, new_completer) assert state['prefetch_started'] == [True] @@ -257,7 +258,7 @@ def format_sqlresult(result: SQLResult, **kwargs: Any) -> list[str]: cli.log_query = lambda query: state['logged_queries'].append(query) cli.log_output = lambda line: state['logged_output'].append(line) cli.format_sqlresult = format_sqlresult - main.MyCli.run_query(cli, 'select 1;\n', checkpoint=str(state['checkpoint_path']), new_line=False) + mycli_client.MyCli.run_query(cli, 'select 1;\n', checkpoint=str(state['checkpoint_path']), new_line=False) state['cli'] = cli return state @@ -344,7 +345,7 @@ def test_run_query_raises_for_error_result_when_requested(tmp_path) -> None: cli.log_output = logged_output.append with pytest.raises(client_query.QueryError, match='source failed'): - main.MyCli.run_query( + mycli_client.MyCli.run_query( cli, '/source test.sql', checkpoint=str(checkpoint_path), @@ -369,7 +370,7 @@ def test_run_query_displays_error_result_by_default(monkeypatch) -> None: monkeypatch.setattr(client_query.special_commands, 'is_show_warnings_enabled', lambda: False) monkeypatch.setattr(client_query.click, 'echo', lambda line, nl=True: echoed.append(line)) - main.MyCli.run_query(cli, '/source test.sql') + mycli_client.MyCli.run_query(cli, '/source test.sql') assert echoed == ['source failed'] @@ -388,7 +389,7 @@ def test_run_query_displays_set_buffer_fallback_outside_repl(monkeypatch) -> Non monkeypatch.setattr(client_query.special_commands, 'is_show_warnings_enabled', lambda: False) monkeypatch.setattr(client_query.click, 'echo', lambda line, nl=True: echoed.append(line)) - main.MyCli.run_query(cli, '/favorite eval report') + mycli_client.MyCli.run_query(cli, '/favorite eval report') assert echoed == [status] @@ -396,11 +397,11 @@ def test_run_query_displays_set_buffer_fallback_outside_repl(monkeypatch) -> Non def test_get_last_query_returns_none() -> None: cli = make_bare_mycli() - assert main.MyCli.get_last_query(cli) is None + assert mycli_client.MyCli.get_last_query(cli) is None def test_get_last_query_returns_latest_query() -> None: cli = make_bare_mycli() cli.query_history = [Query('select 1', True, False), Query('select 2', True, False)] - assert main.MyCli.get_last_query(cli) == 'select 2' + assert mycli_client.MyCli.get_last_query(cli) == 'select 2' diff --git a/mycli_test/pytests/test_main.py b/mycli_test/pytests/test_main.py index 1b0c9d53..3092cab3 100644 --- a/mycli_test/pytests/test_main.py +++ b/mycli_test/pytests/test_main.py @@ -31,6 +31,8 @@ from mycli import config as config_module from mycli import main import mycli.cli_runner +import mycli.client as client_module +from mycli.client import MyCli import mycli.client_connection from mycli.constants import ( DEFAULT_DATABASE, @@ -44,7 +46,6 @@ from mycli.main import ( INT_OR_STRING_CLICK_TYPE, CliArgs, - MyCli, click_entrypoint, get_password_from_file, preprocess_cli_args, @@ -960,7 +961,7 @@ def test_completions_prints_packaged_script_without_initializing_client( def fail(*args: Any, **kwargs: Any) -> None: pytest.fail('Completion output must not initialize the client or preprocess connection arguments.') - monkeypatch.setattr(main, 'MyCli', fail) + monkeypatch.setattr(client_module, 'MyCli', fail) monkeypatch.setattr(main, 'preprocess_cli_args', fail) completion_path = Path(project_root_dir, 'mycli', 'resources', 'completions', *relative_path) @@ -1016,7 +1017,7 @@ def close(self, **args): import mycli.main - monkeypatch.setattr(mycli.main, 'MyCli', MockMyCli) + monkeypatch.setattr(client_module, 'MyCli', MockMyCli) runner = CliRunner() result = runner.invoke(mycli.main.click_entrypoint, args=['--boundary-id', 'ttcp_123', '--execute', 'select 1']) @@ -1026,7 +1027,7 @@ def close(self, **args): def test_dsn(monkeypatch): - # Setup classes to mock mycli.main.MyCli + # Setup classes to mock mycli.client.MyCli class Formatter: format_name = None @@ -1065,7 +1066,7 @@ def close(self, **args): import mycli.main - monkeypatch.setattr(mycli.main, "MyCli", MockMyCli) + monkeypatch.setattr(client_module, 'MyCli', MockMyCli) runner = CliRunner() # When a user supplies a DSN as database argument to mycli, @@ -1327,7 +1328,7 @@ def close(self, **args): import mycli.main - monkeypatch.setattr(mycli.main, 'MyCli', MockMyCli) + monkeypatch.setattr(client_module, 'MyCli', MockMyCli) monkeypatch.setenv('MYSQL_DSN', 'mysql://dsn_user:dsn_passwd@dsn_host:7/dsn_database') runner = CliRunner() @@ -1382,7 +1383,7 @@ def close(self, **args): import mycli.main - monkeypatch.setattr(mycli.main, 'MyCli', MockMyCli) + monkeypatch.setattr(client_module, 'MyCli', MockMyCli) runner = CliRunner() result = runner.invoke( @@ -1443,7 +1444,7 @@ def close(self, **args): import mycli.main - monkeypatch.setattr(mycli.main, 'MyCli', MockMyCli) + monkeypatch.setattr(client_module, 'MyCli', MockMyCli) monkeypatch.setenv('MYSQL_PWD', 'env_password') runner = CliRunner() @@ -1514,7 +1515,7 @@ def close(self, **args): import mycli.main - monkeypatch.setattr(mycli.main, 'MyCli', MockMyCli) + monkeypatch.setattr(client_module, 'MyCli', MockMyCli) runner = CliRunner() with NamedTemporaryFile(prefix=TEMPFILE_PREFIX, mode='w', delete=False) as password_file: @@ -1598,7 +1599,7 @@ def close(self, **args): import mycli.main - monkeypatch.setattr(mycli.main, 'MyCli', MockMyCli) + monkeypatch.setattr(client_module, 'MyCli', MockMyCli) runner = CliRunner() result = runner.invoke( @@ -1673,7 +1674,7 @@ def close(self, **args): import mycli.main - monkeypatch.setattr(mycli.main, 'MyCli', MockMyCli) + monkeypatch.setattr(client_module, 'MyCli', MockMyCli) runner = CliRunner() result = runner.invoke( @@ -1744,7 +1745,7 @@ def close(self, **args): import mycli.main - monkeypatch.setattr(mycli.main, 'MyCli', MockMyCli) + monkeypatch.setattr(client_module, 'MyCli', MockMyCli) runner = CliRunner() result = runner.invoke( @@ -1801,7 +1802,7 @@ def close(self, **args): import mycli.main - monkeypatch.setattr(mycli.main, 'MyCli', MockMyCli) + monkeypatch.setattr(client_module, 'MyCli', MockMyCli) runner = CliRunner() result = runner.invoke( @@ -1872,7 +1873,7 @@ def close(self, **args): import mycli.main - monkeypatch.setattr(mycli.main, 'MyCli', MockMyCli) + monkeypatch.setattr(client_module, 'MyCli', MockMyCli) runner = CliRunner() result = runner.invoke( @@ -1941,7 +1942,7 @@ def close(self, **args): import mycli.main - monkeypatch.setattr(mycli.main, 'MyCli', MockMyCli) + monkeypatch.setattr(client_module, 'MyCli', MockMyCli) monkeypatch.setenv('MYSQL_USER', 'env_user') runner = CliRunner() @@ -2090,7 +2091,7 @@ def close(self): import mycli.main - monkeypatch.setattr(mycli.main, 'MyCli', MockMyCli) + monkeypatch.setattr(client_module, 'MyCli', MockMyCli) return mycli.main, mycli.main_modes.batch, MockMyCli @@ -2124,7 +2125,7 @@ def test_quiet_sets_negative_cli_verbosity(monkeypatch: pytest.MonkeyPatch) -> N 'alias_dsn': {}, } ) - monkeypatch.setattr(main, 'MyCli', dummy_class) + monkeypatch.setattr(client_module, 'MyCli', dummy_class) monkeypatch.setattr(sys, 'stdin', SimpleNamespace(isatty=lambda: True)) cli_args = main.CliArgs() @@ -2206,13 +2207,13 @@ def test_change_prompt_format_without_argument_shows_current_format() -> None: cli = make_bare_mycli() cli.prompt_format = '\\u> ' - assert main.MyCli.change_prompt_format(cli, '')[0].status == 'Prompt format: "\\u> "' + assert client_module.MyCli.change_prompt_format(cli, '')[0].status == 'Prompt format: "\\u> "' assert cli.prompt_format == '\\u> ' def test_change_prompt_format_updates_prompt() -> None: cli = make_bare_mycli() - assert main.MyCli.change_prompt_format(cli, '\\u@\\h> ')[0].status == 'Changed prompt format to: "\\u@\\h> "' + assert client_module.MyCli.change_prompt_format(cli, '\\u@\\h> ')[0].status == 'Changed prompt format to: "\\u@\\h> "' def test_output_timing_logs_and_prints_with_warning_style(monkeypatch: pytest.MonkeyPatch) -> None: @@ -2221,7 +2222,7 @@ def test_output_timing_logs_and_prints_with_warning_style(monkeypatch: pytest.Mo cli.log_output = lambda text: timings_logged.append(text) # type: ignore[assignment] printed: list[tuple[Any, Any]] = [] monkeypatch.setattr(prompt_toolkit, 'print_formatted_text', lambda text, style=None: printed.append((text, style))) - main.MyCli.output_timing(cli, 'Time: 1.000s', is_warnings_style=True) + client_module.MyCli.output_timing(cli, 'Time: 1.000s', is_warnings_style=True) assert timings_logged == ['Time: 1.000s'] assert printed[-1][1] == cli.prompt_toolkit_style @@ -2242,7 +2243,7 @@ def fake_render_prompt_string(mycli: Any, string: str, render_counter: int) -> F monkeypatch.setattr(repl_mode, 'render_prompt_string', fake_render_prompt_string) monkeypatch.setattr(mycli.packages.special_commands, 'is_timing_enabled', lambda: False) - assert main.MyCli.get_output_margin(cli, 'ok') == 5 + assert client_module.MyCli.get_output_margin(cli, 'ok') == 5 assert render_counters == [7] @@ -2262,7 +2263,7 @@ def test_on_completions_refreshed_updates_completer_and_invalidates_prompt() -> copy_other_schemas_from=lambda source, exclude: copy_calls.append((source, exclude)), ), ) - main.MyCli._on_completions_refreshed(cli, new_completer) + client_module.MyCli._on_completions_refreshed(cli, new_completer) assert cli.completer is new_completer assert invalidated == [True] assert entered_lock['count'] == 1 @@ -2278,7 +2279,7 @@ def test_click_entrypoint_callback_covers_dsn_list_init_commands(monkeypatch: py 'alias_dsn.init-commands': {'prod': ['set a=1', 'set b=2']}, } ) - monkeypatch.setattr(main, 'MyCli', dummy_class) + monkeypatch.setattr(client_module, 'MyCli', dummy_class) monkeypatch.setattr(sys, 'stdin', SimpleNamespace(isatty=lambda: True)) monkeypatch.setattr(sys.stderr, 'isatty', lambda: True) @@ -2300,7 +2301,7 @@ def test_click_entrypoint_callback_uses_batch_with_progress_path(monkeypatch: py 'alias_dsn': {}, } ) - monkeypatch.setattr(main, 'MyCli', dummy_class) + monkeypatch.setattr(client_module, 'MyCli', dummy_class) monkeypatch.setattr(sys, 'stdin', SimpleNamespace(isatty=lambda: True)) monkeypatch.setattr(sys.stderr, 'isatty', lambda: True) monkeypatch.setattr(mycli.cli_runner, 'main_batch_with_progress_bar', lambda mycli, cli_args: 12) @@ -2321,7 +2322,7 @@ def test_click_entrypoint_callback_uses_batch_without_progress_path(monkeypatch: 'alias_dsn': {}, } ) - monkeypatch.setattr(main, 'MyCli', dummy_class) + monkeypatch.setattr(client_module, 'MyCli', dummy_class) monkeypatch.setattr(sys, 'stdin', SimpleNamespace(isatty=lambda: True)) monkeypatch.setattr(sys.stderr, 'isatty', lambda: True) monkeypatch.setattr(mycli.cli_runner, 'main_batch_without_progress_bar', lambda mycli, cli_args: 13) @@ -2340,7 +2341,7 @@ def test_format_sqlresult_uses_redirect_formatter_when_redirected() -> None: cli.redirect_formatter = DummyFormatter() result = SQLResult(header=['id'], rows=[(1,)], status='ok') - assert list(main.MyCli.format_sqlresult(cli, result, is_redirected=True)) == ['plain output'] + assert list(client_module.MyCli.format_sqlresult(cli, result, is_redirected=True)) == ['plain output'] assert cli.main_formatter.calls == [] assert len(cli.redirect_formatter.calls) == 1 @@ -2353,7 +2354,7 @@ def test_format_sqlresult_materializes_cursor_rows_when_width_is_limited(monkeyp monkeypatch.setattr(output_module, 'Cursor', FakeCursorBase) result = SQLResult(header=['id'], rows=cast(Any, rows), status='ok') - list(main.MyCli.format_sqlresult(cli, result, max_width=100)) + list(client_module.MyCli.format_sqlresult(cli, result, max_width=100)) formatted_rows = cli.main_formatter.calls[-1][0][0] assert formatted_rows == [(1,)] @@ -2363,14 +2364,14 @@ def test_format_sqlresult_appends_postamble() -> None: cli = make_bare_mycli() result = SQLResult(header=['id'], rows=[(1,)], status='ok', postamble='done') - assert list(main.MyCli.format_sqlresult(cli, result))[-1] == 'done' + assert list(client_module.MyCli.format_sqlresult(cli, result))[-1] == 'done' def test_get_last_query_returns_latest_query() -> None: cli = make_bare_mycli() cli.query_history = [Query('select 1', True, False)] - assert main.MyCli.get_last_query(cli) == 'select 1' + assert client_module.MyCli.get_last_query(cli) == 'select 1' def test_connect_reports_expired_password_login_error(monkeypatch: pytest.MonkeyPatch) -> None: @@ -2390,7 +2391,7 @@ class ExpiredPasswordSQLExecute(RecordingSQLExecute): monkeypatch.setattr(mycli.client_connection, 'SQLExecute', ExpiredPasswordSQLExecute) with pytest.raises(SystemExit): - main.MyCli.connect(cli, host='db', port=3307) + client_module.MyCli.connect(cli, host='db', port=3307) assert any('password has expired' in message for message in echo_calls) @@ -2415,7 +2416,7 @@ def __init__(self, **kwargs: Any) -> None: monkeypatch.setattr(mycli.client_connection, 'SQLExecute', SandboxSQLExecute) - main.MyCli.connect(cli, host='db', port=3307) + client_module.MyCli.connect(cli, host='db', port=3307) assert cli.sandbox_mode is True assert any('password has expired' in message for message in echo_calls) diff --git a/mycli_test/pytests/test_main_modes_list_dsn.py b/mycli_test/pytests/test_main_modes_list_dsn.py index 1cb8e9db..01bc10d4 100644 --- a/mycli_test/pytests/test_main_modes_list_dsn.py +++ b/mycli_test/pytests/test_main_modes_list_dsn.py @@ -11,6 +11,7 @@ import pytest from mycli import cli_runner, main +from mycli import client as client_module from mycli import config as config_module from mycli.main_modes import list_dsn as list_dsn_mode from mycli.packages.utils import dsn_utils @@ -253,7 +254,7 @@ def test_completions_take_precedence_over_list_dsn(monkeypatch: pytest.MonkeyPat def test_checkup_takes_precedence_over_list_dsn(monkeypatch: pytest.MonkeyPatch) -> None: client = object() - monkeypatch.setattr(main, 'MyCli', lambda **kwargs: client) + monkeypatch.setattr(client_module, 'MyCli', lambda **kwargs: client) monkeypatch.setattr(cli_runner, 'load_list_dsn_config', lambda path: pytest.fail('config loaded')) calls: list[object] = [] monkeypatch.setattr(cli_runner, 'main_checkup', calls.append) diff --git a/mycli_test/pytests/test_tabular_output.py b/mycli_test/pytests/test_tabular_output.py index 48e820f9..beb5d314 100644 --- a/mycli_test/pytests/test_tabular_output.py +++ b/mycli_test/pytests/test_tabular_output.py @@ -9,7 +9,7 @@ from pymysql.constants import FIELD_TYPE import pytest -from mycli.main import MyCli +from mycli.client import MyCli from mycli.packages.credentials.password_sources import PasswordCandidates from mycli.packages.sql_result.sql_result import SQLResult from mycli_test.utils import HOST, PASSWORD, PORT, USER, dbtest diff --git a/mycli_test/utils.py b/mycli_test/utils.py index 58ea21df..7fe654c7 100644 --- a/mycli_test/utils.py +++ b/mycli_test/utils.py @@ -16,6 +16,7 @@ import pytest from mycli import main +import mycli.client as mycli_client from mycli.constants import ( DEFAULT_CHARSET, DEFAULT_HOST, @@ -139,7 +140,7 @@ def __init__(self, **kwargs: Any) -> None: def make_bare_mycli() -> Any: - cli = object.__new__(main.MyCli) + cli = object.__new__(mycli_client.MyCli) cli.logger = cast(Any, DummyLogger()) cli.main_formatter = DummyFormatter() cli.redirect_formatter = DummyFormatter() @@ -159,7 +160,7 @@ def make_bare_mycli() -> Any: cli.last_prompt_message = ANSI('') cli.last_custom_toolbar_message = ANSI('') cli.prompt_lines = 0 - cli.prompt_format = main.MyCli.default_prompt + cli.prompt_format = mycli_client.MyCli.default_prompt cli.multiline_continuation_char = '>' cli.toolbar_format = 'default' cli.destructive_warning = False