Skip to content
57 changes: 48 additions & 9 deletions mycli/config.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
from __future__ import print_function
import shutil
from io import BytesIO, TextIOWrapper
import logging
import os
from os.path import expanduser, exists
from os.path import exists
import struct
from configobj import ConfigObj
import sys
from configobj import ConfigObj, ConfigObjError
try:
basestring
except NameError:
basestring = str
try:
from Crypto.Cipher import AES
except ImportError:
Expand All @@ -19,16 +25,49 @@ class CryptoError(Exception):

logger = logging.getLogger(__name__)

def load_config(usr_cfg, def_cfg=None):
cfg = ConfigObj()
cfg.merge(ConfigObj(def_cfg, interpolation=False))
cfg.merge(ConfigObj(expanduser(usr_cfg), interpolation=False))
cfg.filename = expanduser(usr_cfg)
def log(logger, level, message):
"""Logs message to stderr if logging isn't initialized."""

if logger.parent.name != 'root':
logger.log(level, message)
else:
print(message, file=sys.stderr)

def read_config_file(f):
"""Read a config file."""

if isinstance(f, basestring):
f = os.path.expanduser(f)

try:
config = ConfigObj(f, interpolation=False)
except ConfigObjError as e:
log(logger, logging.ERROR, "Unable to parse line {0} of config file "
"'{1}'.".format(e.line_number, f))
log(logger, logging.ERROR, "Using successfully parsed config values.")
return e.config
except (IOError, OSError) as e:
log(logger, logging.WARNING, "You don't have permission to read "
"config file '{0}'.".format(e.filename))
return None

return config

def read_config_files(files):
"""Read and merge a list of config files."""

config = ConfigObj()

for _file in files:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice trick to avoid stomping on built-in file keyword.

_config = read_config_file(_file)
if bool(_config) is True:
config.merge(_config)
config.filename = _config.filename

return cfg
return config

def write_default_config(source, destination, overwrite=False):
destination = expanduser(destination)
destination = os.path.expanduser(destination)
if not overwrite and exists(destination):
return

Expand Down
37 changes: 20 additions & 17 deletions mycli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@
from .sqlexecute import SQLExecute
from .clibuffer import CLIBuffer
from .completion_refresher import CompletionRefresher
from .config import (write_default_config, load_config, get_mylogin_cnf_path,
open_mylogin_cnf, CryptoError)
from .config import (write_default_config, get_mylogin_cnf_path,
open_mylogin_cnf, CryptoError, read_config_file,
read_config_files)
from .key_bindings import mycli_bindings
from .encodingutils import utf8tounicode
from .lexer import MyCliLexer
Expand Down Expand Up @@ -67,9 +68,17 @@ class MyCli(object):
'/etc/my.cnf',
'/etc/mysql/my.cnf',
'/usr/local/etc/my.cnf',
os.path.expanduser('~/.my.cnf')
'~/.my.cnf'
]

system_config_files = [
'/etc/myclirc',
]

default_config_file = os.path.join(PACKAGE_ROOT, 'myclirc')
user_config_file = '~/.myclirc'


def __init__(self, sqlexecute=None, prompt=None,
logfile=None, defaults_suffix=None, defaults_file=None,
login_path=None):
Expand All @@ -85,12 +94,10 @@ def __init__(self, sqlexecute=None, prompt=None,
if defaults_file:
self.cnf_files = [defaults_file]

default_config = os.path.join(PACKAGE_ROOT, 'myclirc')
write_default_config(default_config, '~/.myclirc')


# Load config.
c = self.config = load_config('~/.myclirc', default_config)
config_files = ([self.default_config_file] + self.system_config_files +
[self.user_config_file])
c = self.config = read_config_files(config_files)
self.multi_line = c['main'].as_bool('multi_line')
self.destructive_warning = c['main'].as_bool('destructive_warning')
self.key_bindings = c['main']['key_bindings']
Expand All @@ -100,6 +107,10 @@ def __init__(self, sqlexecute=None, prompt=None,
self.cli_style = c['colors']
self.wider_completion_menu = c['main'].as_bool('wider_completion_menu')

# Write user config if system config wasn't the last config loaded.
if c.filename not in self.system_config_files:
write_default_config(self.default_config_file, self.user_config_file)

# audit log
if self.logfile is None and 'audit_log' in c['main']:
try:
Expand Down Expand Up @@ -241,15 +252,7 @@ def read_my_cnf_files(self, files, keys):
:param keys: list of keys to retrieve
:returns: tuple, with None for missing keys.
"""
cnf = ConfigObj()
for _file in files:
try:
cnf.merge(ConfigObj(_file, interpolation=False))
except ConfigObjError as e:
self.logger.error('Error parsing %r.', _file)
self.logger.error('Recovering partially parsed config values.')
cnf.merge(e.config)
pass
cnf = read_config_files(files)

sections = ['client']
if self.login_path and self.login_path != 'client':
Expand Down
4 changes: 2 additions & 2 deletions mycli/packages/special/favoritequeries.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,5 @@ def delete(self, name):
self.config.write()
return '%s: Deleted' % name

from ...config import load_config
favoritequeries = FavoriteQueries(load_config('~/.myclirc'))
from ...config import read_config_file
favoritequeries = FavoriteQueries(read_config_file('~/.myclirc'))