settings_loader.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. # SPDX-License-Identifier: AGPL-3.0-or-later
  2. from os import environ
  3. from os.path import dirname, join, abspath, isfile
  4. from collections.abc import Mapping
  5. from itertools import filterfalse
  6. import yaml
  7. from searx.exceptions import SearxSettingsException
  8. searx_dir = abspath(dirname(__file__))
  9. def check_settings_yml(file_name):
  10. if isfile(file_name):
  11. return file_name
  12. return None
  13. def load_yaml(file_name):
  14. try:
  15. with open(file_name, 'r', encoding='utf-8') as settings_yaml:
  16. return yaml.safe_load(settings_yaml)
  17. except IOError as e:
  18. raise SearxSettingsException(e, file_name)
  19. except yaml.YAMLError as e:
  20. raise SearxSettingsException(e, file_name)
  21. def get_default_settings_path():
  22. return check_settings_yml(join(searx_dir, 'settings.yml'))
  23. def get_user_settings_path():
  24. # find location of settings.yml
  25. if 'SEARX_SETTINGS_PATH' in environ:
  26. # if possible set path to settings using the
  27. # enviroment variable SEARX_SETTINGS_PATH
  28. return check_settings_yml(environ['SEARX_SETTINGS_PATH'])
  29. # if not, get it from searx code base or last solution from /etc/searx
  30. return check_settings_yml('/etc/searx/settings.yml')
  31. def update_dict(default_dict, user_dict):
  32. for k, v in user_dict.items():
  33. if isinstance(v, Mapping):
  34. default_dict[k] = update_dict(default_dict.get(k, {}), v)
  35. else:
  36. default_dict[k] = v
  37. return default_dict
  38. def update_settings(default_settings, user_settings):
  39. # merge everything except the engines
  40. for k, v in user_settings.items():
  41. if k not in ('use_default_settings', 'engines'):
  42. update_dict(default_settings[k], v)
  43. # parse the engines
  44. remove_engines = None
  45. keep_only_engines = None
  46. use_default_settings = user_settings.get('use_default_settings')
  47. if isinstance(use_default_settings, dict):
  48. remove_engines = use_default_settings.get('engines', {}).get('remove')
  49. keep_only_engines = use_default_settings.get('engines', {}).get('keep_only')
  50. if 'engines' in user_settings or remove_engines is not None or keep_only_engines is not None:
  51. engines = default_settings['engines']
  52. # parse "use_default_settings.engines.remove"
  53. if remove_engines is not None:
  54. engines = list(filterfalse(lambda engine: (engine.get('name')) in remove_engines, engines))
  55. # parse "use_default_settings.engines.keep_only"
  56. if keep_only_engines is not None:
  57. engines = list(filter(lambda engine: (engine.get('name')) in keep_only_engines, engines))
  58. # parse "engines"
  59. user_engines = user_settings.get('engines')
  60. if user_engines:
  61. engines_dict = dict((definition['name'], definition) for definition in engines)
  62. for user_engine in user_engines:
  63. default_engine = engines_dict.get(user_engine['name'])
  64. if default_engine:
  65. update_dict(default_engine, user_engine)
  66. else:
  67. engines.append(user_engine)
  68. # store the result
  69. default_settings['engines'] = engines
  70. return default_settings
  71. def is_use_default_settings(user_settings):
  72. use_default_settings = user_settings.get('use_default_settings')
  73. if use_default_settings is True:
  74. return True
  75. if isinstance(use_default_settings, dict):
  76. return True
  77. if use_default_settings is False or use_default_settings is None:
  78. return False
  79. raise ValueError('Invalid value for use_default_settings')
  80. def load_settings(load_user_setttings=True):
  81. default_settings_path = get_default_settings_path()
  82. user_settings_path = get_user_settings_path()
  83. if user_settings_path is None or not load_user_setttings:
  84. # no user settings
  85. return (load_yaml(default_settings_path),
  86. 'load the default settings from {}'.format(default_settings_path))
  87. # user settings
  88. user_settings = load_yaml(user_settings_path)
  89. if is_use_default_settings(user_settings):
  90. # the user settings are merged with the default configuration
  91. default_settings = load_yaml(default_settings_path)
  92. update_settings(default_settings, user_settings)
  93. return (default_settings,
  94. 'merge the default settings ( {} ) and the user setttings ( {} )'
  95. .format(default_settings_path, user_settings_path))
  96. # the user settings, fully replace the default configuration
  97. return (user_settings,
  98. 'load the user settings from {}'.format(user_settings_path))