diff --git a/CHANGELOG.md b/CHANGELOG.md index ee732346..fa81cc6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,9 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] -- ... +### Fixed + +- Strip a leading UTF-8 BOM from `.env` file contents so the first variable is no longer silently lost when the file is saved with BOM (e.g. by some JetBrains IDEs on Windows) by [@h1whelan] in [#640] ## [1.2.2] - 2026-03-01 @@ -432,6 +434,7 @@ os.PathLike]` instead of just `os.PathLike` (#347 by [@bbc2]). [#563]: https://github.com/theskumar/python-dotenv/pull/563 [#497]: https://github.com/theskumar/python-dotenv/pull/497 [#161]: https://github.com/theskumar/python-dotenv/issues/161 +[#640]: https://github.com/theskumar/python-dotenv/pull/640 [790c5c0]: https://github.com/theskumar/python-dotenv/commit/790c5c02991100aa1bf41ee5330aca75edc51311 @@ -460,6 +463,7 @@ os.PathLike]` instead of just `os.PathLike` (#347 by [@bbc2]). [@gergelyk]: https://github.com/gergelyk [@gongqingkui]: https://github.com/gongqingkui [@greyli]: https://github.com/greyli +[@h1whelan]: https://github.com/h1whelan [@harveer07]: https://github.com/harveer07 [@jadutter]: https://github.com/jadutter [@jankislinger]: https://github.com/jankislinger diff --git a/src/dotenv/parser.py b/src/dotenv/parser.py index eb100b47..66773604 100644 --- a/src/dotenv/parser.py +++ b/src/dotenv/parser.py @@ -68,7 +68,7 @@ class Error(Exception): class Reader: def __init__(self, stream: IO[str]) -> None: - self.string = stream.read() + self.string = stream.read().removeprefix("\ufeff") self.position = Position.start() self.mark = Position.start() diff --git a/tests/test_parser.py b/tests/test_parser.py index 43386e5a..4ec5a5af 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -545,6 +545,35 @@ ), ], ), + # UTF-8 BOM at the start of the file should be stripped + ( + "\ufeffa=b", + [ + Binding( + key="a", + value="b", + original=Original(string="a=b", line=1), + error=False, + ) + ], + ), + ( + "\ufeffa=b\nc=d", + [ + Binding( + key="a", + value="b", + original=Original(string="a=b\n", line=1), + error=False, + ), + Binding( + key="c", + value="d", + original=Original(string="c=d", line=2), + error=False, + ), + ], + ), ], ) def test_parse_stream(test_input, expected):