Как использовать ConfigParser для извлечения неизвестного количества пар значений?

В этом вопросе OP хотел извлечь неизвестное количество разделов из файла конфигурации, используя ConfigParser, где все разделы имели один и тот же внутренний формат.

В моем случае я вынужден ограничить все разделы одним разделом. Я пытаюсь разработать поддерживаемый, вменяемый метод хранения "пар" данных. Придуманный пример объяснит лучше:

[pings]
host1 = foo.com
port1 = 80
interval1 = 60
host2 = bar.com
port2 = 8080
interval2 = 300
host3 = baz.com
port3 = 443
interval3 = 15

Это можно прочитать в цикле инкрементного индекса, и как только мы достигнем итерации, где ожидаемое значение не найдено, мы можем предположить, что достигли конца данных и остановились. Проблема в том, что это некрасиво и неустранимо, и, что еще хуже, предположим, что мы хотели закомментировать host1... Нам нужно изменить нумерацию всего остального в списке, чтобы предотвратить преждевременное завершение сканирования.

Это не кажется мне намного лучше:

[pings]
hosts = foo.com, bar.com, baz.com
ports = 80, 8080, 443
intervals = 60, 300, 15

Слишком трудно читать для любого значительного числа элементов, хрупких для редактирования и т. Д.

Я мог бы повернуть это:

[pings]
ping1 = foo.com, 80, 60
ping2 = bar.com, 8080, 300
ping3 = baz.com, 443, 15

Немного более логически сгруппированы, но все еще страдает от проблемы перенумерации строк, если элементы добавляются / удаляются / переставляются. Также легко потеряться (подумайте, визуально сканируя очень широкую таблицу CSV).

Я хотел бы избежать встраивания JSON или любых формальных литеральных обозначений объектов. Слишком много возможностей для плохого редактирования, чтобы сбить с толку парсер.

Давным-давно я помню работу с PHP, где вы могли добавить переменное количество элементов строки запроса, например ?host[]=foo.com&host[]=bar.com&host[]=baz.com и $_GET['host'] global станет массивом с тремя элементами внутри. Кража этого синтаксиса, я мог бы сделать:

[pings]
host[] = foo.com
port[] = 80
interval[] = 60

host[] = bar.com
port[] = 8080
interval[] = 300

host[] = baz.com
port[] = 443
interval[] = 15

... или же:

[pings]
ping[] = foo.com, 80, 60
ping[] = bar.com, 8080, 300
ping[] = baz.com, 443, 15

... и было бы немного меньше беспокоиться при добавлении / удалении элементов. Но я верю, что на самом деле случится с ConfigParser, если последние элементы (или это самые ранние?) Перезапишут другие... если [] даже принят в качестве ключа конфигурации.

Существуют ли передовые практики для выражения таких вещей в конфигурационных файлах?

1 ответ

Без тестирования я бы написал что-то вроде:

config = ConfigParser.ConfigParser()

with open('path/to/file.ini') as config_f:
    config.readfp(config_f)
    # config.readfile in 3.2+

configitems = config.section('pings')

ping_infos = [{'host':host, 'port':port, 'interval':interval} for
                host, port, interval in [value.split()]} for
              key,value in configitems if key.startswith('ping')]

Затем вы можете написать свой конфигурационный файл как

[pings]
; pingANYUNIQUECHAR = hostname, portnumber, interval
ping-foo.com = foo.com, 80, 60
ping-bar.com = bar.com, 8080, 300

И в итоге вы получите:

ping_infos = [{'host': 'foo.com',
               'port': '80',
               'interval': 60},
              {'host': 'bar.com',
               'port': 8080,
               'interval': 300}]
Другие вопросы по тегам