Add encoding parameter to FileResource (#3580)

* feat: Add encoding parameter to FileResource

- Add optional encoding field (str | None, default None) to FileResource.
- Pass encoding through to read_text() for cross-platform text file reading.
- Preserve backward compatibility by defaulting to system encoding.

* test: Add tests for FileResource encoding parameter

- Test UTF-8 reading with explicit encoding for non-ASCII content.
- Test backward compatibility when no encoding is specified.
- Test that encoding is ignored for binary file reads.
- Test Latin-1 reading with matching encoding.

* docs: Document FileResource encoding parameter

- Add encoding="utf-8" to FileResource example in resource classes guide.
- Update FileResource description to mention encoding support.

* feat: Change FileResource encoding default from None to utf-8

- Default to utf-8 instead of system encoding to prevent cross-platform footgun.
- Update field description to reflect new default.
- Update test to verify default encoding is utf-8 with non-ASCII content.
- Remove redundant encoding="utf-8" from docs example since it is now the default.
This commit is contained in:
Marcus Shu 2026-03-22 21:55:16 +08:00 committed by GitHub
commit 2d7bc4e73d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 70 additions and 2 deletions

View file

@ -119,3 +119,63 @@ class TestFileResource:
await resource.read()
finally:
temp_file.chmod(0o644) # Restore permissions
async def test_read_utf8_with_encoding(self, tmp_path: Path):
"""FileResource should read UTF-8 files correctly when encoding is specified."""
content = (
"Smart quotes: \u201cleft\u201d and apostrophe\u2019s em-dash\u2014here"
)
file = tmp_path / "utf8_test.md"
file.write_text(content, encoding="utf-8")
resource = FileResource(
uri=FileUrl("file:///test/utf8"),
path=file,
mime_type="text/markdown",
encoding="utf-8",
)
result = await resource.read()
assert result.contents[0].content == content
async def test_default_encoding_is_utf8(self, tmp_path: Path):
"""FileResource defaults to UTF-8, reading non-ASCII without explicit encoding."""
content = "Smart quotes: \u201cleft\u201d and em-dash\u2014here"
file = tmp_path / "default_utf8_test.txt"
file.write_text(content, encoding="utf-8")
resource = FileResource(
uri=FileUrl("file:///test/default"),
path=file,
)
assert resource.encoding == "utf-8"
result = await resource.read()
assert result.contents[0].content == content
async def test_encoding_ignored_for_binary(self, tmp_path: Path):
"""Encoding field should be ignored when is_binary=True."""
data = b"\x00\x01\x02\xff"
file = tmp_path / "binary_test.bin"
file.write_bytes(data)
resource = FileResource(
uri=FileUrl("file:///test/binary"),
path=file,
mime_type="application/octet-stream",
encoding="utf-8",
)
result = await resource.read()
assert result.contents[0].content == data
async def test_read_latin1_with_encoding(self, tmp_path: Path):
"""FileResource should read non-UTF-8 files when correct encoding is specified."""
content = "na\u00efve"
file = tmp_path / "latin1_test.txt"
file.write_text(content, encoding="latin-1")
resource = FileResource(
uri=FileUrl("file:///test/latin1"),
path=file,
encoding="latin-1",
)
result = await resource.read()
assert result.contents[0].content == content