Как исключить DEFAULT из Python ConfigParser.items()?

Я использую ConfigParser для загрузки данных из файла конфигурации следующим образом:

test.conf:

[myfiles]
fileone: %(datadir)s/somefile.foo
filetwo: %(datadir)s/nudderfile.foo

load.py:

import ConfigParser

config = ConfigParser.ConfigParser({'datadir': '/tmp'})
config.read('test.conf')

print config.items('myfiles')
print config.get('myfiles', 'datadir')

Вывод:

$ python load.py 
[('datadir', '/tmp'), ('filetwo', '/tmp/nudderfile.foo'), ('fileone', '/tmp/somefile.foo')]
/tmp

Я удивлен, что значения по умолчанию для подстановки переменных ('datadir', '/tmp') отображаются как часть. items() и .get(), как если бы они были значениями в файле конфигурации. Ожидается ли такое поведение? Любая работа вокруг, так что я могу просто перебирать .items(), не получая значения словаря по умолчанию там, но все же используя магическую интерполяцию?

Ссылка: http://docs.python.org/library/configparser.html

Спасибо!

Обновление: было указано, что это ожидаемое поведение: значения по умолчанию похожи на любые другие пары имя/значение в файле конфигурации. Аналогично, пары имя/значение в файле конфигурации также доступны для "магической интерполяции", поэтому, если я определяю:

foo: bar
zap: %(foo)snowl

Я получу [... ('zap': 'barnowl')]

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

Моим конкретным сценарием является следующее: я хотел инициализировать объект конфигурации чем-то вроде {basedir: '/foo/bar'}, поскольку абсолютные пути к определенным файлам зависят от установки. Затем мне нужно передать этот объект конфигурации вокруг и выполнить различные итерации файлов по файлам. Я не хочу, чтобы каждый класс читал конфигурацию, чтобы знать, что она была инициализирована с определенными значениями по умолчанию и что она должна игнорировать их, поскольку они не являются фактическими файлами. Это возможно? Любой способ скрыть значения по умолчанию из .item() и .get(), но все еще иметь интерполяцию? Спасибо!

Ответ 1

Вообще, я нашел класс configparser.Configparser очень полезным, но также отсутствующим. Другие тоже.

Тем не менее, он может быть разделен на подклассы и расширен, иногда красиво, иногда не так красиво (= очень зависит от реализации)

Вот решение вашей проблемы, протестированное в Python3:

class ConfigParser(configparser.ConfigParser):
    """Can get options() without defaults
    """
    def options(self, section, no_defaults=False, **kwargs):
        if no_defaults:
            try:
                return list(self._sections[section].keys())
            except KeyError:
                raise NoSectionError(section)
        else:
            return super().options(section, **kwargs)

Это один из плохих примеров, потому что он частично копирует исходный код options(). Было бы лучше, если бы базовый класс RawConfigParser предоставил внутренний получатель параметров _options(self, section) который включал бы приведение исключений, и options() который использовал бы это. Затем в подклассах мы могли бы повторно использовать _options().

В Python 2 я считаю, что единственным изменением является вызов super() для super(ConfigParser,self).

Затем вы можете использовать:

print config.options('myfiles', no_defaults=True)

А также использовать этот список для итерации.

Ответ 2

Не можете ли вы просто отфильтровать значения по умолчанию?

например:.

filtered_items = [x for x in config.items('myfiles') if x[0] not in config.defaults()]

Ответ 3

Попробуйте следующее:

config = ConfigParser.ConfigParser({'blahblahblah': 'blah'})
config.read('test.conf')

Клавиша blahblahblah также появится в items, а не потому, что это шаблон в файле .ini, а потому, что вы указали его как значение по умолчанию. Таким образом, ConfigParser обрабатывает значения по умолчанию: если он не может найти их в файле, он присваивает свои значения по умолчанию.

Итак, мне кажется, у вас есть простая путаница понятий здесь.

Ответ 4

Следующее должно дать вам только пары ключ/значение в разделе myfiles без перечисления их в DEFAULT:

list(set(config.items('myfiles'))-set(config.items('DEFAULT')))