Интерполяция ConfigParser и String с переменной env

я немного из синтаксиса python, и у меня есть проблема при чтении файла .ini с интерполированными значениями.

это мой ini файл:

[DEFAULT]
home=$HOME
test_home=$home

[test]
test_1=$test_home/foo.csv
test_2=$test_home/bar.csv

Эти строки

from ConfigParser import SafeConfigParser

parser = SafeConfigParser()
parser.read('config.ini')

print parser.get('test', 'test_1')

выводит

$test_home/foo.csv

пока я ожидаю

/Users/nkint/foo.csv

EDIT:

Я предположил, что синтаксис $ неявно включен в так называемую строчную интерполяцию (ссылаясь на manual):

В дополнение к основной функциональности SafeConfigParser поддерживает интерполяция. Это означает, что значения могут содержать строки форматирования, которые обратитесь к другим значениям в том же разделе или значениям в специальном Раздел DEFAULT.

Но я ошибаюсь. Как справиться с этим случаем?

Ответ 1

Прежде всего, согласно документации, вы должны использовать %(test_home)s для интерполяции test_home. Кроме того, ключ нечувствителен к регистру, и вы не можете использовать клавиши HOME и HOME. Наконец, вы можете использовать SafeConfigParser(os.environ) для учета вашей среды.

from ConfigParser import SafeConfigParser
import os


parser = SafeConfigParser(os.environ)
parser.read('config.ini')

Где config.ini есть

[DEFAULT]
test_home=%(HOME)s

[test]
test_1=%(test_home)s/foo.csv
test_2=%(test_home)s/bar.csv

Ответ 2

Вы можете написать пользовательскую интерполяцию в случае Python 3:

import configparser
import os


class EnvInterpolation(configparser.BasicInterpolation):
    """Interpolation which expands environment variables in values."""

    def before_get(self, parser, section, option, value, defaults):
        return os.path.expandvars(value)


cfg = """
[section1]
key = value
my_path = $PATH
"""

config = configparser.ConfigParser(interpolation=EnvInterpolation())
config.read_string(cfg)
print(config['section1']['my_path'])

Ответ 3

Если вы хотите развернуть некоторые переменные среды, вы можете сделать это, используя os.path.expandvars перед анализом потока StringIO:

import ConfigParser
import os
import StringIO

with open('config.ini', 'r') as cfg_file:
    cfg_txt = os.path.expandvars(cfg_file.read())

config = ConfigParser.ConfigParser()
config.readfp(StringIO.StringIO(cfg_txt))

Ответ 4

Значения ConfigParser.get являются строками, даже если вы устанавливаете значения как целое или True. Но в ConfigParser есть getint, getfloat и getboolean.

settings.ini

[default]
home=/home/user/app
tmp=%(home)s/tmp
log=%(home)s/log
sleep=10
debug=True

читатель конфигурации

>>> from ConfigParser import SafeConfigParser
>>> parser = SafeConfigParser()
>>> parser.read('/home/user/app/settings.ini')
>>> parser.get('defaut', 'home')
'/home/user/app'
>>> parser.get('defaut', 'tmp')
'/home/user/app/tmp'
>>> parser.getint('defaut', 'sleep')
10
>>> parser.getboolean('defaut', 'debug')
True

Edit

Действительно, вы можете получить значения имен как environment var, если инициализировать SafeConfigParser с помощью os.environ. Спасибо за ответ Мишель.

Ответ 5

трюк для правильной подстановки переменных из среды заключается в использовании синтаксиса ${} для переменных среды:

[DEFAULT]
test_home=${HOME}

[test]
test_1=%(test_home)s/foo.csv
test_2=%(test_home)s/bar.csv

Ответ 6

Похоже, что в последней версии 3.5.0 ConfigParser не считывал переменные env, поэтому я в итоге предоставил пользовательскую интерполяцию на основе BasicInterpolation.

class EnvInterpolation(BasicInterpolation):
    """Interpolation as implemented in the classic ConfigParser,
    plus it checks if the variable is provided as an environment one in uppercase.
    """

    def _interpolate_some(self, parser, option, accum, rest, section, map,
                          depth):
        rawval = parser.get(section, option, raw=True, fallback=rest)
        if depth > MAX_INTERPOLATION_DEPTH:
            raise InterpolationDepthError(option, section, rawval)
        while rest:
            p = rest.find("%")
            if p < 0:
                accum.append(rest)
                return
            if p > 0:
                accum.append(rest[:p])
                rest = rest[p:]
            # p is no longer used
            c = rest[1:2]
            if c == "%":
                accum.append("%")
                rest = rest[2:]
            elif c == "(":
                m = self._KEYCRE.match(rest)
                if m is None:
                    raise InterpolationSyntaxError(option, section,
                                                   "bad interpolation variable reference %r" % rest)
                var = parser.optionxform(m.group(1))
                rest = rest[m.end():]
                try:
                    v = os.environ.get(var.upper())
                    if v is None:
                        v = map[var]
                except KeyError:
                    raise InterpolationMissingOptionError(option, section, rawval, var) from None
                if "%" in v:
                    self._interpolate_some(parser, option, accum, v,
                                           section, map, depth + 1)
                else:
                    accum.append(v)
            else:
                raise InterpolationSyntaxError(
                    option, section,
                    "'%%' must be followed by '%%' or '(', "
                    "found: %r" % (rest,))

Разница между BasicInterpolation и EnvInterpolation заключается в:

   v = os.environ.get(var.upper())
   if v is None:
       v = map[var]

где я пытаюсь найти var в окружающей среде перед проверкой на map.

Ответ 7

Довольно поздно, но, возможно, это может помочь кому-то еще искать те же ответы, которые я недавно получил. Кроме того, один из комментариев был о том, как получить переменные окружения и значения из других разделов. Вот как я имею дело с преобразованием переменных среды и многосекционных тегов при чтении из INI файла.

INI FILE:

[PKG]
# <VARIABLE_NAME>=<VAR/PATH>
PKG_TAG = Q1_RC1

[DELIVERY_DIRS]
# <DIR_VARIABLE>=<PATH>
NEW_DELIVERY_DIR=${DEL_PATH}\ProjectName_${PKG:PKG_TAG}_DELIVERY

Класс Python, который использует ExtendedInterpolation, чтобы вы могли использовать форматирование типа ${PKG:PKG_TAG}. Я добавляю возможность преобразовывать переменные среды Windows, когда я читаю в INI, в строку, используя встроенную os.path.expandvars() такую как ${DEL_PATH} выше.

import os
from configparser import ConfigParser, ExtendedInterpolation

class ConfigParser(object):

    def __init__(self):
        """
        initialize the file parser with
        ExtendedInterpolation to use ${Section:option} format
        [Section]
        option=variable
        """
        self.config_parser = ConfigParser(interpolation=ExtendedInterpolation())

    def read_ini_file(self, file='./config.ini'):
        """
        Parses in the passed in INI file and converts any Windows environ vars.

        :param file: INI file to parse
        :return: void
        """
        # Expands Windows environment variable paths
        with open(file, 'r') as cfg_file:
            cfg_txt = os.path.expandvars(cfg_file.read())

        # Parses the expanded config string
        self.config_parser.read_string(cfg_txt)

    def get_config_items_by_section(self, section):
        """
        Retrieves the configurations for a particular section

        :param section: INI file section
        :return: a list of name, value pairs for the options in the section
        """
        return self.config_parser.items(section)

    def get_config_val(self, section, option):
        """
        Get an option value for the named section.

        :param section: INI section
        :param option: option tag for desired value
        :return: Value of option tag
        """
        return self.config_parser.get(section, option)

    @staticmethod
    def get_date():
        """
        Sets up a date formatted string.

        :return: Date string
        """
        return datetime.now().strftime("%Y%b%d")

    def prepend_date_to_var(self, sect, option):
        """
        Function that allows the ability to prepend a
        date to a section variable.

        :param sect: INI section to look for variable
        :param option: INI search variable under INI section
        :return: Void - Date is prepended to variable string in INI
        """
        if self.config_parser.get(sect, option):
            var = self.config_parser.get(sect, option)
            var_with_date = var + '_' + self.get_date()
            self.config_parser.set(sect, option, var_with_date)