-- TOC --
Python标准库中的configparser模块,是专门用来读写配置文件的模块,我们常使用后缀为ini
的文件来表示配置文件。据说ini配置文件最早出现在Win系统中,是Initialization File的缩写 ,早已成为一个比较通用的格式,但据说现在python的new best friend,是TOML。(参考:JSON,YAML和TOML)
配置文件是一个文本文件,文件内有用方括号[]
括起来的区域,称为section
,以及该区域内的key/value
对。section的名称是case sensitive的,即大小写相关;而key的名称则大小写无关。一般section的name都用大写,key name都用小写。
key/value使用等号=
或者冒号:
进行连接,key和value都是string,如果需要别的类型,要自己在代码中进行转换。因为都是string,key和value的中间可以有空格,但是两边的空格则会被忽略。value还可以多行,换行后要有前置的空格,表示这是一个换行处。
在配置文件中,还可以有注释,以#
或;
开头的行,都是注释。在解析的时候,注释会被直接忽略掉。注释是一独立行!
下面是一个Python configparser模块支持的ini配置文件的例子:
# file name: example.ini
[DEFAULT]
item1 = 1
ITEM2 = 2
Item3 = 3
item4=4
item5 : 5
item6:6
item 7 = 7 7 7
port = 12345
; this is also comments
sth =
[SERVER]
server = py.maixj.net
#port = 12345
name = i don't want to
tell you my name, haha...
[WEB]
ITEM2=222.222
web server = apache
alternative = nginx
我们来写点代码解析它:
>>> import configparser
>>>
>>> config = configparser.ConfigParser()
>>> config.read('example.ini')
['example.ini']
>>> config.sections()
['SERVER', 'WEB']
这个example.ini配置文件,有一个DEFAULT区域,这个区域有一点点特别,它保存了配置项的默认值,但不会出现在解析后的sections中。当某个配置项只在DEFAULT中存在是,读取这个配置项,读到的就是DEFAULT中的值,如果此配置项还同时存在于其它section中,其它section中保存的值,优先级更高。如下示例,读取port和ITEM2:
>>> config['SERVER']['port']
'12345'
>>> config['WEB']['port']
'12345'
>>> config['SERVER']['ITEM2']
'2'
>>> config['WEB']['ITEM2']
'222.222'
section name大小写有关,而具体的配置项key name大小写无关:
>>> config['WEB']['web server']
'apache'
>>> config['WEB']['WEB SERVER']
'apache'
再看看空格,换行和没有value的配置项:
>>> config['WEB']['item 7']
'7 7 7'
>>> config['SERVER']['name']
"i don't want to\ntell you my name, haha..."
>>> print(config['SERVER']['name'])
i don't want to
tell you my name, haha...
>>> config['SERVER']['sth'] # sth=, = is must
''
前文已经有所叙述,配置文件中的数据默认都是string,如果需要其它类型,要自己在代码中转化。同时,configparser模块也还是提供了几个很顺手的函数接口,专门处理数据类型转换。有 get()
,getint()
,getfloat()
,getboolean()
这四个。get()函数是通用的,读出来的是string。
>>> config.get('WEB', 'web server')
'apache'
>>> config.getint('WEB', 'item5')
5
>>> config.getfloat('WEB', 'item2')
222.222
>>> config.getboolean('WEB', 'item1')
True
注意getboolean()函数,配置项的值如果是
1, yes, on, true
,使用getboolean()函数读出来的就是True;配置项的值如果是0,no,off,false
,读出来的就是False。传统的配置文件,都是字符串,配置文件格式本身缺少对数据类型的支持。而新的TOML在这一点上就很不一样。
config对象的数据组织很像dict对象,可以像遍历dict对象那样编译config对象。
还有另外一种读取配置数据的方式:
>>> config_web = config['WEB']
>>> config_web.get('web server')
'apache'
使用get系列接口,还可以提供fallback值,在配置项缺失的时候,不至于raise:
>>> config_web.get('host') # default fallback to None
>>> config_web.get('host', 'aws')
'aws'
>>> config.get('WEB', 'host', fallback=None)
>>> config.get('WEB', 'host', fallback='aws')
'aws'
注意:config对象(含多个section)与config_web对象(某个具体的section),在使用get的时候,参数不一样!
上面的示例,都是关于如何读取配置文件。下面示例如何写配置文件,在需要自动更新配置文件的时候有用:
>>> import configparser
>>> config = configparser.ConfigParser()
>>> config['DEFAULT'] = {'A':1,'B':2,'C':3}
>>> config['Server'] = {}
>>> config['Server']['A'] = '5'
>>> config['Server']['B'] = '6'
>>> config['Server']['D'] = '8'
>>> with open('anotest.ini','w') as inifile:
... config.write(inifile)
然后,就得到了anotest.ini文件:
$ cat anotest.ini
[DEFAULT]
a = 1
b = 2
c = 3
[Server]
a = 5
b = 6
d = 8
如果要清除某个section下的配置项,调用clear
接口。如:
config['Server'].clear()
以上内容,对于使用configparser模块处理配置文件,基本足够。
configparser模块除了支持从配置文件读取配置外,还可以从str对象和dict对象读取配置。
从str对象读取配置:
>>> configstr = """
... [Server]
... addr = 1.2.3.4
... domain = py.maixj.net
... [MISC]
... port = 12345
... log = on
... """
>>> config = configparser.ConfigParser()
>>> config.read_string(configstr)
>>> config.sections()
['Server', 'MISC']
从dict对象读取配置:
>>> config_dict = {'Server':{'addr' : '1.2.3.4',
... 'domain' : 'py.maixj.net'},
... 'MISC' : {'port' : 12345,
... 'log' : 'on'}}
>>> config = configparser.ConfigParser()
>>> config.read_dict(config_dict)
>>> config.sections()
['Server', 'MISC']
>>> config['MISC'].getboolean('log')
True
如果我们的配置文件长这样:
$ cat example.ini
[ABC]
abc
abc配置项后面连等号都没有,此时这个配置项就是no value。默认情况下,这是个错误的配置文件,configparser模块在读取的时候,会raise。不过,我们增加一个参数来支持这种情况:
>> import configparser
>>> config = configparser.ConfigParser(allow_no_value=True)
>>> config.read('example.ini')
['example.ini']
>>> config.get('ABC', 'abc')
>>> config.get('ABC', 'abc') is None
True
度出来的no value配置项,其value为None。
basic interpolation
>>> config_str = """
... [Path]
... home = /home/xinlin
... video = %(home)s/Video
... movie = %(video)s/Movie
... """
>>> config = configparser.ConfigParser()
>>> config.read_string(config_str)
>>> config['Path']['home']
'/home/xinlin'
>>> config['Path']['video']
'/home/xinlin/Video'
>>> config['Path']['movie']
'/home/xinlin/Video/Movie'
所谓basic initerpolation,即这种插入替换只能出现在相同的section,不能跨区,正如上面的代码,home与video,audio在同一个Path区域。插入替换home值的方式是:%(home)s
。上面的代码,实现的是多层插入替换,movie的值来之video,video的值又来自home。
extended interpolation
可以跨区,而且语法上也有区别。Basic和Extended两种方式的语法不兼容。下面是Extended Interpolation的代码示例:
>>> config_str = """
... [Home]
... home = /home/xinlin
... [Lib]
... lib = ${Home:home}/Lib
... gcc_lib = ${lib}/gcc
... [DEFAULT]
... binpath = /usr/bin
... [Path]
... path = ${binpath}/secret
... """
>>> from configparser import ConfigParser, ExtendedInterpolation
>>> config = ConfigParser(interpolation=ExtendedInterpolation())
>>> config.read_string(config_str)
>>> config['Lib']['lib']
'/home/xinlin/Lib'
>>> config['Lib']['gcc_lib']
'/home/xinlin/Lib/gcc'
>>> config['Path']['path']
'/usr/bin/secret'
下面是我一直在使用的两个读写ini配置文件的接口函数,供参考:
def readConfig(cfile, section, name):
"""Return None or config value by specifying cfile, section and name.
cfile is formatted according to configparser module.
"""
config = configparser.ConfigParser()
config.read(cfile)
return config.get(section, name, fallback=None)
#try:
# config = configparser.ConfigParser()
# config.read(cfile)
# rv = config[section][name]
#except KeyError:
# return None
#else:
# return rv
def writeConfig(cfile, section, name, value):
"""Add or update an configurable item in config file.
Create config file (cfile) if it is not existed,
cfile is formatted according to configparser module.
"""
config = configparser.ConfigParser()
config.read(cfile) # no raise if file isn't existed.
if section not in config:
config[section] = {}
config[section][name] = value
with open(cfile+'.config.tmp', 'w') as f:
config.write(f)
try: # in case cfile is not existed
os.remove(cfile)
except FileNotFoundError:
pass
os.rename(cfile+'.config.tmp', cfile)
flake8的配置文件,默认文件名为tox.ini
,其实也是configparser模块支持的格式。例如:
$ cat tox.ini
[flake8]
format = %(path)s: %(row)d,%(col)d: %(code)s: %(text)s
exclude =
.git,
__pycache__,
README.md,
LICENSE
ignore =
# E127: continuation line over-indented for visual indent
E127,
# E225,E226,E227,E228: missing whitespace around somewhere
E225,
E226,
E227,
E228,
# E231: missing whitespace after ','
E231,
# E241: multiple spaces after ','
E241,
# E701: multiple statements on one line (colon)
E701,
# W391: blank line at end of file
W391,
# W503,W504: line break before binary operator
W503,
W504
可以在不断地换行中注释!
针对这个tox.ini文件,在使用configparser模块读取的时候,要设置get接口的format这个option:
>>> import configparser
>>>
>>> config = configparser.ConfigParser()
>>> config.read('tox.ini')
['tox.ini']
>>> config.get('flake8','format',raw=True)
'%(path)s: %(row)d,%(col)d: %(code)s: %(text)s'
>>>
>>> config['flake8']['exclude']
'\n.git,\n__pycache__,\nREADME.md,\nLICENSE'
>>> config['flake8']['ignore']
'\nE127,\nE225,\nE226,\nE227,\nE228,\nE231,\nE241,\nE701,\nW391,\nW503,\nW504'
如果不使用raw=True
读取,会raise。
本文链接:https://cs.pynote.net/sf/python/202203131/
-- EOF --
-- MORE --