Skip to content

Commit c15d458

Browse files
Fix failed pyproject.toml read when Unicode characters present (#1422)
* Initial plan * Fix unicode encoding issue in file read/write operations Co-authored-by: nathanjmcdougall <18602289+nathanjmcdougall@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: nathanjmcdougall <18602289+nathanjmcdougall@users.noreply.github.com>
1 parent 889eb18 commit c15d458

7 files changed

Lines changed: 30 additions & 8 deletions

File tree

src/usethis/_file/yaml/io_.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ def read_yaml(
449449
guess_indent: bool = True,
450450
) -> Generator[YAMLDocument, None, None]:
451451
"""A context manager to read a YAML file."""
452-
with yaml_path.open(mode="r") as f:
452+
with yaml_path.open(mode="r", encoding="utf-8") as f:
453453
try:
454454
yaml_document = _get_yaml_document(f, guess_indent=guess_indent)
455455
except YAMLError as err:

src/usethis/_init.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ def project_init():
4444
f"""\
4545
def hello() -> str:
4646
return "Hello from {project_name}!"
47-
"""
47+
""",
48+
encoding="utf-8",
4849
)
4950
(src_dir / pkg_name / "py.typed").touch(exist_ok=True)
5051
else:
@@ -115,7 +116,8 @@ def ensure_pyproject_toml(*, author: bool = True) -> None:
115116
[build-system]
116117
requires = ["hatchling"]
117118
build-backend = "hatchling.build"
118-
"""
119+
""",
120+
encoding="utf-8",
119121
)
120122
else:
121123
assert_never(backend)

src/usethis/_integrations/mkdocs/core.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@ def add_docs_dir() -> None:
2121
# {get_project_name()}
2222
2323
Welcome to the documentation for {get_project_name()}.
24-
"""
24+
""",
25+
encoding="utf-8",
2526
)

src/usethis/_integrations/pytest/core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def add_pytest_dir() -> None:
1919

2020
tick_print("Writing '/tests/conftest.py'.")
2121
(tests_dir / "conftest.py").write_text(
22-
"collect_ignore_glob = []\npytest_plugins = []\n"
22+
"collect_ignore_glob = []\npytest_plugins = []\n", encoding="utf-8"
2323
)
2424

2525

src/usethis/_integrations/sonarqube/config.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ def get_sonar_project_properties() -> str:
2424
# Get Python version
2525
try:
2626
python_version = PythonVersion.from_string(
27-
(usethis_config.cpd() / ".python-version").read_text().strip()
27+
(usethis_config.cpd() / ".python-version")
28+
.read_text(encoding="utf-8")
29+
.strip()
2830
).to_short_string()
2931
except (FileNotFoundError, PythonVersionParseError):
3032
python_version = PythonVersion.from_interpreter().to_short_string()

src/usethis/_io.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ def write_file(self) -> None:
116116
if not self.path.exists():
117117
return
118118

119-
self.path.write_text(self._dump_content())
119+
self.path.write_text(self._dump_content(), encoding="utf-8")
120120

121121
def read_file(self) -> DocumentT:
122122
"""Read the document from disk and store it in memory.
@@ -132,7 +132,7 @@ def read_file(self) -> DocumentT:
132132
)
133133
raise UnexpectedFileIOError(msg)
134134
try:
135-
document = self._parse_content(self.path.read_text())
135+
document = self._parse_content(self.path.read_text(encoding="utf-8"))
136136
except FileNotFoundError:
137137
msg = f"'{self.name}' not found in the current directory at '{self.path}'."
138138
raise FileNotFoundError(msg) from None

tests/usethis/_file/pyproject_toml/test_pyproject_toml_io_.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,23 @@ def test_read_file_invalid_toml(self, tmp_path: Path):
7676
with pytest.raises(PyprojectTOMLDecodeError), manager:
7777
manager.read_file()
7878

79+
def test_read_file_with_unicode_chars(self, tmp_path: Path):
80+
# Unicode characters like 'ā' in comments should not cause read failures.
81+
82+
# Arrange
83+
with change_cwd(tmp_path):
84+
(tmp_path / "pyproject.toml").write_text(
85+
'# Comment with unicode: ā\n[project]\nname = "foo"\n',
86+
encoding="utf-8",
87+
)
88+
manager = PyprojectTOMLManager()
89+
90+
# Act & Assert (should not raise)
91+
with manager:
92+
result = manager.get()
93+
94+
assert result.value["project"]["name"] == "foo"
95+
7996
def test_commit_and_get(self, tmp_path: Path):
8097
with change_cwd(tmp_path):
8198
# Arrange

0 commit comments

Comments
 (0)