Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions news/6714.bugfix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed `reflex rename` corrupting or failing on UTF-8 source files on non-UTF-8 platform locales (e.g. Windows cp1252) by reading and writing files as UTF-8.
4 changes: 2 additions & 2 deletions reflex/utils/rename.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def rename_imports_and_app_name(file_path: str | Path, old_name: str, new_name:
new_name: The new name to use.
"""
file_path = Path(file_path)
content = file_path.read_text()
content = file_path.read_text(encoding="utf-8")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Encoding Cookie Is Ignored

When the app contains valid Python source declared as cp1252 or latin-1, a non-ASCII byte such as 0xe9 now raises UnicodeDecodeError. The rename traversal passes every .py file here and may already have rewritten earlier files, leaving the app partially renamed; detect the source encoding and preserve it instead of always decoding as UTF-8.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor declared Python source encodings

For .py files that are valid Python but declare a non-UTF-8 source encoding (for example # -*- coding: cp1252 -*-) and contain bytes outside UTF-8, this unconditional UTF-8 read now raises UnicodeDecodeError before renaming imports. process_directory applies this to every Python file, so one such module can abort reflex rename part-way through an app; using Python's source-encoding detection for .py files would preserve the fix for UTF-8 files without rejecting valid encoded modules.

Useful? React with 👍 / 👎.


# Replace `from old_name.` or `from old_name` with `from new_name`
content = re.sub(
Expand Down Expand Up @@ -127,7 +127,7 @@ def rename_imports_and_app_name(file_path: str | Path, old_name: str, new_name:
content,
)

file_path.write_text(content)
file_path.write_text(content, encoding="utf-8")


def process_directory(
Expand Down
47 changes: 47 additions & 0 deletions tests/units/test_prerequisites.py
Original file line number Diff line number Diff line change
Expand Up @@ -1457,6 +1457,53 @@ def test_regex_edge_cases(temp_directory):
assert updated_content == expected_content


def test_rename_imports_and_app_name_preserves_utf8(
temp_directory, monkeypatch: pytest.MonkeyPatch
):
"""UTF-8 source is preserved even when the platform default encoding is not UTF-8.

Simulates a Western Windows locale (cp1252) where ``Path.read_text`` /
``write_text`` without an explicit ``encoding`` would mis-decode UTF-8 source,
silently corrupting or aborting on non-ASCII characters (curly quotes, accents).

Args:
temp_directory: A temporary directory fixture.
monkeypatch: The pytest monkeypatch fixture.
"""
original_read_text = Path.read_text
original_write_text = Path.write_text

def read_text(self, encoding=None, errors=None):
return original_read_text(
self, encoding=encoding if encoding is not None else "cp1252", errors=errors
)

def write_text(self, data, encoding=None, errors=None, newline=None):
return original_write_text(
self,
data,
encoding=encoding if encoding is not None else "cp1252",
errors=errors,
newline=newline,
)

monkeypatch.setattr(Path, "read_text", read_text)
monkeypatch.setattr(Path, "write_text", write_text)

source = (
"import old_name # \u201cquoted\u201d caf\u00e9 na\u00efve r\u00e9sum\u00e9\n"
)
file_path = temp_directory / "example.py"
file_path.write_bytes(source.encode("utf-8"))

rename_imports_and_app_name(file_path, "old_name", "new_name")

expected = (
"import new_name # \u201cquoted\u201d caf\u00e9 na\u00efve r\u00e9sum\u00e9\n"
)
assert file_path.read_bytes() == expected.encode("utf-8")


def test_cli_rename_command(temp_directory):
foo_dir = temp_directory / "foo"
foo_dir.mkdir()
Expand Down
Loading