Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Features
---------
* Respond to `-h` alone with the helpdoc.
* Allow `--hostname` as an alias for `--host`.
* Deprecate `$DSN` environment variable in favor of `$MYSQL_DSN`.


Bug Fixes
Expand Down
12 changes: 11 additions & 1 deletion mycli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2055,7 +2055,7 @@ class CliArgs:
'-d',
type=str,
default='',
envvar='DSN',
envvar='MYSQL_DSN',
help='DSN alias configured in the ~/.myclirc file, or a full DSN.',
)
list_dsn: bool = clickdc.option(
Expand Down Expand Up @@ -2344,6 +2344,16 @@ def get_password_from_file(password_file: str | None) -> str | None:
if not cli_args.socket:
cli_args.socket = os.environ['MYSQL_UNIX_PORT']

if 'DSN' in os.environ:
# deprecated 2026-03
click.secho(
"The DSN environment variable is deprecated in favor of MYSQL_DSN. Support for DSN will be removed in a future release.",
err=True,
fg="red",
)
if not cli_args.dsn:
cli_args.dsn = os.environ['DSN']

# Choose which ever one has a valid value.
database = cli_args.dbname or cli_args.database

Expand Down
106 changes: 106 additions & 0 deletions test/pytests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -1118,6 +1118,112 @@ def run_query(self, query, new_line=True):
assert MockMyCli.connect_args['character_set'] == 'utf8mb3'


def test_mysql_dsn_envvar(monkeypatch):
class Formatter:
format_name = None

class Logger:
def debug(self, *args, **args_dict):
pass

def warning(self, *args, **args_dict):
pass

class MockMyCli:
config = {
'main': {},
'alias_dsn': {},
'connection': {
'default_keepalive_ticks': 0,
},
}

def __init__(self, **_args):
self.logger = Logger()
self.destructive_warning = False
self.main_formatter = Formatter()
self.redirect_formatter = Formatter()
self.ssl_mode = 'auto'
self.my_cnf = {'client': {}, 'mysqld': {}}
self.default_keepalive_ticks = 0

def connect(self, **args):
MockMyCli.connect_args = args

def run_query(self, query, new_line=True):
pass

import mycli.main

monkeypatch.setattr(mycli.main, 'MyCli', MockMyCli)
monkeypatch.setenv('MYSQL_DSN', 'mysql://dsn_user:dsn_passwd@dsn_host:7/dsn_database')
runner = CliRunner()

result = runner.invoke(mycli.main.click_entrypoint)
assert result.exit_code == 0, result.output + ' ' + str(result.exception)
assert 'DSN environment variable is deprecated' not in result.output
assert (
MockMyCli.connect_args['user'] == 'dsn_user'
and MockMyCli.connect_args['passwd'] == 'dsn_passwd'
and MockMyCli.connect_args['host'] == 'dsn_host'
and MockMyCli.connect_args['port'] == 7
and MockMyCli.connect_args['database'] == 'dsn_database'
)


def test_legacy_dsn_envvar_warns_and_falls_back(monkeypatch):
class Formatter:
format_name = None

class Logger:
def debug(self, *args, **args_dict):
pass

def warning(self, *args, **args_dict):
pass

class MockMyCli:
config = {
'main': {},
'alias_dsn': {},
'connection': {
'default_keepalive_ticks': 0,
},
}

def __init__(self, **_args):
self.logger = Logger()
self.destructive_warning = False
self.main_formatter = Formatter()
self.redirect_formatter = Formatter()
self.ssl_mode = 'auto'
self.my_cnf = {'client': {}, 'mysqld': {}}
self.default_keepalive_ticks = 0

def connect(self, **args):
MockMyCli.connect_args = args

def run_query(self, query, new_line=True):
pass

import mycli.main

monkeypatch.setattr(mycli.main, 'MyCli', MockMyCli)
monkeypatch.setenv('DSN', 'mysql://dsn_user:dsn_passwd@dsn_host:8/dsn_database')
runner = CliRunner()

result = runner.invoke(mycli.main.click_entrypoint)
assert result.exit_code == 0, result.output + ' ' + str(result.exception)
assert 'The DSN environment variable is deprecated' in result.output
assert (
MockMyCli.connect_args['user'] == 'dsn_user'
and MockMyCli.connect_args['passwd'] == 'dsn_passwd'
and MockMyCli.connect_args['host'] == 'dsn_host'
and MockMyCli.connect_args['port'] == 8
and MockMyCli.connect_args['database'] == 'dsn_database'
)


def test_password_flag_uses_sentinel(monkeypatch):
class Formatter:
format_name = None
Expand Down
Loading