ConfigParser gebruiken om een ​​bestand zonder sectienaam te lezen

Ik gebruik ConfigParserom de runtime-configuratie van een script te lezen.

Ik zou graag de flexibiliteit hebben om geen sectienaam op te geven (er zijn scripts die eenvoudig genoeg zijn; ze hebben geen ‘sectie’ nodig). ConfigParsergenereert een NoSectionError-uitzondering en accepteert het bestand niet.

Hoe kan ik ConfigParser eenvoudig de (key, value)-tupels van een configuratiebestand laten ophalen zonder sectienamen?

Bijvoorbeeld:

key1=val1
key2:val2

Ik schrijf liever niet naar het configuratiebestand.


Antwoord 1, autoriteit 100%

Alex Martelli heeft een oplossing gebodenvoor het gebruik van ConfigParserom .properties-bestanden te ontleden (die schijnbaar sectieloze configuratiebestanden zijn).

Zijn oplossingis een bestandsachtige wrapper die automatisch een dummy-sectiekop invoegt om te voldoen aan ConfigParser‘s vereisten.


Antwoord 2, autoriteit 84%

Verlicht door dit antwoord van jterrace, bedenk ik deze oplossing:

  1. Lees het hele bestand in een string
  2. Voorvoegsel met een standaard sectienaam
  3. Gebruik StringIO om een ​​bestandsachtig object na te bootsen
ini_str = '[root]\n' + open(ini_path, 'r').read()
ini_fp = StringIO.StringIO(ini_str)
config = ConfigParser.RawConfigParser()
config.readfp(ini_fp)

EDITvoor toekomstige googlers: vanaf Python 3.4+ is readfpverouderd en is StringIOniet meer nodig. In plaats daarvan kunnen we read_stringrechtstreeks gebruiken:

with open('config_file') as f:
    file_content = '[dummy_section]\n' + f.read()
config_parser = ConfigParser.RawConfigParser()
config_parser.read_string(file_content)

Antwoord 3, autoriteit 82%

Je kunt dit doen in een enkele regel code.

In python 3 voegt u een nep-sectieheader toe aan uw configuratiebestandsgegevens en geeft u deze door aan read_string().

from configparser import ConfigParser
parser = ConfigParser()
with open("foo.conf") as stream:
    parser.read_string("[top]\n" + stream.read())  # This line does the trick.

U kunt ook itertools.chain()om een ​​sectiekop voor read_file(). Dit kan geheugenefficiënter zijn dan de bovenstaande benadering, wat handig kan zijn als je grote configuratiebestanden hebt in een beperkte runtime-omgeving.

from configparser import ConfigParser
from itertools import chain
parser = ConfigParser()
with open("foo.conf") as lines:
    lines = chain(("[top]",), lines)  # This line does the trick.
    parser.read_file(lines)

In python 2 voegt u een nep-sectiekop voor uw configuratiebestandsgegevens toe en plaatst u het resultaat in een StringIOobject en geef het door aan readfp().

from ConfigParser import ConfigParser
from StringIO import StringIO
parser = ConfigParser()
with open("foo.conf") as stream:
    stream = StringIO("[top]\n" + stream.read())  # This line does the trick.
    parser.readfp(stream)

Bij elk van deze benaderingen zijn uw configuratie-instellingen beschikbaar in parser.items('top').

Je zou StringIO ook in python 3 kunnen gebruiken, misschien voor compatibiliteit met zowel oude als nieuwe python-interpreters, maar merk op dat het nu in het io-pakket en readfp()is nu verouderd.

U kunt ook overwegen een TOML-parser te gebruiken in plaats van ConfigParser.


Antwoord 4, autoriteit 35%

Je kunt de ConfigObj-bibliotheek gebruiken om dat eenvoudig te doen: http://www.voidspace .org.uk/python/configobj.html

Bijgewerkt: vind hierde nieuwste code.

Als u zich onder Debian/Ubuntu bevindt, kunt u deze module installeren met uw pakketbeheerder:

apt-get install python-configobj

Een voorbeeld van gebruik:

from configobj import ConfigObj
config = ConfigObj('myConfigFile.ini')
config.get('key1') # You will get val1
config.get('key2') # You will get val2

Antwoord 5, autoriteit 14%

De gemakkelijkste manier om dit te doen is naar mijn mening de CSV-parser van Python te gebruiken. Hier is een lees-/schrijffunctie die deze aanpak demonstreert, evenals een teststuurprogramma. Dit zou moeten werken op voorwaarde dat de waarden niet uit meerdere regels bestaan. 🙂

import csv
import operator
def read_properties(filename):
    """ Reads a given properties file with each line of the format key=value.  Returns a dictionary containing the pairs.
    Keyword arguments:
        filename -- the name of the file to be read
    """
    result={ }
    with open(filename, "rb") as csvfile:
        reader = csv.reader(csvfile, delimiter='=', escapechar='\\', quoting=csv.QUOTE_NONE)
        for row in reader:
            if len(row) != 2:
                raise csv.Error("Too many fields on row with contents: "+str(row))
            result[row[0]] = row[1] 
    return result
def write_properties(filename,dictionary):
    """ Writes the provided dictionary in key-sorted order to a properties file with each line of the format key=value
    Keyword arguments:
        filename -- the name of the file to be written
        dictionary -- a dictionary containing the key/value pairs.
    """
    with open(filename, "wb") as csvfile:
        writer = csv.writer(csvfile, delimiter='=', escapechar='\\', quoting=csv.QUOTE_NONE)
        for key, value in sorted(dictionary.items(), key=operator.itemgetter(0)):
                writer.writerow([ key, value])
def main():
    data={
        "Hello": "5+5=10",
        "World": "Snausage",
        "Awesome": "Possum"
    }
    filename="test.properties"
    write_properties(filename,data)
    newdata=read_properties(filename)
    print "Read in: "
    print newdata
    print
    contents=""
    with open(filename, 'rb') as propfile:
        contents=propfile.read()
    print "File contents:"
    print contents
    print ["Failure!", "Success!"][data == newdata]
    return
if __name__ == '__main__': 
     main() 

Antwoord 6, autoriteit 11%

Nadat ik dit probleem zelf tegenkwam, schreef ik een complete wrapper naar ConfigParser (de versie in Python 2) die bestanden zonder secties transparant kan lezen en schrijven, gebaseerd op de aanpak van Alex Martelli, gekoppeld aan het geaccepteerde antwoord. Het zou een drop-in vervanging moeten zijn voor elk gebruik van ConfigParser. Plaats het voor het geval iemand die dat nodig heeft deze pagina vindt.

import ConfigParser
import StringIO
class SectionlessConfigParser(ConfigParser.RawConfigParser):
    """
    Extends ConfigParser to allow files without sections.
    This is done by wrapping read files and prepending them with a placeholder
    section, which defaults to '__config__'
    """
    def __init__(self, *args, **kwargs):
        default_section = kwargs.pop('default_section', None)
        ConfigParser.RawConfigParser.__init__(self, *args, **kwargs)
        self._default_section = None
        self.set_default_section(default_section or '__config__')
    def get_default_section(self):
        return self._default_section
    def set_default_section(self, section):
        self.add_section(section)
        # move all values from the previous default section to the new one
        try:
            default_section_items = self.items(self._default_section)
            self.remove_section(self._default_section)
        except ConfigParser.NoSectionError:
            pass
        else:
            for (key, value) in default_section_items:
                self.set(section, key, value)
        self._default_section = section
    def read(self, filenames):
        if isinstance(filenames, basestring):
            filenames = [filenames]
        read_ok = []
        for filename in filenames:
            try:
                with open(filename) as fp:
                    self.readfp(fp)
            except IOError:
                continue
            else:
                read_ok.append(filename)
        return read_ok
    def readfp(self, fp, *args, **kwargs):
        stream = StringIO()
        try:
            stream.name = fp.name
        except AttributeError:
            pass
        stream.write('[' + self._default_section + ']\n')
        stream.write(fp.read())
        stream.seek(0, 0)
        return ConfigParser.RawConfigParser.readfp(self, stream, *args,
                                                   **kwargs)
    def write(self, fp):
        # Write the items from the default section manually and then remove them
        # from the data. They'll be re-added later.
        try:
            default_section_items = self.items(self._default_section)
            self.remove_section(self._default_section)
            for (key, value) in default_section_items:
                fp.write("{0} = {1}\n".format(key, value))
            fp.write("\n")
        except ConfigParser.NoSectionError:
            pass
        ConfigParser.RawConfigParser.write(self, fp)
        self.add_section(self._default_section)
        for (key, value) in default_section_items:
            self.set(self._default_section, key, value)

Antwoord 7, Autoriteit 9%

Blueicefield’s antwoord vermeld configobj, maar de oorspronkelijke lib ondersteunt alleen Python 2. Het heeft nu een Python 3 + compatibele poort:

https://github.com/DiffSK/configobj

API’s zijn niet veranderd, zie het doc .

Other episodes