from copy import copy from imp import load_source import os import sys from six import text_type from . import logs, types class _DefaultRulesNames(types.RulesNamesList): def __add__(self, items): return _DefaultRulesNames(list(self) + items) def __contains__(self, item): return item.enabled_by_default or \ super(_DefaultRulesNames, self).__contains__(item) def __eq__(self, other): if isinstance(other, _DefaultRulesNames): return super(_DefaultRulesNames, self).__eq__(other) else: return False DEFAULT_RULES = _DefaultRulesNames([]) DEFAULT_PRIORITY = 1000 DEFAULT_SETTINGS = {'rules': DEFAULT_RULES, 'wait_command': 3, 'require_confirmation': True, 'no_colors': False, 'debug': False, 'priority': {}, 'env': {'LC_ALL': 'C', 'LANG': 'C', 'GIT_TRACE': '1'}} ENV_TO_ATTR = {'THEFUCK_RULES': 'rules', 'THEFUCK_WAIT_COMMAND': 'wait_command', 'THEFUCK_REQUIRE_CONFIRMATION': 'require_confirmation', 'THEFUCK_NO_COLORS': 'no_colors', 'THEFUCK_PRIORITY': 'priority', 'THEFUCK_DEBUG': 'debug'} SETTINGS_HEADER = u"""# ~/.thefuck/settings.py: The Fuck settings file # # The rules are defined as in the example bellow: # # rules = ['cd_parent', 'git_push', 'python_command', 'sudo'] # # The default values are as follows. Uncomment and change to fit your needs. # See https://github.com/nvbn/thefuck#settings for more information. # """ def _settings_from_file(user_dir): """Loads settings from file.""" settings = load_source('settings', text_type(user_dir.joinpath('settings.py'))) return {key: getattr(settings, key) for key in DEFAULT_SETTINGS.keys() if hasattr(settings, key)} def _rules_from_env(val): """Transforms rules list from env-string to python.""" val = val.split(':') if 'DEFAULT_RULES' in val: val = DEFAULT_RULES + [rule for rule in val if rule != 'DEFAULT_RULES'] return val def _priority_from_env(val): """Gets priority pairs from env.""" for part in val.split(':'): try: rule, priority = part.split('=') yield rule, int(priority) except ValueError: continue def _val_from_env(env, attr): """Transforms env-strings to python.""" val = os.environ[env] if attr == 'rules': return _rules_from_env(val) elif attr == 'priority': return dict(_priority_from_env(val)) elif attr == 'wait_command': return int(val) elif attr in ('require_confirmation', 'no_colors', 'debug'): return val.lower() == 'true' else: return val def _settings_from_env(): """Loads settings from env.""" return {attr: _val_from_env(env, attr) for env, attr in ENV_TO_ATTR.items() if env in os.environ} def get_settings(user_dir): """Returns settings filled with values from `settings.py` and env.""" conf = copy(DEFAULT_SETTINGS) try: conf.update(_settings_from_file(user_dir)) except Exception: logs.exception("Can't load settings from file", sys.exc_info(), types.Settings(conf)) try: conf.update(_settings_from_env()) except Exception: logs.exception("Can't load settings from env", sys.exc_info(), types.Settings(conf)) if not isinstance(conf['rules'], types.RulesNamesList): conf['rules'] = types.RulesNamesList(conf['rules']) return types.Settings(conf) def initialize_settings_file(user_dir): settings_path = user_dir.joinpath('settings.py') if not settings_path.is_file(): with settings_path.open(mode='w') as settings_file: settings_file.write(SETTINGS_HEADER) for setting in DEFAULT_SETTINGS.items(): settings_file.write(u'# {} = {}\n'.format(*setting))