CFFI backend parity gaps: garbled `ZstdError` messages, `__exit__` ordering mismatch with C backend, minor parameter-validation drift
メンテナーはふだん 10 日以内に返信
まだ誰も着手していません。
評価
調査の方向性
zstandard/backend_cffi.py で報告されている箇所から始め、対応する C-backend の実装と比較してください。可能であれば PyPy または CFFI のみを使用する環境で、報告されている 3 つのパリティの差異を検証してください。エラー表示が修正され、exit の順序が一致し、パラメーター検証の動作が C-backend と一致すれば完了です。
索引モデルが issue の本文から書いたものです。
説明
Summary
The pure-Python CFFI backend has several small-but-real parity gaps with the C extension backend. The most visible is that several ZstdError constructors are called with positional args as if %-formatting were going to happen, but it doesn't — so user-facing error messages look like garbled tuples. Two additional gaps affect resource-lifecycle invariants and parameter validation.
Impact
- Severity: User-visible garbled error messages (main issue); resource-lifecycle invariant violation in
__exit__(secondary); small parameter-validation drift. - Reachability: Any user running the CFFI backend — typically PyPy, or environments where the C extension can't be built.
- Version: 0.25.0 (commit
7a77a75). - Note: Report was produced without a live CFFI environment available; the below is based on static review of
zstandard/backend_cffi.py. Happy to verify with a concrete reproducer if you set up a PyPy / CFFI-only test environment.
Gap 1: Tuple-args to ZstdError — garbled error messages
Pattern: raise ZstdError("... %s", error) passes two positional args to the exception constructor, making .args == ("... %s", error). No %-formatting happens. The rendered message looks like ('..., %s', <something>) instead of the intended interpolated string.
Sites: zstandard/backend_cffi.py:1531, 1575, 1682.
Fix: raise ZstdError("... %s" % error) or an f-string.
Gap 2: __exit__ ordering mismatch with the C backend
C backend's __exit__ calls close() first, then clears the compressor/decompressor field. CFFI backend sets _compressor = None first, then calls close() — which runs with the invariants already broken. Any reference to self._compressor inside close() observes None.
Sites: zstandard/backend_cffi.py:1413, 3161.
Fix: Reorder to match the C backend:
def __exit__(self, exc_type, exc_value, tb):
self.close() # first
self._compressor = None # then clear
return False
Gap 3: Minor parameter-validation drift
- Off-by-one in level-validation error message. CFFI says
"less than 22"; C says"less than 23". One of them has the boundary wrong (if the C backend's boundary is correct —ZSTD_maxCLevel()returns 22 — then both messages should say"less than 23"in the "strictly-less-than" formulation or"more than 22"in the symmetric one). compressobj(size=-1)is accepted by CFFI but raisesOverflowErrorin C — signed/unsigned mismatch somewhere in the CFFI argument handling.- CFFI backend does not declare
Py_MOD_GIL_NOT_USED(doesn't apply — it's pure Python), but has no FT-story either; worth either documenting or gating free-threaded wheels to C-backend-only.
Suggested PR shape
All three gaps are one small PR (pure-Python fixes, likely ~20 lines of diff). If you'd prefer to keep C-side and CFFI-side PRs separate I can do that — otherwise one combined parity PR seems cleanest.
Methodology
Found via cext-review-toolkit — the parity-checker agent identifies places where two implementations of the same interface diverge. Gaps 1 and 2 were flagged via structural pattern matching against the C backend; Gap 3 via argument-validation diffing. The CFFI environment wasn't available during the analysis, so none of the above was live-reproduced; verification is recommended but the diffs are small and the patterns are unambiguous on inspection.
Discovery, root-cause analysis, and issue drafting were performed by Claude Code and reviewed by a human before filing.
Full report
Complete multi-agent analysis (48 FIX findings across 13 categories, plus a reproducer appendix): https://gist.github.com/devdanzin/b86039ac097141579590c1a0f3a43605
- 主要言語
- C
- スター
- 642
- フォーク
- 118
- 平均マージ
- 9日 3時間
- マージ済み PR(30日)
- 6
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートなし
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
indygreg/python-zstandard のほかの issue
-
`multi_decompress_to_buffer([])` terminates the process with SIGFPE対応中かも @mikamikasuki が 15 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
indygreg/python-zstandard#335 ·
メンテナーはふだん 10 日以内に返信
-
Silent data-correctness bug: `readinto()` / `readinto1()` on `stream_reader` return `tell() == 0` after successful reads対応中かも @CAOShurong が 8 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
indygreg/python-zstandard#295 ·
メンテナーはふだん 10 日以内に返信
-
CFFI backend: crash when ZstdDecompressor instances share a ZstdCompressionDict対応中かも @pylaterreur が今日担当しました。 オープン
難易度 3/5 1〜2日 初心者へのやさしさ 45/100
indygreg/python-zstandard#361 ·
メンテナーはふだん 10 日以内に返信
-
Python 3.15 wheels対応中かも このイシューにリンクされたプルリクエストがオープン中、またはマージ済みです。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 55/100
indygreg/python-zstandard#354 · リアクション 1 件 ·
メンテナーはふだん 10 日以内に返信
-
`estimated_compression_context_size()` raises SIGFPE with `enable_ldm=True対応中かも @yousefissa が 3 日前に担当しました。 オープン
難易度 3/5 1〜2日 初心者へのやさしさ 64/100
indygreg/python-zstandard#345 ·
メンテナーはふだん 10 日以内に返信
indygreg/python-zstandard の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 77/100
メンテナーはふだん 8 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 90/100
メンテナーはふだん 1 日以内に返信
-
native-cpu: -Wreturn-type in skainet_row_threads.c:150 (worker fn has no return)対応中かも @Doomguy-BFG9000 が今日担当しました。 オープンgood first issue size:xs skill:native
難易度 1/5 1時間未満 初心者へのやさしさ 78/100
SKaiNET-developers/SKaiNET#1358 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
I/O internal correctness medium priority rt_lib
難易度 2/5 1〜3時間 初心者へのやさしさ 79/100
メンテナーはふだん 1 日以内に返信