2016-06-19 1 views
0

Я пытался читать и писать в файл конфигурации следующим образом:Неожиданный выбор дизайна в ConfigParser Пайтона

with open(file, 'r') as ini: 
     config = ConfigParser() 
     config.read(ini) 
     config['section'] = {'foo':45} 
with open(file, 'w') as ini: 
     config.write(ini) 

Я просто не мог заставить его читать уже сохраненные данные. Это заняло у меня некоторое время, чтобы узнать, что я на самом деле нужно прочитать из файла, как это:

config.read(file) 

Почему это не соответствует? Почему я должен читать и писать двумя разными способами? Есть ли веская причина такого выбора дизайна, что-то о I/O, которого я еще не знаю?

И почему это не вызвало исключения, когда я пытался читать из файлового буфера?

Существуют ли другие встроенные модули, которые несовместимы в обработке файлов?

+1

Вы прочитали [документацию] (https://docs.python.org/2/library/configparser.html#ConfigParser.RawConfigParser.read) и документацию для альтернативного метода readfp? – BrenBarn

ответ

2

Из документов есть на самом деле два (вроде три, если считать list аргумент) способов сделать это:

import ConfigParser, os 

config = ConfigParser.ConfigParser() 
config.readfp(open('defaults.cfg')) 
config.read(['site.cfg', os.path.expanduser('~/.myapp.cfg')]) 

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

Почему это не соответствует тому, что оно называется readfp, но write, а не writefp ...? Вы можете прочитать на своем history. Также here.

В конце концов, дизайны (и дизайнеры) не всегда идеальны, особенности не всегда пойманы, но как только он становится стандартным lib, интерфейс заморожен.

Мы можем посмотреть на ConfigParser источник, чтобы увидеть, почему read() с FileObject молча игнорируется:

def read(self, filenames): 
    """Read and parse a filename or a list of filenames. 

    Files that cannot be opened are silently ignored; this is 
    designed so that you can specify a list of potential 
    configuration file locations (e.g. current directory, user's 
    home directory, systemwide directory), and all existing 
    configuration files in the list will be read. A single 
    filename may also be given. 

    Return list of successfully read files. 
    """ 
    if isinstance(filenames, basestring): 
     filenames = [filenames] 
    read_ok = [] 
    for filename in filenames: 
     try: 
      fp = open(filename) 
     except IOError: 
      continue 
     self._read(fp, filename) 
     fp.close() 
     read_ok.append(filename) 
    return read_ok 

О - ничего себе - это будет интересно!

Ваш fileobject не является basestring, поэтому он предполагает, что он должен быть итерируемым. Затем он выполняет итерацию в файле fileobject. Это означает, что он читает ваш файл для списка имен файлов, которые он пытается открыть.

Например, я сделал файл f и заполнил его a-g, каждая буква в строке. strace показывает:

open("f", O_RDONLY)      = 3 
open("a\n", O_RDONLY)     = -1 ENOENT (No such file or directory) 
open("b\n", O_RDONLY)     = -1 ENOENT (No such file or directory) 
open("c\n", O_RDONLY)     = -1 ENOENT (No such file or directory) 
open("d\n", O_RDONLY)     = -1 ENOENT (No such file or directory) 
open("e\n", O_RDONLY)     = -1 ENOENT (No such file or directory) 
open("f\n", O_RDONLY)     = -1 ENOENT (No such file or directory) 
open("g\n", O_RDONLY)     = -1 ENOENT (No such file or directory) 

... и так как API предназначен игнорировать файлы, которые он не может открыть, он просто игнорирует ошибки.

Из документов,

Если ни один из указанных файлов не существует, то экземпляр ConfigParser будет содержать пустой набор данных.Приложение, которое требует начальных значений должны быть загружено из файла необходимо загрузить нужный файл или файлы, используя readfp() перед вызовом чтения() для любых дополнительных файлов:

Такого поведение достаточно удивительно, что я подал http://bugs.python.org/issue27351 чтобы они знали об этом краевом случае.

+0

Я согласен с ответом, если вы также можете объяснить, почему метод не вызывает исключение при попытке config.read из объекта файла, который может возникнуть в строке. Он просто терпел неудачу. – uzumaki