dj.Diagram SVG output is not byte-reproducible: set iteration order leaks into node emission order
まだ誰も着手していません。
評価
- 難易度
- 3/5
- 見積もり時間
- 1〜2日
- 初心者へのやさしさ
- 78/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- python
調査の方向性
diagram.py の1210行目および1251-1252行目付近にある set を消費する経路から始め、その後、issue に記載されている他の出力ループを調べます。異なる PYTHONHASHSEED 値を使って別々のプロセスで SVG 生成を再現し、図のテストを見つけます。完了条件は、レンダリングされた図を維持しながら、すべてのハッシュシードで SVG バイト列が同一になることです。
索引モデルが issue の本文から書いたものです。
説明
dj.Diagram's SVG output is not byte-reproducible across processes. Two identical runs in one environment — same DataJoint, same pydot, same graphviz, same database — produce SVGs that differ in the order nodes and clusters are emitted. The rendered picture is unaffected: layout coordinates, colors, shapes and edges are all identical. Only the emission order moves.
Reproduction
Rendering a single-schema diagram twice in separate processes:
$ python gen.py && cp out.svg a.svg
$ python gen.py && cp out.svg b.svg
$ diff a.svg b.svg | grep -c '^[<>]'
88
The diff is entirely reordering — clust2/clust4 swap, node1/node2 swap, and the <path> coordinates travel with them unchanged:
-<g id="clust2" class="cluster">
-<title>cluster_cluster_entity_Fluorescence</title>
+<g id="clust4" class="cluster">
+<title>cluster_cluster_entity_Segmentation</title>
Pinning the hash seed makes it go away, which identifies the mechanism:
| result | |
|---|---|
| two runs, default (randomized) seed | differ |
two runs, PYTHONHASHSEED=0 |
identical |
PYTHONHASHSEED=0 vs PYTHONHASHSEED=12345 |
differ |
Cause
nodes_to_show and _expanded_nodes are Python sets of table-name strings (diagram.py:245, :261, and the result.nodes_to_show = ... assignments at :470, :489, :556). Several emission-path loops iterate them directly, or iterate the result of a set intersection:
# diagram.py:1210 — dimension marking
valid_nodes = self.nodes_to_show.intersection(set(self.nodes()))
for name in valid_nodes:
...
# diagram.py:1251-1252 — collapse
valid_nodes = self.nodes_to_show.intersection(set(self.nodes()))
valid_expanded = self._expanded_nodes.intersection(set(self.nodes()))
str.__hash__ is salted per process by default (PEP 456), so set iteration order for table names varies between runs, and that order reaches the emitted document.
Why it matters
It defeats byte-comparison of generated figures. datajoint-docs commits three generated SVGs and ships scripts/gen_pipeline_diagrams.py --check to detect renderer drift in CI (datajoint/datajoint-docs#266). That check cannot be relied on today: one of the three figures fails against its own committed output roughly half the time for reasons unrelated to the renderer, so a genuine notation change is indistinguishable from reshuffling. It also means every regeneration produces large spurious diffs — the real change gets buried.
More generally, anyone committing a dj.Diagram SVG to version control gets churn on every re-render.
Suggested fix
Sort where the set is consumed, not where it is built — the sets are the right structure for the membership tests they exist for. sorted(...) at the emission-path call sites above is a small, behavior-preserving change; the ordering only needs to be stable, not meaningful. Worth a test that renders the same diagram in two subprocesses with different PYTHONHASHSEED values and asserts the output matches, since an in-process test cannot catch this.
Found while verifying the committed docs figures against released 2.3.3 for datajoint/datajoint-docs#266.
- 主要言語
- Python
- スター
- 197
- フォーク
- 98
- 平均マージ
- 6日 7時間
- マージ済み PR(30日)
- 1
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
datajoint/datajoint-python のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
datajoint/datajoint-python#1539 · コメント 3 件 ·
-
難易度 5/5 1週間以上 初心者へのやさしさ 35/100
datajoint/datajoint-python#1550 ·
-
難易度 5/5 1週間以上 初心者へのやさしさ 35/100
datajoint/datajoint-python#1547 ·
-
難易度 4/5 3〜5日 初心者へのやさしさ 52/100
datajoint/datajoint-python#1546 · コメント 1 件 ·
-
Make key_source restrict-only: add key_source_restriction, deprecate parent-redefining overrides オープン
難易度 5/5 1週間以上 初心者へのやさしさ 35/100
datajoint/datajoint-python#1523 · コメント 1 件 ·
datajoint/datajoint-python の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
anthropics/skills#1811 · コメント 1 件 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
speaches-ai/speaches#678 ·
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
datalayer/mcp-compose#42 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
conda-forge/spacy-feedstock#177 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
UKGovernmentBEIS/inspect_evals#2523 ·