def from_files(files: List[str]) -> Dict[str, Any]: """ Iterate through the config files passed in, loading all of them and merging their contents. Files are loaded in sequence, parameters in later configuration files override the same parameter from an earlier file (last definition wins). :param files: List of file paths :return: configuration dictionary """ # Keep this method as staticmethod, so it can be used from interactive environments config: Dict[str, Any] = {} if not files: return constants.MINIMAL_CONFIG.copy() # We expect here a list of config filenames for path in files: logger.info(f'Using config: {path} ...') # Merge config options, overwriting old values config = deep_merge_dicts(load_config_file(path), config) # Normalize config if 'internals' not in config: config['internals'] = {} # validate configuration before returning logger.info('Validating configuration ...') validate_config_schema(config) return config
def load_from_files(self, files: List[str]) -> Dict[str, Any]: # Keep this method as staticmethod, so it can be used from interactive environments config: Dict[str, Any] = {} if not files: return deepcopy(constants.MINIMAL_CONFIG) # We expect here a list of config filenames for path in files: logger.info(f'Using config: {path} ...') # Merge config options, overwriting old values config = deep_merge_dicts(load_config_file(path), config) # Normalize config if 'internals' not in config: config['internals'] = {} # TODO: This can be deleted along with removal of deprecated # experimental settings if 'ask_strategy' not in config: config['ask_strategy'] = {} if 'pairlists' not in config: config['pairlists'] = [] # validate configuration before returning logger.info('Validating configuration ...') validate_config_schema(config) return config
def test_load_config_default_exchange_name(all_conf) -> None: """ config['exchange']['name'] option is required so it cannot be omitted in the config """ del all_conf['exchange']['name'] assert 'name' not in all_conf['exchange'] with pytest.raises(ValidationError, match=r"'name' is a required property"): validate_config_schema(all_conf)
def test_load_config_default_exchange(all_conf) -> None: """ config['exchange'] subtree has required options in it so it cannot be omitted in the config """ del all_conf['exchange'] assert 'exchange' not in all_conf with pytest.raises(ValidationError, match=r"'exchange' is a required property"): validate_config_schema(all_conf)
def test_load_config_missing_attributes(default_conf) -> None: conf = deepcopy(default_conf) conf.pop('exchange') with pytest.raises(ValidationError, match=r".*'exchange' is a required property.*"): validate_config_schema(conf) conf = deepcopy(default_conf) conf.pop('stake_currency') conf['runmode'] = RunMode.DRY_RUN with pytest.raises(ValidationError, match=r".*'stake_currency' is a required property.*"): validate_config_schema(conf)
def test_load_config_default_subkeys(all_conf, keys) -> None: """ Test for parameters with default values in sub-paths so they can be omitted in the config and the default value should is added to the config. """ # Get first level key key = keys[0] # get second level key subkey = keys[1] del all_conf[key][subkey] assert subkey not in all_conf[key] validate_config_schema(all_conf) assert subkey in all_conf[key] assert all_conf[key][subkey] == keys[2]
def test_load_config_incorrect_stake_amount(default_conf) -> None: default_conf['stake_amount'] = 'fake' with pytest.raises(ValidationError, match=r".*'fake' does not match 'unlimited'.*"): validate_config_schema(default_conf)
def test_load_config_missing_attributes(default_conf) -> None: default_conf.pop('exchange') with pytest.raises(ValidationError, match=r".*'exchange' is a required property.*"): validate_config_schema(default_conf)
def test_load_config_invalid_pair(default_conf) -> None: default_conf['exchange']['pair_whitelist'].append('ETH-BTC') with pytest.raises(ValidationError, match=r'.*does not match.*'): validate_config_schema(default_conf)
def test_validate_default_conf(default_conf) -> None: # Validate via our validator - we allow setting defaults! validate_config_schema(default_conf)
def test_load_config_stoploss_exchange_limit_ratio(all_conf) -> None: all_conf['order_types']['stoploss_on_exchange_limit_ratio'] = 1.15 with pytest.raises(ValidationError, match=r"1.15 is greater than the maximum"): validate_config_schema(all_conf)