Skip to content

FIX Write non-BMP characters as surrogate pairs in UnicodeReplacementConverter - #3132

Merged
Roman Lutz (romanlutz) merged 3 commits into
microsoft:mainfrom
ShivamSharma43:fix/unicode-replacement-astral
Oct 14, 2026
Merged

Roman Lutz (romanlutz) merged 3 commits into
microsoft:mainfrom
ShivamSharma43:fix/unicode-replacement-astral

Conversation

@ShivamSharma43

Copy link
Copy Markdown
Contributor

Description

Fixes #3130.

UnicodeReplacementConverter.convert_word_async formatted each character as \u{ord(ch):04x}. :04x is a minimum width, so anything above U+FFFF came out with five or six hex digits, e.g. 😀 became ὠ0, which decodes to ὠ0. A \u escape always holds exactly four hex digits.

The word is now encoded as UTF-16 code units, so characters outside the BMP are written as their surrogate pair (😀). This is the same form JsonStringConverter already produces. Output for BMP characters, and the encode_spaces handling, are unchanged.

Tests and Documentation

  • Added test_unicode_replacement_non_bmp_uses_surrogate_pairs. It checks the exact output for hi 😀 and that json.loads decodes it back to the input. It fails on main (ὠ0) and passes with the change.
  • pytest tests/unit/converter/test_unicode_replacement_converter.py tests/unit/converter/test_converter.py tests/unit/converter/test_seeded_converter_determinism.py: all pass.
  • ruff check, ruff format --check and ty check are clean on the changed files.
  • No documentation changes; JupyText not run, since no notebook output depends on non-BMP input.

🤖 Generated with Claude Code

…Converter

`:04x` is a minimum width, so a character above U+FFFF was written with
five or six hex digits (`ὠ0` for U+1F600), which every `\u` decoder
reads as a different character followed by a stray digit. Encode the word
as UTF-16 code units so these characters become a surrogate pair
(`😀`). BMP characters are unchanged.

Fixes microsoft#3130

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 11, 2026 15:26

Copilot AI left a comment

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Comment thread pyrit/converter/unicode_replacement_converter.py Outdated
A Python str can hold an unpaired surrogate (for example from
json.loads), and strict UTF-16 encoding raised "surrogates not allowed"
for it. Encode with errors="surrogatepass" so a lone surrogate is
written as its own code unit, as before, while non-BMP characters are
still written as surrogate pairs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@romanlutz
Roman Lutz (romanlutz) added this pull request to the merge queue Oct 14, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 14, 2026
@romanlutz
Roman Lutz (romanlutz) added this pull request to the merge queue Oct 14, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 14, 2026
@romanlutz
Roman Lutz (romanlutz) added this pull request to the merge queue Oct 14, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 14, 2026
@romanlutz
Roman Lutz (romanlutz) added this pull request to the merge queue Oct 14, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 14, 2026
@romanlutz
Roman Lutz (romanlutz) added this pull request to the merge queue Oct 14, 2026
Merged via the queue into microsoft:main with commit 1db48cb Oct 14, 2026
55 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG UnicodeReplacementConverter writes characters outside the BMP as invalid five-digit \u escapes

3 participants