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
7 changes: 6 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ Upcoming (TBD)

Features
---------
* Makes short toolbar message show after initial prompt
* Make short toolbar message show after initial prompt.


Internal
---------
* Migrate more repeated values to `constants.py`.


1.62.0 (2026/03/07)
Expand Down
8 changes: 8 additions & 0 deletions mycli/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,11 @@
REPO_URL = 'https://github.com/dbcli/mycli'
DOCS_URL = f'{HOME_URL}/docs'
ISSUES_URL = f'{REPO_URL}/issues'

DEFAULT_CHARSET = 'utf8mb4'
DEFAULT_DATABASE = 'mysql'
DEFAULT_HOST = 'localhost'
DEFAULT_PORT = 3306
DEFAULT_USER = 'root'

TEST_DATABASE = 'mycli_test_db'
25 changes: 16 additions & 9 deletions mycli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,14 @@
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.constants import HOME_URL, ISSUES_URL, REPO_URL
from mycli.constants import (
DEFAULT_CHARSET,
DEFAULT_HOST,
DEFAULT_PORT,
HOME_URL,
ISSUES_URL,
REPO_URL,
)
from mycli.key_bindings import mycli_bindings
from mycli.lexer import MyCliLexer
from mycli.packages import special
Expand Down Expand Up @@ -630,8 +637,8 @@ def connect(

int_port = port and int(port)
if not int_port:
int_port = 3306
if not host or host == "localhost":
int_port = DEFAULT_PORT
if not host or host == DEFAULT_HOST:
socket = (
socket
or user_connection_config.get("default_socket")
Expand All @@ -655,7 +662,7 @@ def connect(
elif 'default-character-set' in cnf:
character_set = cnf['default-character-set']
if not character_set:
character_set = 'utf8mb4'
character_set = DEFAULT_CHARSET

# Favor whichever local_infile option is set.
use_local_infile = False
Expand Down Expand Up @@ -824,15 +831,15 @@ def _connect(

# Else fall back to TCP/IP localhost
socket = ""
host = "localhost"
port = 3306
host = DEFAULT_HOST
port = DEFAULT_PORT
# todo should reload the keyring identifier here instead of invalidating
_connect(keyring_save_eligible=False)
else:
raise e
else:
host = host or "localhost"
port = port or 3306
host = host or DEFAULT_HOST
port = port or DEFAULT_PORT
# could try loading the keyring again here instead of assuming nothing important changed

# Bad ports give particularly daft error messages
Expand Down Expand Up @@ -1689,7 +1696,7 @@ def get_prompt(self, string: str, _render_counter: int) -> str:
elif sqlexecute.host is not None:
prompt_host = sqlexecute.host
else:
prompt_host = "localhost"
prompt_host = DEFAULT_HOST
short_prompt_host, _, _ = prompt_host.partition('.')
if re.match(r'^[\d\.]+$', short_prompt_host):
short_prompt_host = prompt_host
Expand Down
12 changes: 7 additions & 5 deletions test/features/db_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import pymysql

from mycli.constants import DEFAULT_CHARSET, DEFAULT_HOST, DEFAULT_PORT

def create_db(hostname="localhost", port=3306, username=None, password=None, dbname=None):

def create_db(hostname=DEFAULT_HOST, port=DEFAULT_PORT, username=None, password=None, dbname=None):
"""Create test database.

:param hostname: string
Expand All @@ -15,7 +17,7 @@ def create_db(hostname="localhost", port=3306, username=None, password=None, dbn

"""
cn = pymysql.connect(
host=hostname, port=port, user=username, password=password, charset="utf8mb4", cursorclass=pymysql.cursors.DictCursor
host=hostname, port=port, user=username, password=password, charset=DEFAULT_CHARSET, cursorclass=pymysql.cursors.DictCursor
)

with cn.cursor() as cr:
Expand Down Expand Up @@ -45,14 +47,14 @@ def create_cn(hostname, port, password, username, dbname):
user=username,
password=password,
db=dbname,
charset="utf8mb4",
charset=DEFAULT_CHARSET,
cursorclass=pymysql.cursors.DictCursor,
)

return cn


def drop_db(hostname="localhost", port=3306, username=None, password=None, dbname=None):
def drop_db(hostname=DEFAULT_HOST, port=DEFAULT_PORT, username=None, password=None, dbname=None):
"""Drop database.

:param hostname: string
Expand All @@ -68,7 +70,7 @@ def drop_db(hostname="localhost", port=3306, username=None, password=None, dbnam
user=username,
password=password,
db=dbname,
charset="utf8mb4",
charset=DEFAULT_CHARSET,
cursorclass=pymysql.cursors.DictCursor,
)

Expand Down
7 changes: 4 additions & 3 deletions test/features/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import fixture_utils as fixutils
import pexpect

from mycli.constants import DEFAULT_HOST, DEFAULT_PORT, DEFAULT_USER
from steps.wrappers import run_cli, wait_prompt
from test.utils import TEMPFILE_PREFIX

Expand Down Expand Up @@ -54,9 +55,9 @@ def before_all(context):

# Store get params from config/environment variables
context.conf = {
"host": context.config.userdata.get("my_test_host", os.getenv("PYTEST_HOST", "localhost")),
"port": context.config.userdata.get("my_test_port", int(os.getenv("PYTEST_PORT", "3306"))),
"user": context.config.userdata.get("my_test_user", os.getenv("PYTEST_USER", "root")),
"host": context.config.userdata.get("my_test_host", os.getenv("PYTEST_HOST", DEFAULT_HOST)),
"port": context.config.userdata.get("my_test_port", int(os.getenv("PYTEST_PORT", DEFAULT_PORT))),
"user": context.config.userdata.get("my_test_user", os.getenv("PYTEST_USER", DEFAULT_USER)),
"pass": context.config.userdata.get("my_test_pass", os.getenv("PYTEST_PASSWORD", None)),
"cli_command": context.config.userdata.get("my_cli_command", None)
or sys.executable + ' -c "import coverage ; coverage.process_startup(); import mycli.main; mycli.main.cli()"',
Expand Down
6 changes: 4 additions & 2 deletions test/features/steps/crud_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import pexpect
import wrappers

from mycli.constants import DEFAULT_DATABASE


@when("we create database")
def step_db_create(context):
Expand Down Expand Up @@ -53,8 +55,8 @@ def step_db_connect_tmp(context):
@when("we connect to dbserver")
def step_db_connect_dbserver(context):
"""Send connect to database."""
context.currentdb = "mysql"
context.cli.sendline("use mysql")
context.currentdb = DEFAULT_DATABASE
context.cli.sendline(f"use {DEFAULT_DATABASE}")


@then("dbcli exits")
Expand Down
65 changes: 36 additions & 29 deletions test/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@
from click.testing import CliRunner
from pymysql.err import OperationalError

from mycli.constants import (
DEFAULT_DATABASE,
DEFAULT_HOST,
DEFAULT_PORT,
DEFAULT_USER,
TEST_DATABASE,
)
from mycli.main import EMPTY_PASSWORD_FLAG_SENTINEL, MyCli, cli, thanks_picker
from mycli.packages.parseutils import is_valid_connection_scheme
import mycli.packages.special
Expand Down Expand Up @@ -40,7 +47,7 @@
default_config_file,
"--defaults-file",
default_config_file,
"mycli_test_db",
TEST_DATABASE,
]


Expand Down Expand Up @@ -137,12 +144,12 @@ def test_select_from_empty_table(executor):


def test_is_valid_connection_scheme_valid(executor, capsys):
is_valid, scheme = is_valid_connection_scheme("mysql://test@localhost:3306/dev")
is_valid, scheme = is_valid_connection_scheme(f"mysql://test@{DEFAULT_HOST}:{DEFAULT_PORT}/dev")
assert is_valid


def test_is_valid_connection_scheme_invalid(executor, capsys):
is_valid, scheme = is_valid_connection_scheme("nope://test@localhost:3306/dev")
is_valid, scheme = is_valid_connection_scheme(f"nope://test@{DEFAULT_HOST}:{DEFAULT_PORT}/dev")
assert not is_valid


Expand Down Expand Up @@ -285,8 +292,8 @@ def test_reconnect_with_different_database(executor):
None,
None,
)
database_1 = "mycli_test_db"
database_2 = "mysql"
database_1 = TEST_DATABASE
database_2 = DEFAULT_DATABASE
sql_1 = f"use {database_1}"
sql_2 = f"\\r {database_2}"
_result_1 = next(mycli.packages.special.execute(executor, sql_1))
Expand Down Expand Up @@ -316,7 +323,7 @@ def test_reconnect_with_same_database(executor):
None,
None,
)
database = "mysql"
database = DEFAULT_DATABASE
sql = f"\\u {database}"
result = next(mycli.packages.special.execute(executor, sql))
sql = f"\\r {database}"
Expand All @@ -333,11 +340,11 @@ def test_prompt_no_host_only_socket(executor):
mycli.sqlexecute.server_info = ServerInfo.from_version_string("8.0.44-0ubuntu0.24.04.1")
mycli.sqlexecute.host = None
mycli.sqlexecute.socket = "/var/run/mysqld/mysqld.sock"
mycli.sqlexecute.user = "root"
mycli.sqlexecute.dbname = "mysql"
mycli.sqlexecute.port = "3306"
mycli.sqlexecute.user = DEFAULT_USER
mycli.sqlexecute.dbname = DEFAULT_DATABASE
mycli.sqlexecute.port = DEFAULT_PORT
prompt = mycli.get_prompt(mycli.prompt_format, 0)
assert prompt == "MySQL root@localhost:mysql> "
assert prompt == f"MySQL {DEFAULT_USER}@{DEFAULT_HOST}:{DEFAULT_DATABASE}> "


@dbtest
Expand All @@ -348,11 +355,11 @@ def test_prompt_socket_overrides_port(executor):
mycli.sqlexecute.server_info = ServerInfo.from_version_string("8.0.44-0ubuntu0.24.04.1")
mycli.sqlexecute.host = None
mycli.sqlexecute.socket = "/var/run/mysqld/mysqld.sock"
mycli.sqlexecute.user = "root"
mycli.sqlexecute.dbname = "mysql"
mycli.sqlexecute.port = "3306"
mycli.sqlexecute.user = DEFAULT_USER
mycli.sqlexecute.dbname = DEFAULT_DATABASE
mycli.sqlexecute.port = DEFAULT_PORT
prompt = mycli.get_prompt(mycli.prompt_format, 0)
assert prompt == "MySQL root@localhost:mysqld.sock mysql> "
assert prompt == f"MySQL {DEFAULT_USER}@{DEFAULT_HOST}:mysqld.sock {DEFAULT_DATABASE}> "


@dbtest
Expand All @@ -361,13 +368,13 @@ def test_prompt_socket_short_host(executor):
mycli.prompt_format = "\\t \\u@\\H:\\k \\d> "
mycli.sqlexecute = SQLExecute
mycli.sqlexecute.server_info = ServerInfo.from_version_string("8.0.44-0ubuntu0.24.04.1")
mycli.sqlexecute.host = 'localhost.localdomain'
mycli.sqlexecute.host = f'{DEFAULT_HOST}.localdomain'
mycli.sqlexecute.socket = None
mycli.sqlexecute.user = "root"
mycli.sqlexecute.dbname = "mysql"
mycli.sqlexecute.port = "3306"
mycli.sqlexecute.user = DEFAULT_USER
mycli.sqlexecute.dbname = DEFAULT_DATABASE
mycli.sqlexecute.port = DEFAULT_PORT
prompt = mycli.get_prompt(mycli.prompt_format, 0)
assert prompt == "MySQL root@localhost:3306 mysql> "
assert prompt == f"MySQL {DEFAULT_USER}@{DEFAULT_HOST}:{DEFAULT_PORT} {DEFAULT_DATABASE}> "


@dbtest
Expand All @@ -391,11 +398,11 @@ def test_disable_show_warnings(executor):
@dbtest
def test_output_ddl_with_warning_and_show_warnings_enabled(executor):
runner = CliRunner()
db = "mycli_test_db"
db = TEST_DATABASE
table = "table_that_definitely_does_not_exist_1234"
sql = f"DROP TABLE IF EXISTS {db}.{table}"
result = runner.invoke(cli, args=CLI_ARGS + ["--show-warnings", "--no-warn"], input=sql)
expected = "Level\tCode\tMessage\nNote\t1051\tUnknown table 'mycli_test_db.table_that_definitely_does_not_exist_1234'\n"
expected = f"Level\tCode\tMessage\nNote\t1051\tUnknown table '{db}.table_that_definitely_does_not_exist_1234'\n"
assert expected in result.output


Expand Down Expand Up @@ -992,42 +999,42 @@ def run_query(self, query, new_line=True):
result = runner.invoke(
mycli.main.cli,
args=[
'mysql://dsn_user:dsn_passwd@localhost/dsn_database?socket=mysql.sock',
f'mysql://dsn_user:dsn_passwd@{DEFAULT_HOST}/dsn_database?socket=mysql.sock',
],
)
assert result.exit_code == 0, result.output + ' ' + str(result.exception)
assert MockMyCli.connect_args['user'] == 'dsn_user'
assert MockMyCli.connect_args['passwd'] == 'dsn_passwd'
assert MockMyCli.connect_args['host'] == 'localhost'
assert MockMyCli.connect_args['host'] == DEFAULT_HOST
assert MockMyCli.connect_args['database'] == 'dsn_database'
assert MockMyCli.connect_args['socket'] == 'mysql.sock'

# accept character_set as a query parameter
result = runner.invoke(
mycli.main.cli,
args=[
'mysql://dsn_user:dsn_passwd@localhost/dsn_database?character_set=latin1',
f'mysql://dsn_user:dsn_passwd@{DEFAULT_HOST}/dsn_database?character_set=latin1',
],
)
assert result.exit_code == 0, result.output + ' ' + str(result.exception)
assert MockMyCli.connect_args['user'] == 'dsn_user'
assert MockMyCli.connect_args['passwd'] == 'dsn_passwd'
assert MockMyCli.connect_args['host'] == 'localhost'
assert MockMyCli.connect_args['host'] == DEFAULT_HOST
assert MockMyCli.connect_args['database'] == 'dsn_database'
assert MockMyCli.connect_args['character_set'] == 'latin1'

# --character_set overrides character_set as a query parameter
result = runner.invoke(
mycli.main.cli,
args=[
'mysql://dsn_user:dsn_passwd@localhost/dsn_database?character_set=latin1',
f'mysql://dsn_user:dsn_passwd@{DEFAULT_HOST}/dsn_database?character_set=latin1',
'--character-set=utf8mb3',
],
)
assert result.exit_code == 0, result.output + ' ' + str(result.exception)
assert MockMyCli.connect_args['user'] == 'dsn_user'
assert MockMyCli.connect_args['passwd'] == 'dsn_passwd'
assert MockMyCli.connect_args['host'] == 'localhost'
assert MockMyCli.connect_args['host'] == DEFAULT_HOST
assert MockMyCli.connect_args['database'] == 'dsn_database'
assert MockMyCli.connect_args['character_set'] == 'utf8mb3'

Expand Down Expand Up @@ -1078,9 +1085,9 @@ def run_query(self, query, new_line=True):
'--user',
'user',
'--host',
'localhost',
DEFAULT_HOST,
'--port',
'3306',
f'{DEFAULT_PORT}',
'--database',
'database',
'--password',
Expand Down
3 changes: 2 additions & 1 deletion test/test_sqlexecute.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import pymysql
import pytest

from mycli.constants import TEST_DATABASE
from mycli.sqlexecute import ServerInfo, ServerSpecies
from test.utils import dbtest, is_expanded_output, run, set_expanded_output

Expand Down Expand Up @@ -125,7 +126,7 @@ def test_table_and_columns_query(executor):
@dbtest
def test_database_list(executor):
databases = executor.databases()
assert "mycli_test_db" in databases
assert TEST_DATABASE in databases


@dbtest
Expand Down
Loading