适用于Pluto风格ini文件的I/O库。
项目描述
inifix
inifix
是一个小的Python库,提供类似于标准库模块 json
或 tomllib
(与 tomli_w
一起)的 load/dump
接口,用于Pluto风格和 Idefix 风格的ini配置文件。
虽然其主要目标是遵循Idefix的'ini'格式规范,但它支持其一小部分。
主要区别在于
inifix
支持无节定义。这意味着也支持来自 FARGO 3D 的配置文件。- 在
inifix
中,字符串可以使用'
或"
转义。这允许在字符串值中有空格,并强制字符串类型解码,其中数值和布尔类型可以工作。
在极少数Idefix的'ini'格式与Pluto不匹配的情况下,inifix
采取最简单的路径来支持两者。
已知差异是
- Idefix 允许将布尔值写成
yes
和no
,inifix 也可以这样做,但在 Pluto 中(截至版本 4.4)这些是不合法的。请注意,与 Idefix 不同,Idefix 对这些特殊字符串真正不区分大小写,而从版本 5.0.0 开始的inifix
只解析一组受限制的未转义字符串作为布尔值,如true
、TRUE
、True
、yes
、Yes
和YES
,但如TruE
或yES
这样的字符串将被解析为字符串。 - Idefix 允许使用十进制表示法来编写整数(例如
1.0
或1e3
)。在反序列化这样的字符串时,这会产生一些歧义,因为期望的类型(int
或float
)不能被明确猜测。默认情况下,从版本 5.0 开始的inifix
将这些解析为float
,允许进行 1:1 的往返。从版本 2.1 开始的 Idefix 对以十进制形式编写的整数也有抵抗力,因此inifix
不会通过加载/修补/转储例程破坏任何有效的 inifile。有关更多信息,请参阅 读取选项。
文件格式规范详情
展开 !
- 参数名称是字母数字字符串 - 名称和值由非换行空白字符分隔 - 值以 Unicode 字符表示 - 所有的值如果可能都视为数字(例如,`1e3` 被读取为 `1000`) - 数字值如果不会丢失精度,则读取为整数,否则为浮点数 - 未转义的字符串 `true`、`false`、`yes` 和 `no` 转换为布尔值,以及它们相应的 uppercase 和 "title" 变体(例如 `TRUE` 或 `True`)。 - 不能读取为数字或布尔值的值被读取为字符串。 - 字符串分隔符 `"` 和 `'` 可用于包含空白的字符串,或用于强制将值作为字符串读取,否则这些值会被读取为数字和布尔值。 - 一个参数可以关联到单个值或由空白字符分隔的值列表 - 部分标题以 `[` 开始,以 `]` 结束 - 注释以 `#` 开始,将被忽略如果调用 inifix.load(<filename>)
不引发错误,则认为文件是有效的。
示例
以下内容被认为是有效的
# My awesome experiment
[Grid]
x 1 2 u 10 # a comment
y 4 5 l 100
[Time Integrator]
CFL 1e-3
tstop 1E3
并映射到
{
"Grid": {
"x": [1, 2, "u", 10],
"y": [4, 5, "l", 100]
},
"Time Integrator": {
"CFL": 0.001,
"tstop": 1000.0
}
}
以下无标题的格式不符合 Pluto/Idefix 的规范,但在 inifix
中也是有效的
mode fargo
# Time integrator
CFL 1e-3
tstop 1e3
并映射到
{
"mode": "fargo",
"CFL": 0.001,
"tstop": 1000.0
}
请注意,使用 e-表示法(例如 1e-3
或 1E3
)的字符串被解码为浮点数。相反,在写入文件时,如果使用 e-表示法会导致更紧凑的表示,则浮点数将以 e-表示法重新编码。例如,100000.0
被编码为 1e5
,但 189.0
保持不变,因为 1.89e2
需要多一个字符。在两种表示法同样紧凑的情况下(例如 1.0
与 1e0
),在编码时优先使用十进制。
在解码时,`e` 可以是大写或小写,但它们在编码时始终以小写形式表示。
安装
python -m pip install inifix
用法
公共 API 模仿 Python 标准库 json
,并包含四个主要功能
inifix.load
和inifix.dump
分别从文件读取和写入inifix.loads
从str
读取并返回一个dict
,而inifix.dumps
执行相反的操作。
读取数据
inifix.load
从文件读取并返回一个 dict
import inifix
with open("pluto.ini", "rb") as fh:
conf = inifix.load(fh)
# or equivalently
conf = inifix.load("pluto.ini")
假设文件以 UTF-8 编码。
读取选项
inifix.load
和 inifix.loads
接受一个可选的布尔标志 parse_scalars_as_lists
(从 inifix
v4.0.0 开始),这对于简化处理未知数据很有用:所有值都可以安全地处理为数组,并且可以迭代,即使在存在标量字符串的情况下也是如此。例如
>>> import inifix
>>> from pprint import pprint
>>> pprint(inifix.load("example.ini"))
{'Grid': {'x': [1, 2, 'u', 10], 'y': [4, 5, 'l', 100]},
'Time Integrator': {'CFL': 0.001, 'tstop': 1000.0}}
>>> pprint(inifix.load("example.ini", parse_scalars_as_lists=True))
{'Grid': {'x': [1, 2, 'u', 10], 'y': [4, 5, 'l', 100]},
'Time Integrator': {'CFL': [0.001], 'tstop': [1000.0]}}
inifix.load
和 inifix.loads
也接受一个 integer_casting
参数(自 inifix
v5.0.0 新增),该参数可以设置为决定十进制表示法中恰好具有整数值的数字(例如 1e2
或 30000.
)应该如何解析。此参数接受两个值:'stable'
(默认值)提供 float
而且提供 'aggressive'
提供 int
,与 inifix
v4.5.0 的行为相匹配。
主要区别在于默认策略在类型上是往返稳定的,而积极模式则不是
>>> import inifix
>>> data = {'option_a': [0, 1., 2e3, 4.5]}
>>> data
{'option_a': [0, 1.0, 2000.0, 4.5]}
>>> inifix.loads(inifix.dumps(data))
{'option_a': [0, 1.0, 2000.0, 4.5]}
>>> inifix.loads(inifix.dumps(data), integer_casting='aggressive')
{'option_a': [0, 1, 2000, 4.5]}
积极转换也可能导致超出一定范围的精度丢失
>>> import inifix
>>> data = {'option_b': 9_007_199_254_740_993}
>>> inifix.loads(inifix.dumps(data))
{'option_b': 9007199254740993}
>>> inifix.loads(inifix.dumps(data), integer_casting='aggressive')
{'option_b': 9007199254740992}
默认情况下,inifix.load
和 inifix.loads
验证输入数据。可以通过指定 skip_validation=True
跳过此步骤。
写入文件或字符串
inifix.dump
将数据写入文件。
一个典型用例是将 inifix.load
和 inifix.dump
结合起来,通过加载/修补/转储例程在运行时程序化地更新现有配置文件。
>>> import inifix
>>> with open("pluto.ini", "rb") as fr:
... inifix.load(fr)
>>> conf["Time"]["CFL"] = 0.1
>>> with open("pluto-mod.ini", "wb") as fw:
... inifix.dump(conf, fw)
或者,等价地
>>> import inifix
>>> inifix.load("pluto.ini")
>>> conf["Time"]["CFL"] = 0.1
>>> inifix.dump(conf, "pluto-mod.ini")
在写入时,数据将根据 inifix 的格式规范进行验证。文件始终以 UTF-8 编码。
inifix.dumps
与 inifix.dump
相同,不同之处在于它返回一个字符串而不是写入文件。
>>> import inifix
>>> data = {"option_a": 1, "option_b": True}
>>> print(inifix.dumps(data))
option_a 1
option_b True
默认情况下,inifix.dump
和 inifix.dumps
验证输入数据。可以通过指定 skip_validation=True
跳过此步骤。
模式验证
inifix.validate_inifile_schema
可以用来验证任意字典是否符合 inifile 的格式,遵循库的格式。如果字典 data
无效,将引发异常(ValueError
)。
inifix.validate_inifile_schema(data)
运行时格式化
inifix.format_string
格式化表示 ini 文件内容的字符串。有关如何大规模使用此功能,请参阅 格式化 CLI。
编写类型安全的 inifix.load(s)
应用程序
inifix.load
对任何特定参数的类型没有内置期望;相反,所有类型都在运行时推断,这使得该函数能够无缝地与任意参数文件一起工作。
但这也意味着输出不是(也不能是)类型安全的。换句话说,类型检查器(例如 mypy
)无法推断输出的确切类型,这在依赖类型检查的应用程序中是一个问题。
此问题的解决方案实际上不仅限于 inifix.load
,并且与任何任意形成的 dict
都兼容,即创建一个管道,其中实现可类型检查的代码,其中数据也进行运行时验证。
我们将通过一个来自 nonos
的真实生活示例来展示这一点。例如,如果我们只关心 idefix.ini
中 [Output]
和 [Hydro]
部分的几个参数。让我们围绕这些参数构建一个类型安全的 read_parameter_file
函数。
class IdefixIni:
def __init__(self, *, Hydro, Output, **kwargs):
self.hydro = IdefixIniHydro(**Hydro)
self.output = IdefixIniOutput(**Output)
class IdefixIniHydro:
def __init__(self, **kwargs):
if "rotation" in kwargs:
self.frame = "COROT"
self.rotation = float(kwargs["rotation"])
else:
self.frame = "UNSET"
self.rotation = 0.0
class IdefixIniOutput:
def __init__(self, *, vtk, **kwargs):
self.vtk = float(vtk)
def read_parameter_file(file) -> IdefixIni:
return IdefixIni(**inifix.load(file))
ini = read_parameter_file("idefix.ini")
类型检查器现在可以安全地假设 ini.hydro.frame
、ini.hydro.rotation
和 ini.output.vtk
都存在,并且分别为 str
、float
和 float
类型。如果在运行时不验证此假设,将引发 TypeError
。
请注意,我们已使用通配符 **kwargs
构造来捕获可选参数以及任何其他我们不在乎的参数(可能是没有)。
命令行界面
包附带命令行工具,用于验证或格式化兼容的 inifiles。
验证 CLI
此命令行工具检查您的 inifiles 是否可以使用 inifix.load
从命令行加载。
$ inifix-validate pluto.ini
Validated pluto.ini
此 CLI 也可以作为 python -m inifix.validate
调用。
格式化 CLI
要就地格式化文件,请使用
$ inifix-format pluto.ini
inifix格式保证保留注释,并且只编辑(添加或删除)空白字符。
文件始终以UTF-8编码。
要打印到标准输出而不是编辑文件,请使用--diff
标志
$ inifix-format pluto.ini --diff
此CLI也可以作为python -m inifix.format
调用。
默认情况下,inifix-format
也会验证输入数据。可以通过--skip-validation
标志跳过此步骤
pre-commit钩子
inifix-validate
和inifix-format
可以作为以下配置的pre-commit
钩子使用(添加到.pre-commit-config.yaml
)
- repo: https://github.com/neutrinoceros/inifix.git
rev: v5.0.2
hooks:
- id: inifix-validate
或者
- repo: https://github.com/neutrinoceros/inifix.git
rev: v5.0.2
hooks:
- id: inifix-format
请注意,inifix-format
默认也会验证数据,因此同时使用两个钩子是多余的。验证和格式化可以解耦如下
- repo: https://github.com/neutrinoceros/inifix.git
rev: v5.0.2
hooks:
- id: inifix-validate
- id: inifix-format
+ args: [--skip-validation]
默认情况下,这两个钩子针对匹配正则表达式(\.ini)$
的文件。可以覆盖此表达式,例如
hooks:
- id: inifix-format
+ files: (\.ini|\.par)$
测试
我们使用pytest框架来测试inifix
。可以通过简单的pytest
调用从顶级运行测试套件。
$ python -m pip install --requirement requirements/tests.txt
$ pytest
项目详情
下载文件
下载您平台的文件。如果您不确定选择哪个,请了解有关安装包的更多信息。
源分布
构建分布
inifix-5.0.2.tar.gz的散列
算法 | 散列摘要 | |
---|---|---|
SHA256 | 22201da0b2fb4bbe1af1b57a6c981b147d7a3fe163c332cac3dcc973b484972a |
|
MD5 | 20a8a6c613eb60a25cc791cbc76b95c1 |
|
BLAKE2b-256 | 9b056e366ec42af21911bdbc9842872eda8a861819905894c0b9c63a84a8ecb5 |