Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/littlefs/lfs.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ from littlefs.context import UserContext
from littlefs import errors


FILENAME_ENCODING = 'ascii'
"""Default filename encoding"""
FILENAME_ENCODING = 'utf-8'
"""Default filename encoding.

littlefs stores names as opaque byte strings, so any encoding works on the
C side. UTF-8 is used so that non-ASCII filenames are supported; since ASCII
is a strict subset of UTF-8, existing ASCII names encode/decode identically.
"""

class LFSStat(NamedTuple):
"""Littlefs File / Directory status."""
Expand Down
66 changes: 66 additions & 0 deletions test/test_unicode_filenames.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import pytest

from littlefs import LittleFS

# A mix of Latin-1, CJK and astral-plane (emoji) characters to exercise
# multi-byte UTF-8 sequences.
UNICODE_NAMES = ["café.txt", "日本語.bin", "emoji_😀.dat"]


@pytest.fixture(scope="function")
def fs():
yield LittleFS(block_size=128, block_count=64)


@pytest.mark.parametrize("name", UNICODE_NAMES)
def test_open_write_read_roundtrip(fs, name):
payload = name.encode("utf-8")
with fs.open(name, "wb") as f:
f.write(payload)
with fs.open(name, "rb") as f:
assert f.read() == payload


@pytest.mark.parametrize("name", UNICODE_NAMES)
def test_stat_preserves_name(fs, name):
with fs.open(name, "wb") as f:
f.write(b"x")
assert fs.stat(name).name == name


def test_listdir_roundtrips_unicode(fs):
for name in UNICODE_NAMES:
with fs.open(name, "wb") as f:
f.write(b"x")
assert set(fs.listdir("/")) == set(UNICODE_NAMES)


@pytest.mark.parametrize("name", UNICODE_NAMES)
def test_mkdir_and_nested_file(fs, name):
fs.mkdir(name)
assert name in fs.listdir("/")
nested = name + "/inner_£.txt"
with fs.open(nested, "wb") as f:
f.write(b"x")
assert fs.stat(nested).name == "inner_£.txt"


@pytest.mark.parametrize("name", UNICODE_NAMES)
def test_rename_and_remove(fs, name):
with fs.open(name, "wb") as f:
f.write(b"x")
renamed = "renamed_" + name
fs.rename(name, renamed)
assert renamed in fs.listdir("/")
assert name not in fs.listdir("/")
fs.remove(renamed)
assert renamed not in fs.listdir("/")


def test_ascii_names_still_work(fs):
"""ASCII is a strict subset of UTF-8: existing names must be unaffected."""
with fs.open("plain.txt", "wb") as f:
f.write(b"hello")
with fs.open("plain.txt", "rb") as f:
assert f.read() == b"hello"
assert fs.stat("plain.txt").name == "plain.txt"
Loading