René's URL Explorer Experiment


Title: [RFC] csv: discuss the reader parser architecture for CPython compatibility · Issue #8310 · RustPython/RustPython · GitHub

Open Graph Title: [RFC] csv: discuss the reader parser architecture for CPython compatibility · Issue #8310 · RustPython/RustPython

X Title: [RFC] csv: discuss the reader parser architecture for CPython compatibility · Issue #8310 · RustPython/RustPython

Description: Summary Recent CSV compatibility work suggests that RustPython is reaching the architectural boundary of the current csv-core adapter. This is not intended to argue that the recent incremental fixes were wrong. In particular: #8260 fixed...

Open Graph Description: Summary Recent CSV compatibility work suggests that RustPython is reaching the architectural boundary of the current csv-core adapter. This is not intended to argue that the recent incremental fixe...

X Description: Summary Recent CSV compatibility work suggests that RustPython is reaching the architectural boundary of the current csv-core adapter. This is not intended to argue that the recent incremental fixe...

Opengraph URL: https://github.com/RustPython/RustPython/issues/8310

X: @github

direct link

Domain: github.com


Hey, it has json ld scripts:
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"[RFC] csv: discuss the reader parser architecture for CPython compatibility","articleBody":"## Summary\n\nRecent CSV compatibility work suggests that RustPython is reaching the architectural boundary of the current `csv-core` adapter.\n\nThis is not intended to argue that the recent incremental fixes were wrong. In particular:\n\n- #8260 fixed a real compatibility gap for `QUOTE_NONE` with `escapechar`.\n- #8304 fixes incorrect and unsafe `skipinitialspace` preprocessing and needs quote provenance for `QUOTE_NOTNULL` / `QUOTE_STRINGS`.\n\nHowever, the implementation has started to grow a second CSV parser alongside `csv-core`. Features that are still missing—multiline records across iterator items, `strict`, complete escape handling, Unicode dialect characters, empty-line behavior, and the remaining quote modes—will require more persistent parser state and more CPython-specific semantics.\n\nAt this point, it seems useful to compare the contracts of CPython, `csv-core`, and RustPython before the custom parser grows further.\n\nThis issue:\n\n1. summarizes the current implementation differences;\n2. explains why an unchanged `csv-core` wrapper cannot provide full CPython compatibility without duplicating much of the grammar;\n3. compares the main long-term architecture options without selecting one.\n\nMy initial preference is to preserve the performance model of `csv-core`, potentially through a RustPython-specific adaptation or fork. However, Unicode and strict error semantics may make that a much larger redesign than it first appears, so this issue intentionally leaves the final choice open.\n\n## Related work\n\n| Item | Area | Relevance |\n|---|---|---|\n| #8253 | writer dialect resolution | `quoting` and `lineterminator` from a dialect were ignored. This shows that dialect resolution should be centralized before configuring reader/writer backends. |\n| #8260 | reader escape handling | Passed dialect `escapechar` into `csv-core` and added a custom `QUOTE_NONE` parser because `csv-core` does not apply escape handling to unquoted fields. |\n| #8284 | dialect validation | RustPython accepts invalid non-string `escapechar` values that CPython rejects. |\n| #8302 | writer quoting semantics | Embedded CR/LF must be quoted under `QUOTE_MINIMAL` even when a custom `lineterminator` does not contain CR/LF. |\n| #8304 | reader space and quote handling | Replaces unsafe delimiter splitting with a quote-aware scanner and expands the custom path to `QUOTE_NOTNULL` and `QUOTE_STRINGS`. |\n\nThe issues are individually valid and can continue to receive narrow fixes. The architectural concern is that each fix currently has to decide independently whether its semantics belong in `FormatOptions`, preprocessing, `csv-core`, a custom parser, or post-processing.\n\n## Scope\n\nThe primary subject is the **reader parser architecture**.\n\nDialect representation and validation are included only where they constrain the parser architecture, especially Unicode dialect characters and the conversion to `csv-core` types.\n\nWriter implementation is not proposed to share the reader state machine. The writer issues above are included as evidence that the current dialect abstraction also affects writer compatibility.\n\nThis issue is not intended to block small compatibility fixes. Its purpose is to make the long-term cost of each parser direction visible before larger changes accumulate around the current hybrid structure.\n\n## Current implementation layers\n\nAt a high level, RustPython currently has these layers:\n\n```text\nPython arguments / dialect object\n        |\n        v\nFormatOptions + PyDialect\n        |\n        +-----------------------+\n        |                       |\n        v                       v\ncsv_core::Reader         custom Rust parser paths\n(common quote modes)     (currently selected cases)\n        |                       |\n        +-----------+-----------+\n                    |\n                    v\n          Python field conversion\n```\n\nThis layering has allowed incremental progress, but parser semantics are now distributed across:\n\n- argument parsing and dialect conversion;\n- `csv-core::ReaderBuilder`;\n- raw-input preprocessing such as `skipinitialspace`;\n- custom quote-mode paths;\n- post-processing for `QUOTE_NONNUMERIC`, `QUOTE_STRINGS`, and `QUOTE_NOTNULL`;\n- outer iterator lifecycle and `line_num`.\n\nThe same input may be quote-scanned by RustPython and then parsed again by `csv-core`.\n\n## 1. CPython 3.14 contract\n\nReference:\n\n- [`Modules/_csv.c` at v3.14.6](https://github.com/python/cpython/blob/v3.14.6/Modules/_csv.c)\n- [`Lib/test/test_csv.py` at v3.14.6](https://github.com/python/cpython/blob/v3.14.6/Lib/test/test_csv.py)\n\n### Dialect representation\n\nCPython stores the following as Python Unicode or Unicode code points:\n\n- `delimiter`: one Unicode character;\n- `quotechar`: one Unicode character or `None`;\n- `escapechar`: one Unicode character or `None`;\n- `lineterminator`: an arbitrary string.\n\nIt validates the dialect once when constructing the internal dialect object. Validation includes:\n\n- exact accepted types;\n- exactly one character where required;\n- valid quoting enum values;\n- requiring `quotechar` when quoting is enabled;\n- conflicts among delimiter, quotechar, escapechar, and lineterminator;\n- special restrictions involving CR, LF, spaces, and `skipinitialspace`.\n\nThe reader does **not** use `lineterminator` to recognize input records. CPython's reader recognizes CR and LF directly. `lineterminator` is primarily a writer setting.\n\n### Reader state machine\n\nCPython's reader uses a persistent nine-state parser:\n\n```text\nSTART_RECORD\nSTART_FIELD\nESCAPED_CHAR\nIN_FIELD\nIN_QUOTED_FIELD\nESCAPE_IN_QUOTED_FIELD\nQUOTE_IN_QUOTED_FIELD\nEAT_CRNL\nAFTER_ESCAPED_CRNL\n```\n\nThe parser consumes Python Unicode code points, not encoded bytes.\n\nImportant properties:\n\n- One `reader.__next__()` may pull multiple strings from the input iterator until a complete CSV record is produced.\n- After each iterator item, CPython injects a virtual `EOL` sentinel.\n- Virtual `EOL` means “this iterator item ended”; it is not true iterator EOF and is not identical to CR or LF.\n- True input EOF occurs only when the input iterator raises `StopIteration`.\n- Parser state persists across iterator items.\n- A quoted record may therefore span multiple items even when an item does not end with a physical newline.\n- An escape at an iterator-item boundary has defined behavior.\n- `strict=True` can reject invalid characters after a closing quote and unexpected true EOF.\n- The parser retains whether the field was quoted. That metadata is required by `QUOTE_NOTNULL`, `QUOTE_STRINGS`, and numeric conversion.\n- Empty physical lines and empty iterator items have defined behavior.\n- `line_num` counts successfully obtained input iterator items, not returned CSV records.\n\n### Output conversion\n\nParsing and Python-level conversion are related but separable:\n\n- quoted/unquoted provenance determines `None` conversion;\n- unquoted non-empty fields may be converted with `float()` for `QUOTE_NONNUMERIC` and `QUOTE_STRINGS`;\n- field-size checks occur while building a field;\n- the parser produces one Python record only after reaching `START_RECORD` again.\n\n## 2. `csv-core` contract\n\nReferences:\n\n- [`csv-core` reader documentation](https://docs.rs/csv-core/latest/csv_core/struct.Reader.html)\n- [`csv-core/src/reader.rs`](https://github.com/BurntSushi/rust-csv/blob/master/csv-core/src/reader.rs)\n\nRustPython declares `csv-core = \"0.1.11\"` as a caret dependency; the current lockfile resolves `0.1.13`.\n\n### Performance model\n\n`csv-core` is intentionally optimized around byte input:\n\n- parser syntax characters are `u8`;\n- input is expected to be at least ASCII-compatible;\n- the implementation contains an explicit NFA and generates a compact DFA transition table from it;\n- parser configuration is compiled into the transition table;\n- `read_record` writes unescaped field data into caller-provided output buffers and writes field end offsets into another buffer;\n- the core parser does not allocate.\n\nThis is a strong implementation for its intended contract.\n\n### Semantic differences from CPython\n\n`csv-core` deliberately prefers a parse over rejecting malformed input.\n\nRelevant differences include:\n\n- special dialect characters are bytes, not arbitrary Python Unicode characters;\n- an empty byte slice is the parser's true EOF signal;\n- there is no Python iterator-item boundary event;\n- parsing never returns a malformed-CSV error;\n- open quoted fields are finalized at EOF instead of producing CPython's strict error;\n- escape handling is implemented inside quoted fields, not in unquoted fields;\n- `skipinitialspace` is not part of the reader state machine;\n- quoted/unquoted field provenance is not returned;\n- empty input-line behavior differs;\n- internal NFA/DFA state is private, so an adapter cannot inspect whether `InputEmpty` occurred in an unquoted field, quoted field, escape state, or closing-quote state;\n- the reader's `Terminator` abstraction is byte-oriented and differs from CPython's input model.\n\nThese are not necessarily bugs in `csv-core`; they reflect a different public contract and design philosophy.\n\n## 3. RustPython's current contract\n\nReference:\n\n- [`crates/stdlib/src/csv.rs`](https://github.com/RustPython/RustPython/blob/main/crates/stdlib/src/csv.rs)\n- #8260\n- #8304\n\n### Dialect model\n\nRustPython currently stores:\n\n```rust\nstruct PyDialect {\n    delimiter: u8,\n    quotechar: Option\u003cu8\u003e,\n    escapechar: Option\u003cu8\u003e,\n    doublequote: bool,\n    skipinitialspace: bool,\n    lineterminator: csv_core::Terminator,\n    quoting: QuoteStyle,\n    strict: bool,\n}\n```\n\nConsequences:\n\n- “one Python character” is currently implemented as “exactly one encoded byte” in several paths;\n- non-ASCII delimiter, quotechar, or escapechar cannot be represented correctly;\n- a full Python `lineterminator` string cannot be represented;\n- reader and writer configuration are constrained by `csv-core` types before the Python dialect contract has been fully resolved;\n- validation and dialect override behavior are spread across `TryFromObject`, `FromArgs`, `result`, `to_reader`, and `to_writer`;\n- invalid-type behavior can differ depending on whether a setting comes from a dialect object or a keyword argument, as seen in #8284.\n\n### Reader lifecycle\n\nThe current outer `Reader.next()` fetches one Python iterator item.\n\nThe `csv-core` path may call `read_record` repeatedly to resize buffers or consume the remainder of that same item, but it does not fetch another Python item for the same record. When the current byte slice is exhausted, passing an empty slice to `csv-core` also acts as `csv-core` EOF and can finalize an open quote.\n\nThis differs from CPython, where:\n\n- iterator-item end is a virtual `EOL` event;\n- true EOF is a separate event;\n- one record may consume multiple iterator items.\n\n### Custom parser growth\n\n#8260 added a small parser for `QUOTE_NONE + escapechar`, because an escaped delimiter in an unquoted field cannot be expressed through the current `csv-core` reader API.\n\n#8304 generalizes this into quote-aware scanning and uses custom parsing for:\n\n- `QUOTE_NONE` with an escape character;\n- `QUOTE_NOTNULL`;\n- `QUOTE_STRINGS`.\n\nIt also uses the scanner as a preprocessing pass for `skipinitialspace` before the common `csv-core` path.\n\nThis is the point where the custom implementation becomes more than a local exception:\n\n- it recognizes field starts;\n- it tracks quoted/unquoted state;\n- it handles quote closing;\n- it handles doubled quotes;\n- it handles escapes;\n- it recognizes delimiters and record terminators;\n- it records quoted-field provenance.\n\nThose are the core responsibilities of a CSV parser.\n\nThe custom path still has local, per-item state, so extending it to multiline records and strict EOF behavior will require moving parser state into the reader object and changing the outer iterator lifecycle.\n\n### Performance uncertainty\n\nThe common path retains `csv-core`'s DFA, but `skipinitialspace` in #8304 first quote-scans and copies the complete input before `csv-core` parses it again.\n\nThe custom path allocates per-field buffers instead of using `csv-core`'s contiguous output buffer and field-end offsets.\n\nThis does not prove that the end-to-end implementation is slow. Python object creation may dominate total cost. It does mean that the performance benefit of keeping `csv-core` should be measured at the RustPython API level rather than assumed from `csv-core` microbenchmarks.\n\n## Compatibility matrix\n\nThe “RustPython proposed” column refers to the architecture direction exposed by #8304, not necessarily merged `main`.\n\n| Capability | CPython 3.14 | `csv-core` | RustPython current / proposed |\n|---|---|---|---|\n| Input alphabet | Python Unicode code points | bytes | Python strings converted to bytes |\n| Delimiter / quote / escape | one Unicode character | one `u8` | one `u8` |\n| Writer lineterminator | arbitrary string | CRLF or one byte terminator | `csv_core::Terminator` |\n| Reader record termination | CR/LF; ignores dialect lineterminator | configured byte terminator | configured through `csv-core` |\n| Iterator item boundary | explicit virtual `EOL` | none | currently treated like parser EOF |\n| True iterator EOF | separate from item end | empty input slice | not cleanly separated for the core parser |\n| Record spans iterator items | yes | yes, if caller supplies more slices without EOF | no in current outer lifecycle |\n| Escape in quoted field | yes | yes | common path and custom scanner |\n| Escape in unquoted field | yes | no | custom path for selected modes |\n| `skipinitialspace` | parser state transition | unsupported | preprocessing scanner |\n| Strict malformed-CSV errors | yes | intentionally no | dialect flag exists; parser semantics incomplete |\n| Quoted/unquoted provenance | yes | not returned | custom path for selected modes |\n| `QUOTE_STRINGS` conversion | yes | outside its scope | partial |\n| `QUOTE_NOTNULL` conversion | yes | outside its scope | custom path |\n| Empty line semantics | returns an empty record according to iterator/input state | empty records are generally skipped | special-cased outside the core parser |\n| Allocation strategy | growable field buffer + Python objects | caller-provided contiguous buffers | core buffers on common path; per-field vectors on custom path |\n| Malformed input philosophy | compatible permissive mode plus strict mode | always prefer a parse | split between core and RustPython checks |\n\n## Why an unchanged thin wrapper is insufficient\n\nSome missing behavior can be implemented around `csv-core`:\n\n- dialect validation;\n- Python object conversion;\n- line counting;\n- some empty-line handling;\n- quote provenance, if a separate scanner tracks it.\n\nThe difficult cases are those where `csv-core` consumes syntax before the wrapper can reinterpret it.\n\n### Unquoted escape\n\nIn CPython, escape is recognized in both `START_FIELD` and `IN_FIELD`.\n\nIf an escaped byte is a delimiter, CR, LF, quote, or another special byte, unchanged `csv-core` will still interpret that byte according to its grammar. A wrapper would need to:\n\n- replace escaped syntax with collision-free placeholders and restore it later;\n- bypass `csv-core` output for parts of a field;\n- or dispatch those dialects to another parser.\n\nAll of these add a second grammar and difficult state synchronization.\n\n### Strict parsing\n\n`csv-core` does not expose enough internal state to distinguish:\n\n- a normal unquoted field;\n- an open quoted field;\n- an escape-pending state;\n- the state immediately after a closing quote.\n\nA wrapper can run a shadow CPython-like FSM over the raw input, but then the wrapper is implementing most of the parser grammar solely to validate another parser.\n\n### Iterator boundaries\n\n`ReadRecordResult::InputEmpty` does not tell the caller whether more Python iterator items should be fetched as part of the same record. An empty input slice means true EOF to `csv-core`, while CPython requires a distinct item-boundary event.\n\nA shadow state machine can decide whether to fetch another item, but again this duplicates the parser state.\n\nTherefore, a complete wrapper is technically possible, but it is no longer a thin adapter and may be harder to reason about than a single compatibility parser.\n\n## Architecture options\n\n### Option A: Continue the current hybrid approach\n\nKeep `csv-core` for common cases and add custom paths or preprocessing for each unsupported feature.\n\n**Pros**\n\n- smallest incremental changes;\n- existing working paths remain stable;\n- preserves `csv-core` throughput for inputs that avoid preprocessing and custom dispatch;\n- individual contributors can fix isolated compatibility failures.\n\n**Cons**\n\n- quote modes and dialect combinations can select different parsers;\n- parser semantics are distributed across preprocessing, core parsing, custom parsing, and conversion;\n- multiline and strict handling require persistent state in every applicable path;\n- each new feature increases the cross-product of dialect options and parser paths;\n- duplicate grammars can diverge on malformed input and boundaries;\n- performance can include double scanning and extra allocation;\n- Unicode still requires a separate design.\n\n**Assessment**\n\nReasonable for narrow fixes, but not a good long-term architecture if CPython compatibility remains the goal.\n\n### Option B: Keep unchanged `csv-core` plus a comprehensive wrapper/shadow FSM\n\nUse `csv-core` for output generation while a RustPython-owned FSM tracks CPython state, validates strict mode, handles iterator boundaries, and records provenance.\n\n**Pros**\n\n- continues using the current dependency without a fork;\n- retains the optimized DFA and contiguous output buffers;\n- may preserve good performance for ASCII data;\n- can be introduced incrementally around the current backend.\n\n**Cons**\n\n- the shadow FSM duplicates most of the CSV grammar;\n- two parsers must consume exactly the same logical input and remain synchronized;\n- unquoted escape cannot be represented cleanly through the existing public API;\n- placeholder or output-bypass schemes complicate field offsets and correctness;\n- private `csv-core` state prevents direct synchronization checks;\n- debugging requires understanding both parsers;\n- Unicode remains outside the core backend.\n\n**Assessment**\n\nPossible, but the complexity is hidden rather than removed. A prototype should demonstrate that it is materially simpler or faster than an independent parser before choosing this option.\n\n### Option C: Implement one RustPython-owned CPython-compatible Unicode parser\n\nModel the CPython state machine directly in Rust, operating on a Python-character representation rather than `u8`.\n\n**Pros**\n\n- one source of truth for quote, escape, delimiter, strict, multiline, empty-line, and boundary semantics;\n- parser states map directly to CPython tests and source;\n- arbitrary Python delimiter, quotechar, and escapechar become representable;\n- virtual item end and true EOF can be first-class events;\n- quote provenance and conversion metadata are natural outputs;\n- no backend dispatch by quote style;\n- easier differential testing against CPython.\n\n**Cons**\n\n- RustPython owns parser correctness and maintenance;\n- initial implementation and review scope are larger;\n- careful buffer design is needed to avoid unnecessary allocation;\n- the existing `csv-core` reader performance path may be lost;\n- performance needs to be recovered through profiling and optimization;\n- CPython's behavior includes subtle permissive cases, not just the obvious nine states.\n\n**Assessment**\n\nThe clearest route to compatibility and maintainability, but potentially the largest short-term implementation and performance risk.\n\n### Option D: Adapt, fork, or vendor `csv-core` for RustPython\n\nAdd the missing states and metadata to the `csv-core` NFA/DFA design:\n\n- `skipinitialspace`;\n- unquoted escape states;\n- iterator-item boundary versus true EOF;\n- strict error transitions;\n- quoted/unquoted provenance;\n- CPython empty-line behavior;\n- Unicode-capable dialect syntax.\n\nThe practical form could be a RustPython-specific fork, vendored code, or a substantial adapter-owned variant. An upstream contribution would only be realistic where the required changes are sufficiently general; it should not be assumed as the primary path.\n\n**Pros**\n\n- preserves the NFA-to-DFA approach and configuration-specific transition tables;\n- can retain contiguous caller-provided output buffers;\n- unsupported semantics become real parser transitions instead of preprocessing;\n- one parser can potentially serve all quote modes;\n- offers the strongest chance of combining compatibility with the current performance model.\n\n**Cons**\n\n- `csv-core` intentionally uses bytes and intentionally never reports malformed-CSV errors; CPython compatibility conflicts with both design choices;\n- Unicode support is not a small type substitution because APIs, input indexing, output buffers, and equivalence-class construction are byte-oriented;\n- a RustPython-specific fork introduces dependency maintenance and rebase burden;\n- the result could become a mostly independent parser while retaining complex DFA machinery;\n- implementation and review cost are high;\n- the required work may exceed the value of preserving the existing backend.\n\n**Important note**\n\nUnicode does not make a DFA impossible. A parser can classify each Python character into a small set such as delimiter, quote, escape, space, CR, LF, item-end, and other. The difficulty is that the current `csv-core` API and buffer model are byte-based, so reaching that design requires a substantial refactor.\n\n**Assessment**\n\nPotentially the best performance/compatibility combination, but also the highest architectural and maintenance ambition. Its feasibility is uncertain enough that it should remain an option rather than the conclusion of this issue.\n\n### Option E: Use two explicit backends\n\nUse a fast ASCII backend when a dialect is fully representable and a compatibility backend otherwise.\n\nFor example:\n\n```text\nResolvedDialect\n      |\n      +-- Fast backend: csv-core or an extended csv-core\n      |\n      +-- Compatibility backend: RustPython Unicode FSM\n```\n\n**Pros**\n\n- preserves optimized performance for common ASCII CSV;\n- provides a direct path to full Unicode and CPython semantics;\n- makes fallback conditions explicit instead of growing quote-mode-specific exceptions;\n- backend performance can evolve independently.\n\n**Cons**\n\n- two parser implementations must remain semantically equivalent in their overlapping domain;\n- differential testing becomes mandatory;\n- dispatch criteria can become another source of bugs;\n- malformed-input behavior may drift;\n- maintenance and code size are higher than a unified parser;\n- fast-path wins may be small after Python object conversion.\n\n**Assessment**\n\nA practical compromise if benchmarks show a meaningful end-to-end benefit. It should use a shared lifecycle and output contract so that only the transition engine differs.\n\n## Note on compatibility fixes during this discussion\n\nNarrow fixes with focused tests can still be useful. The concern here is not the existence of custom code by itself, but whether larger additions continue to deepen several partially overlapping parser paths without an agreed long-term direction.\n\n## Discussion goal\n\nThe main questions for this issue are:\n\n1. Is the current hybrid structure a sustainable long-term architecture, or should it be treated as transitional?\n2. Among Options B–E, which direction best balances CPython compatibility, implementation complexity, maintenance cost, and the performance value of `csv-core`?\n\nThis issue does not need to choose an implementation plan immediately. Reaching a shared understanding of the architectural trade-offs would already provide guidance for subsequent CSV work.\n\n## Appendix: backend-independent constraints\n\nThe following points are relevant to any replacement or dual-backend design, but they are secondary to the architecture comparison above.\n\n- The internal dialect model must be able to represent CPython's Unicode delimiter, quotechar, and escapechar before converting to backend-specific types.\n- A Python iterator-item boundary must remain distinct from true input EOF.\n- Parser state must be able to persist across iterator items for multiline records and boundary-sensitive escape handling.\n- The parser must retain quoted/unquoted provenance for `QUOTE_NOTNULL`, `QUOTE_STRINGS`, and numeric conversion.\n- Syntax parsing and Python object conversion should remain distinguishable responsibilities.\n- `skipinitialspace` is safer as a parser transition than as a preprocessing pass that rewrites the input before another parser sees it.\n\nThese constraints do not imply one specific backend. They describe compatibility requirements that Options C, D, and E would need to address, and that make Option B difficult to keep thin.\n\n## References\n\n### RustPython\n\n- [Current `csv.rs`](https://github.com/RustPython/RustPython/blob/main/crates/stdlib/src/csv.rs)\n- [#8253: `csv.writer` ignores a dialect's quoting and lineterminator](https://github.com/RustPython/RustPython/issues/8253)\n- [#8260: `csv: fix csv escape fieldsep`](https://github.com/RustPython/RustPython/pull/8260)\n- [#8284: `csv: reader allows escapechar=1`](https://github.com/RustPython/RustPython/issues/8284)\n- [#8302: embedded CR/LF not quoted with custom lineterminator](https://github.com/RustPython/RustPython/issues/8302)\n- [#8304: `csv: handle empty fields with skipinitialspace`](https://github.com/RustPython/RustPython/pull/8304)\n\n### CPython\n\n- [`Modules/_csv.c` at v3.14.6](https://github.com/python/cpython/blob/v3.14.6/Modules/_csv.c)\n- [`Lib/test/test_csv.py` at v3.14.6](https://github.com/python/cpython/blob/v3.14.6/Lib/test/test_csv.py)\n- [`csv` documentation](https://docs.python.org/3.14/library/csv.html)\n\n### rust-csv\n\n- [`csv-core` documentation](https://docs.rs/csv-core/latest/csv_core/)\n- [`csv-core/src/reader.rs`](https://github.com/BurntSushi/rust-csv/blob/master/csv-core/src/reader.rs)\n- [`csv-core/src/writer.rs`](https://github.com/BurntSushi/rust-csv/blob/master/csv-core/src/writer.rs)\n","author":{"url":"https://github.com/widehyo1","@type":"Person","name":"widehyo1"},"datePublished":"2026-07-18T01:06:22.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":1},"url":"https://github.com/8310/RustPython/issues/8310"}

route-pattern/_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format)
route-controllervoltron_issues_fragments
route-actionissue_layout
fetch-noncev2:0ae4c5b4-9379-fcad-6608-14af18381afa
current-catalog-service-hash81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114
request-idEC78:3D312F:999E27:C84FE6:6A600739
html-safe-noncecba10c98027d2e145af4f574156955191abdaae91615daed7f4a507cceb9a381
visitor-payloadeyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJFQzc4OjNEMzEyRjo5OTlFMjc6Qzg0RkU2OjZBNjAwNzM5IiwidmlzaXRvcl9pZCI6IjgxMjk5ODc2NTg0NTI1Njc4NjUiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ==
visitor-hmacb11b77b5d94f1d120b9318510aa9e21c73bf5a3b57f356371ade6cb28cf6d3b3
hovercard-subject-tagissue:4915983277
github-keyboard-shortcutsrepository,issues,copilot
google-site-verificationApib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I
octolytics-urlhttps://collector.github.com/github/collect
analytics-location///voltron/issues_fragments/issue_layout
fb:app_id1401488693436528
apple-itunes-appapp-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/RustPython/RustPython/8310/issue_layout
twitter:imagehttps://opengraph.githubassets.com/3f6b2a311d864d4900f352051cb13dffa53670f2454536fcb11dc0b5a29dfa2e/RustPython/RustPython/issues/8310
twitter:cardsummary_large_image
og:imagehttps://opengraph.githubassets.com/3f6b2a311d864d4900f352051cb13dffa53670f2454536fcb11dc0b5a29dfa2e/RustPython/RustPython/issues/8310
og:image:altSummary Recent CSV compatibility work suggests that RustPython is reaching the architectural boundary of the current csv-core adapter. This is not intended to argue that the recent incremental fixe...
og:image:width1200
og:image:height600
og:site_nameGitHub
og:typeobject
og:author:usernamewidehyo1
hostnamegithub.com
expected-hostnamegithub.com
None5789899e92b20db289de946d86eb20bf5c9626276695d68f58a8c47cdda699b7
turbo-cache-controlno-preview
go-importgithub.com/RustPython/RustPython git https://github.com/RustPython/RustPython.git
octolytics-dimension-user_id39710557
octolytics-dimension-user_loginRustPython
octolytics-dimension-repository_id135201145
octolytics-dimension-repository_nwoRustPython/RustPython
octolytics-dimension-repository_publictrue
octolytics-dimension-repository_is_forkfalse
octolytics-dimension-repository_network_root_id135201145
octolytics-dimension-repository_network_root_nwoRustPython/RustPython
turbo-body-classeslogged-out env-production page-responsive
disable-turbofalse
browser-stats-urlhttps://api.github.com/_private/browser/stats
browser-errors-urlhttps://api.github.com/_private/browser/errors
release6ddc048ddf80664b9b33547b619db10313c482f1
ui-targetfull
theme-color#1e2327
color-schemelight dark

Links:

Skip to contenthttps://github.com/RustPython/RustPython/issues/8310#start-of-content
https://github.com/
Sign in https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2FRustPython%2FRustPython%2Fissues%2F8310
GitHub CopilotWrite better code with AIhttps://github.com/features/copilot
GitHub Copilot appDirect agents from issue to mergehttps://github.com/features/ai/github-app
MCP RegistryNewIntegrate external toolshttps://github.com/mcp
ActionsAutomate any workflowhttps://github.com/features/actions
CodespacesInstant dev environmentshttps://github.com/features/codespaces
IssuesPlan and track workhttps://github.com/features/issues
Code ReviewManage code changeshttps://github.com/features/code-review
Code QualityEnforce quality at mergehttps://github.com/features/code-quality
GitHub Advanced SecurityFind and fix vulnerabilitieshttps://github.com/security/advanced-security
Code securitySecure your code as you buildhttps://github.com/security/advanced-security/code-security
Secret protectionStop leaks before they starthttps://github.com/security/advanced-security/secret-protection
Why GitHubhttps://github.com/why-github
Documentationhttps://docs.github.com
Bloghttps://github.blog
Changeloghttps://github.blog/changelog
Marketplacehttps://github.com/marketplace
View all featureshttps://github.com/features
Enterpriseshttps://github.com/enterprise
Small and medium teamshttps://github.com/team
Startupshttps://github.com/enterprise/startups
Nonprofitshttps://github.com/solutions/industry/nonprofits
App Modernizationhttps://github.com/solutions/use-case/app-modernization
DevSecOpshttps://github.com/solutions/use-case/devsecops
DevOpshttps://github.com/solutions/use-case/devops
CI/CDhttps://github.com/solutions/use-case/ci-cd
View all use caseshttps://github.com/solutions/use-case
Healthcarehttps://github.com/solutions/industry/healthcare
Financial serviceshttps://github.com/solutions/industry/financial-services
Manufacturinghttps://github.com/solutions/industry/manufacturing
Governmenthttps://github.com/solutions/industry/government
View all industrieshttps://github.com/solutions/industry
View all solutionshttps://github.com/solutions
AIhttps://github.com/resources/articles?topic=ai
Software Developmenthttps://github.com/resources/articles?topic=software-development
DevOpshttps://github.com/resources/articles?topic=devops
Securityhttps://github.com/resources/articles?topic=security
View all topicshttps://github.com/resources/articles
Customer storieshttps://github.com/customer-stories
Events & webinarshttps://github.com/resources/events
Ebooks & reportshttps://github.com/resources/whitepapers
Business insightshttps://github.com/solutions/executive-insights
GitHub Skillshttps://skills.github.com
Documentationhttps://docs.github.com
Customer supporthttps://support.github.com
Community forumhttps://github.com/orgs/community/discussions
Trust centerhttps://github.com/trust-center
Partnershttps://github.com/partners
View all resourceshttps://github.com/resources
GitHub SponsorsFund open source developershttps://github.com/open-source/sponsors
Security Labhttps://securitylab.github.com
Maintainer Communityhttps://maintainers.github.com
Acceleratorhttps://github.com/open-source/accelerator
GitHub Starshttps://stars.github.com
Archive Programhttps://archiveprogram.github.com
Topicshttps://github.com/topics
Trendinghttps://github.com/trending
Collectionshttps://github.com/collections
Enterprise platformAI-powered developer platformhttps://github.com/enterprise
GitHub Advanced SecurityEnterprise-grade security featureshttps://github.com/security/advanced-security
Copilot for BusinessEnterprise-grade AI featureshttps://github.com/features/copilot/copilot-business
Premium SupportEnterprise-grade 24/7 supporthttps://github.com/enterprise/premium-support
Pricinghttps://github.com/pricing
Search syntax tipshttps://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax
documentationhttps://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax
Sign in https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2FRustPython%2FRustPython%2Fissues%2F8310
Sign up https://github.com/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fvoltron%2Fissues_fragments%2Fissue_layout&source=header-repo&source_repo=RustPython%2FRustPython
Reloadhttps://github.com/RustPython/RustPython/issues/8310
Reloadhttps://github.com/RustPython/RustPython/issues/8310
Reloadhttps://github.com/RustPython/RustPython/issues/8310
Please reload this pagehttps://github.com/RustPython/RustPython/issues/8310
RustPython https://github.com/RustPython
RustPythonhttps://github.com/RustPython/RustPython
Notifications https://github.com/login?return_to=%2FRustPython%2FRustPython
Fork 1.5k https://github.com/login?return_to=%2FRustPython%2FRustPython
Star 22.2k https://github.com/login?return_to=%2FRustPython%2FRustPython
Code https://github.com/RustPython/RustPython
Issues 294 https://github.com/RustPython/RustPython/issues
Pull requests 105 https://github.com/RustPython/RustPython/pulls
Discussions https://github.com/RustPython/RustPython/discussions
Actions https://github.com/RustPython/RustPython/actions
Projects https://github.com/RustPython/RustPython/projects
Models https://github.com/RustPython/RustPython/models
Wiki https://github.com/RustPython/RustPython/wiki
Security and quality 0 https://github.com/RustPython/RustPython/security
Insights https://github.com/RustPython/RustPython/pulse
Code https://github.com/RustPython/RustPython
Issues https://github.com/RustPython/RustPython/issues
Pull requests https://github.com/RustPython/RustPython/pulls
Discussions https://github.com/RustPython/RustPython/discussions
Actions https://github.com/RustPython/RustPython/actions
Projects https://github.com/RustPython/RustPython/projects
Models https://github.com/RustPython/RustPython/models
Wiki https://github.com/RustPython/RustPython/wiki
Security and quality https://github.com/RustPython/RustPython/security
Insights https://github.com/RustPython/RustPython/pulse
[RFC] csv: discuss the reader parser architecture for CPython compatibilityhttps://github.com/RustPython/RustPython/issues/8310#top
RFCRequest for commentshttps://github.com/RustPython/RustPython/issues?q=state%3Aopen%20label%3A%22RFC%22
z-ca-2026Tag to track Contribution Academy 2026https://github.com/RustPython/RustPython/issues?q=state%3Aopen%20label%3A%22z-ca-2026%22
https://github.com/widehyo1
widehyo1https://github.com/widehyo1
on Jul 18, 2026https://github.com/RustPython/RustPython/issues/8310#issue-4915983277
csv: fix csv escape fieldsep #8260https://github.com/RustPython/RustPython/pull/8260
csv: handle empty fields with skipinitialspace #8304https://github.com/RustPython/RustPython/pull/8304
#8253https://github.com/RustPython/RustPython/issues/8253
#8260https://github.com/RustPython/RustPython/pull/8260
#8284https://github.com/RustPython/RustPython/issues/8284
#8302https://github.com/RustPython/RustPython/issues/8302
#8304https://github.com/RustPython/RustPython/pull/8304
Modules/_csv.c at v3.14.6https://github.com/python/cpython/blob/v3.14.6/Modules/_csv.c
Lib/test/test_csv.py at v3.14.6https://github.com/python/cpython/blob/v3.14.6/Lib/test/test_csv.py
csv-core reader documentationhttps://docs.rs/csv-core/latest/csv_core/struct.Reader.html
csv-core/src/reader.rshttps://github.com/BurntSushi/rust-csv/blob/master/csv-core/src/reader.rs
crates/stdlib/src/csv.rshttps://github.com/RustPython/RustPython/blob/main/crates/stdlib/src/csv.rs
csv: fix csv escape fieldsep #8260https://github.com/RustPython/RustPython/pull/8260
csv: handle empty fields with skipinitialspace #8304https://github.com/RustPython/RustPython/pull/8304
csv: reader allows escapechar=1 #8284https://github.com/RustPython/RustPython/issues/8284
#8260https://github.com/RustPython/RustPython/pull/8260
#8304https://github.com/RustPython/RustPython/pull/8304
#8304https://github.com/RustPython/RustPython/pull/8304
#8304https://github.com/RustPython/RustPython/pull/8304
Current csv.rshttps://github.com/RustPython/RustPython/blob/main/crates/stdlib/src/csv.rs
#8253: csv.writer ignores a dialect's quoting and lineterminatorhttps://github.com/RustPython/RustPython/issues/8253
#8260: csv: fix csv escape fieldsephttps://github.com/RustPython/RustPython/pull/8260
#8284: csv: reader allows escapechar=1https://github.com/RustPython/RustPython/issues/8284
#8302: embedded CR/LF not quoted with custom lineterminatorhttps://github.com/RustPython/RustPython/issues/8302
#8304: csv: handle empty fields with skipinitialspacehttps://github.com/RustPython/RustPython/pull/8304
Modules/_csv.c at v3.14.6https://github.com/python/cpython/blob/v3.14.6/Modules/_csv.c
Lib/test/test_csv.py at v3.14.6https://github.com/python/cpython/blob/v3.14.6/Lib/test/test_csv.py
csv documentationhttps://docs.python.org/3.14/library/csv.html
csv-core documentationhttps://docs.rs/csv-core/latest/csv_core/
csv-core/src/reader.rshttps://github.com/BurntSushi/rust-csv/blob/master/csv-core/src/reader.rs
csv-core/src/writer.rshttps://github.com/BurntSushi/rust-csv/blob/master/csv-core/src/writer.rs
RFCRequest for commentshttps://github.com/RustPython/RustPython/issues?q=state%3Aopen%20label%3A%22RFC%22
z-ca-2026Tag to track Contribution Academy 2026https://github.com/RustPython/RustPython/issues?q=state%3Aopen%20label%3A%22z-ca-2026%22
https://github.com
Termshttps://docs.github.com/site-policy/github-terms/github-terms-of-service
Privacyhttps://docs.github.com/site-policy/privacy-policies/github-privacy-statement
Securityhttps://github.com/security
Statushttps://www.githubstatus.com/
Communityhttps://github.community/
Docshttps://docs.github.com/
Contacthttps://support.github.com?tags=dotcom-footer

Viewport: width=device-width


URLs of crawlers that visited me.