web-dev-qa-db-ja.com

環境変数を使用したConfigParserおよび文字列補間

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

編集:

$構文が、いわゆるstring内挿に暗黙的に含まれていると想定しました( manual を参照) :

コア機能に加えて、SafeConfigParserは補間をサポートしています。つまり、値には、同じセクションの他の値、または特別なDEFAULTセクションの値を参照するフォーマット文字列を含めることができます。

しかし、私は間違っています。このケースをどのように処理しますか?

15
nkint

まず、ドキュメントによれば、%(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
_
23
Michele d'Amico

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'])
5
Alex Markov

一部の環境変数を拡張する場合は、StringIOストリームを解析する前にos.path.expandvarsを使用して拡張できます。

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))
3
gagnonlg

ConfigParser.get 値を整数またはTrueに設定した場合でも、値は文字列です。ただし、ConfigParserには getintgetfloat および 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

編集

実際、SafeConfigParseros.environで初期化すると、名前の値を環境変数として取得できます。 Michele's の回答に感謝します。

2
Mauro Baraldi

環境からの適切な変数置換の秘訣は、環境変数に$ {}構文を使用することです。

[DEFAULT]
test_home=${HOME}

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

かなり遅いですが、多分それは私が最近持っていたのと同じ答えを探している誰かを助けることができます。また、コメントの1つは、他のセクションから環境変数と値をフェッチする方法でした。 INIファイルから読み込むときに、環境変数とマルチセクションタグの両方を変換する方法を以下に示します。

INIファイル:

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

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

_${PKG:PKG_TAG}_タイプのフォーマットを使用できるようにExtendedInterpolationを使用するPythonクラス。上記の_${DEL_PATH}_などの組み込みのos.path.expandvars()関数を使用して、INIを文字列に読み込んだときに、Windows環境変数を変換する機能を追加します。

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

_
1
L0ngSh0t

前回のバージョンのようです3.5.0、ConfigParserは環境変数を読み取っていなかったため、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,))

BasicInterpolationEnvInterpolationの違いは次のとおりです。

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

varをチェックインする前に、環境内でmapを見つけようとしています。

0
Franzi