2010-05-21 2 views
60

Я использую ConfigParser для чтения конфигурации сценария выполнения.Использование ConfigParser Python для чтения файла без имени раздела

Я хотел бы иметь гибкость, не предоставляя имя раздела (есть достаточно простые сценарии, они не нуждаются в «разделе»). ConfigParser будет генерировать исключение NoSectionError и не будет принимать файл.

Как заставить ConfigParser просто получить кортежи (key, value) файла конфигурации без имен разделов? Например:

key1=val1 
key2:val2 

Я предпочел бы не писать в конфигурационный файл.

ответ

33

Alex Martelli provided a solution для использования ConfigParser для разбора .properties файлов (которые, по-видимому, не имеют конфигурационных файлов без раздела).

His solution - это файловая обертка, которая автоматически вставляет заголовок раздела в соответствии с требованиями ConfigParser.

+0

+1, потому что это именно то, что я собирался предложить. Зачем добавлять всю сложность, когда все, что вам нужно сделать, это просто добавить раздел! – jathanism

+2

@jathanism: есть случаи, когда вы хотите работать с существующими файлами конфигурации/свойств, которые считываются с помощью существующего кода Java, и вы не знаете риска изменения этих заголовков. – Tshepang

+0

очень вдохновил! –

12

Вы можете использовать библиотеку ConfigObj, чтобы сделать это просто: http://www.voidspace.org.uk/python/configobj.html

Обновлено: Найти последнюю версию кода here.

Если вы под Debian/Ubuntu, вы можете установить этот модуль с помощью диспетчера пакетов:

apt-get install python-configobj 

Пример использования:

from configobj import ConfigObj 

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

После натолкнулся на эту проблему сам, я написал полную оболочку для ConfigParser (версия на Python 2), которая может читать и писать файлы без разделов прозрачно, основываясь на подходе Алекса Мартелли, связанного с принятым ответом. Это должно быть заменой на любое использование ConfigParser. Проводка его на случай, если кто-то, кто нуждается в этом, найдет эту страницу.

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) 
34

просвещенный this answer by jterrace, я пришел к этому решению:

  1. Читать весь файл в строку
  2. префикс с именем в разделе по умолчанию
  3. Используйте StringIO, чтобы имитировать файл типа объект
ini_str = '[root]\n' + open(ini_path, 'r').read() 
ini_fp = StringIO.StringIO(ini_str) 
config = ConfigParser.RawConfigParser() 
config.readfp(ini_fp) 
+0

Это также творит чудеса, чтобы разобрать простой Makefile (только с псевдонимами)! [Вот полный скрипт для подстановки псевдонимов по их полным командам в Python] (https://github.com/tqdm/tqdm/commit/7405a59ff2f1986922393c2bec77fced9932b28e#diff-2eeaed663bd0d25b7e608891384b7298R15), вдохновленный этим ответом. – gaborous

6

Самый простой способ сделать это - использовать синтаксический анализатор CSV python, на мой взгляд. Вот функция чтения/записи, демонстрирующая этот подход, а также тестовый драйвер. Это должно работать, если значения не могут быть многострочными. :)

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() 
+0

+1 Умное использование модуля 'csv' для решения общей жалобы' ConfigParser'. Легко обобщили больше и сделали [оба совместимы с Python 2 и 3] (http://stackoverflow.com/a/17796539/355230). – martineau

27

Вы можете сделать это с помощью одной дополнительной строки кода. (Две строки, если вы считаете import.)

В python 3 используйте itertools.chain() для моделирования заголовка раздела для read_file().

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) 

В Python 2, предварять строку заголовка раздела к данным, считанным из файла конфигурации, обернуть результат в StringIO объекта, и передать его в 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) 

При любом подходе, параметры конфигурации будут доступны в parser.items('top').

Вы можете использовать StringIO в Python 3, а также, за счет импорта ее из своего нового дома в io упаковке, но учтите, что readfp() является устаревшим в Python 3, и поэтому их следует избегать.

5

ответ Blueicefield упоминается configobj, но оригинальный Lib поддерживает только Python 2. Теперь он имеет Python 3+ совместимый порт:

https://github.com/DiffSK/configobj

API, не изменились, увидеть его doc.

1

configobj Lib может помочь в случае KEY="value"

from configobj import ConfigObj 
cfg = ConfigObj('/home/.aws/config') 
access_key_id = cfg['aws_access_key_id'] 
secret_access_key = cfg['aws_secret_access_key'] 
+3

Кажется, это дубликат ответа @ Blueicefield. –