settings_loader.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  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) from e
  19. except yaml.YAMLError as e:
  20. raise SearxSettingsException(e, file_name) from e
  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. if k in default_settings:
  43. update_dict(default_settings[k], v)
  44. else:
  45. default_settings[k] = v
  46. # parse the engines
  47. remove_engines = None
  48. keep_only_engines = None
  49. use_default_settings = user_settings.get('use_default_settings')
  50. if isinstance(use_default_settings, dict):
  51. remove_engines = use_default_settings.get('engines', {}).get('remove')
  52. keep_only_engines = use_default_settings.get('engines', {}).get('keep_only')
  53. if 'engines' in user_settings or remove_engines is not None or keep_only_engines is not None:
  54. engines = default_settings['engines']
  55. # parse "use_default_settings.engines.remove"
  56. if remove_engines is not None:
  57. engines = list(filterfalse(lambda engine: (engine.get('name')) in remove_engines, engines))
  58. # parse "use_default_settings.engines.keep_only"
  59. if keep_only_engines is not None:
  60. engines = list(filter(lambda engine: (engine.get('name')) in keep_only_engines, engines))
  61. # parse "engines"
  62. user_engines = user_settings.get('engines')
  63. if user_engines:
  64. engines_dict = dict((definition['name'], definition) for definition in engines)
  65. for user_engine in user_engines:
  66. default_engine = engines_dict.get(user_engine['name'])
  67. if default_engine:
  68. update_dict(default_engine, user_engine)
  69. else:
  70. engines.append(user_engine)
  71. # store the result
  72. default_settings['engines'] = engines
  73. return default_settings
  74. def is_use_default_settings(user_settings):
  75. use_default_settings = user_settings.get('use_default_settings')
  76. if use_default_settings is True:
  77. return True
  78. if isinstance(use_default_settings, dict):
  79. return True
  80. if use_default_settings is False or use_default_settings is None:
  81. return False
  82. raise ValueError('Invalid value for use_default_settings')
  83. def load_settings(load_user_setttings=True):
  84. default_settings_path = get_default_settings_path()
  85. user_settings_path = get_user_settings_path()
  86. if user_settings_path is None or not load_user_setttings:
  87. # no user settings
  88. return (load_yaml(default_settings_path),
  89. 'load the default settings from {}'.format(default_settings_path))
  90. # user settings
  91. user_settings = load_yaml(user_settings_path)
  92. if is_use_default_settings(user_settings):
  93. # the user settings are merged with the default configuration
  94. default_settings = load_yaml(default_settings_path)
  95. update_settings(default_settings, user_settings)
  96. return (default_settings,
  97. 'merge the default settings ( {} ) and the user setttings ( {} )'
  98. .format(default_settings_path, user_settings_path))
  99. # the user settings, fully replace the default configuration
  100. return (user_settings,
  101. 'load the user settings from {}'.format(user_settings_path))