Впервые опубликовано на мой блог
Файлы конфигурации анализа – это то, что мы делаем программисты каждый день. Но вы уверены, что делаете это правильно?
Давай выясним!
В остальной части этой статьи мы предположим, что хотим проанализировать файл конфигурации, содержащий токен доступа GitHub в программе командной строки под названием фрук
.
Вы можете написать что -то вроде этого:
/* in config.json */ { "auth": { "github": { "token": "ab642ef9zf" } } }
/* in frob.js */ const config = require('./config'); const token = config.auth.github.token; ...
Ну, это предполагает, что мы используем Узел
Анкет Сделать эту работу в браузере или в любом другом контексте JavaScript остается в качестве упражнения для читателя:)
Есть несколько проблем с вышеупомянутым подходом, хотя. Чтобы объяснить их, мы собираемся перейти на язык, который я знаю намного лучше и увидим список проблем и потенциальных решений.
Во -первых, используя Json
Для файлов конфигурации может быть не такой хорошей идеей. Итак, мы собираемся использовать YAML. Вот несколько причин, почему:
Как и JSON, мы можем отображать непосредственно с типами питонов «простых старых данных» (списки, словари, целые числа, поплавки, струны и логические)
Синтаксис четко определен, и все реализации ведут себя одинаково. (Это не Дело для JSON, см. Парирование JSON – это минное поле для деталей)
Мы можем иметь комментарии в файле конфигурации.
Файл легче читать для людей. Сравнивать:
{ "auth": { "github": { "token": "ab642ef9zf" } } }
auth: github: token: "ab642ef9zf"
Элементы могут быть произвольными вложенными. (
.ini
Есть несколько способов выразить одни и те же данные, поэтому мы можем выбрать то, что более читабельно:
shopping_list: - eggs - bacon - tomatoes - beans tags: ["python", "testing"]
- Пробел является значительным, поэтому файл имеет быть должным образом отступа.
Второй , config.json
Файл жестко кодируется, чтобы находиться рядом с исходным кодом.
Это означает, что возможно, что он будет добавлен и подтолкнут к системе управления версиями, если мы не будем осторожны.
Поэтому вместо этого мы постараемся быть совместимым с Стандарты Freedesktop Анкет
В основном это означает, что мы должны:
- Ищите файл конфигурации в
$ Xdg_config_home/frob.yml
Если установлена переменная среды xdg_config_home. - Если нет, ищите это в
~/.config/frob.yml
- И если не найдено в доме, ищите по умолчанию в
/etc/xdg/frob.yml
Это поможет нам следовать за Принцип наименьшего удивления Потому что, поскольку многие программы следуют этим правилам сегодня, пользователи нашей реализации будут Ожидайте нам сделать то же самое.
К счастью, нам не нужно реализовать все это, мы можем использовать pyxdg
библиотека:
import xdg.BaseDirectory cfg_path = xdg.BaseDirectory.load_first_config("frob.yml") if cfg_path: ...
Иногда файл вообще не существует, поэтому мы захотим сообщить об этом нашему пользователю:
cfg_path = xdg.BaseDirectory.load_first_config("frob.yml") if not cfg_path: raise InvalidConfig("frob.yml not found")
Иногда файл будет существовать, но read_text ()
По какой -то причине потерпит неудачу (например, проблема с разрешением):
import pathlib try: config_file = pathlib.Path(cfg_path) contents = config_file.read_text() except OSError as read_error: raise InvalidConfig(f"Could not read file {cfg_path}: {read_error}")
Иногда файл будет существовать, но будет содержать недействительный YAML:
import ruamel.yaml contents = config_file.read_text() try: parsed = ruamel.yaml.safe_load(contents) except ruamel.yaml.error.YAMLError as yaml_error: details = format_error(yaml_error.context_mark.line, yaml_error.context_mark.column) message = f"{cfg_path}: YAML error: {details}" raise InvalidConfig(message)
Вот где все становится сложно. Что если файл существует, читабелен, содержит действительный код YAML, но пользователь сделал опечатку при его написании?
Вот несколько случаев, которые мы должны справиться:
# empty config: no error # `auth` section is here but does not contain # a `github` entry: no error auth: gitlab: ... # `auth.github` section is here but does not # contain `token`, this is an error: auth: github: tken: "ab642ef9zf"
Наивный способ справиться с этим – написать код таким образом:
parsed = ruamel.yaml.safe_load(contents) auth = parsed.get("auth") if auth: github = auth.get("github") token = github.get("token") if not token: raise InvalidConfig("Expecting a key named 'token' in the 'github' section of 'auth' config")
Это становится утомительным очень быстро. Лучший способ – использовать схема
библиотека:
import schema auth_schema = schema.Schema( { schema.Optional("auth"): { schema.Optional("github") : { "token": str, } } } ) try: auth_schema.validate(parsed) except schema.SchemaError as schema_error: raise InvalidConfig(file_path, schema_error)
И последнее, но не менее важное: иногда мы захотим автоматически сохранить файл конфигурации.
В этом случае важно, чтобы сохраненный файл конфигурации все еще напоминал исходный.
С ruamel.yaml
, это делается с помощью Загрузчик
def save_token(token): contents = config_file.read_text() config = ruamel.yaml.load(contents, ruamel.yaml.RoundTripLoader) config["auth"]["github"]["token"] = token dumped = ruamel.yaml.dump(config, Dumper=ruamel.yaml.RoundTripDumper) config_file.write_text(dumped)
Профь! Это была большая работа для, казалось бы, простой задачи. Но я верю, что стоит пройти через все эти проблемы: мы рассмотрели много краевых случаев и убедились, что у нас всегда были очень четкие сообщения об ошибках. Пользователи кода, написанные так, будут очень благодарны, когда дела пойдут на юг. Ваше здоровье!
Спасибо, что прочитали это далеко:)
Я хотел бы услышать, что вы скажете, поэтому, пожалуйста, оставьте комментарий ниже или прочитайте Страница обратной связи Для большего количества способов связаться со мной.
Оригинал: “https://dev.to/dmerejkowsky/parsing-config-files-the-right-way”