Skip to content

Drop lone surrogates in the Cython quoter to match the pure-Python backend#1751

Open
rodrigobnogueira wants to merge 2 commits into
aio-libs:masterfrom
rodrigobnogueira:quoter-drop-lone-surrogates
Open

Drop lone surrogates in the Cython quoter to match the pure-Python backend#1751
rodrigobnogueira wants to merge 2 commits into
aio-libs:masterfrom
rodrigobnogueira:quoter-drop-lone-surrogates

Conversation

@rodrigobnogueira

@rodrigobnogueira rodrigobnogueira commented Jun 9, 2026

Copy link
Copy Markdown
Member

What do these changes do?

The Cython quoting backend kept a lone Unicode surrogate code point
(U+D800..U+DFFF) in its output when the surrogate was the only
character that needed handling, because the surrogate branch of
_write_utf8 returned without marking the writer as changed. The
pure-Python quoter has always dropped surrogates via
str.encode("utf8", errors="ignore"), so the two backends produced
different output for the same input. The C quoter now drops lone
surrogates as well, so both backends agree.

Are there changes in behavior for the user?

Yes, on the compiled extension only: quoted components (for example
URL paths) built from strings containing lone surrogates no longer
retain them; the result is plain ASCII, matching the pure-Python build.
This also restores the invariant that raw_path is always
ASCII-encodable.

Is it a substantial burden for the maintainers to support this?

No. It is a one-line change in yarl/_quoting_c.pyx that brings the C
quoter in line with the existing pure-Python behaviour; there is no
public API change.

Related issue number

No public issue; this corrects an inconsistency between the two quoting
backends.

Checklist

  • I think the code is well written
  • Unit tests for the changes exist
  • Documentation reflects the changes (N/A; internal behaviour, no public API change)
  • If you provide code modification, please add yourself to CONTRIBUTORS.txt (N/A; file not present in repo)
  • Add a new news fragment into the CHANGES/ folder
Test details (optional, for reviewers)

Tests (both backends): python -m pytest tests/test_quoting.py tests/test_url_parsing.py, and the same with YARL_NO_EXTENSIONS=1; all pass. The two new test_quoting.py cases run under both the py_quoter and c_quoter fixtures and fail on the unpatched C extension, confirming they catch the regression. Pre-commit (ruff, codespell, cython-lint, mypy) passes on the changed files; the docs spell check reports only pre-existing words unrelated to this change.

@psf-chronographer psf-chronographer Bot added the bot:chronographer:provided There is a change note present in this PR label Jun 9, 2026
@codspeed-hq

codspeed-hq Bot commented Jun 9, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 97 untouched benchmarks


Comparing rodrigobnogueira:quoter-drop-lone-surrogates (bc12854) with master (a57cbee)

Open in CodSpeed

@codecov

codecov Bot commented Jun 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.86%. Comparing base (a57cbee) to head (bc12854).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #1751   +/-   ##
=======================================
  Coverage   99.86%   99.86%           
=======================================
  Files          21       21           
  Lines        4310     4325   +15     
  Branches      252      252           
=======================================
+ Hits         4304     4319   +15     
  Misses          4        4           
  Partials        2        2           
Flag Coverage Δ
CI-GHA 99.79% <100.00%> (+<0.01%) ⬆️
OS-Linux 99.79% <100.00%> (+<0.01%) ⬆️
OS-Windows 98.54% <100.00%> (+<0.01%) ⬆️
OS-macOS 98.72% <100.00%> (+<0.01%) ⬆️
Py-3.10 99.69% <100.00%> (+<0.01%) ⬆️
Py-3.11 99.69% <100.00%> (+<0.01%) ⬆️
Py-3.12 99.69% <100.00%> (+<0.01%) ⬆️
Py-3.13 99.74% <100.00%> (+<0.01%) ⬆️
Py-3.14 99.74% <100.00%> (+<0.01%) ⬆️
Py-3.14t 99.74% <100.00%> (+<0.01%) ⬆️
Py-pypy-3.11 99.37% <100.00%> (+<0.01%) ⬆️
VM-macos-latest 98.72% <100.00%> (+<0.01%) ⬆️
VM-ubuntu-latest 99.79% <100.00%> (+<0.01%) ⬆️
VM-windows-latest 98.54% <100.00%> (+<0.01%) ⬆️
pytest 99.79% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…ckend

The Cython quoter left a lone surrogate code point in the output when it
was the only character that needed handling, because the surrogate branch
of _write_utf8 returned without marking the writer changed. The
pure-Python quoter has always dropped surrogates via errors="ignore", so
the two backends produced different output for the same input. Mark the
writer changed so the C quoter also drops them, which keeps the backends
consistent and restores the invariant that raw_path is ASCII-encodable.
@rodrigobnogueira rodrigobnogueira force-pushed the quoter-drop-lone-surrogates branch from 640d40f to b22c9c3 Compare June 9, 2026 03:57
@rodrigobnogueira rodrigobnogueira marked this pull request as ready for review June 9, 2026 04:19
@coveralls

coveralls commented Jun 9, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 27182468552

Coverage increased (+0.003%) to 98.743%

Details

  • Coverage increased (+0.003%) from the base build.
  • Patch coverage: 15 of 15 lines across 2 files are fully covered (100%).
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 6225
Covered Lines: 6146
Line Coverage: 98.73%
Relevant Branches: 61
Covered Branches: 61
Branch Coverage: 100.0%
Branches in Coverage %: Yes
Coverage Strength: 0.99 hits per line

💛 - Coveralls

test_quote_drops_surrogate_with_encoded_char passes on the unpatched
C build because "é" forces a rewrite on its own, so it is a parity
guard for the mixed case rather than the isolated regression guard;
relabel it and point at test_quote_drops_lone_surrogate. Reword the
news fragment so it no longer implies the old C quoter dropped lone
surrogates unconditionally (it only retained one when it was the sole
character forcing a rewrite).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:chronographer:provided There is a change note present in this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants