From ad631099729021b9711fb160d5c7e2cbb6a6463b Mon Sep 17 00:00:00 2001 From: Pitchfork-and-Torch Date: Fri, 18 Sep 2026 02:15:38 +0000 Subject: [PATCH] Strip leading UTF-8 BOM when reading INI files Windows editors often write a UTF-8 BOM; with encoding=utf-8 that left U+FEFF on the first section line and raised ParseError. Strip the BOM after read for both IniConfig() and IniConfig.parse(). --- src/iniconfig/__init__.py | 6 ++++++ testing/test_iniconfig.py | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/iniconfig/__init__.py b/src/iniconfig/__init__.py index b84809f..e9fd907 100644 --- a/src/iniconfig/__init__.py +++ b/src/iniconfig/__init__.py @@ -112,6 +112,9 @@ def __init__( if data is None: with open(self.path, encoding=encoding) as fp: data = fp.read() + # Strip a leading UTF-8 BOM so editors that write one still parse. + if data.startswith(""): + data = data.removeprefix("") # Use old behavior (no stripping) for backward compatibility sections_data, sources = _parse.parse_ini_data( @@ -166,6 +169,9 @@ def parse( if data is None: with open(fspath, encoding=encoding) as fp: data = fp.read() + # Strip a leading UTF-8 BOM so editors that write one still parse. + if data.startswith(""): + data = data.removeprefix("") sections_data, sources = _parse.parse_ini_data( fspath, diff --git a/testing/test_iniconfig.py b/testing/test_iniconfig.py index 85193c5..488178f 100644 --- a/testing/test_iniconfig.py +++ b/testing/test_iniconfig.py @@ -412,3 +412,23 @@ def test_unicode_whitespace_in_key_names() -> None: ) assert "key" in config["section"] assert config["section"]["key"] == "value" + + +def test_utf8_bom_file(tmp_path): + """Files saved with a UTF-8 BOM (common on Windows editors) must parse.""" + path = tmp_path / "bom.ini" + path.write_bytes(b"\xef\xbb\xbf[section]\nkey = value\n") + config = IniConfig(path) + assert config["section"]["key"] == "value" + + +def test_utf8_bom_in_data_string(): + config = IniConfig("x.ini", data="\ufeff[section]\nkey = value\n") + assert config["section"]["key"] == "value" + + +def test_parse_utf8_bom_file(tmp_path): + path = tmp_path / "bom.ini" + path.write_bytes(b"\xef\xbb\xbf[section]\nkey = value\n") + config = IniConfig.parse(path) + assert config["section"]["key"] == "value"