From 388a872854c890529a9a19804e119933359bbd21 Mon Sep 17 00:00:00 2001 From: Roland Walker Date: Sat, 21 Feb 2026 11:10:08 -0500 Subject: [PATCH] accept true/false for ssl_mode, keeping on/off More liberally accept true/false/on/off (and capitalization variants) for --ssl_mode. Some inconsistencies in the interface have crept in as features were added. Some settings from ~/.myclirc especially, but also in the CLI arguments, use str_to_bool() for liberal boolean parsing. Other settings, such as --ssl_mode, are using on/off only. Other settings, such as --use_keyring, are using true/false only. This change is a step towards converging all boolean settings on liberal str_to_bool(). It should ultimately include DSN query parameters, too. The "auto" value for ssl_mode is still special-cased, and not affected by these changes. The only loss is that click doesn't automatically display the choices in the helpdoc, but they are already listed in the description. We keep the description targeted to on/off, and only silently accept the other variants. --- changelog.md | 1 + mycli/config.py | 7 +++++++ mycli/main.py | 24 +++++++++++++++++++++--- test/test_config.py | 20 ++++++++++++++++++++ test/test_main.py | 22 ++++++++++++++++++++++ 5 files changed, 71 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 188adc7f..2b2d34fb 100644 --- a/changelog.md +++ b/changelog.md @@ -10,6 +10,7 @@ Features * Let `help ` list similar keywords when not found. * Optionally highlight fuzzy search previews. * Make `\edit` synonymous with the `\e` command. +* More liberally accept `on`/`off` values for `true`/`false`, and vice versa. Bug Fixes diff --git a/mycli/config.py b/mycli/config.py index a79b1021..17fe4541 100644 --- a/mycli/config.py +++ b/mycli/config.py @@ -305,6 +305,13 @@ def str_to_bool(s: str | bool) -> bool: raise ValueError(f'not a recognized boolean value: {s}') +def str_to_on_off(s: str | bool) -> str: + bool_str = str(str_to_bool(s)) + if bool_str == 'True': + return 'on' + return 'off' + + def strip_matching_quotes(s: str) -> str: """Remove matching, surrounding quotes from a string. diff --git a/mycli/main.py b/mycli/main.py index 52110157..27eab5aa 100755 --- a/mycli/main.py +++ b/mycli/main.py @@ -58,7 +58,15 @@ from mycli.clitoolbar import create_toolbar_tokens_func from mycli.compat import WIN from mycli.completion_refresher import CompletionRefresher -from mycli.config import get_mylogin_cnf_path, open_mylogin_cnf, read_config_files, str_to_bool, strip_matching_quotes, write_default_config +from mycli.config import ( + get_mylogin_cnf_path, + open_mylogin_cnf, + read_config_files, + str_to_bool, + str_to_on_off, + strip_matching_quotes, + write_default_config, +) from mycli.constants import ISSUES_URL from mycli.key_bindings import mycli_bindings from mycli.lexer import MyCliLexer @@ -226,7 +234,9 @@ def __init__( # set ssl_mode if a valid option is provided in a config file, otherwise None ssl_mode = c["main"].get("ssl_mode", None) or c["connection"].get("default_ssl_mode", None) - if ssl_mode not in ("auto", "on", "off", None): + if ssl_mode is None: + self.ssl_mode = ssl_mode + elif ssl_mode.lower() not in ("auto", "on", "off", "1", "0", "true", "false"): self.echo(f"Invalid config option provided for ssl_mode ({ssl_mode}); ignoring.", err=True, fg="red") self.ssl_mode = None else: @@ -1720,7 +1730,7 @@ def get_last_query(self) -> str | None: "--ssl-mode", "ssl_mode", help="Set desired SSL behavior. auto=preferred if TCP/IP, on=required, off=off.", - type=click.Choice(["auto", "on", "off"]), + type=str, ) @click.option("--ssl/--no-ssl", "ssl_enable", default=None, help="Enable SSL for connection (automatically enabled with other flags).") @click.option("--ssl-ca", help="CA file in PEM format.", type=click.Path(exists=True)) @@ -2080,6 +2090,14 @@ def get_password_from_file(password_file: str | None) -> str | None: keepalive_ticks = keepalive_ticks if keepalive_ticks is not None else mycli.default_keepalive_ticks ssl_mode = ssl_mode or mycli.ssl_mode # cli option or config option + if ssl_mode: + ssl_mode = ssl_mode.lower() + if ssl_mode and ssl_mode != 'auto': + try: + ssl_mode = str_to_on_off(ssl_mode) + except ValueError: + click.secho('Unknown value for ssl_mode', err=True, fg='red') + sys.exit(1) # if there is a mismatch between the ssl_mode value and other sources of ssl config, show a warning # specifically using "is False" to not pickup the case where ssl_enable is None (not set by the user) diff --git a/test/test_config.py b/test/test_config.py index 5bb0ab4f..fda9bc4e 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -16,6 +16,7 @@ read_and_decrypt_mylogin_cnf, read_config_file, str_to_bool, + str_to_on_off, strip_matching_quotes, ) @@ -149,6 +150,25 @@ def test_str_to_bool(): str_to_bool(None) +def test_str_to_on_off(): + assert str_to_on_off(False) == 'off' + assert str_to_on_off(True) == 'on' + assert str_to_on_off("False") == 'off' + assert str_to_on_off("True") == 'on' + assert str_to_on_off("TRUE") == 'on' + assert str_to_on_off("1") == 'on' + assert str_to_on_off("0") == 'off' + assert str_to_on_off("on") == 'on' + assert str_to_on_off("off") == 'off' + assert str_to_on_off("off") == 'off' + + with pytest.raises(ValueError): + str_to_on_off("foo") + + with pytest.raises(TypeError): + str_to_on_off(None) + + def test_read_config_file_list_values_default(): """Test that reading a config file uses list_values by default.""" diff --git a/test/test_main.py b/test/test_main.py index fc8b3a9b..79700524 100644 --- a/test/test_main.py +++ b/test/test_main.py @@ -156,6 +156,17 @@ def test_ssl_mode_on(executor, capsys): assert ssl_cipher +@dbtest +def test_ssl_mode_true(executor, capsys): + runner = CliRunner() + ssl_mode = 'true' + sql = 'select * from performance_schema.session_status where variable_name = "Ssl_cipher"' + result = runner.invoke(cli, args=CLI_ARGS + ['--csv', '--ssl-mode', ssl_mode], input=sql) + result_dict = next(csv.DictReader(result.stdout.split('\n'))) + ssl_cipher = result_dict.get('VARIABLE_VALUE', None) + assert ssl_cipher + + @dbtest def test_ssl_mode_auto(executor, capsys): runner = CliRunner() @@ -178,6 +189,17 @@ def test_ssl_mode_off(executor, capsys): assert not ssl_cipher +@dbtest +def test_ssl_mode_false(executor, capsys): + runner = CliRunner() + ssl_mode = 'False' + sql = 'select * from performance_schema.session_status where variable_name = "Ssl_cipher"' + result = runner.invoke(cli, args=CLI_ARGS + ['--csv', '--ssl-mode', ssl_mode], input=sql) + result_dict = next(csv.DictReader(result.stdout.split('\n'))) + ssl_cipher = result_dict.get('VARIABLE_VALUE', None) + assert not ssl_cipher + + @dbtest def test_ssl_mode_overrides_ssl(executor, capsys): runner = CliRunner()