python / python/cpython

Add converter and formatter parameters to csv.reader and csv.writer

オープン
#155,097 コメント 0 件 リアクション 1 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

extension-modules type-feature
主要言語
Python
スター
77.2k
フォーク
35.9k
PR マージ指標
PR 指標を取得中

説明

Feature or enhancement

Proposal:

The conversion between Python values and CSV fields is hard-coded in both directions. The reader converts unquoted fields with float(), and only in the QUOTE_NONNUMERIC and QUOTE_STRINGS modes. The writer converts every non-string value with str().

This is the common cause of several open issues:

  • gh-74232 -- bool is written unquoted as True, which cannot be read back.
  • gh-98485 -- the same for complex; Fraction and IntEnum are affected too, and Decimal silently round-trips through float.
  • gh-110852 -- there is no way to write floats with a fixed precision, because preformatted strings are quoted in the QUOTE_NONNUMERIC mode.
  • gh-85002 -- there is no way to reject values which are neither strings nor numbers.

I propose two parameters, mirroring parse_float in json:

  • csv.reader(f, converter=None) -- called as converter(index, field) instead of float().
  • csv.writer(f, formatter=None) -- called as formatter(index, value) instead of str(). It must return a string.

index is the 0-based position of the field in the record. Both default to None, which keeps the current behavior. The hooks only replace the existing calls -- what is not passed to float() or str() now is not passed to them either. Quoting is still decided by the original value.

The index goes first, like in enumerate(). This also makes a wrong one-argument callable fail at once: converter=int raises TypeError on the first field instead of taking the index as the base.

The index makes the hooks per-column, which is what the dtype and converters parameters of pandas.read_csv() are used for:

>>> types = [str, int, Decimal, Fraction]
>>> list(csv.reader(['spam,42,1.10,1/2'], quoting=csv.QUOTE_NONNUMERIC,
...                 converter=lambda i, field: types[i](field)))
[['spam', 42, Decimal('1.10'), Fraction(1, 2)]]
>>> def money(index, value):
...     return format(value, '.2f') if index == 2 else str(value)
>>> csv.writer(sys.stdout, formatter=money).writerow(['a', 1, 0.0, 3.14159])
a,1,0.00,3.14159

gh-85002 no longer needs a parameter of its own -- a strict writer is a formatter which refuses everything except numbers.

I have a working prototype (about 90 lines in Modules/_csv.c).

Open question: should these be parameters of the reader and the writer, or attributes of the dialect? A dialect is a portable description of the file syntax -- it is registered under a global name, sniffed, and copied -- so keeping callables out of it seems better.

Linked PRs
  • gh-155099

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

Modules/_csv.c と提案で説明されている reader/writer のエントリポイントから始め、既存の float() と str() の変換経路、およびそこに記載されているプロトタイプを確認してください。パラメータとダイアレクトの各選択肢を比較し、リンクされている gh-155099 の作業を使って、合意された API と完了基準を判断してください。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
python
領域
data
issue の種類
機能追加
難易度
5/5
見積もり時間
1週間以上
活発さ
停滞
明瞭さ
おおむね明確
初心者へのやさしさ
25/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。