Skip to content

csv: support non-ASCII Unicode dialect characters - #9367

Merged
youknowone merged 2 commits into
RustPython:mainfrom
deronnax:fix/csv-unicode-dialect-chars
Oct 27, 2026
Merged

youknowone merged 2 commits into
RustPython:mainfrom
deronnax:fix/csv-unicode-dialect-chars

Conversation

@deronnax

@deronnax deronnax commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #8408

delimiter, quotechar and escapechar were stored as u8, so valid one-character values outside the byte range (e.g. €) were rejected. They are now stored as char, and the reader state machine and the writer operate on code points. Empty and multi-character values are still rejected with the same messages.

Notes:

  • The field size limit is now counted in characters instead of bytes, as in CPython.
  • Lone surrogates are still not supported as dialect characters or data (unchanged: they already failed with UnicodeDecodeError in the data path).
  • Lib/test/test_csv.py: test_dialect_apply (which uses a Λ delimiter) now passes, so its expectedFailure is removed.
  • Added test_non_ascii_dialect_chars to the existing extra_tests/snippets/stdlib_csv.py.
  • The first commit makes scripts/check_redundant_patches.py executable: the redundant-test-patches hook uses language: script and fails on any commit touching Lib/test otherwise. It is independent and can be dropped if preferred.

Tested locally: cargo run --release -- -m test test_csv, the snippet, cargo clippy -p rustpython-stdlib --all-targets, cargo test -p rustpython-stdlib.

AI assistance: written with Claude (see Assisted-by trailers).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • CSV readers and writers now support non-ASCII delimiter, quote, and escape characters.
    • Field size limits count characters rather than UTF-8 bytes.
  • Bug Fixes
    • CSV input is decoded as UTF-8, with invalid input reported as a Unicode decode error.
    • CSV writing handles Unicode characters in fields and line terminators correctly.

The redundant-test-patches pre-commit hook uses `language: script`, which
requires the executable bit, and fails on every commit touching Lib/test.

Assisted-by: Claude:claude-sonnet-5-5
Store delimiter, quotechar and escapechar as `char` instead of `u8`, and
run the reader state machine and the writer over code points, so that a
single non-ASCII character such as `€` is accepted like in CPython. The
field size limit is now counted in characters rather than bytes.

Fixes RustPython#8408

Assisted-by: Claude:claude-sonnet-5-5
@coderabbitai

coderabbitai Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between d1c3cb7 and eea7622.

Files ignored due to path filters: 1
  • Lib/test/test_csv.py is excluded by !Lib/**
Files selected for processing: 3
  • crates/stdlib/src/csv.rs
  • extra_tests/snippets/stdlib_csv.py
  • scripts/check_redundant_patches.py

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Walkthrough

CSV dialect attributes, parser input, field storage, and writer output now use Unicode characters. The reader decodes UTF-8 input, and field limits count characters. Tests cover non-ASCII dialect characters and invalid delimiter values.

Changes: Unicode CSV support

Layer / File(s) Summary
Unicode dialect attributes
crates/stdlib/src/csv.rs
Dialect character attributes use char values. Parsing and validation retain checks for empty or multi-character values and line-break characters.
Unicode CSV input and parsing
crates/stdlib/src/csv.rs
The reader decodes input as UTF-8. The parser processes characters, stores fields in a String, and counts field limits by character.
Unicode CSV output and validation
crates/stdlib/src/csv.rs, extra_tests/snippets/stdlib_csv.py
The writer builds and writes a String, with character-based quoting and escaping. Tests cover non-ASCII dialect characters and invalid delimiter values.

Priority: Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Low

Suggested reviewers: widehyo1, hyoinandout

Merge Risk: Minimal · up to eea76

No concrete issue requiring a fix before merge is established by the available evidence.

Pre-merge checks: Passed 3 · Failed 2

Failed Failed checks: 2 warnings

Check name Status Explanation Resolution
Out of Scope Changes check Warning The pull request description identifies making scripts/check_redundant_patches.py executable as an independent change. This change does not implement or test issue #8408's CSV requirements. Remove the independent executable-bit change from this pull request, or move it to a separate pull request.
Docstring Coverage Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 21 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 3 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title "csv: support non-ASCII Unicode dialect characters" matches the main change. The PR changes CSV delimiter, quotechar, and escapechar from u8 to char, and the reader and writer now operate on…
Linked Issues check Passed Issue #8408 requires full Unicode code points for delimiter, quotechar, and escapechar, while preserving empty and multi-character validation. The summary reports char/String storage, charac…
Full details: Title check

Explanation

The title "csv: support non-ASCII Unicode dialect characters" matches the main change. The PR changes CSV delimiter, quotechar, and escapechar from u8 to char, and the reader and writer now operate on code points. The added test covers non-ASCII delimiter, quote, and escape characters. The title is concise, specific, and follows the linked issue #8408 scope. Empty and multi-character values remain rejected, as the title implies.

Full details: Linked Issues check

Explanation

Issue #8408 requires full Unicode code points for delimiter, quotechar, and escapechar, while preserving empty and multi-character validation. The summary reports char/String storage, character-based reader and writer processing, and rejection of invalid lengths. The added test_non_ascii_dialect_chars covers reader and writer round trips with €, invalid delimiter lengths, and delimiter/quote conflicts. The reported behavior satisfies the issue requirements.

Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

📦 Library Dependencies

The following Lib/ modules were modified. Here are their dependencies:

[x] lib: cpython/Lib/traceback.py
[x] test: cpython/Lib/test/test_traceback.py (TODO: 3)

dependencies:

  • traceback

dependent tests: (170 tests)

  • traceback: test_asyncio test_builtin test_code_module test_concurrent_futures test_contextlib test_contextlib_async test_coroutines test_dictcomps test_exceptions test_http_cookiejar test_importlib test_iter test_listcomps test_pyexpat test_setcomps test_socket test_ssl test_subprocess test_sys test_threadedtempfile test_threading test_traceback test_unittest test_with test_zipimport
    • code:
      • pdb: test_pdb
      • sqlite3.main: test_sqlite3
    • concurrent.futures.interpreter: test_concurrent_futures
    • concurrent.futures.process: test_compileall test_concurrent_futures
    • http.cookiejar: test_urllib2
      • urllib.request: test_httpservers test_pathlib test_pydoc test_sax test_site test_urllib test_urllib2_localnet test_urllib2net test_urllibnet
    • logging: test_asyncio test_decimal test_genericalias test_hashlib test_logging test_pkgutil test_support test_unittest
      • multiprocessing.util: test_asyncio test_concurrent_futures
      • venv: test_venv
    • multiprocessing: test_fcntl test_importlib test_memoryview test_multiprocessing_forkserver test_multiprocessing_main_handling test_profiling test_re
    • profiling.sampling.dump: test_profiling
    • py_compile: test_cmd_line_script test_importlib test_modulefinder test_py_compile test_runpy
      • zipfile: test_shutil test_zipapp test_zipfile test_zipfile64 test_zipimport_support
    • pydoc: test_enum
      • xmlrpc.server: test_docxmlrpc test_xmlrpc
    • socketserver: test_imaplib test_socketserver test_wsgiref
    • threading: test_android test_asyncio test_bytes test_bz2 test_code test_concurrent_futures test_context test_ctypes test_email test_enumerate test_external_inspection test_fork1 test_frame test_ftplib test_functools test_gc test_httplib test_importlib test_inspect test_io test_ioctl test_itertools test_linecache test_opcache test_pathlib test_poll test_poplib test_pyrepl test_queue test_robotparser test_sched test_signal test_smtplib test_sqlite3 test_super test_syslog test_termios test_threading_local test_time test_weakref test_winreg test_zstd
      • bdb: test_bdb
      • dummy_threading: test_dummy_threading
      • importlib.util: test_asdl_parser test_ctypes test_doctest test_importlib test_reprlib
      • queue: test_dummy_thread
      • subprocess: test_asyncio test_atexit test_audit test_c_locale_coercion test_cmd_line test_ctypes test_dtrace test_embed test_faulthandler test_file_eintr test_gzip test_json test_launcher test_msvcrt test_ntpath test_os test_osx_env test_peg_generator test_platform test_plistlib test_profiling test_pyrepl test_quopri test_regrtest test_repl test_script_helper test_select test_sys_settrace test_sysconfig test_tempfile test_tkinter test_tstring test_unittest test_utf8_mode test_wait3 test_webbrowser test_xpickle
      • sysconfig: test_importlib test_os test_tools
      • trace: test_trace
    • timeit: test_timeit
    • tkinter: test_tkinter test_ttk test_ttk_textonly

[ ] test: cpython/Lib/test/test_external_inspection.py

dependencies:

dependent tests: (no tests depend on external_inspection)

[ ] lib: cpython/Lib/profiling
[ ] test: cpython/Lib/test/test_profiling

dependencies:

  • profiling (native: main, _child_monitor, _css_utils, _format_utils, _lsprof, _remote_debugging, binary_collector, binary_reader, cli, collector, constants, display, dump, errors, gecko_collector, heatmap_collector, importlib.machinery, importlib.resources, importlib.util, itertools, jsonl_collector, live_collector, marshal, math, module_utils, opcode_utils, profiling.tracing, profiling.tracing._utils, pstats_collector, sample, stack_collector, string_table, sys, time, trend_tracker, widgets)
    • curses (native: _curses, _curses_panel, curses.ascii, has_key, sys)
    • io (native: _io, _thread, errno, msvcrt, sys)
    • opcode (native: _opcode, builtins)
    • platform (native: _wmi, itertools, sys, vms_lib, winreg)
    • site (native: _io, _pyrepl.main, _pyrepl.pager, _pyrepl.readline, _pyrepl.unix_console, _pyrepl.windows_console, atexit, builtins, errno, readline, sitecustomize, sys, usercustomize)
    • sysconfig (native: _sysconfig, _winapi, importlib.machinery, importlib.util, os.path, sys)
    • _colorize, abc, argparse, base64, collections, contextlib, dataclasses, functools, html, json, linecache, locale, optparse, os, pathlib, pstats, re, runpy, selectors, socket, statistics, subprocess, tempfile, threading, traceback, types, typing, uuid, webbrowser

dependent tests: (3 tests)

  • profiling: test_profiling
    • cProfile: test_cprofile test_pstats

[ ] test: cpython/Lib/test/test_set.py (TODO: 2)

dependencies:

dependent tests: (no tests depend on set)

[x] lib: cpython/Lib/csv.py
[x] test: cpython/Lib/test/test_csv.py

dependencies:

  • csv

dependent tests: (4 tests)

  • csv: test_csv test_genericalias
    • importlib.metadata: test_importlib test_zoneinfo

Legend:

  • [+] path exists in CPython
  • [x] up-to-date, [ ] outdated

@deronnax
deronnax marked this pull request as ready for review October 25, 2026 15:17
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-25T15:18:52.401592Z
We couldn’t complete this review. Comment @codex review to try again.
eea7622 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@youknowone

Copy link
Copy Markdown
Member

@deronnax Thank you for contributing! welcome to RustPython project

@widehyo1 could you please review this PR?

@deronnax

Copy link
Copy Markdown
Contributor Author

That would require some sort of speed benchmark/testing, because I think perf should change a bit. But it might go from « unnoticeable » to « significant ». But anyway, the previous behavior was incorrect (delimiter chars never meant to be 1 byte). And also, by doing this change, it fixes few existing unicode mis-handlings.

@widehyo1

Copy link
Copy Markdown
Contributor

Thanks for the fix! The implementation looks good to me.

@youknowone youknowone left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks!

@youknowone
youknowone merged commit a463449 into RustPython:main Oct 27, 2026
21 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.

csv: support non-ASCII Unicode dialect characters

3 participants