stable/freqtrade/configuration/load_config.py

117 lines
3.8 KiB
Python
Raw Permalink Normal View History

2019-08-10 12:15:09 +00:00
"""
This module contain functions to load the configuration file
"""
import logging
import re
2019-08-10 12:15:09 +00:00
import sys
2022-04-07 18:13:52 +00:00
from copy import deepcopy
from pathlib import Path
2023-01-21 14:01:56 +00:00
from typing import Any, Dict, List, Optional
2019-08-10 12:15:09 +00:00
import rapidjson
2019-08-10 12:15:09 +00:00
2022-09-18 11:20:36 +00:00
from freqtrade.constants import MINIMAL_CONFIG, Config
from freqtrade.exceptions import OperationalException
2022-04-07 18:13:52 +00:00
from freqtrade.misc import deep_merge_dicts
2019-08-10 12:15:09 +00:00
2020-09-28 17:39:41 +00:00
2019-08-10 12:15:09 +00:00
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
2022-04-11 16:02:02 +00:00
subtext = text[offset - min(80, offset):offset + 80]
segments = subtext.split('\n')
2020-03-23 06:54:27 +00:00
if len(segments) > 3:
# Remove first and last lines, to avoid odd truncations
return '\n'.join(segments[1:-1])
else:
return subtext
return ''
2021-04-06 09:59:58 +00:00
def load_file(path: Path) -> Dict[str, Any]:
try:
with path.open('r') as file:
config = rapidjson.load(file, parse_mode=CONFIG_PARSE_MODE)
except FileNotFoundError:
2021-05-30 18:14:41 +00:00
raise OperationalException(f'File "{path}" not found!')
2021-04-06 09:59:58 +00:00
return config
2019-08-10 12:15:09 +00:00
def load_config_file(path: str) -> Dict[str, Any]:
"""
Loads a config file from the given path
:param path: path as str
:return: configuration as dictionary
"""
try:
# Read config from stdin if requested in the options
2023-02-25 16:08:02 +00:00
with Path(path).open() if path != '-' else sys.stdin as file:
config = rapidjson.load(file, parse_mode=CONFIG_PARSE_MODE)
2019-08-10 12:15:09 +00:00
except FileNotFoundError:
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.'
)
2019-08-10 12:15:09 +00:00
return config
2022-04-07 18:13:52 +00:00
2023-01-21 14:01:56 +00:00
def load_from_files(
files: List[str], base_path: Optional[Path] = None, level: int = 0) -> Dict[str, Any]:
2022-04-07 18:29:03 +00:00
"""
Recursively load configuration files if specified.
Sub-files are assumed to be relative to the initial config.
"""
2022-09-18 11:20:36 +00:00
config: Config = {}
2022-04-07 18:29:03 +00:00
if level > 5:
raise OperationalException("Config loop detected.")
2022-04-07 18:13:52 +00:00
if not files:
return deepcopy(MINIMAL_CONFIG)
2022-04-08 15:26:51 +00:00
files_loaded = []
2022-04-07 18:13:52 +00:00
# We expect here a list of config filenames
2022-04-07 18:29:03 +00:00
for filename in files:
logger.info(f'Using config: {filename} ...')
if filename == '-':
# Immediately load stdin and return
return load_config_file(filename)
file = Path(filename)
if base_path:
# Prepend basepath to allow for relative assignments
file = base_path / file
config_tmp = load_config_file(str(file))
2022-04-08 15:30:23 +00:00
if 'add_config_files' in config_tmp:
2022-04-08 15:36:50 +00:00
config_sub = load_from_files(
config_tmp['add_config_files'], file.resolve().parent, level + 1)
2022-04-08 15:26:51 +00:00
files_loaded.extend(config_sub.get('config_files', []))
config_tmp = deep_merge_dicts(config_tmp, config_sub)
2022-04-07 18:29:03 +00:00
2022-04-08 15:26:51 +00:00
files_loaded.insert(0, str(file))
2022-04-07 18:29:03 +00:00
# Merge config options, overwriting prior values
config = deep_merge_dicts(config_tmp, config)
2022-04-07 18:13:52 +00:00
2022-04-08 15:26:51 +00:00
config['config_files'] = files_loaded
2022-04-07 18:13:52 +00:00
return config