dj.Diagram SVG output is not byte-reproducible: set iteration order leaks into node emission order
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 3/5
- Thời gian dự kiến
- 1-2 ngày
- Mức phù hợp với người mới
- 78/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Đặc tả rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- python
- Lĩnh vực
- data-visualization
Hướng nghiên cứu
Bắt đầu trong diagram.py, tại các đường đi sử dụng set quanh các dòng 1210 và 1251-1252, sau đó kiểm tra các vòng lặp phát sinh khác được mô tả trong issue. Tái tạo quá trình tạo SVG trong các tiến trình riêng biệt với các giá trị PYTHONHASHSEED khác nhau và xác định các bài kiểm thử của sơ đồ. Được xem là hoàn tất khi các byte SVG giống hệt nhau qua mọi hash seed mà vẫn giữ nguyên sơ đồ đã render.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
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.
- Ngôn ngữ chính
- Python
- Star
- 197
- Fork
- 98
- Merge trung bình
- 6 ngày 10 giờ
- Pull request đã merge (30 ngày)
- 4
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của datajoint/datajoint-python
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
datajoint/datajoint-python#1539 · 3 bình luận ·
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
datajoint/datajoint-python#1550 ·
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
datajoint/datajoint-python#1547 ·
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 52/100
datajoint/datajoint-python#1546 · 1 bình luận ·
-
Make key_source restrict-only: add key_source_restriction, deprecate parent-redefining overrides Đang mở
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
datajoint/datajoint-python#1523 · 1 bình luận ·
Tất cả issue của datajoint/datajoint-python
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 88/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
-
enhancement
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 74/100