使用Hatch构建Python3.11库时触发TOML解码错误及相关配置咨询
使用Hatch构建Python3.11库时触发TOML解码错误及相关配置咨询
问题现象:执行构建命令时触发TOML解码错误
执行命令 hatch build pyproject.toml 或 python3 -m build pyproject.toml 时,出现以下TOML解码错误:
hatch build pyproject.toml ╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮ │ /home/gort/Saber/lib/python3.11/site-packages/hatch/cli/__init__.py:221 in main │ │ │ │ 218 │ │ 219 def main(): # no cov │ │ 220 │ try: │ │ ❱ 221 │ │ hatch(prog_name='hatch', windows_expand_args=False) │ │ 222 │ except Exception: # noqa: BLE001 │ │ 223 │ │ import sys │ │ 224 │ │ │ │ /home/gort/Saber/lib/python3.11/site-packages/click/core.py:1157 in __call__ │ │ │ │ /home/gort/Saber/lib/python3.11/site-packages/click/core.py:1078 in main │ │ │ │ /home/gort/Saber/lib/python3.11/site-packages/click/core.py:1688 in invoke │ │ │ │ /home/gort/Saber/lib/python3.11/site-packages/click/core.py:1434 in invoke │ │ │ │ /home/gort/Saber/lib/python3.11/site-packages/click/core.py:783 in invoke │ │ │ │ /home/gort/Saber/lib/python3.11/site-packages/click/decorators.py:45 in new_func │ │ │ │ /home/gort/Saber/lib/python3.11/site-packages/hatch/cli/build/__init__.py:55 in build │ │ │ │ 52 @click.pass_obj │ │ 53 def build(app: Application, location, targets, hooks_only, no_hooks, ext, clean, clean_h │ │ 54 │ """Build a project.""" │ │ ❱ 55 │ app.ensure_environment_plugin_dependencies() │ │ 56 │ │ │ 57 │ from hatch.config.constants import AppEnvVars │ │ 58 │ from hatch.utils.fs import Path │ │ │ │ /home/gort/Saber/lib/python3.11/site-packages/hatch/cli/application.py:239 in │ │ ensure_environment_plugin_dependencies │ │ │ │ 236 │ │ │ 237 │ def ensure_environment_plugin_dependencies(self) -> None: │ │ 238 │ │ self.ensure_plugin_dependencies( │ │ ❱ 239 │ │ │ self.project.config.env_requires_complex, wait_message='Syncing environment │ │ 240 │ │ ) │ │ 241 │ │ │ 242 │ def ensure_plugin_dependencies(self, dependencies: list[Requirement], *, wait_messag │ │ │ │ /home/gort/Saber/lib/python3.11/site-packages/hatch/project/core.py:46 in config │ │ │ │ 43 │ │ if self._config is None: │ │ 44 │ │ │ from hatch.project.config import ProjectConfig │ │ 45 │ │ │ │ │ ❱ 46 │ │ │ self._config = ProjectConfig(self.location, self.metadata.hatch.config, self │ │ 47 │ │ │ │ 48 │ │ return self._config │ │ 49 │ │ │ │ /home/gort/Saber/lib/python3.11/site-packages/hatch/project/core.py:127 in metadata │ │ │ │ 124 │ │ if self._metadata is None: │ │ 125 │ │ │ from hatchling.metadata.core import ProjectMetadata │ │ 126 │ │ │ │ │ ❱ 127 │ │ │ self._metadata = ProjectMetadata(self.location, self.plugin_manager, self.ra │ │ 128 │ │ │ │ 129 │ │ return self._metadata │ │ 130 │ │ │ │ /home/gort/Saber/lib/python3.11/site-packages/hatch/project/core.py:140 in raw_config │ │ │ │ 137 │ │ │ else: │ │ 138 │ │ │ │ from hatch.utils.toml import load_toml_file │ │ 139 │ │ │ │ │ │ ❱ 140 │ │ │ │ raw_config = load_toml_file(str(self._project_file_path)) │ │ 141 │ │ │ │ # Assume environment management only │ │ 142 │ │ │ │ if 'project' not in raw_config: │ │ 143 │ │ │ │ │ raw_config['project'] = {'name': self.location.name} │ │ │ │ /home/gort/Saber/lib/python3.11/site-packages/hatch/utils/toml.py:18 in load_toml_file │ │ │ │ 15 │ │ 16 def load_toml_file(path: str) -> dict[str, Any]: │ │ 17 │ with open(path, encoding='utf-8') as f: │ │ ❱ 18 │ │ return tomllib.loads(f.read()) │ │ 19 │ │ │ │ /usr/lib/python3.11/tomllib/_parser.py:102 in loads │ │ │ │ 99 │ │ │ pos += 1 │ │ 100 │ │ │ continue │ │ 101 │ │ if char in KEY_INITIAL_CHARS: │ │ ❱ 102 │ │ │ pos = key_value_rule(src, pos, out, header, parse_float) │ │ 103 │ │ │ pos = skip_chars(src, pos, TOML_WS) │ │ 104 │ │ elif char == "[": │ │ 105 │ │ │ try: │ │ │ │ /usr/lib/python3.11/tomllib/_parser.py:326 in key_value_rule │ │ │ │ 323 def key_value_rule( │ │ 324 │ src: str, pos: Pos, out: Output, header: Key, parse_float: ParseFloat │ │ 325 ) -> Pos: │ │ ❱ 326 │ pos, key, value = parse_key_value_pair(src, pos, parse_float) │ │ 327 │ key_parent, key_stem = key[:-1], key[-1] │ │ 328 │ abs_key_parent = header + key_parent │ │ 329 │ │ │ │ /usr/lib/python3.11/tomllib/_parser.py:369 in parse_key_value_pair │ │ │ │ 366 │ │ raise suffixed_err(src, pos, "Expected '=' after a key in a key/value pair") │ │ 367 │ pos += 1 │ │ 368 │ pos = skip_chars(src, pos, TOML_WS) │ │ ❱ 369 │ pos, value = parse_value(src, pos, parse_float) │ │ 370 │ return pos, key, value │ │ 371 │ │ 372 │ │ │ │ /usr/lib/python3.11/tomllib/_parser.py:616 in parse_value │ │ │ │ 613 │ │ │ 614 │ # Arrays │ │ 615 │ if char == "[": │ │ ❱ 616 │ │ return parse_array(src, pos, parse_float) │ │ 617 │ │ │ 618 │ # Inline tables │ │ 619 │ if char == "{": │ │ │ │ /usr/lib/python3.11/tomllib/_parser.py:428 in parse_array │ │ │ │ 425 │ │ if c == "]": │ │ 426 │ │ │ return pos + 1, array │ │ 427 │ │ if c != ",": │ │ ❱ 428 │ │ │ raise suffixed_err(src, pos, "Unclosed array") │ │ 429 │ │ pos += 1 │ │ 430 │ │ │ │ 431 │ │ pos = skip_comments_and_array_ws(src, pos) │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ TOMLDecodeError: Unclosed array (at line 25, column 31)
环境说明
- 系统:基于Debian的单板计算机(SBC)
- Python版本:3.11
- 环境管理:使用
venv虚拟环境,通过pip3或python3 -m pip管理包 - 构建工具:hatch/hatchling
项目的pyproject.toml内容
[build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "Saber" version = "0.5.0" dependencies = [ "*pyserial", # "gidgethub[httpx]>4.0.0", # "django>2.1; os_name != 'nt'", # "django>2.0; os_name == 'nt'", ] requires-python = ">=3.11" authors = [ {name = "User_One", email = "<User_One@example.com>"}, {name = "User_Two", email = "<User_Two@example.com>"}, ] maintainers = [ {name = "Seth", email = "seth@example.com"}, ] description = "Making UARTs work on new 64-bit Machines with Dimension Engineering!" readme = "readme.rst" license = {file = "MIT"} keywords = ["beagleboard.org" "BeagleY-AI" "UART" "Dimension Engineering"] classifiers = [ "Development Status :: 4 - Beta", "Programming Language :: Python", ] [project.optional-dependencies] #gui = ["PyQt5"] cli = [ "tomli", "pep517", "setuptools-pep660", ]
问题分析与解决方案
1. 触发TOML解码错误的根本原因
错误提示显示 Unclosed array (at line 25, column 31),对应pyproject.toml中的keywords数组:
keywords = ["beagleboard.org" "BeagleY-AI" "UART" "Dimension Engineering"]
TOML规范中,数组的元素之间必须用逗号分隔,这里的元素直接空格连接,违反了TOML语法,导致解析器认为数组未闭合。
修复方法:在keywords数组的每个元素之间添加逗号:
keywords = ["beagleboard.org", "BeagleY-AI", "UART", "Dimension Engineering"]
2. 关于tomli/tomllib的解码/编码疑问解答
- Python 3.11及以上版本已经内置了
tomllib(符合TOML 1.0规范的解析器),无需额外安装tomli。你遇到的解析错误是语法问题,和解析库无关,修复语法后即可正常解析。 - 如果需要手动处理TOML文件(比如解码错误的文件、编码TOML内容),可以使用
tomllib(内置)或tomli-w(用于编码,因为tomllib只有解析能力):- 解码TOML内容:
import tomllib with open("pyproject.toml", "rb") as f: data = tomllib.load(f) - 编码生成TOML内容:
需先安装tomli-w:pip install tomli-w,然后:import tomli_w data = {"project": {"name": "Saber", "version": "0.5.0"}} with open("pyproject.toml", "wb") as f: tomli_w.dump(data, f)
- 解码TOML内容:
3. 关于PEP 517/PEP 660的补充说明
- PEP 517是构建后端的标准,hatchling已经实现了该标准,所以你的
[build-system]配置是正确的,无需额外依赖pep517包(除非你需要使用pep517命令行工具)。 - PEP 660支持可编辑安装,
setuptools-pep660是setuptools的兼容层,但hatch本身已经支持PEP 660的可编辑安装(通过hatch develop命令),所以如果使用hatch作为环境和构建管理工具,无需安装setuptools-pep660。
4. 构建命令的小提示
hatch build命令不需要指定pyproject.toml文件,只要在项目根目录(pyproject.toml所在目录)执行即可,简化为:
hatch build
验证修复
修复pyproject.toml的keywords数组后,再次在项目根目录执行hatch build,即可正常完成构建。
备注:内容来源于stack exchange,提问作者Mala Dies




