fix(rename): read and write source files as UTF-8

Merged
AN
3mo ago
+89-2

All Submissions:

  • Have you followed the guidelines stated in CONTRIBUTING.md file?
  • Have you checked to ensure there aren't any other open Pull Requests for the desired changed?

Type of change

  • Bug fix (non-breaking change which fixes an issue)

What and why

reflex rename <new_name> walks the user's app and rewrites imports and the app
name in every .py and .md file. The read/modify/write in
rename_imports_and_app_name (reflex/utils/rename.py) used
Path.read_text() and Path.write_text() with no encoding= argument, so both
default to the platform's locale encoding. On a Western Windows locale that is
cp1252, not UTF-8.

The consequences on non-UTF-8 locales:

  • Silent corruption (Western Windows, cp1252): a UTF-8-authored file that
    contains non-ASCII bytes (curly quotes in docstrings, accented identifiers or
    string literals, emoji) is decoded as cp1252, rewritten, and written back as
    cp1252, mangling the user's own source.
  • Hard failure (CJK codepages): read_text() raises UnicodeDecodeError,
    aborting the rename partway through the tree and leaving some files already
    rewritten and others not, i.e. an inconsistent app.

On a POSIX UTF-8 locale it round-trips, so this is effectively Windows-specific.

The fix passes encoding="utf-8" on both calls. This mirrors the repo's own
reflex/utils/path_ops.py find_replace, which already reads and writes the same
read/replace/write pattern with encoding="utf-8"; rename.py was the outlier.

Changes

  • reflex/utils/rename.py: add encoding="utf-8" to the read_text() and
    write_text() calls in rename_imports_and_app_name (+2 / -2).
  • tests/units/test_prerequisites.py: add regression test
    test_rename_imports_and_app_name_preserves_utf8, colocated with the existing
    rename tests. It simulates a cp1252 default (monkeypatching Path.read_text /
    write_text so a missing encoding falls back to cp1252, while an explicit
    encoding= still passes through), writes a UTF-8 file with realistic non-ASCII
    content, runs the rename, and asserts the bytes are preserved exactly and the
    rename still happened. The test fails before the fix (UnicodeDecodeError) and
    passes after.
  • news/<pr>.bugfix.md: towncrier changelog fragment.

Changes To Core Features:

  • Have you added an explanation of what your changes do and why you'd like us to include them? (above)
  • Have you written new tests for your core changes, as applicable?
  • Have you successfully ran tests with your changes locally?

Locally (all green):

  • uv run pytest tests/units/test_prerequisites.py -k rename -> 6 passed
  • uv run pytest tests/units/test_prerequisites.py tests/units/utils -> 677 passed, 4 skipped
  • uv run ruff check . (changed files) -> All checks passed
  • uv run ruff format --check . (changed files) -> already formatted
  • uv run pyright reflex/utils/rename.py tests/units/test_prerequisites.py -> 0 errors
  • uv run towncrier check --compare-with origin/main -> fragment found

Greptile Summary

This PR makes reflex rename preserve source-file text across platform locales. The main changes are:

  • Detect and preserve declared Python source encodings.
  • Read and write other supported files as UTF-8.
  • Preserve LF and CRLF line endings.
  • Add tests for UTF-8, cp1252, and line-ending behavior.

Confidence Score: 5/5

This looks safe to merge.

  • The declared-encoding failure is addressed with Python's standard encoding detection.
  • Reads and writes use the same detected encoding.
  • Tests cover the affected encoding and line-ending paths.
  • No blocking issues remain in the changed code.

Important Files Changed

FilenameOverview
reflex/utils/rename.pyDetects declared Python encodings and preserves the selected encoding and original line endings during rename.
tests/units/test_prerequisites.pyAdds tests for UTF-8 under a non-UTF-8 locale, declared cp1252 source, and LF/CRLF preservation.
news/6761.bugfix.mdDocuments the source-encoding and line-ending preservation fix.

Reviews (5): Last reviewed commit: "fix(rename): preserve source line ending..." | Re-trigger Greptile

Context used:

  • Context used - CLAUDE.md (source)
  • Context used - AGENTS.md (source)