Layered Configuration 多層級自訂組態

讓使用者可以在 system、user、local 等不同層級自訂組態的設計很常見,例如 Git 就是將組態分為 3 個層級(後者可以覆寫前者)- system (/etc/gitconfig)、global (~/.gitconfig) 及 local (.git/config)。

在 Python 中 ConfigParser 提供相關的支援,只要搭配 readfp()read(filenames) 就可以實作出類似的機制。

RawConfigParser.read(filenames)

This is designed so that you can specify a list of potential configuration file locations (for example, the current directory, the user’s home directory, and some system-wide directory), and all existing configuration files in the list will be read. If none of the named files exist, the ConfigParser instance will contain an empty dataset. An application which requires initial values to be loaded from a file should load the required file or files using readfp() before calling read() for any optional files:

import ConfigParser, os

config = ConfigParser.ConfigParser()
config.readfp(open('defaults.cfg'))
config.read(['site.cfg', os.path.expanduser('~/.myapp.cfg')])

只是有些細節在官方文件並沒有講清楚,尤其是 readfp(fp)read(filenames) 的關係:

  • 原始碼看來,readfp()read() 最後都是呼叫到 _read() 來解析檔案內容,就結果來看兩者並沒有差別。
  • _read() 發現 section 已經存在時,會直接調出現有 session 在裡頭新增 options 或更新現有的 values。

無論是透過 readfp()read() 讀取設定檔,後讀取的設定會 “併入" 現有的設定,覆寫的單位是 options 而非整個 section。

  • readfp() 預期檔案可以正常讀取,但 read() 則會略過無法正常讀取的檔案。

基於這樣的差異,readfp() 會用在 required/default files,而 read() 則用在 optional files,也才會有上述 “load the REQUIRED file or files using readfp() before calling read() for any OPTIONAL files" 的說法。

回到文件一開頭的應用情境,我們可以:

  • readfp()read() 依序讀入不同的設定檔,後者會併入/覆寫前者。
  • 必要的檔案用 readfp(),非必要的檔案則用 read()

假設只有 system.cfg 一定要存在的設定檔,使用者可以透過 user.cfglocal.cfg 來自訂。

[general]
name=Someone
greeting=Hello, World!

[security]
enalbed=yes
[general]
name=Python
>>> from ConfigParser import RawConfigParser
>>> configs = RawConfigParser()
>>> with open('system.cfg') as fp:
...     configs.readfp(fp)
...
>>> configs.read(['user.cfg', 'local.cfg'])
['local.cfg']
>>> configs.items('general')
[('name', 'Python'), ('greeting', 'Hello, World!')]
>>> configs.getboolean('security', 'enabled')
True
  1. 由於 local.cfg -> user.cfg -> system.cfg 的覆寫關係,讀取的順序要倒過來。
  2. 只有 system.cfg 是必要的檔案,所以用 readfp() 讀取,其餘則用 read()
  3. 確實這裡沒有自訂 user.cfg,所以 read() 回傳成功讀取的檔案清單。
  4. name=Python 的結果來看,local.cfg 確實覆寫了 system.cfg 的設定。
廣告

發表迴響

Please log in using one of these methods to post your comment:

WordPress.com Logo

您的留言將使用 WordPress.com 帳號。 登出 / 變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 / 變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 / 變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 / 變更 )

連結到 %s