Merge pull request #3097 from freqtrade/parse_config_error
Improve config parse error handling
This commit is contained in:
		| @@ -1,13 +1,15 @@ | ||||
| """ | ||||
| This module contain functions to load the configuration file | ||||
| """ | ||||
| import rapidjson | ||||
| import logging | ||||
| import re | ||||
| import sys | ||||
| from pathlib import Path | ||||
| from typing import Any, Dict | ||||
|  | ||||
| from freqtrade.exceptions import OperationalException | ||||
| import rapidjson | ||||
|  | ||||
| from freqtrade.exceptions import OperationalException | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
| @@ -15,6 +17,26 @@ logger = logging.getLogger(__name__) | ||||
| CONFIG_PARSE_MODE = rapidjson.PM_COMMENTS | rapidjson.PM_TRAILING_COMMAS | ||||
|  | ||||
|  | ||||
| def log_config_error_range(path: str, errmsg: str) -> str: | ||||
|     """ | ||||
|     Parses configuration file and prints range around error | ||||
|     """ | ||||
|     if path != '-': | ||||
|         offsetlist = re.findall(r'(?<=Parse\serror\sat\soffset\s)\d+', errmsg) | ||||
|         if offsetlist: | ||||
|             offset = int(offsetlist[0]) | ||||
|             text = Path(path).read_text() | ||||
|             # Fetch an offset of 80 characters around the error line | ||||
|             subtext = text[offset-min(80, offset):offset+80] | ||||
|             segments = subtext.split('\n') | ||||
|             if len(segments) > 3: | ||||
|                 # Remove first and last lines, to avoid odd truncations | ||||
|                 return '\n'.join(segments[1:-1]) | ||||
|             else: | ||||
|                 return subtext | ||||
|     return '' | ||||
|  | ||||
|  | ||||
| def load_config_file(path: str) -> Dict[str, Any]: | ||||
|     """ | ||||
|     Loads a config file from the given path | ||||
| @@ -29,5 +51,12 @@ def load_config_file(path: str) -> Dict[str, Any]: | ||||
|         raise OperationalException( | ||||
|             f'Config file "{path}" not found!' | ||||
|             ' Please create a config file or check whether it exists.') | ||||
|     except rapidjson.JSONDecodeError as e: | ||||
|         err_range = log_config_error_range(path, str(e)) | ||||
|         raise OperationalException( | ||||
|             f'{e}\n' | ||||
|             f'Please verify the following segment of your configuration:\n{err_range}' | ||||
|             if err_range else 'Please verify your configuration file for syntax errors.' | ||||
|         ) | ||||
|  | ||||
|     return config | ||||
|   | ||||
| @@ -18,7 +18,7 @@ from freqtrade.configuration.config_validation import validate_config_schema | ||||
| from freqtrade.configuration.deprecated_settings import ( | ||||
|     check_conflicting_settings, process_deprecated_setting, | ||||
|     process_temporary_deprecated_settings) | ||||
| from freqtrade.configuration.load_config import load_config_file | ||||
| from freqtrade.configuration.load_config import load_config_file, log_config_error_range | ||||
| from freqtrade.constants import DEFAULT_DB_DRYRUN_URL, DEFAULT_DB_PROD_URL | ||||
| from freqtrade.exceptions import OperationalException | ||||
| from freqtrade.loggers import _set_loggers, setup_logging | ||||
| @@ -66,6 +66,30 @@ def test_load_config_file(default_conf, mocker, caplog) -> None: | ||||
|     assert validated_conf.items() >= default_conf.items() | ||||
|  | ||||
|  | ||||
| def test_load_config_file_error(default_conf, mocker, caplog) -> None: | ||||
|     del default_conf['user_data_dir'] | ||||
|     filedata = json.dumps(default_conf).replace( | ||||
|         '"stake_amount": 0.001,', '"stake_amount": .001,') | ||||
|     mocker.patch('freqtrade.configuration.load_config.open', mocker.mock_open(read_data=filedata)) | ||||
|     mocker.patch.object(Path, "read_text", MagicMock(return_value=filedata)) | ||||
|  | ||||
|     with pytest.raises(OperationalException, match=f".*Please verify the following segment.*"): | ||||
|         load_config_file('somefile') | ||||
|  | ||||
|  | ||||
| def test_load_config_file_error_range(default_conf, mocker, caplog) -> None: | ||||
|     del default_conf['user_data_dir'] | ||||
|     filedata = json.dumps(default_conf).replace( | ||||
|         '"stake_amount": 0.001,', '"stake_amount": .001,') | ||||
|     mocker.patch.object(Path, "read_text", MagicMock(return_value=filedata)) | ||||
|  | ||||
|     x = log_config_error_range('somefile', 'Parse error at offset 64: Invalid value.') | ||||
|     assert isinstance(x, str) | ||||
|     assert (x == '{"max_open_trades": 1, "stake_currency": "BTC", ' | ||||
|             '"stake_amount": .001, "fiat_display_currency": "USD", ' | ||||
|             '"ticker_interval": "5m", "dry_run": true, ') | ||||
|  | ||||
|  | ||||
| def test__args_to_config(caplog): | ||||
|  | ||||
|     arg_list = ['trade', '--strategy-path', 'TestTest'] | ||||
|   | ||||
		Reference in New Issue
	
	Block a user