Skip to content

Commit 0e4cf81

Browse files
committed
remove support for reading vendor my.cnf files
* remove reading of configuration from my.cnf files, in any location * remove reading of [client] and [mysqld] configuration from mylogin.cnf files * retain definition of host endpoints via login-path names in mylogin.cnf file * define behave pager boundary in test/myclirc, since it depended on the [client] stanza of mylogin.cnf * remove deprecation warning when my.cnf was read * ssl_config no longer needs merge logic with my.cnf values * config file includes no longer need to be supported * remove --defaults-file CLI flag * remove --defaults-group-suffix CLI flag Preparation for release 2.0
1 parent ce8c444 commit 0e4cf81

21 files changed

Lines changed: 118 additions & 585 deletions

changelog.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ Upcoming (TBD)
22
==============
33

44
Breaking Changes
5-
---------
65
* Change default table format to `mysql_unicode`.
76
* Remove support for deprecated environment variable `$DSN`.
87
* Remove support for deprecated environment variable `$MYSQL_UNIX_PORT`.
98
* Remove support for `--ssl/--no-ssl` CLI arguments in favor of `--ssl-mode`.
109
* Remove support for deprecated SSH jump functionality.
10+
* Remove support for `my.cnf` vendor MySQL option files.
1111

1212

1313
Internal

mycli/app_state.py

Lines changed: 17 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
from __future__ import annotations
22

3-
from collections import defaultdict
43
import re
54
from typing import TYPE_CHECKING, Any
65

76
from configobj import ConfigObj
87

9-
from mycli.config import str_to_bool, strip_matching_quotes
8+
from mycli.config import strip_matching_quotes
109

1110
if TYPE_CHECKING:
1211
from mycli.client import MyCli
@@ -19,21 +18,13 @@ def normalize_ssl_mode(config: ConfigObj) -> tuple[str | None, str | None]:
1918
return ssl_mode, None
2019

2120

22-
def ensure_my_cnf_sections(my_cnf: ConfigObj) -> None:
23-
if not my_cnf.get('client'):
24-
my_cnf['client'] = {}
25-
if not my_cnf.get('mysqld'):
26-
my_cnf['mysqld'] = {}
27-
28-
2921
def configure_prompt_state(
3022
mycli: MyCli,
3123
config: ConfigObj,
3224
prompt: str | None,
33-
prompt_cnf: str | None,
3425
toolbar_format: str | None,
3526
) -> None:
36-
mycli.prompt_format = prompt or prompt_cnf or config['main']['prompt'] or mycli.default_prompt
27+
mycli.prompt_format = prompt or config['main']['prompt'] or mycli.default_prompt
3728
mycli.prompt_lines = 0
3829
mycli.multiline_continuation_char = config['main']['prompt_continuation']
3930
mycli.toolbar_format = toolbar_format or config['main']['toolbar']
@@ -61,47 +52,22 @@ def llm_prompt_truncation(config: ConfigObj) -> tuple[int, int]:
6152

6253

6354
class AppStateMixin:
64-
defaults_suffix: str | None
6555
login_path: str | None
6656

67-
def read_my_cnf(self, cnf: ConfigObj, keys: list[str]) -> dict[str, Any]:
68-
sections = ['client', 'mysqld']
69-
key_transformations = {
70-
'mysqld': {
71-
'socket': 'default_socket',
72-
'port': 'default_port',
73-
'user': 'default_user',
74-
},
75-
}
76-
77-
if self.login_path and self.login_path != 'client':
78-
sections.append(self.login_path)
79-
80-
if self.defaults_suffix:
81-
sections.extend([sect + self.defaults_suffix for sect in sections])
82-
83-
configuration: dict[str, Any] = defaultdict(lambda: None)
84-
for key in keys:
85-
for section in cnf:
86-
if section not in sections or key not in cnf[section]:
87-
continue
88-
new_key = key_transformations.get(section, {}).get(key) or key
89-
configuration[new_key] = strip_matching_quotes(cnf[section][key])
90-
91-
return configuration
92-
93-
def merge_ssl_with_cnf(self, ssl: dict[str, Any], cnf: dict[str, Any]) -> dict[str, Any]:
94-
merged = {}
95-
merged.update(ssl)
96-
prefix = 'ssl-'
97-
for key, value in cnf.items():
98-
if not key.startswith(prefix):
99-
continue
100-
if value is None:
57+
def read_mylogin_cnf(self, cnf: ConfigObj) -> dict[str, Any]:
58+
allowed_keys = [
59+
'user',
60+
'password',
61+
'host',
62+
'port',
63+
'socket',
64+
]
65+
configuration: dict[str, Any] = dict.fromkeys(allowed_keys)
66+
for section in cnf:
67+
if section != self.login_path:
10168
continue
102-
if key == 'ssl-verify-server-cert':
103-
merged['check_hostname'] = str_to_bool(value)
104-
else:
105-
merged[key[len(prefix) :]] = value
69+
for key in allowed_keys:
70+
if key in cnf[section]:
71+
configuration[key] = strip_matching_quotes(cnf[section][key])
10672

107-
return merged
73+
return configuration

mycli/cli_runner.py

Lines changed: 1 addition & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
import os
44
import re
55
import sys
6-
from textwrap import dedent
76
from typing import TYPE_CHECKING, Any, Callable
87
from urllib.parse import parse_qs, unquote, urlparse
98

109
import click
1110

1211
from mycli.config import str_to_bool
13-
from mycli.constants import EMPTY_PASSWORD_FLAG_SENTINEL, ISSUES_URL, REPO_URL
12+
from mycli.constants import EMPTY_PASSWORD_FLAG_SENTINEL, ISSUES_URL
1413
from mycli.main_modes.batch import main_batch_from_stdin, main_batch_with_progress_bar, main_batch_without_progress_bar
1514
from mycli.main_modes.checkup import main_checkup
1615
from mycli.main_modes.execute import main_execute_from_cli
@@ -101,8 +100,6 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
101100
prompt=cli_args.prompt,
102101
toolbar_format=cli_args.toolbar,
103102
logfile=cli_args.logfile,
104-
defaults_suffix=cli_args.defaults_group_suffix,
105-
defaults_file=cli_args.defaults_file,
106103
login_path=cli_args.login_path,
107104
auto_vertical_output=cli_args.auto_vertical_output,
108105
warn=cli_args.warn,
@@ -314,82 +311,6 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
314311
use_keyring = str_to_bool(cli_args.use_keyring)
315312
reset_keyring = False
316313

317-
# todo: removeme after a period of transition
318-
for tup in [
319-
('client', 'prompt', 'prompt', 'main', 'prompt'),
320-
('client', 'pager', 'pager', 'main', 'pager'),
321-
('client', 'skip-pager', 'skip-pager', 'main', 'enable_pager'),
322-
# this is a white lie, because default_character_set can actually be read from the package config
323-
('client', 'default-character-set', 'default-character-set', 'connection', 'default_character_set'),
324-
# local-infile can be read from both sections
325-
('mysqld', 'local-infile', 'local-infile', 'connection', 'default_local_infile'),
326-
('client', 'local-infile', 'local-infile', 'connection', 'default_local_infile'),
327-
('mysqld', 'loose-local-infile', 'loose-local-infile', 'connection', 'default_local_infile'),
328-
('client', 'loose-local-infile', 'loose-local-infile', 'connection', 'default_local_infile'),
329-
# todo: in the future we should add default_port, etc, but only in .myclirc
330-
# they are currently ignored in my.cnf
331-
('mysqld', 'default_socket', 'socket', 'connection', 'default_socket'),
332-
('client', 'ssl-ca', 'ssl-ca', 'connection', 'default_ssl_ca'),
333-
('client', 'ssl-cert', 'ssl-cert', 'connection', 'default_ssl_cert'),
334-
('client', 'ssl-key', 'ssl-key', 'connection', 'default_ssl_key'),
335-
('client', 'ssl-cipher', 'ssl-cipher', 'connection', 'default_ssl_cipher'),
336-
('client', 'ssl-verify-server-cert', 'ssl-verify-server-cert', 'connection', 'default_ssl_verify_server_cert'),
337-
]:
338-
(
339-
mycnf_section_name,
340-
mycnf_item_name,
341-
printable_mycnf_item_name,
342-
myclirc_section_name,
343-
myclirc_item_name,
344-
) = tup
345-
if str_to_bool(mycli.config['main'].get('my_cnf_transition_done', 'False')):
346-
break
347-
if (
348-
mycli.my_cnf[mycnf_section_name].get(mycnf_item_name) is None
349-
and mycli.my_cnf[mycnf_section_name].get(mycnf_item_name.replace('-', '_')) is None
350-
):
351-
continue
352-
user_section = mycli.config_without_package_defaults.get(myclirc_section_name, {})
353-
if user_section.get(myclirc_item_name) is None:
354-
cnf_value = mycli.my_cnf[mycnf_section_name].get(mycnf_item_name)
355-
if cnf_value is None:
356-
cnf_value = mycli.my_cnf[mycnf_section_name].get(mycnf_item_name.replace('-', '_'))
357-
click.secho(
358-
dedent(
359-
f"""
360-
Reading configuration from my.cnf files is deprecated.
361-
See {ISSUES_URL}/1490 .
362-
The cause of this message is the following in a my.cnf file without a corresponding
363-
~/.myclirc entry:
364-
365-
[{mycnf_section_name}]
366-
{printable_mycnf_item_name} = {cnf_value}
367-
368-
To suppress this message, remove the my.cnf item add or the following to ~/.myclirc:
369-
370-
[{myclirc_section_name}]
371-
{myclirc_item_name} = <value>
372-
373-
The ~/.myclirc setting will take precedence. In the future, the my.cnf will be ignored.
374-
375-
Values are documented at {REPO_URL}/blob/main/mycli/myclirc . An
376-
empty <value> is generally accepted.
377-
378-
To ignore all of this, set
379-
380-
[main]
381-
my_cnf_transition_done = True
382-
383-
in ~/.myclirc.
384-
385-
--------
386-
387-
"""
388-
),
389-
err=True,
390-
fg='yellow',
391-
)
392-
393314
mycli.connect(
394315
database=database,
395316
user=cli_args.user,

mycli/client.py

Lines changed: 18 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from typing import IO, Literal
88

99
from cli_helpers.tabular_output import TabularOutputFormatter
10+
from configobj import ConfigObj
1011
from prompt_toolkit.formatted_text import to_formatted_text
1112
from prompt_toolkit.shortcuts import PromptSession
1213
import sqlparse
@@ -15,7 +16,6 @@
1516
AppStateMixin,
1617
configure_prompt_state,
1718
destructive_keywords_from_config,
18-
ensure_my_cnf_sections,
1919
llm_prompt_truncation,
2020
normalize_ssl_mode,
2121
)
@@ -24,7 +24,13 @@
2424
from mycli.client_query import ClientQueryMixin
2525
from mycli.clistyle import style_factory_helpers, style_factory_ptoolkit
2626
from mycli.completion_refresher import CompletionRefresher
27-
from mycli.config import get_mylogin_cnf_path, open_mylogin_cnf, read_config_files, write_default_config
27+
from mycli.config import (
28+
get_mylogin_cnf_path,
29+
open_mylogin_cnf,
30+
read_config_file,
31+
read_config_files,
32+
write_default_config,
33+
)
2834
from mycli.constants import DEFAULT_PROMPT
2935
from mycli.main_modes import repl as repl_package
3036
from mycli.output import OutputMixin
@@ -44,19 +50,10 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
4450
default_prompt = DEFAULT_PROMPT
4551
default_prompt_splitln = "\\u@\\h\\n(\\t):\\d>"
4652
max_len_prompt = 45
47-
defaults_suffix = None
4853
prompt_lines: int
4954
sqlexecute: SQLExecute | None
5055
numeric_alignment: str
5156

52-
# In order of being loaded. Files lower in list override earlier ones.
53-
cnf_files: list[str | IO[str]] = [
54-
"/etc/my.cnf",
55-
"/etc/mysql/my.cnf",
56-
"/usr/local/etc/my.cnf",
57-
os.path.expanduser("~/.my.cnf"),
58-
]
59-
6057
# check XDG_CONFIG_HOME exists and not an empty string
6158
xdg_config_home = os.environ.get("XDG_CONFIG_HOME", "~/.config")
6259
system_config_files: list[str | IO[str]] = [
@@ -72,8 +69,6 @@ def __init__(
7269
prompt: str | None = None,
7370
toolbar_format: str | None = None,
7471
logfile: TextIOWrapper | Literal[False] | None = None,
75-
defaults_suffix: str | None = None,
76-
defaults_file: str | None = None,
7772
login_path: str | None = None,
7873
auto_vertical_output: bool = False,
7974
warn: bool | None = None,
@@ -83,7 +78,6 @@ def __init__(
8378
) -> None:
8479
self.sqlexecute = sqlexecute
8580
self.logfile = logfile
86-
self.defaults_suffix = defaults_suffix
8781
self.login_path = login_path
8882
self.toolbar_error_message: str | None = None
8983
self.prompt_session: PromptSession | None = None
@@ -92,23 +86,13 @@ def __init__(
9286
self.sandbox_mode: bool = False
9387
self.checkpoint: IO | None = None
9488

95-
# self.cnf_files is a class variable that stores the list of mysql
96-
# config files to read in at launch.
97-
# If defaults_file is specified then override the class variable with
98-
# defaults_file.
99-
if defaults_file:
100-
self.cnf_files = [defaults_file]
101-
10289
# Load config.
10390
config_files: list[str | IO[str]] = self.system_config_files + [myclirc] + [self.pwd_config_file]
10491

10592
c = self.config = read_config_files(config_files)
106-
# this parallel config exists to
107-
# * compare with my.cnf
108-
# * support the --checkup feature
109-
# todo: after removing my.cnf, create the parallel configs only when --checkup is set
93+
# only needed in --checkup mode. todo: only load when needed
11094
self.config_without_package_defaults = read_config_files(config_files, ignore_package_defaults=True)
111-
# this parallel config exists to compare with my.cnf support the --checkup feature
95+
# only needed in --checkup mode. todo: only load when needed
11296
self.config_without_user_options = read_config_files(config_files, ignore_user_options=True)
11397
self.multi_line = c["main"].as_bool("multi_line")
11498
self.key_bindings = c["main"]["key_bindings"]
@@ -201,20 +185,16 @@ def __init__(
201185
self.register_special_commands()
202186

203187
# Load .mylogin.cnf if it exists.
204-
mylogin_cnf_path = get_mylogin_cnf_path()
205-
if mylogin_cnf_path:
206-
mylogin_cnf = open_mylogin_cnf(mylogin_cnf_path)
207-
if mylogin_cnf_path and mylogin_cnf:
208-
# .mylogin.cnf gets read last, even if defaults_file is specified.
209-
self.cnf_files.append(mylogin_cnf)
210-
elif mylogin_cnf_path and not mylogin_cnf:
211-
# There was an error reading the login path file.
188+
self.mylogin_cnf = ConfigObj()
189+
mylogin_cnf_h = None
190+
if mylogin_cnf_path := get_mylogin_cnf_path():
191+
mylogin_cnf_h = open_mylogin_cnf(mylogin_cnf_path)
192+
if mylogin_cnf_h:
193+
self.mylogin_cnf = read_config_file(mylogin_cnf_h, list_values=False) or ConfigObj()
194+
else:
212195
print("Error: Unable to read login path file.")
213196

214-
self.my_cnf = read_config_files(self.cnf_files, list_values=False)
215-
ensure_my_cnf_sections(self.my_cnf)
216-
prompt_cnf = self.read_my_cnf(self.my_cnf, ["prompt"])["prompt"]
217-
configure_prompt_state(self, c, prompt, prompt_cnf, toolbar_format)
197+
configure_prompt_state(self, c, prompt, toolbar_format)
218198
self.prompt_session = None
219199
self.destructive_keywords = destructive_keywords_from_config(c)
220200
special.set_destructive_keywords(self.destructive_keywords)

0 commit comments

Comments
 (0)