使用Python来解析配置文件

715 阅读3分钟

有时,一个程序需要足够多的参数,把它们全部作为命令行参数或环境变量是不愉快的,也是不可行的。在这些情况下,你会想使用一个配置文件。

有几种流行的配置文件格式。其中有古老的 (虽然偶尔定义不足)INI 格式,流行的但有时难以手写的JSON 格式,广泛的但偶尔在细节上令人惊讶的YAML 格式,以及最新增加的TOML ,许多人还没有听说。

你的首要任务是选择一种格式,然后记录这一选择。在这个简单的部分完成后,现在是解析配置的时候了。

有时,有一个与配置中的 "抽象 "数据相对应的类是个好主意。因为这段代码不会对配置做任何事情,这是展示解析逻辑的最简单方法。

想象一下一个文件处理器的配置:它包括一个输入目录,一个输出目录,以及要接收哪些文件。

配置类的抽象定义可能看起来是这样的。

from __future__ import annotations
import attr
@attr.frozen
class Configuration:
    @attr.frozen
    class Files:
        input_dir: str
        output_dir: str
    files: Files
    @attr.frozen
    class Parameters:
        patterns: List[str]
    parameters: Parameters

为了使特定格式的代码更简单,你还将写一个函数来解析这个类的字典。注意,这假设配置将使用破折号,而不是下划线。这种差异并不罕见。

def configuration_from_dict(details):
    files = Configuration.Files(
        input_dir=details["files"]["input-dir"],
        output_dir=details["files"]["output-dir"],
    )
    parameters = Configuration.Paraneters(
        patterns=details["parameters"]["patterns"]
    )
    return Configuration(
        files=files,
        parameters=parameters,
    )

JSON

JSON(JavaScript Object Notation)是一种类似JavaScript的格式。

下面是一个JSON格式的配置例子。

json_config = """
{
    "files": {
        "input-dir": "inputs",
        "output-dir": "outputs"
    },
    "parameters": {
        "patterns": [
            "*.txt",
            "*.md"
        ]
    }
}
"""

解析逻辑使用json 模块将 JSON 解析为 Python 的内置数据结构 (字典、列表、字符串),然后从字典中创建类。

import json
def configuration_from_json(data):
    parsed = json.loads(data)
    return configuration_from_dict(parsed)

INI

INI格式,最初在Windows上流行,成为事实上的配置标准。

这里是作为 INI 的相同配置。

ini_config="""
[files]
input-dir = inputs
output-dir = outputs
[parameters]
patterns = ['*.txt', '*.md']
"""

Python 可以使用内置的configparser 模块对其进行解析。解析器表现为一个dict-样的对象,所以它可以直接传递给configuration_from_dict

import configparser
def configuration_from_ini(data):
    parser = configparser.ConfigParser()
    parser.read_string(data)
    return configuration_from_dict(parser)

YAML

YAML (Yet Another Markup Language) 是JSON的一个扩展,旨在更容易手工编写。它实现了这一点,部分原因是它有一个很长的规范。

下面是YAML中的相同配置。

yaml_config = """
files:
  input-dir: inputs
  output-dir: outputs
parameters:
  patterns:
  - '*.txt'
  - '*.md'
"""

为了让Python解析这个,你需要安装一个第三方模块。最流行的是PyYAML (pip install pyyaml)。YAML 解析器也会返回内置的 Python 数据类型,可以传递给configuration_from_dict 。然而,YAML解析器期望的是一个流,所以你需要将字符串转换为一个流。

import io
import yaml
def configuration_from_yaml(data):
    fp = io.StringIO(data)
    parsed = yaml.safe_load(fp)
    return configuration_from_dict(parsed)

TOML

TOML(Tom's Own Markup Language)被设计成YAML的轻量级替代品。它的规范更短,而且在一些地方已经很流行了(例如,Rust的包管理器Cargo就使用它来进行包配置)。

下面是与TOML相同的配置。

toml_config = """
[files]
input-dir = "inputs"
output-dir = "outputs"
[parameters]
patterns = [ "*.txt", "*.md",]
"""

为了解析TOML,你需要安装一个第三方的包。最流行的一个叫,简单地说,toml 。像YAML和JSON一样,它返回基本的Python数据类型。

import toml
def configuration_from_toml(data):
    parsed = toml.loads(data)
    return configuration_from_dict(parsed)

总结

选择一种配置格式是一种微妙的权衡。然而,一旦你做出决定,Python 可以用几行代码来解析大多数流行的格式。