forked from Ponysearch/Ponysearch
[mod] ./utils/get_setting.py tiny YAML parser for settings.yml
This allow to read settings on the fly even without virtualenv. The ultimate goal of the commit is to remove utils/brand.env from the git repository. The code includes a tiny yaml parser that **should** be good enough. The code read searx/settings.yml directly (and ignore the environment variables). yq [1] is a more reliable alternative but this require to download a binary from github which is not great. [1] https://github.com/mikefarah/yq/#install
This commit is contained in:
parent
3dea7e609b
commit
60bc5baea3
1 changed files with 134 additions and 0 deletions
134
utils/get_setting.py
Normal file
134
utils/get_setting.py
Normal file
|
@ -0,0 +1,134 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""build environment used by shell scripts
|
||||
"""
|
||||
|
||||
# set path
|
||||
import sys
|
||||
import importlib.util
|
||||
import re
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
repo_root = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
# If you add or remove variables here, do not forget to update:
|
||||
# - ./docs/admin/engines/settings.rst
|
||||
# - ./docs/dev/makefile.rst (section make buildenv)
|
||||
|
||||
name_val = [
|
||||
("SEARXNG_URL", "server.base_url"),
|
||||
("SEARXNG_PORT", "server.port"),
|
||||
("SEARXNG_BIND_ADDRESS", "server.bind_address"),
|
||||
]
|
||||
|
||||
|
||||
def main(setting_name):
|
||||
|
||||
settings_path = repo_root / "searx" / "settings.yml"
|
||||
with open(settings_path) as f:
|
||||
settings = parse_yaml(f.read())
|
||||
print(get_setting_value(settings, setting_name))
|
||||
|
||||
|
||||
def get_setting_value(settings, name):
|
||||
value = settings
|
||||
for a in name.split("."):
|
||||
value = value[a]
|
||||
if value is True:
|
||||
value = "1"
|
||||
elif value is False:
|
||||
value = ""
|
||||
return value
|
||||
|
||||
|
||||
def parse_yaml(yaml_str):
|
||||
"""A simple YAML parser that converts a YAML string to a Python dictionary.
|
||||
This parser can handle nested dictionaries, but does not handle list or JSON
|
||||
like structures.
|
||||
|
||||
Good enough parser to get the values of server.base_url, server.port and
|
||||
server.bind_address
|
||||
|
||||
"""
|
||||
|
||||
def get_type_and_value_without_comment(line):
|
||||
"""Extract value without comment and quote
|
||||
|
||||
Returns a tuple:
|
||||
|
||||
1. str or None: str when the value is written inside quote, None otherwise
|
||||
2. the value without quote if any
|
||||
"""
|
||||
match = re.search(r"\"(.*)\"(\s+#)?|\'(.*)\'(\s+#)?|([^#]*)(\s+#)?", line)
|
||||
if match:
|
||||
g = match.groups()
|
||||
if g[0] is not None:
|
||||
return str, g[0]
|
||||
elif g[2] is not None:
|
||||
return str, g[2]
|
||||
elif g[4] is not None:
|
||||
return None, g[4].strip()
|
||||
return None, line.strip()
|
||||
|
||||
# fmt: off
|
||||
true_values = ("y", "Y", "yes", "Yes", "YES", "true", "True", "TRUE", "on", "On", "ON",)
|
||||
false_values = ("n", "N", "no", "No", "NO", "false", "False", "FALSE", "off", "Off", "OFF",)
|
||||
# fmt: on
|
||||
|
||||
def process_line(line):
|
||||
"""Extract key and value from a line, considering its indentation."""
|
||||
if ": " in line:
|
||||
key, value = line.split(": ", 1)
|
||||
key = key.strip()
|
||||
value_type, value = get_type_and_value_without_comment(value)
|
||||
if value in true_values and value_type is None:
|
||||
value = True
|
||||
elif value in false_values and value_type is None:
|
||||
value = False
|
||||
elif value.replace(".", "").isdigit() and value_type is None:
|
||||
for t in (int, float):
|
||||
try:
|
||||
value = t(value)
|
||||
break
|
||||
except ValueError:
|
||||
continue
|
||||
return key, value
|
||||
return None, None
|
||||
|
||||
def get_indentation_level(line):
|
||||
"""Determine the indentation level of a line."""
|
||||
return len(line) - len(line.lstrip())
|
||||
|
||||
yaml_dict = {}
|
||||
lines = yaml_str.split("\n")
|
||||
stack = [yaml_dict]
|
||||
|
||||
for line in lines:
|
||||
if not line.strip():
|
||||
continue # Skip empty lines
|
||||
|
||||
indentation_level = get_indentation_level(line)
|
||||
# Assuming 2 spaces per indentation level
|
||||
# see .yamllint.yml
|
||||
current_level = indentation_level // 2
|
||||
|
||||
# Adjust the stack based on the current indentation level
|
||||
while len(stack) > current_level + 1:
|
||||
stack.pop()
|
||||
|
||||
if line.endswith(":"):
|
||||
key = line[0:-1].strip()
|
||||
new_dict = {}
|
||||
stack[-1][key] = new_dict
|
||||
stack.append(new_dict)
|
||||
else:
|
||||
key, value = process_line(line)
|
||||
if key is not None:
|
||||
stack[-1][key] = value
|
||||
|
||||
return yaml_dict
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1])
|
Loading…
Reference in a new issue