简体   繁体   中英

How to use ConfigParser to extract an unknown number of value pairs?

In this question , the OP wanted to extract an unknown number of sections from a config file using ConfigParser, where all of the sections shared the same internal format.

In my case, I'm constrained to keep everything confined to one section. I'm trying to work out a maintainable, sane method of storing "pairs" of data. A contrived example will explain things better:

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

This could be read in an incrementing-index loop, and once we reach an iteration where an expected value is not found, we can surmise that we've reached the end of the data and stop. Problem is this is ugly and unmaintainable, and what's worse, suppose we wanted to comment out host1 ... We would have to renumber everything else in the list to prevent the scan from terminating early.

This doesn't strike me as being much better:

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

Too hard to read for any significant number of items, fragile to edit, etc.

I could pivot it:

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

A bit more logically grouped, but still suffers from the line renumbering problem if items are added/removed/rearranged. Also it's easy to get lost (think visually scanning a very wide CSV table).

I'd like to avoid embedding JSON or any kind of formal object literal notation. Too many opportunities for a bad edit to trip up the parser.

A long time ago I remember working with PHP where you could add a variable number of query string items like ?host[]=foo.com&host[]=bar.com&host[]=baz.com and the $_GET['host'] global would become an array with the three items inside. Stealing that syntax, I could do:

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

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

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

... or:

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

... and there would be a little less to worry about when adding/removing items. But I believe what would actually happen with ConfigParser is the latest items (or is it the earliest ones?) would overwrite the others... if [] is even accepted as a config key.

Are there any established best practices to express things like this in config files?

Without testing it, I'd write something like:

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')]

Then you can write your config file as

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

And you'll end up with:

ping_infos = [{'host': 'foo.com',
               'port': '80',
               'interval': 60},
              {'host': 'bar.com',
               'port': 8080,
               'interval': 300}]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM