使用env变量进行ConfigParser和String插值

时间:2014-10-27 11:41:58

标签: python 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

编辑:

我认为$语法隐含在所谓的字符串插值中(参考manual):

  

除了核心功能之外,SafeConfigParser还支持   插值。这意味着值可以包含格式字符串   引用同一部分中的其他值,或特殊值中的值   默认部分。

但我错了。如何处理这种情况?

8 个答案:

答案 0 :(得分:19)

首先,根据文档,您应使用%(test_home)s来插入test_home。此外,密钥不区分大小写,您无法同时使用HOMEhome密钥。最后,您可以使用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

答案 1 :(得分:5)

你可以在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'])

答案 2 :(得分: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))

答案 3 :(得分:1)

即使您将值设置为整数或True,

ConfigParser.get值也是字符串。但ConfigParser有getintgetfloatgetboolean

<强>的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('defauts', 'home')
'/home/user/app'
>>> parser.get('defauts', 'tmp')
'/home/user/app/tmp'
>>> parser.getint('defauts', 'sleep')
10
>>> parser.getboolean('defauts', 'debug')
True

修改

如果使用SafeConfigParser初始化os.environ,确实可以将名称值设置为environ var。感谢Michele's回答。

答案 4 :(得分:1)

从环境中进行适当的变量替换的技巧是对环境变量使用$ {}语法:

[DEFAULT]
test_home=${HOME}

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

答案 5 :(得分:0)

在上一个版本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的地方。

答案 6 :(得分:0)

很晚了,但也许可以帮助其他人寻找与我最近的答案相同的答案。此外,评论之一是如何从其他部分中获取环境变量和值。从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

使用ExtendedInterpolation的Python类,以便您可以使用${PKG:PKG_TAG}类型格式。我添加了使用内置os.path.expandvars()函数(例如上述${DEL_PATH})将INI读取为字符串时将Windows环境vars转换的功能。

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)

答案 7 :(得分:0)

基于@alex-markov 答案(和代码)和@srand9 评论,以下解决方案适用于环境变量和横截面引用。

请注意,插值现在基于 ExtendedInterpolation 以允许横截面引用,并且基于 before_read 而不是 before_get

#!/usr/bin/env python3
import configparser
import os


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

    def before_read(self, parser, section, option, value):
        value = super().before_read(parser, section, option, value)
        return os.path.expandvars(value)


cfg = """
[paths]
foo : ${HOME}
[section1]
key = value
my_path = ${paths:foo}/path
"""

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