Skip to content

Fix: Plotly Spectrum Annotation bug - #139

Closed
singjc wants to merge 10 commits into
OpenMS:mainfrom
singjc:main
Closed

Fix: Plotly Spectrum Annotation bug#139
singjc wants to merge 10 commits into
OpenMS:mainfrom
singjc:main

Conversation

@singjc

@singjc singjc commented Jan 16, 2026

Copy link
Copy Markdown
Collaborator

This fixes issue 88 where the annotations do not appear correctly when plotting a single trace spectrum.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed hover tooltip alignment in Plotly plots when custom hover data is present with expanded traces.
  • Chores

    • Removed debug console output from annotation processing.
    • Updated testing infrastructure dependencies to syrupy >=5.0.0 and refreshed test snapshots.

✏️ Tip: You can customize this high-level summary in your review settings.

Copilot AI review requested due to automatic review settings January 16, 2026 15:04
@coderabbitai

coderabbitai Bot commented Jan 16, 2026

Copy link
Copy Markdown

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

The PR updates the testing infrastructure to support syrupy 5.0.0+ by migrating snapshot extensions to binary serialization mode, fixes a debug print statement in annotation handling, improves tooltip data alignment in Plotly traces, and regenerates snapshot test files with updated Bokeh version references and binary-encoded data payloads.

Changes

Cohort / File(s) Summary
Core visualization fixes
pyopenms_viz/_core.py
Removed stray runtime print statement in _get_annotations that logged annotation color when None, eliminating unwanted console output.
Plotly tooltip enhancements
pyopenms_viz/_plotly/core.py
Added numpy.repeat import; enhanced _add_tooltips to expand custom hover data when a single trace's x-array has been expanded (e.g., stems), ensuring hover data length matches actual trace length by repeating rows.
Binary snapshot serialization
pyopenms_viz/testing/BokehSnapshotExtension.py, pyopenms_viz/testing/MatplotlibSnapshotExtension.py, pyopenms_viz/testing/PlotlySnapshotExtension.py
Migrated all three snapshot extension classes to syrupy 5.0.0+ binary mode: added _write_mode = WriteMode.BINARY attribute, changed serialize() return type from str to bytes, implemented UTF-8 encoding in serialization, updated matches() methods to decode bytes before processing, and removed legacy read/write helper methods.
Snapshot data format updates
test/__snapshots__/test_spectrum/* (Bokeh HTML snapshots), test/__snapshots__/test_spectrum/* (Plotly JSON snapshots), test/__snapshots__/test_chromatogram/*, test/__snapshots__/test_peakmap/*, test/__snapshots__/test_peakmap3d/*, test/__snapshots__/test_peakmap_marginals/*
Updated snapshot files with downgraded Bokeh version references (3.6.0 → 3.4.1), regenerated document IDs and root element references; replaced explicit numeric arrays in Plotly traces with compact binary-encoded representations using dtype and bdata fields; replaced heatmapgl traces with scattermap placeholders across multiple test outputs.
Dependency specifications
pyproject.toml, requirements-dev.txt, requirements.txt
Updated syrupy dependency constraints: pyproject.toml and requirements-dev.txt now require syrupy>=5.0.0; requirements.txt updated from exact pin ==5.0.0 to >=5.0.0.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Poem

🐰 Bytes now dance where strings once lay,
Binary snapshots light the way,
Tooltips expand with utmost care,
No more prints float in the air,
Syrupy 5 brings order fair!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix: Plotly Spectrum Annotation bug' directly corresponds to the PR's main objective of fixing issue #88 where annotations fail to render correctly for single-trace Plotly spectra.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request fixes an issue where annotations do not appear correctly when plotting a single trace spectrum in the Plotly backend. The issue occurs because when spectrum data is converted to line plot format (to draw stems), each m/z value is repeated 3 times, but the custom hover data wasn't being expanded to match this increased trace length for single-trace scenarios.

Changes:

  • Added logic to expand custom_hover_data when a single trace has more points than the original data (e.g., when spectrum plotting repeats each m/z value multiple times to draw stems)
  • Removed a debug print statement that was left in the code
  • Imported the repeat function from NumPy to support the data expansion

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
pyopenms_viz/_plotly/core.py Added logic in _add_tooltips to handle single-trace scenarios where the trace length is expanded by repeating data points, and imported the repeat function from NumPy
pyopenms_viz/_core.py Removed a debug print statement for annotation color

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pyopenms_viz/_plotly/core.py Outdated
Comment thread pyopenms_viz/_core.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@pyopenms_viz/_plotly/core.py`:
- Around line 163-180: The code can raise ZeroDivisionError when
custom_hover_data has zero rows; in the block that computes ratio =
int(trace_len / custom_hover_data.shape[0]) (used to possibly repeat
custom_hover_data via repeat) add a defensive guard to ensure
custom_hover_data.shape[0] > 0 before performing the division and only
compute/repeat when that condition holds (otherwise skip expansion or leave
custom_hover_data as-is), then call self.fig.update_traces(...) with the
unchanged custom_hover_data.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6e4aa2a and 0cc8996.

📒 Files selected for processing (2)
  • pyopenms_viz/_core.py
  • pyopenms_viz/_plotly/core.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: CodeQL analysis (python)
  • GitHub Check: Agent
  • GitHub Check: build
🔇 Additional comments (2)
pyopenms_viz/_core.py (1)

996-998: LGTM! Debug print statement removal.

Removing stray debug output is good hygiene before merging.

pyopenms_viz/_plotly/core.py (1)

7-7: LGTM!

The repeat import is necessary for the new hover data alignment logic.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +163 to 180
else:
# If there is a single trace but the trace x-array was expanded
# (for example spectrum plotting repeats each mz value several times
# to draw stems), expand the custom_hover_data to match the trace length.
trace = self.fig.data[0]
trace_len = len(trace.x) if hasattr(trace, "x") else None
if (
trace_len is not None
and custom_hover_data is not None
and custom_hover_data.shape[0] != trace_len
):
# If trace length is an integer multiple of custom data rows,
# repeat each row accordingly.
ratio = int(trace_len / custom_hover_data.shape[0])
if ratio > 1 and custom_hover_data.shape[0] * ratio == trace_len:
custom_hover_data = repeat(custom_hover_data, ratio, axis=0)

self.fig.update_traces(hovertemplate=tooltips, customdata=custom_hover_data)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard against division by zero when custom_hover_data is empty.

The logic correctly aligns hover data for single-trace spectra where x-values are repeated (e.g., for stem plots). However, if custom_hover_data has zero rows, line 176 will raise a ZeroDivisionError.

🛡️ Suggested defensive guard
         else:
             # If there is a single trace but the trace x-array was expanded
             # (for example spectrum plotting repeats each mz value several times
             # to draw stems), expand the custom_hover_data to match the trace length.
             trace = self.fig.data[0]
             trace_len = len(trace.x) if hasattr(trace, "x") else None
             if (
                 trace_len is not None
                 and custom_hover_data is not None
+                and custom_hover_data.shape[0] > 0
                 and custom_hover_data.shape[0] != trace_len
             ):
                 # If trace length is an integer multiple of custom data rows,
                 # repeat each row accordingly.
                 ratio = int(trace_len / custom_hover_data.shape[0])
                 if ratio > 1 and custom_hover_data.shape[0] * ratio == trace_len:
                     custom_hover_data = repeat(custom_hover_data, ratio, axis=0)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
else:
# If there is a single trace but the trace x-array was expanded
# (for example spectrum plotting repeats each mz value several times
# to draw stems), expand the custom_hover_data to match the trace length.
trace = self.fig.data[0]
trace_len = len(trace.x) if hasattr(trace, "x") else None
if (
trace_len is not None
and custom_hover_data is not None
and custom_hover_data.shape[0] != trace_len
):
# If trace length is an integer multiple of custom data rows,
# repeat each row accordingly.
ratio = int(trace_len / custom_hover_data.shape[0])
if ratio > 1 and custom_hover_data.shape[0] * ratio == trace_len:
custom_hover_data = repeat(custom_hover_data, ratio, axis=0)
self.fig.update_traces(hovertemplate=tooltips, customdata=custom_hover_data)
else:
# If there is a single trace but the trace x-array was expanded
# (for example spectrum plotting repeats each mz value several times
# to draw stems), expand the custom_hover_data to match the trace length.
trace = self.fig.data[0]
trace_len = len(trace.x) if hasattr(trace, "x") else None
if (
trace_len is not None
and custom_hover_data is not None
and custom_hover_data.shape[0] > 0
and custom_hover_data.shape[0] != trace_len
):
# If trace length is an integer multiple of custom data rows,
# repeat each row accordingly.
ratio = int(trace_len / custom_hover_data.shape[0])
if ratio > 1 and custom_hover_data.shape[0] * ratio == trace_len:
custom_hover_data = repeat(custom_hover_data, ratio, axis=0)
self.fig.update_traces(hovertemplate=tooltips, customdata=custom_hover_data)
🤖 Prompt for AI Agents
In `@pyopenms_viz/_plotly/core.py` around lines 163 - 180, The code can raise
ZeroDivisionError when custom_hover_data has zero rows; in the block that
computes ratio = int(trace_len / custom_hover_data.shape[0]) (used to possibly
repeat custom_hover_data via repeat) add a defensive guard to ensure
custom_hover_data.shape[0] > 0 before performing the division and only
compute/repeat when that condition holds (otherwise skip expansion or leave
custom_hover_data as-is), then call self.fig.update_traces(...) with the
unchanged custom_hover_data.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pyopenms_viz/testing/MatplotlibSnapshotExtension.py (1)

65-85: Fix the return type annotation: method returns bytes, not str.

The serialize method returns buf.getvalue() which is bytes, but the signature declares -> str. This inconsistency will cause type checker errors and is inconsistent with the other snapshot extensions in this PR (Plotly and Bokeh both correctly declare -> bytes).

🐛 Proposed fix for type annotation and docstring
-    def serialize(self, data: SerializableData, **kwargs: Any) -> str:
+    def serialize(self, data: SerializableData, **kwargs: Any) -> bytes:
         """
         Serialize the matplotlib Axis or Figure object to a png

         Args:
             data (SerializableData): Matplotlib data to serialize, should be an axis object

         Returns:
-            str: Image object
+            bytes: PNG image bytes
         """
♻️ Duplicate comments (1)
pyopenms_viz/_plotly/core.py (1)

169-177: Guard against zero-row hoverdata before dividing.

custom_hover_data.shape[0] can be 0, which raises ZeroDivisionError at Line 176. Add a >0 check before computing ratio or repeating.

🔧 Proposed fix
             if (
                 trace_len is not None
                 and custom_hover_data is not None
+                and custom_hover_data.shape[0] > 0
                 and custom_hover_data.shape[0] != trace_len
             ):
                 # If trace length is an integer multiple of custom data rows,
                 # repeat each row accordingly.
                 ratio = trace_len // custom_hover_data.shape[0]
🧹 Nitpick comments (1)
pyopenms_viz/testing/MatplotlibSnapshotExtension.py (1)

19-38: Consider catching more specific exceptions for type detection.

The broad except Exception blocks work for fallback logic, but catching more specific exceptions (e.g., TypeError, AttributeError, or PIL-specific exceptions) would be safer and avoid masking unexpected errors. However, given this is test infrastructure and the intent is clear, this is acceptable as-is.

♻️ Optional: More specific exception handling
         try:
             serialized_img = Image.open(BytesIO(serialized_data))
-        except Exception:
+        except (TypeError, AttributeError, IOError):
             # If already an Image object, use it directly
             serialized_img = serialized_data

         try:
             snapshot_img = Image.open(BytesIO(snapshot_data))
-        except Exception:
+        except (TypeError, AttributeError, IOError):
             snapshot_img = snapshot_data
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0cc8996 and 616a6e3.

📒 Files selected for processing (4)
  • pyopenms_viz/_plotly/core.py
  • pyopenms_viz/testing/BokehSnapshotExtension.py
  • pyopenms_viz/testing/MatplotlibSnapshotExtension.py
  • pyopenms_viz/testing/PlotlySnapshotExtension.py
🧰 Additional context used
🧬 Code graph analysis (1)
pyopenms_viz/testing/PlotlySnapshotExtension.py (4)
pyopenms_viz/testing/BokehSnapshotExtension.py (1)
  • serialize (168-178)
pyopenms_viz/testing/MatplotlibSnapshotExtension.py (1)
  • serialize (65-85)
pyopenms_viz/testing/PandasSnapshotExtension.py (1)
  • serialize (54-55)
pyopenms_viz/testing/NumpySnapshotExtension.py (1)
  • serialize (59-60)
🪛 Ruff (0.14.11)
pyopenms_viz/testing/PlotlySnapshotExtension.py

98-98: Unused method argument: kwargs

(ARG002)

pyopenms_viz/testing/BokehSnapshotExtension.py

168-168: Unused method argument: kwargs

(ARG002)

pyopenms_viz/testing/MatplotlibSnapshotExtension.py

23-23: Do not catch blind exception: Exception

(BLE001)


29-29: Do not catch blind exception: Exception

(BLE001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build
  • GitHub Check: test (windows-latest, 3.12)
  • GitHub Check: test (ubuntu-latest, 3.12)
🔇 Additional comments (6)
pyopenms_viz/testing/PlotlySnapshotExtension.py (3)

17-30: LGTM! Robust bytes/string handling for JSON comparison.

The defensive decoding of both serialized_data and snapshot_data ensures compatibility whether the data arrives as bytes or strings. This aligns well with the binary I/O changes throughout the file.


72-80: LGTM! Consistent binary I/O semantics.

Reading in binary mode and writing bytes directly is consistent with the serialize() method now returning bytes. The defensive str → bytes conversion in write_snapshot_collection handles edge cases gracefully.

Also applies to: 92-96


98-108: LGTM! Serialization correctly returns UTF-8 encoded bytes.

The implementation properly encodes the JSON output to bytes, matching the updated return type annotation. The kwargs parameter is inherited from the SingleFileSnapshotExtension interface and is acceptable to leave unused for interface compliance.

pyopenms_viz/testing/BokehSnapshotExtension.py (3)

78-83: LGTM! Defensive bytes decoding for HTML parsing.

Properly handles both bytes and string inputs, ensuring compatibility with the binary read operations.


142-150: LGTM! Consistent binary I/O operations.

Reading and writing in binary mode aligns with the serialize() method returning bytes. The defensive str → bytes encoding in write_snapshot_collection handles any edge cases.

Also applies to: 162-166


168-178: LGTM! Clean bytes serialization.

The implementation correctly encodes HTML to UTF-8 bytes with proper type annotation and docstring. The kwargs parameter is part of the inherited interface.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copilot AI review requested due to automatic review settings January 16, 2026 15:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 48 out of 63 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@test/__snapshots__/test_spectrum/test_mirror_spectrum`[ms_plotly].raw:
- Around line 8-15: pyproject.toml currently lists "plotly" without a version
while requirements.txt pins plotly==5.24.1; update the pyproject.toml dependency
entry for plotly to match the pinned version (5.24.1) — e.g., set the "plotly"
dependency in the pyproject.toml dependencies section to "5.24.1" (or the same
specifier used in requirements.txt) so both manifests consistently pin plotly
and ensure stable snapshots.

In
`@test/__snapshots__/test_spectrum/test_spectrum_binning`[ms_bokeh-kwargs0].raw:
- Around line 1-61: convert_for_line_plots() is currently stripping all columns
except x, y and group before building the Bokeh ColumnDataSource, which removes
metadata like native_id referenced by the HoverTool tooltips; update
convert_for_line_plots() (or the helper that builds the ColumnDataSource) to
preserve any non-index metadata columns (e.g., native_id) when present so the
ColumnDataSource contains x, y, group plus metadata fields, or alternatively
remove native_id from the HoverTool tooltips construction (where HoverTool is
created) so tooltips only reference fields guaranteed to exist.

In
`@test/__snapshots__/test_spectrum/test_spectrum_binning`[ms_plotly-kwargs0].raw:
- Around line 8-15: The snapshot contains misaligned binary arrays: the "x"
field (dtype f8) decodes to 28 values while "y" (dtype i1) decodes to 27, and
neither matches the 9 entries in "customdata" in test_spectrum_binning; fix by
ensuring the generator producing these arrays creates matching lengths—make "x"
and "y" the same length and align them with the number of "customdata" entries
(e.g., adjust the data-producing code in the test or fixture so "x", "y" and
"customdata" all have the same count), then re-encode the bdata for "x" and "y"
to reflect the corrected arrays and update the snapshot for
test_spectrum_binning.

In
`@test/__snapshots__/test_spectrum/test_spectrum_plot_with_peak_color`[ms_bokeh-kwargs1].raw:
- Line 15: The stored snapshot references Bokeh 3.4.1 in the script tag and must
be updated to match the current test environment (Bokeh 3.6.x); regenerate the
snapshot for test_spectrum_plot_with_peak_color[ms_bokeh-kwargs1] so the script
src reflects the resolved Bokeh version (e.g., update the <script ...
bokeh-3.4.1.min.js> entry to bokeh-3.6.x.min.js) by re-running the snapshot test
or snapshot update command to produce the new expected output.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 616a6e3 and c75d4ac.

📒 Files selected for processing (58)
  • test/__snapshots__/test_spectrum/test_mirror_spectrum[ms_bokeh].html
  • test/__snapshots__/test_spectrum/test_mirror_spectrum[ms_bokeh].raw
  • test/__snapshots__/test_spectrum/test_mirror_spectrum[ms_matplotlib].raw
  • test/__snapshots__/test_spectrum/test_mirror_spectrum[ms_plotly].raw
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs0].html
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs0].raw
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs1].html
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs1].raw
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs2].html
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs2].raw
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs3].html
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs3].raw
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs4].html
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs4].raw
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_matplotlib-kwargs0].raw
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_matplotlib-kwargs1].raw
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_matplotlib-kwargs2].raw
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_matplotlib-kwargs3].raw
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_matplotlib-kwargs4].raw
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs0].raw
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs1].raw
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs2].raw
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs3].raw
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs4].raw
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs0].html
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs0].raw
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs1].html
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs1].raw
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs2].html
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs2].raw
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs3].html
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs3].raw
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_matplotlib-kwargs0].raw
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_matplotlib-kwargs1].raw
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_matplotlib-kwargs2].raw
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_matplotlib-kwargs3].raw
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_plotly-kwargs0].raw
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_plotly-kwargs1].raw
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_plotly-kwargs2].raw
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_plotly-kwargs3].raw
  • test/__snapshots__/test_spectrum/test_spectrum_plot_with_peak_color[ms_bokeh-kwargs0].raw
  • test/__snapshots__/test_spectrum/test_spectrum_plot_with_peak_color[ms_bokeh-kwargs1].raw
  • test/__snapshots__/test_spectrum/test_spectrum_plot_with_peak_color[ms_matplotlib-kwargs0].raw
  • test/__snapshots__/test_spectrum/test_spectrum_plot_with_peak_color[ms_matplotlib-kwargs1].raw
  • test/__snapshots__/test_spectrum/test_spectrum_plot_with_peak_color[ms_plotly-kwargs0].raw
  • test/__snapshots__/test_spectrum/test_spectrum_plot_with_peak_color[ms_plotly-kwargs1].raw
  • test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_bokeh-kwargs0].html
  • test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_bokeh-kwargs0].raw
  • test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_bokeh-kwargs1].html
  • test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_bokeh-kwargs1].raw
  • test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_bokeh-kwargs2].html
  • test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_bokeh-kwargs2].raw
  • test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_matplotlib-kwargs0].raw
  • test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_matplotlib-kwargs1].raw
  • test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_matplotlib-kwargs2].raw
  • test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_plotly-kwargs0].raw
  • test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_plotly-kwargs1].raw
  • test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_plotly-kwargs2].raw
💤 Files with no reviewable changes (13)
  • test/snapshots/test_spectrum/test_spectrum_with_annotations[ms_bokeh-kwargs2].html
  • test/snapshots/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs3].html
  • test/snapshots/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs0].html
  • test/snapshots/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs2].html
  • test/snapshots/test_spectrum/test_mirror_spectrum[ms_bokeh].html
  • test/snapshots/test_spectrum/test_spectrum_with_annotations[ms_bokeh-kwargs1].html
  • test/snapshots/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs4].html
  • test/snapshots/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs1].html
  • test/snapshots/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs2].html
  • test/snapshots/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs1].html
  • test/snapshots/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs0].html
  • test/snapshots/test_spectrum/test_spectrum_with_annotations[ms_bokeh-kwargs0].html
  • test/snapshots/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs3].html
✅ Files skipped from review due to trivial changes (1)
  • test/snapshots/test_spectrum/test_spectrum_with_annotations[ms_bokeh-kwargs2].raw
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CodeQL analysis (python)
🔇 Additional comments (36)
test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_plotly-kwargs1].raw (2)

8-15: Snapshot update looks consistent with binary serialization.
No issues flagged here.


461-472: Template update for scattermap looks fine.
No concerns from the snapshot perspective.

test/__snapshots__/test_spectrum/test_mirror_spectrum[ms_bokeh].raw (1)

1-61: Snapshot update looks consistent.
No issues spotted in the serialized Bokeh HTML; the snapshot appears deterministic and aligned with expected embedding behavior.

test/__snapshots__/test_spectrum/test_spectrum_plot[ms_plotly-kwargs3].raw (2)

8-15: Binary-encoded x/y arrays look consistent with the new snapshot format.

No issues spotted in the serialized structure for x and y.


461-472: scattermap template block addition looks fine.

This aligns with the updated Plotly template expectations reflected in the snapshot.

test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs4].raw (1)

1-61: Snapshot update looks consistent with raw HTML serialization.

No issues spotted in this updated snapshot artifact.

test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs2].raw (1)

1-61: Auto-generated test snapshot file looks valid.

This is a Bokeh HTML snapshot file for the spectrum binning test. The structure follows standard Bokeh embedding patterns with CDN script loading, embedded JSON document data, and the polling-based embedding script. Since this is auto-generated test output used for snapshot comparison, no manual changes are expected.

test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_bokeh-kwargs1].raw (1)

1-61: LGTM - Well-structured Bokeh test snapshot.

This is an auto-generated Bokeh HTML snapshot for testing spectrum annotations. The file correctly includes:

  • Self-contained BokehJS embedding with proper fallback polling for library availability
  • Spectrum data with annotation labels (e.g., "100.5332\ncustom3") in the JSON document
  • Hover tool configuration for m/z and intensity tooltips

This aligns with the PR's annotation fix and the broader snapshot handling changes mentioned in the summary.

test/__snapshots__/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs3].raw (1)

1-61: Test snapshot file appears valid.

This is an auto-generated Bokeh HTML snapshot capturing the expected visualization output. The structure follows standard Bokeh embedding patterns with proper CDN loading, JSON document definition, and fallback embedding logic.

The snapshot includes the expected spectrum annotations (Label objects for m/z values like "100.5332", "74.1324", etc.), which aligns with the PR's goal of verifying annotation rendering.

Note: The snapshot contains dynamically generated UUIDs (e.g., line 21, 23, 32-33). If your snapshot comparison is exact-match based, ensure these UUIDs remain stable across test runs or that your comparison logic accounts for them.

test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs1].raw (1)

1-61: Snapshot update looks consistent.

No issues to flag in this snapshot content.

test/__snapshots__/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs1].raw (1)

1-61: LGTM - Valid Bokeh test snapshot for multi-trace spectrum.

This auto-generated snapshot correctly captures a 3-trace Mass Spectrum visualization with annotation labels, validating the fix for spectrum annotations.

test/__snapshots__/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs2].raw (1)

1-61: LGTM - Valid Bokeh test snapshot for color-annotated spectrum.

This snapshot correctly captures a 4-trace visualization with explicit color annotations, using float-typed intensity values. The annotation labels are properly embedded.

test/__snapshots__/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs0].raw (1)

1-61: LGTM - Critical test snapshot for single-trace annotation fix.

This snapshot is central to validating the PR's fix for issue #88. It correctly captures a single-trace spectrum with all 5 annotation labels properly embedded (Labels p1047-p1051), confirming that annotations now render correctly for single-trace spectra.

test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs3].raw (1)

1-61: Snapshot file verified.

This is an auto-generated Bokeh HTML snapshot used for regression testing with the correct structure.

test/__snapshots__/test_spectrum/test_spectrum_plot_with_peak_color[ms_bokeh-kwargs1].raw (2)

21-24: Snapshot regeneration changes look consistent.

The updated UUIDs and document IDs are expected when regenerating Bokeh snapshots. Internal references are consistent:

  • Root id p1384 in the div's data-root-id correctly maps to the root in render_items
  • Document JSON structure and visualization data remain intact

32-33: Embed references are internally consistent.

The JavaScript embed code correctly references the updated IDs:

  • getElementById targets the correct JSON script element
  • render_items docid matches the JSON document key
  • Root mapping (p1384da160197-...) aligns with the div element
test/__snapshots__/test_spectrum/test_spectrum_plot[ms_plotly-kwargs2].raw (2)

9-16: Snapshot binary encoding update looks consistent.

These blocks align with the new bytes-based Plotly serialization and appear coherent for the scatter traces.

Also applies to: 88-95, 122-129, 171-178


609-619: No action required. The repository's Plotly version (5.24.1) fully supports scattermap, which was introduced in Plotly.py v5.24.0. The snapshot is compatible with the pinned dependency and does not present version-dependent failure risks.

Likely an incorrect or invalid review comment.

test/__snapshots__/test_spectrum/test_mirror_spectrum[ms_plotly].raw (2)

66-80: Second trace correctly omits customdata for mirror visualization.

The mirrored trace uses the same binary encoding and appropriately lacks customdata/hovertemplate since hover info on the primary trace is sufficient for mirror spectrum plots.


476-487: Template now includes both scattermap and scattermapbox trace types.

This addition reflects Plotly's transition to MapLibre-powered map traces. The scattermap type is a new MapLibre-based alternative to scattermapbox for map visualizations. Note that heatmapgl (used in other snapshots) serves a different purpose and remains unchanged. The project should ensure the unconstrained plotly dependency in pyproject.toml is compatible with the snapshot expectations, as Plotly 5.24.0+ is required for scattermap support.

Likely an incorrect or invalid review comment.

test/__snapshots__/test_spectrum/test_spectrum_plot[ms_plotly-kwargs1].raw (2)

9-193: Snapshot encoding update looks consistent.
The binary dtype/bdata x/y encoding while preserving customdata and hover templates matches Plotly’s array serialization style.


590-600: Scattermap template addition looks good.
Including the scattermap defaults in the template keeps trace rendering consistent with the updated Plotly schema.

test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs3].raw (2)

8-15: Binary x/y encoding aligns with new serialization.
The switch to dtype/bdata for coordinates is consistent with Plotly’s binary array format.


461-472: Scattermap template defaults are fine.
This template addition is consistent with the other Plotly snapshot updates.

test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs2].raw (2)

8-15: Binary coordinate encoding looks correct.
The dtype/bdata shift for x/y matches the updated Plotly snapshot format.


461-472: Scattermap template block is consistent with the new format.
No concerns with this addition.

test/__snapshots__/test_spectrum/test_spectrum_plot_with_peak_color[ms_plotly-kwargs1].raw (2)

9-203: Binary x/y serialization looks consistent across traces.
The dtype/bdata blocks for both traces align with the updated Plotly snapshot format.


616-627: Scattermap template defaults look fine.
This addition matches the other Plotly snapshot updates.

test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs4].raw (2)

8-15: Binary coordinate encoding is consistent.
The snapshot reflects the expected Plotly dtype/bdata serialization.


461-472: Scattermap template addition looks good.
No issues noted with the template expansion.

test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_plotly-kwargs2].raw (1)

9-277: Snapshot update aligns with the new Plotly serialization.

Binary-encoded coordinate blocks and the added scattermap template look consistent with the updated snapshot format.

Also applies to: 674-685

test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_plotly-kwargs0].raw (1)

9-278: Snapshot update aligns with the new Plotly serialization.

Binary-encoded coordinate blocks and the scattermap template addition are consistent with the new snapshot format.

Also applies to: 674-685

test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs1].raw (1)

8-65: Snapshot update aligns with the new Plotly serialization.

The binary-encoded coordinate data and scattermap template entry look consistent with the updated snapshot format.

Also applies to: 461-472

test/__snapshots__/test_spectrum/test_spectrum_plot_with_peak_color[ms_plotly-kwargs0].raw (1)

9-219: Snapshot update aligns with the new Plotly serialization.

Binary-encoded coordinates and the scattermap template addition look consistent with the serializer changes.

Also applies to: 616-627

test/__snapshots__/test_spectrum/test_spectrum_plot[ms_plotly-kwargs0].raw (1)

8-65: Snapshot update aligns with the new Plotly serialization.

Binary-encoded coordinate blocks and the scattermap template entry look consistent with the updated snapshot format.

Also applies to: 461-472

test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs0].raw (1)

461-472: Template addition aligns with Plotly conventions.

The new scattermap trace type follows the same structure as existing trace definitions (e.g., scattermapbox, scattergeo). This is likely introduced by a Plotly version update adding native support for this trace type.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +8 to +15
"x": {
"dtype": "f8",
"bdata": "O99PjZd+SUA730+Nl35JQDvfT42XfklAY3\u002fZPXmIUkBjf9k9eYhSQGN\u002f2T15iFJAio7k8h8iWUCKjuTyHyJZQIqO5PIfIllAexSuR+FiWUB7FK5H4WJZQHsUrkfhYllALpCg+DGiWUAukKD4MaJZQC6QoPgxollAZmZmZmYGZEBmZmZmZgZkQGZmZmZmBmRAsp3vp8b\u002fZUCyne+nxv9lQLKd76fG\u002f2VAWvW52ooNaUBa9bnaig1pQFr1udqKDWlAWvW52oodaUBa9bnaih1pQFr1udqKHWlA"
},
"y": {
"dtype": "f8",
"bdata": "AAAAAAAAAAAAAAAAAABEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4QAAAAAAAAAAAAAAAAAAAAAAAAAAAAABKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABRQAAAAAAAAAAAAAAAAAAAAAABAAAAAAA8QAAAAAAAAAAA"
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Find package files and Plotly version
find . -type f \( -name "package.json" -o -name "requirements.txt" -o -name "pyproject.toml" \) | head -20

Repository: OpenMS/pyopenms_viz

Length of output: 149


🏁 Script executed:

# Check if the snapshot file exists and examine its structure
ls -la test/__snapshots__/test_spectrum/test_mirror_spectrum* 2>/dev/null || echo "Snapshot file path needs verification"

Repository: OpenMS/pyopenms_viz

Length of output: 422


🏁 Script executed:

# Find any Plotly-related dependencies
rg "plotly" --type json --type toml --type txt -i | head -20

Repository: OpenMS/pyopenms_viz

Length of output: 418


🏁 Script executed:

# Examine the snapshot file structure and content
head -100 test/__snapshots__/test_spectrum/test_mirror_spectrum[ms_plotly].raw

Repository: OpenMS/pyopenms_viz

Length of output: 3033


🏁 Script executed:

# Check line count to understand file size
wc -l test/__snapshots__/test_spectrum/test_mirror_spectrum[ms_plotly].raw

Repository: OpenMS/pyopenms_viz

Length of output: 136


🏁 Script executed:

# Search for the specific lines mentioned in the review (lines 8-15)
sed -n '1,20p' test/__snapshots__/test_spectrum/test_mirror_spectrum[ms_plotly].raw

Repository: OpenMS/pyopenms_viz

Length of output: 974


🌐 Web query:

Plotly.js 5.24.1 binary data serialization dtype bdata format

💡 Result:

Short answer

  • "bdata" is Plotly.js's typed-array serialization: an object with keys dtype (e.g. "f8","f4","i4","u4","i2","u2","i1","u1","u1c"), bdata (a base64-encoded ArrayBuffer or raw ArrayBuffer), and optional shape for multi‑dim arrays. It is used so Plotly can accept binary typed arrays (faster/more compact) instead of JSON arrays. [1][2]

What each token means

  • dtype → numeric type mapping:
    • f8 → Float64Array
    • f4 → Float32Array
    • i4 → Int32Array
    • u4 → Uint32Array
    • i2 → Int16Array
    • u2 → Uint16Array
    • i1 → Int8Array
    • u1 → Uint8Array
    • u1c → Uint8ClampedArray
  • bdata → base64 string representing the ArrayBuffer bytes (or an actual ArrayBuffer when sent in memory).
  • shape → comma-separated dimensions for multidimensional arrays (optional for 1‑D). [1]

Notes and gotchas

  • Plotly.py (and Dash) will base64-encode NumPy arrays when passing them to Plotly.js; the Python side may add an internal _inputArray wrapper when serializing. [2]
  • Endianness can affect interoperability (big-endian NumPy dtypes have caused issues in the past). If you see odd values, check byte-order/ dtype conversion before encoding. [3]

Quick JS decode example (concept)

  • Given an object obj = {dtype: 'f8', bdata: '...base64...'}:
    1. bytes = Uint8Array.from(atob(obj.bdata), c => c.charCodeAt(0));
    2. buffer = bytes.buffer;
    3. arr = new Float64Array(buffer); // use type matching obj.dtype
    4. if obj.shape present, reshape accordingly.

Sources
[1] Plotly.js typed-array/typed data spec in source (coerce/typed-array handling).
[2] Plotly.py performance guide describing base64-encoded NumPy arrays → Plotly.js typed arrays.
[3] Plotly community reports about dtype/binary encoding and endianness issues.


🌐 Web query:

Plotly typed array bdata f8 float64 serialization

💡 Result:

Short answer

  • In Plotly's typed-array spec, dtype "f8" = float64 (JS Float64Array). bdata is the array buffer either as a base64 string or as an ArrayBuffer; shape is optional for 1‑D or required for multi‑D. Plotly.py encodes NumPy arrays into this spec (dtype short names like "f8") and Plotly.js decodes them back into typed arrays for rendering. [1][2][3]

Minimal JSON example

  • 1-D float64:
    {"dtype":"f8","bdata":""}
  • 2-D (shape as rows,cols):
    {"dtype":"f8","bdata":"","shape":"5,100"}

Notes

  • bdata may be an ArrayBuffer (binary) or a base64 string; Plotly.js will b64-decode if necessary and construct a Float64Array using the BYTES_PER_ELEMENT of the f8 type. [2][3]
  • Plotly.py maps NumPy dtypes to short dtype names ("float64" -> "f8") when producing the typed-array spec. [1]

Sources

  • Plotly.py dtype mapping / to_typed_array_spec implementation. [1]
  • Plotly.js typed-array spec description (coerce/decodeTypedArraySpec). [2]
  • Plotly.js runtime decode implementation (creates typed arrays from bdata). [3]

Ensure Plotly version constraint is consistent across all configuration files.

The snapshot correctly shows Plotly's typed-array serialization format (dtype: "f8" for 64-bit floats, bdata for base64-encoded binary data). This is standard behavior when Plotly.py encodes NumPy arrays for rendering.

However, Plotly's version is pinned to 5.24.1 in requirements.txt but lacks a version constraint in pyproject.toml (specifies "plotly" without version). Update pyproject.toml to match the version pin in requirements.txt to ensure snapshot stability across environments.

🤖 Prompt for AI Agents
In `@test/__snapshots__/test_spectrum/test_mirror_spectrum`[ms_plotly].raw around
lines 8 - 15, pyproject.toml currently lists "plotly" without a version while
requirements.txt pins plotly==5.24.1; update the pyproject.toml dependency entry
for plotly to match the pinned version (5.24.1) — e.g., set the "plotly"
dependency in the pyproject.toml dependencies section to "5.24.1" (or the same
specifier used in requirements.txt) so both manifests consistently pin plotly
and ensure stable snapshots.

Comment on lines +1 to +61
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Bokeh Application</title>
<style>
html, body {
box-sizing: border-box;
display: flow-root;
height: 100%;
margin: 0;
padding: 0;
}
</style>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-3.4.1.min.js"></script>
<script type="text/javascript">
Bokeh.set_log_level("info");
</script>
</head>
<body>
<div id="d5627e74-de6b-4c45-9b4e-869ae55955b5" data-root-id="p1454" style="display: contents;"></div>

<script type="application/json" id="cbb7b83d-4a93-4e1d-b75f-64f7c08ed51d">
{"4678ae8c-f3e7-4997-a76d-6ff7e04d46ee":{"version":"3.4.1","title":"Bokeh Application","roots":[{"type":"object","name":"Figure","id":"p1454","attributes":{"width":500,"height":500,"x_range":{"type":"object","name":"Range1d","id":"p1506","attributes":{"start":40.791199999999996,"end":241.10784}},"y_range":{"type":"object","name":"Range1d","id":"p1507","attributes":{"end":28.75}},"x_scale":{"type":"object","name":"LinearScale","id":"p1464"},"y_scale":{"type":"object","name":"LinearScale","id":"p1465"},"title":{"type":"object","name":"Title","id":"p1457","attributes":{"text":"Mass Spectrum","text_font_size":"18pt"}},"renderers":[{"type":"object","name":"GlyphRenderer","id":"p1496","attributes":{"data_source":{"type":"object","name":"ColumnDataSource","id":"p1487","attributes":{"selected":{"type":"object","name":"Selection","id":"p1488","attributes":{"indices":[],"line_indices":[]}},"selection_policy":{"type":"object","name":"UnionRenderers","id":"p1489"},"data":{"type":"map","entries":[["index",{"type":"ndarray","array":{"type":"bytes","data":"AAAAAAAAAAAAAAAAAQAAAAEAAAABAAAAAgAAAAIAAAACAAAAAwAAAAMAAAADAAAABAAAAAQAAAAEAAAABQAAAAUAAAAFAAAABgAAAAYAAAAGAAAABwAAAAcAAAAHAAAACAAAAAgAAAAIAAAA"},"shape":[27],"dtype":"int32","order":"little"}],["mz",{"type":"ndarray","array":{"type":"bytes","data":"O99PjZd+SUA730+Nl35JQDvfT42XfklAY3/ZPXmIUkBjf9k9eYhSQGN/2T15iFJAio7k8h8iWUCKjuTyHyJZQIqO5PIfIllAexSuR+FiWUB7FK5H4WJZQHsUrkfhYllALpCg+DGiWUAukKD4MaJZQC6QoPgxollAZmZmZmYGZEBmZmZmZgZkQGZmZmZmBmRAsp3vp8b/ZUCyne+nxv9lQLKd76fG/2VAWvW52ooNaUBa9bnaig1pQFr1udqKDWlAWvW52oodaUBa9bnaih1pQFr1udqKHWlA"},"shape":[27],"dtype":"float64","order":"little"}],["intensity",{"type":"ndarray","array":{"type":"bytes","data":"AAAAAAoAAAAAAAAAAAAAABQAAAAAAAAAAAAAABkAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAYAAAAAAAAAAAAAAA0AAAAAAAAAAAAAAAgAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAcAAAAAAAAA"},"shape":[27],"dtype":"int32","order":"little"}]]}}},"view":{"type":"object","name":"CDSView","id":"p1497","attributes":{"filter":{"type":"object","name":"AllIndices","id":"p1498"}}},"glyph":{"type":"object","name":"Line","id":"p1493","attributes":{"x":{"type":"field","field":"mz"},"y":{"type":"field","field":"intensity"},"line_color":"#4575B4"}},"nonselection_glyph":{"type":"object","name":"Line","id":"p1494","attributes":{"x":{"type":"field","field":"mz"},"y":{"type":"field","field":"intensity"},"line_color":"#4575B4","line_alpha":0.1}},"muted_glyph":{"type":"object","name":"Line","id":"p1495","attributes":{"x":{"type":"field","field":"mz"},"y":{"type":"field","field":"intensity"},"line_color":"#4575B4","line_alpha":0.2}}}}],"toolbar":{"type":"object","name":"Toolbar","id":"p1463","attributes":{"tools":[{"type":"object","name":"PanTool","id":"p1476"},{"type":"object","name":"WheelZoomTool","id":"p1477","attributes":{"renderers":"auto"}},{"type":"object","name":"BoxZoomTool","id":"p1478","attributes":{"overlay":{"type":"object","name":"BoxAnnotation","id":"p1479","attributes":{"syncable":false,"level":"overlay","visible":false,"left":{"type":"number","value":"nan"},"right":{"type":"number","value":"nan"},"top":{"type":"number","value":"nan"},"bottom":{"type":"number","value":"nan"},"left_units":"canvas","right_units":"canvas","top_units":"canvas","bottom_units":"canvas","line_color":"black","line_alpha":1.0,"line_width":2,"line_dash":[4,4],"fill_color":"lightgrey","fill_alpha":0.5}}}},{"type":"object","name":"SaveTool","id":"p1484"},{"type":"object","name":"ResetTool","id":"p1485"},{"type":"object","name":"HelpTool","id":"p1486"},{"type":"object","name":"HoverTool","id":"p1499","attributes":{"renderers":"auto","tooltips":[["m/z","@mz"],["intensity","@intensity"],["native id","@native_id"]]}}]}},"toolbar_location":"above","left":[{"type":"object","name":"LinearAxis","id":"p1471","attributes":{"ticker":{"type":"object","name":"BasicTicker","id":"p1472","attributes":{"mantissas":[1,2,5]}},"formatter":{"type":"object","name":"BasicTickFormatter","id":"p1473"},"axis_label":"Intensity","axis_label_text_font_size":"16pt","major_label_policy":{"type":"object","name":"AllLabels","id":"p1474"},"major_label_text_font_size":"14pt"}}],"below":[{"type":"object","name":"LinearAxis","id":"p1466","attributes":{"ticker":{"type":"object","name":"BasicTicker","id":"p1467","attributes":{"mantissas":[1,2,5]}},"formatter":{"type":"object","name":"BasicTickFormatter","id":"p1468"},"axis_label":"mass-to-charge","axis_label_text_font_size":"16pt","major_label_policy":{"type":"object","name":"AllLabels","id":"p1469"},"major_label_text_font_size":"14pt"}}],"center":[{"type":"object","name":"Grid","id":"p1470","attributes":{"axis":{"id":"p1466"}}},{"type":"object","name":"Grid","id":"p1475","attributes":{"dimension":1,"axis":{"id":"p1471"}}},{"type":"object","name":"Label","id":"p1500","attributes":{"text":"100.5332","text_color":"black","text_font_size":"13pt","x":100.5332,"y":25,"x_offset":1}},{"type":"object","name":"Label","id":"p1501","attributes":{"text":"74.1324","text_color":"black","text_font_size":"13pt","x":74.1324,"y":20,"x_offset":1}},{"type":"object","name":"Label","id":"p1502","attributes":{"text":"200.4232","text_color":"black","text_font_size":"13pt","x":200.4232,"y":17,"x_offset":1}},{"type":"object","name":"Label","id":"p1503","attributes":{"text":"160.2","text_color":"black","text_font_size":"13pt","x":160.2,"y":13,"x_offset":1}},{"type":"object","name":"Label","id":"p1504","attributes":{"text":"101.545","text_color":"black","text_font_size":"13pt","x":101.545,"y":12,"x_offset":1}},{"type":"object","name":"Span","id":"p1505","attributes":{"location":0,"line_color":"#EEEEEE","line_width":2}}],"min_border":0}}]}}
</script>
<script type="text/javascript">
(function() {
const fn = function() {
Bokeh.safely(function() {
(function(root) {
function embed_document(root) {
const docs_json = document.getElementById('cbb7b83d-4a93-4e1d-b75f-64f7c08ed51d').textContent;
const render_items = [{"docid":"4678ae8c-f3e7-4997-a76d-6ff7e04d46ee","roots":{"p1454":"d5627e74-de6b-4c45-9b4e-869ae55955b5"},"root_ids":["p1454"]}];
root.Bokeh.embed.embed_items(docs_json, render_items);
}
if (root.Bokeh !== undefined) {
embed_document(root);
} else {
let attempts = 0;
const timer = setInterval(function(root) {
if (root.Bokeh !== undefined) {
clearInterval(timer);
embed_document(root);
} else {
attempts++;
if (attempts > 100) {
clearInterval(timer);
console.log("Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing");
}
}
}, 10, root)
}
})(window);
});
};
if (document.readyState != "loading") fn();
else document.addEventListener("DOMContentLoaded", fn);
})();
</script>
</body>
</html> No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for native_id references in Bokeh snapshot files
echo "=== Files containing native_id ===" 
find test/__snapshots__ -name "*.raw" -type f -exec grep -l "native_id" {} \; 2>/dev/null

echo -e "\n=== Checking this specific snapshot for native_id in data ===" 
file_path="test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs0].raw"
if [ -f "$file_path" ]; then
  grep -o '"data":{[^}]*}' "$file_path" | grep -q "native_id" && echo "native_id found in data" || echo "native_id NOT found in data"
  # Check HoverTool references
  grep -o '"tooltips":\[\[.*\]\]' "$file_path"
else
  echo "File not found at: $file_path"
fi

echo -e "\n=== Checking other spectrum Bokeh snapshots ===" 
ls -la test/__snapshots__/test_spectrum/*bokeh*.raw 2>/dev/null | wc -l

Repository: OpenMS/pyopenms_viz

Length of output: 1328


🏁 Script executed:

#!/bin/bash
# Check if ANY of the snapshot files have native_id in their ColumnDataSource data
echo "=== Checking if native_id appears in data within any snapshot ===" 
for file in test/__snapshots__/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs0].raw test/__snapshots__/test_spectrum/test_mirror_spectrum[ms_bokeh].raw; do
  if [ -f "$file" ]; then
    echo "File: $(basename $file)"
    # Extract the data entries section and check for native_id
    grep -o '"data":{[^}]*}' "$file" | head -c 500
    echo ""
    if grep -o '"data":{[^}]*}' "$file" | grep -q "native_id"; then
      echo "✓ native_id found in data"
    else
      echo "✗ native_id NOT found in data"
    fi
    echo ""
  fi
done

echo "=== Summary: Checking all 15 Bokeh snapshots for native_id in data ===" 
count=0
for file in test/__snapshots__/test_spectrum/*bokeh*.raw; do
  if grep -o '"data":{[^}]*}' "$file" 2>/dev/null | grep -q "native_id"; then
    ((count++))
  fi
done
echo "Snapshots with native_id in data: $count / 15"

Repository: OpenMS/pyopenms_viz

Length of output: 1106


🏁 Script executed:

#!/bin/bash
# Find the source code that generates these Bokeh visualizations
echo "=== Finding Python files that generate Bokeh plots ===" 
find . -name "*.py" -type f | xargs grep -l "native_id" 2>/dev/null | grep -v __pycache__ | head -20

echo -e "\n=== Check if native_id is part of the spectrum data structure ===" 
find . -name "*.py" -type f | xargs grep -l "native.id\|native_id" 2>/dev/null | grep -v __pycache__ | grep -v test

echo -e "\n=== Look for HoverTool configuration with native_id ===" 
find . -name "*.py" -type f | xargs grep -B5 -A5 "native_id" 2>/dev/null | head -40

Repository: OpenMS/pyopenms_viz

Length of output: 2440


🏁 Script executed:

#!/bin/bash
# Find the test file that generates this snapshot
echo "=== Finding test file for spectrum_binning ===" 
find . -name "*.py" -path "*/test*" -type f | xargs grep -l "test_spectrum_binning" 2>/dev/null | head -5

# Look at the test file to understand the test data
echo -e "\n=== Examining test file structure ===" 
test_file=$(find . -name "*.py" -path "*/test*" -type f | xargs grep -l "test_spectrum_binning" 2>/dev/null | head -1)
if [ -n "$test_file" ]; then
  echo "Test file: $test_file"
  wc -l "$test_file"
fi

# Check what the actual test data contains
echo -e "\n=== Looking for spectrum test data initialization ===" 
find . -name "*.py" -path "*/test*" -type f | xargs grep -A10 "def test_spectrum" 2>/dev/null | head -60

Repository: OpenMS/pyopenms_viz

Length of output: 2868


🏁 Script executed:

#!/bin/bash
# Look at the test file to find the spectrum_data fixture
echo "=== Finding spectrum_data fixture ===" 
grep -n "spectrum_data\|@pytest.fixture" ./test/test_spectrum.py | head -30

echo -e "\n=== Examining full test file ===" 
cat -n ./test/test_spectrum.py | head -60

echo -e "\n=== Looking for conftest.py with fixtures ===" 
find ./test -name "conftest.py" -type f | xargs cat 2>/dev/null

Repository: OpenMS/pyopenms_viz

Length of output: 5966


🏁 Script executed:

#!/bin/bash
# Check the actual test data file
echo "=== Looking for TestSpectrumDf.tsv ===" 
find ./test -name "TestSpectrumDf.tsv" -type f

echo -e "\n=== Reading the test data file ===" 
test_data_file=$(find ./test -name "TestSpectrumDf.tsv" -type f)
if [ -f "$test_data_file" ]; then
  echo "File: $test_data_file"
  head -5 "$test_data_file"
  echo "..."
  echo "Columns:"
  head -1 "$test_data_file" | tr '\t' '\n' | nl
fi

echo -e "\n=== Checking _core.py to see how native_id is handled ===" 
grep -B5 -A15 'entries = {"m/z": x, "intensity": y}' ./pyopenms_viz/_core.py

Repository: OpenMS/pyopenms_viz

Length of output: 1691


🏁 Script executed:

#!/bin/bash
# Check the columns in the test data properly
echo "=== Test data columns ===" 
head -1 ./test/test_data/TestSpectrumDf.tsv | tr '\t' '\n' | cat -n

# Now check how Bokeh backend creates the ColumnDataSource
echo -e "\n=== Searching for Bokeh ColumnDataSource creation ===" 
find ./pyopenms_viz -name "*.py" -type f | xargs grep -l "ColumnDataSource" 2>/dev/null

echo -e "\n=== Checking Bokeh backend code ===" 
find ./pyopenms_viz -name "*bokeh*" -type f

echo -e "\n=== Looking at bokeh implementation ===" 
find ./pyopenms_viz -name "*.py" | xargs grep -B3 -A10 "ColumnDataSource\|custom_hover_data" 2>/dev/null | head -80

Repository: OpenMS/pyopenms_viz

Length of output: 4878


🏁 Script executed:

#!/bin/bash
# Now let's look at how the Bokeh backend builds the ColumnDataSource data
echo "=== Full bokeh core.py to understand data source creation ===" 
wc -l ./pyopenms_viz/_bokeh/core.py

echo -e "\n=== Looking for where plot() method adds data ===" 
grep -n "def plot\|ColumnDataSource\|self.fig.line\|source=" ./pyopenms_viz/_bokeh/core.py | head -30

echo -e "\n=== Searching for where custom_hover_data is used ===" 
grep -B10 -A10 "custom_hover_data" ./pyopenms_viz/_bokeh/core.py

echo -e "\n=== Check spectrum-specific implementation ===" 
find ./pyopenms_viz/_bokeh -name "*.py" | xargs grep -l "spectrum"

Repository: OpenMS/pyopenms_viz

Length of output: 3951


🏁 Script executed:

#!/bin/bash
# Let's look at the spectrum-specific implementation more carefully
echo "=== Spectrum implementation in bokeh ===" 
grep -n "class.*Spectrum\|def plot" ./pyopenms_viz/_bokeh/core.py | head -20

echo -e "\n=== Looking at how self.data is constructed in spectrum ===" 
sed -n '289,350p' ./pyopenms_viz/_bokeh/core.py

echo -e "\n=== Let's look at the parent class to understand data flow ===" 
grep -n "class.*Spectrum" ./pyopenms_viz/_core.py

echo -e "\n=== Checking if there's any column dropping in spectrum ===" 
grep -B5 -A15 "class.*Spectrum" ./pyopenms_viz/_core.py | head -80

Repository: OpenMS/pyopenms_viz

Length of output: 3364


🏁 Script executed:

#!/bin/bash
# Let's trace how custom_hover_data should be used
echo "=== Checking how custom_hover_data is passed to ColumnDataSource ===" 
grep -n "custom_hover_data" ./pyopenms_viz/_bokeh/core.py

echo -e "\n=== Checking the base class to understand custom_hover_data intent ===" 
grep -B10 -A10 "def _create_tooltips" ./pyopenms_viz/_core.py

echo -e "\n=== Looking at how other backends use custom_hover_data ===" 
find ./pyopenms_viz -name "*.py" | xargs grep -n "custom_hover_data" 2>/dev/null | grep -v "^Binary"

Repository: OpenMS/pyopenms_viz

Length of output: 5433


🏁 Script executed:

#!/bin/bash
# Key question: Is native_id being filtered out of self.data before Bokeh plotting?
echo "=== Checking if there's any column filtering in spectrum plot ===" 
grep -B20 -A30 "class BOKEHSpectrumPlot" ./pyopenms_viz/_bokeh/core.py

echo -e "\n=== Let's check if self.data is modified before ColumnDataSource ===" 
sed -n '538,650p' ./pyopenms_viz/_bokeh/core.py

echo -e "\n=== Check if Bokeh's ColumnDataSource should auto-include all dataframe columns ===" 
grep -B5 -A15 "def plot.*:" ./pyopenms_viz/_bokeh/core.py | head -60

Repository: OpenMS/pyopenms_viz

Length of output: 7317


🏁 Script executed:

#!/bin/bash
# Let's check what _create_tooltips returns and how it's supposed to be used
echo "=== Full _create_tooltips implementation ===" 
grep -B5 -A50 "def _create_tooltips" ./pyopenms_viz/_core.py | head -100

echo -e "\n=== Checking how Plotly uses custom_hover_data ===" 
sed -n '142,180p' ./pyopenms_viz/_plotly/core.py

echo -e "\n=== Checking if Bokeh should use custom_hover_data but doesn't ===" 
sed -n '107,120p' ./pyopenms_viz/_bokeh/core.py

Repository: OpenMS/pyopenms_viz

Length of output: 4151


🏁 Script executed:

#!/bin/bash
# Key insight: Bokeh creates ColumnDataSource from self.data, which should include all columns
# Let's verify if self.data actually contains all columns including native_id

echo "=== Checking spectrum plot flow in _core.py ===" 
sed -n '770,810p' ./pyopenms_viz/_core.py

echo -e "\n=== What data is passed to the plot ===" 
grep -B20 "spectrumPlot.generate" ./pyopenms_viz/_core.py | head -40

echo -e "\n=== Check if native_id gets dropped somewhere ===" 
grep -n "drop\|select\|columns" ./pyopenms_viz/_core.py | grep -i spectrum

Repository: OpenMS/pyopenms_viz

Length of output: 3050


🏁 Script executed:

#!/bin/bash
# The key is convert_for_line_plots - let's check what it does
echo "=== Finding convert_for_line_plots method ===" 
grep -n "def convert_for_line_plots" ./pyopenms_viz/_core.py

echo -e "\n=== Full convert_for_line_plots implementation ===" 
grep -B3 -A30 "def convert_for_line_plots" ./pyopenms_viz/_core.py

echo -e "\n=== Check if this method only keeps x and y columns ===" 
sed -n '648,710p' ./pyopenms_viz/_core.py

Repository: OpenMS/pyopenms_viz

Length of output: 3781


🏁 Script executed:

#!/bin/bash
# Let's confirm that native_id SHOULD be included in the tooltip but the data pipeline removes it
echo "=== Checking the tooltip creation for spectrum ===" 
sed -n '1090,1115p' ./pyopenms_viz/_core.py

echo -e "\n=== Verify: native_id is explicitly checked but data was stripped ===" 
grep -B2 "native_id" ./pyopenms_viz/_core.py | head -20

Repository: OpenMS/pyopenms_viz

Length of output: 1509


The HoverTool tooltip references @native_id but the data will be missing from ColumnDataSource.

The test data includes a native_id column, and the spectrum plotting code explicitly checks for and includes it in tooltips (if present). However, convert_for_line_plots() strips all columns except x, y, and the grouping column before creating the Bokeh ColumnDataSource, removing native_id and other metadata columns. This causes the tooltip to reference a field that won't exist in the data, resulting in empty hover values for "native id".

This pattern affects all 15 Bokeh spectrum snapshots. The fix requires either:

  1. Preserving metadata columns (like native_id) through the line plot conversion, or
  2. Removing native_id from the tooltip entries if it won't be available in the final data source
🤖 Prompt for AI Agents
In `@test/__snapshots__/test_spectrum/test_spectrum_binning`[ms_bokeh-kwargs0].raw
around lines 1 - 61, convert_for_line_plots() is currently stripping all columns
except x, y and group before building the Bokeh ColumnDataSource, which removes
metadata like native_id referenced by the HoverTool tooltips; update
convert_for_line_plots() (or the helper that builds the ColumnDataSource) to
preserve any non-index metadata columns (e.g., native_id) when present so the
ColumnDataSource contains x, y, group plus metadata fields, or alternatively
remove native_id from the HoverTool tooltips construction (where HoverTool is
created) so tooltips only reference fields guaranteed to exist.

Comment on lines +8 to +15
"x": {
"dtype": "f8",
"bdata": "O99PjZd+SUA730+Nl35JQDvfT42XfklAY3\u002fZPXmIUkBjf9k9eYhSQGN\u002f2T15iFJAio7k8h8iWUCKjuTyHyJZQIqO5PIfIllAexSuR+FiWUB7FK5H4WJZQHsUrkfhYllALpCg+DGiWUAukKD4MaJZQC6QoPgxollAZmZmZmYGZEBmZmZmZgZkQGZmZmZmBmRAsp3vp8b\u002fZUCyne+nxv9lQLKd76fG\u002f2VAWvW52ooNaUBa9bnaig1pQFr1udqKDWlAWvW52oodaUBa9bnaih1pQFr1udqKHWlA"
},
"y": {
"dtype": "i1",
"bdata": "AAoAABQAABkAAAwAAAYAAA0AAAgAABEAAAcA"
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

python3 << 'EOF'
import base64
import struct

# From the snapshot
x_bdata = "O99PjZd+SUA730+Nl35JQDvfT42XfklAY3/ZPXmIUkBjf9k9eYhSQGN/2T15iFJAio7k8h8iWUCKjuTyHyJZQIqO5PIfIllAexSuR+FiWUB7FK5H4WJZQHsUrkfhYllALpCg+DGiWUAukKD4MaJZQC6QoPgxollAZmZmZmYGZEBmZmZmZgZkQGZmZmZmBmRAsp3vp8b/ZUCyne+nxv9lQLKd76fG/2VAWvW52ooNaUBa9bnaig1pQFr1udqKDWlAWvW52oodaUBa9bnaih1pQFr1udqKHWlA"
y_bdata = "AAoAABQAABkAAAwAAAYAAA0AAAgAABEAAAcA"

# Decode x data (float64 = 8 bytes each)
x_bytes = base64.b64decode(x_bdata)
x_count = len(x_bytes) // 8
print(f"x data points: {x_count}")

# Decode y data (int8 = 1 byte each)  
y_bytes = base64.b64decode(y_bdata)
y_count = len(y_bytes)
print(f"y data bytes: {y_count}")

# Print decoded y values to see actual content
y_values = struct.unpack('b' * y_count, y_bytes)
print(f"y values: {y_values}")

print(f"\nExpected customdata entries: 9")
print(f"Match: {x_count == 9 and y_count == 9}")
EOF

Repository: OpenMS/pyopenms_viz

Length of output: 149


🏁 Script executed:

# Check if the snapshot file exists and get its context
find . -name "test_spectrum_binning*.raw" -type f | head -20

Repository: OpenMS/pyopenms_viz

Length of output: 1277


🏁 Script executed:

# Read the snapshot file to examine the actual structure
head -n 100 "./test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs0].raw"

Repository: OpenMS/pyopenms_viz

Length of output: 2288


🏁 Script executed:

# Extract just the x and y bdata values and decode them
python3 << 'EOFINLINE'
import base64
import json

# Read the snapshot file
with open("./test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs0].raw", "r") as f:
    content = f.read()

# Find the x and y data sections
# Extract bdata strings
import re
x_match = re.search(r'"x":\s*\{\s*"dtype":\s*"f8",\s*"bdata":\s*"([^"]+)"', content)
y_match = re.search(r'"y":\s*\{\s*"dtype":\s*"i1",\s*"bdata":\s*"([^"]+)"', content)

if x_match and y_match:
    x_bdata = x_match.group(1)
    y_bdata = y_match.group(1)
    
    # Decode
    x_bytes = base64.b64decode(x_bdata)
    y_bytes = base64.b64decode(y_bdata)
    
    x_count = len(x_bytes) // 8  # float64
    y_count = len(y_bytes)        # int8
    
    print(f"x data points (float64): {x_count}")
    print(f"y data points (int8): {y_count}")
    print(f"Data aligned: {x_count == y_count}")
else:
    print("Could not find x/y bdata in file")
EOFINLINE

Repository: OpenMS/pyopenms_viz

Length of output: 135


Critical: Binary-encoded x/y data is misaligned.

The x/y data in binary format does not align. Decoding reveals 28 float64 x-values but only 27 int8 y-values. Plotly requires matching array lengths for x and y coordinates to render correctly. This mismatch will cause rendering errors. Additionally, neither count (28/27) matches the 9 customdata entries, indicating a broader data structure problem that needs resolution.

🤖 Prompt for AI Agents
In
`@test/__snapshots__/test_spectrum/test_spectrum_binning`[ms_plotly-kwargs0].raw
around lines 8 - 15, The snapshot contains misaligned binary arrays: the "x"
field (dtype f8) decodes to 28 values while "y" (dtype i1) decodes to 27, and
neither matches the 9 entries in "customdata" in test_spectrum_binning; fix by
ensuring the generator producing these arrays creates matching lengths—make "x"
and "y" the same length and align them with the number of "customdata" entries
(e.g., adjust the data-producing code in the test or fixture so "x", "y" and
"customdata" all have the same count), then re-encode the bdata for "x" and "y"
to reflect the corrected arrays and update the snapshot for
test_spectrum_binning.

@@ -12,25 +12,25 @@
padding: 0;
}
</style>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-3.6.0.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-3.4.1.min.js"></script>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check Bokeh version specified in project dependencies
echo "=== Checking pyproject.toml ===" 
fd -t f "pyproject.toml" -x cat {}

echo -e "\n=== Checking requirements files ===" 
fd -g "requirements*.txt" -x cat {}

echo -e "\n=== Checking setup.py/setup.cfg ===" 
fd -t f \( -name "setup.py" -o -name "setup.cfg" \) -x cat {}

echo -e "\n=== Checking poetry.lock or pip.lock ===" 
fd -t f \( -name "poetry.lock" -o -name "pip.lock" \) -x head -100 {}

Repository: OpenMS/pyopenms_viz

Length of output: 13825


Regenerate snapshot with current Bokeh version.

The snapshot references Bokeh 3.4.1, but the project's resolved dependencies use Bokeh 3.6.0 (requirements.txt) or 3.6.2 (docs/requirements.txt). Regenerate this snapshot with the current environment to ensure it reflects the actual tested behavior.

🤖 Prompt for AI Agents
In
`@test/__snapshots__/test_spectrum/test_spectrum_plot_with_peak_color`[ms_bokeh-kwargs1].raw
at line 15, The stored snapshot references Bokeh 3.4.1 in the script tag and
must be updated to match the current test environment (Bokeh 3.6.x); regenerate
the snapshot for test_spectrum_plot_with_peak_color[ms_bokeh-kwargs1] so the
script src reflects the resolved Bokeh version (e.g., update the <script ...
bokeh-3.4.1.min.js> entry to bokeh-3.6.x.min.js) by re-running the snapshot test
or snapshot update command to produce the new expected output.

Copilot AI review requested due to automatic review settings January 16, 2026 16:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 50 out of 99 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pyopenms_viz/testing/MatplotlibSnapshotExtension.py (1)

40-60: Update serialize docstring and silence unused kwargs.

The return type is now bytes and Ruff flags kwargs as unused.

📝 Suggested fix
-    def serialize(self, data: SerializableData, **kwargs: Any) -> bytes:
+    def serialize(self, data: SerializableData, **kwargs: Any) -> bytes:  # noqa: ARG002
@@
-        Returns:
-            str: Image object
+        Returns:
+            bytes: PNG bytes
🤖 Fix all issues with AI agents
In `@pyopenms_viz/testing/BokehSnapshotExtension.py`:
- Line 142: The serialize method is currently leaving **kwargs unused which
triggers Ruff ARG002; update the method signature in BokehSnapshotExtension.py
(serialize(self, data: SerializableData, **kwargs: Any) -> bytes) to silence the
warning by marking the var as intentionally unused—e.g., change **kwargs to
**_kwargs or explicitly reference kwargs in the body with a no-op assignment
like _ = kwargs or add a noqa ARG002 comment—so the signature stays compatible
but Ruff no longer flags it.

In `@requirements-dev.txt`:
- Around line 1-2: Update the pytest requirement to ensure compatibility with
syrupy>=5.0.0 by changing the pytest entry in requirements-dev.txt from "pytest"
to "pytest>=8"; this guarantees pytest version >=8 for the "pytest" dependency
to satisfy "syrupy>=5.0.0".

In `@test/__snapshots__/test_spectrum/test_spectrum_plot`[ms_bokeh-kwargs0].html:
- Line 15: The snapshot HTML contains a hardcoded script tag referencing
bokeh-3.4.1; update the test snapshot for
test_spectrum_plot[ms_bokeh-kwargs0].html to reference the project's pinned
bokeh version (bokeh-3.6.0) by regenerating the snapshot (re-run the snapshot
generation/test that produces this file) so the embedded script src becomes
https://cdn.bokeh.org/bokeh/release/bokeh-3.6.0.min.js and the test artifacts
match requirements.txt.

In `@test/__snapshots__/test_spectrum/test_spectrum_plot`[ms_bokeh-kwargs2].html:
- Line 15: The snapshot HTML references "bokeh-3.4.1.min.js" but locked deps
require bokeh 3.6.x; re-generate the failing snapshot file (the snapshot named
test_spectrum_plot[ms_bokeh-kwargs2].html that contains the script tag
"bokeh-3.4.1.min.js") in an environment with the pinned bokeh version (install
bokeh==3.6.0 or 3.6.2 per requirements.txt / docs/requirements.txt), run the
test that creates the Bokeh output to update the snapshot (so the script tag
becomes the correct bokeh-3.6.x URL), and commit the regenerated snapshot.
Ensure your test environment uses the same dependency resolution as CI to avoid
future mismatches.
♻️ Duplicate comments (2)
test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs0].json (1)

8-15: Verify binary-encoded x/y data alignment.

A previous review flagged potential misalignment between x (float64) and y (int8) binary arrays. The customdata array now contains 27 entries (9 unique peaks × 3 points each for stick-plot rendering). Please verify that both x and y decode to exactly 27 values to match.

#!/bin/bash
# Verify binary data alignment in the snapshot

python3 << 'EOF'
import base64
import json

# Read the snapshot file
with open("test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs0].json", "r") as f:
    data = json.load(f)

trace = data["data"][0]

# Decode x (float64 = 8 bytes each)
x_bdata = trace["x"]["bdata"]
x_bytes = base64.b64decode(x_bdata)
x_count = len(x_bytes) // 8

# Decode y (int8 = 1 byte each)
y_bdata = trace["y"]["bdata"]
y_bytes = base64.b64decode(y_bdata)
y_count = len(y_bytes)

# Count customdata entries
customdata_count = len(trace["customdata"])

print(f"x data points (float64): {x_count}")
print(f"y data points (int8): {y_count}")
print(f"customdata entries: {customdata_count}")
print(f"\nAll arrays aligned: {x_count == y_count == customdata_count}")

if x_count != y_count or x_count != customdata_count:
    print("\n⚠️  MISMATCH DETECTED - Arrays must have equal lengths for Plotly to render correctly")
else:
    print("\n✓ Data alignment verified")
EOF
test/__snapshots__/test_spectrum/test_mirror_spectrum[ms_plotly].json (1)

566-577: LGTM on the scattermap template addition.

Consistent with the other Plotly snapshot updates in this PR.

🧹 Nitpick comments (3)
requirements.txt (1)

269-270: Consider re-freezing the pip-compiled requirements.

Line 269 leaves syrupy as a range in an auto-generated lockfile, which can make environments non-reproducible. If unintentional, re-run pip-compile so this file pins an exact version (or update the header if ranges are intentional).

pyopenms_viz/testing/PlotlySnapshotExtension.py (1)

47-68: Consider removing debug print statements.

The compare_json method contains multiple print() statements that output comparison details. While useful for debugging, these may clutter test output in CI/CD pipelines. Consider using logging at DEBUG level or removing them entirely if not needed for diagnostics.

♻️ Suggested refactor using logging
+import logging
+
+logger = logging.getLogger(__name__)
+
 class PlotlySnapshotExtension(SingleFileSnapshotExtension):
     ...
     `@staticmethod`
     def compare_json(json1, json2) -> bool:
         ...
         if isinstance(json1, dict) and isinstance(json2, dict):
             for key in json1.keys():
                 if key not in json2:
-                    print(f"Key {key} not in second json")
+                    logger.debug(f"Key {key} not in second json")
                     return False
                 if not PlotlySnapshotExtension.compare_json(json1[key], json2[key]):
-                    print(f"Values for key {key} not equal")
+                    logger.debug(f"Values for key {key} not equal")
                     return False
             return True
         elif isinstance(json1, list) and isinstance(json2, list):
             if len(json1) != len(json2):
-                print("Lists have different lengths")
+                logger.debug("Lists have different lengths")
                 return False
             ...
         else:
             if isinstance(json1, float):
                 if not math.isclose(json1, json2):
-                    print(f"Values not equal: {json1} != {json2}")
+                    logger.debug(f"Values not equal: {json1} != {json2}")
                     return False
             else:
                 if json1 != json2:
-                    print(f"Values not equal: {json1} != {json2}")
+                    logger.debug(f"Values not equal: {json1} != {json2}")
                     return False
             return True
pyopenms_viz/testing/MatplotlibSnapshotExtension.py (1)

19-38: Avoid broad Exception when decoding PNG bytes.

Catching all exceptions can mask corrupted PNGs; prefer explicit Image type checks to fail fast on invalid bytes.

♻️ Suggested refactor
-        try:
-            serialized_img = Image.open(BytesIO(serialized_data))
-        except Exception:
-            # If already an Image object, use it directly
-            serialized_img = serialized_data
+        if isinstance(serialized_data, Image.Image):
+            serialized_img = serialized_data
+        else:
+            serialized_img = Image.open(BytesIO(serialized_data))
 
-        try:
-            snapshot_img = Image.open(BytesIO(snapshot_data))
-        except Exception:
-            snapshot_img = snapshot_data
+        if isinstance(snapshot_data, Image.Image):
+            snapshot_img = snapshot_data
+        else:
+            snapshot_img = Image.open(BytesIO(snapshot_data))
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9b29854 and a827ddd.

⛔ Files ignored due to path filters (22)
  • test/__snapshots__/test_chromatogram/test_chromatogram_plot[ms_matplotlib-kwargs0].png is excluded by !**/*.png
  • test/__snapshots__/test_chromatogram/test_chromatogram_plot[ms_matplotlib-kwargs3].png is excluded by !**/*.png
  • test/__snapshots__/test_chromatogram/test_chromatogram_plot[ms_matplotlib-kwargs4].png is excluded by !**/*.png
  • test/__snapshots__/test_mobilogram/test_mobilogram_plot[ms_matplotlib-kwargs0].png is excluded by !**/*.png
  • test/__snapshots__/test_mobilogram/test_mobilogram_plot[ms_matplotlib-kwargs3].png is excluded by !**/*.png
  • test/__snapshots__/test_peakmap3d/test_peakmap_plot[ms_matplotlib-kwargs0].png is excluded by !**/*.png
  • test/__snapshots__/test_peakmap3d/test_peakmap_plot[ms_matplotlib-kwargs2].png is excluded by !**/*.png
  • test/__snapshots__/test_spectrum/test_mirror_spectrum[ms_matplotlib].png is excluded by !**/*.png
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_matplotlib-kwargs0].png is excluded by !**/*.png
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_matplotlib-kwargs1].png is excluded by !**/*.png
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_matplotlib-kwargs2].png is excluded by !**/*.png
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_matplotlib-kwargs3].png is excluded by !**/*.png
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_matplotlib-kwargs4].png is excluded by !**/*.png
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_matplotlib-kwargs0].png is excluded by !**/*.png
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_matplotlib-kwargs1].png is excluded by !**/*.png
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_matplotlib-kwargs2].png is excluded by !**/*.png
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_matplotlib-kwargs3].png is excluded by !**/*.png
  • test/__snapshots__/test_spectrum/test_spectrum_plot_with_peak_color[ms_matplotlib-kwargs0].png is excluded by !**/*.png
  • test/__snapshots__/test_spectrum/test_spectrum_plot_with_peak_color[ms_matplotlib-kwargs1].png is excluded by !**/*.png
  • test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_matplotlib-kwargs0].png is excluded by !**/*.png
  • test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_matplotlib-kwargs1].png is excluded by !**/*.png
  • test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_matplotlib-kwargs2].png is excluded by !**/*.png
📒 Files selected for processing (75)
  • pyopenms_viz/testing/BokehSnapshotExtension.py
  • pyopenms_viz/testing/MatplotlibSnapshotExtension.py
  • pyopenms_viz/testing/PlotlySnapshotExtension.py
  • pyproject.toml
  • requirements-dev.txt
  • requirements.txt
  • test/__snapshots__/test_chromatogram/test_chromatogram_plot[ms_bokeh-kwargs0].html
  • test/__snapshots__/test_chromatogram/test_chromatogram_plot[ms_bokeh-kwargs1].html
  • test/__snapshots__/test_chromatogram/test_chromatogram_plot[ms_bokeh-kwargs2].html
  • test/__snapshots__/test_chromatogram/test_chromatogram_plot[ms_bokeh-kwargs3].html
  • test/__snapshots__/test_chromatogram/test_chromatogram_plot[ms_bokeh-kwargs4].html
  • test/__snapshots__/test_chromatogram/test_chromatogram_plot[ms_plotly-kwargs0].json
  • test/__snapshots__/test_chromatogram/test_chromatogram_plot[ms_plotly-kwargs1].json
  • test/__snapshots__/test_chromatogram/test_chromatogram_plot[ms_plotly-kwargs2].json
  • test/__snapshots__/test_chromatogram/test_chromatogram_plot[ms_plotly-kwargs3].json
  • test/__snapshots__/test_chromatogram/test_chromatogram_plot[ms_plotly-kwargs4].json
  • test/__snapshots__/test_chromatogram/test_chromatogram_with_annotation[ms_bokeh-kwargs0].html
  • test/__snapshots__/test_chromatogram/test_chromatogram_with_annotation[ms_plotly-kwargs0].json
  • test/__snapshots__/test_mobilogram/test_mobilogram_plot[ms_bokeh-kwargs0].html
  • test/__snapshots__/test_mobilogram/test_mobilogram_plot[ms_bokeh-kwargs1].html
  • test/__snapshots__/test_mobilogram/test_mobilogram_plot[ms_bokeh-kwargs2].html
  • test/__snapshots__/test_mobilogram/test_mobilogram_plot[ms_bokeh-kwargs3].html
  • test/__snapshots__/test_mobilogram/test_mobilogram_plot[ms_plotly-kwargs0].json
  • test/__snapshots__/test_mobilogram/test_mobilogram_plot[ms_plotly-kwargs1].json
  • test/__snapshots__/test_mobilogram/test_mobilogram_plot[ms_plotly-kwargs2].json
  • test/__snapshots__/test_mobilogram/test_mobilogram_plot[ms_plotly-kwargs3].json
  • test/__snapshots__/test_peakmap/test_peakmap_mz_im[ms_bokeh].html
  • test/__snapshots__/test_peakmap/test_peakmap_mz_im[ms_plotly].json
  • test/__snapshots__/test_peakmap/test_peakmap_plot[ms_bokeh-kwargs0].html
  • test/__snapshots__/test_peakmap/test_peakmap_plot[ms_bokeh-kwargs1].html
  • test/__snapshots__/test_peakmap/test_peakmap_plot[ms_bokeh-kwargs2].html
  • test/__snapshots__/test_peakmap/test_peakmap_plot[ms_bokeh-kwargs3].html
  • test/__snapshots__/test_peakmap/test_peakmap_plot[ms_bokeh-kwargs4].html
  • test/__snapshots__/test_peakmap/test_peakmap_plot[ms_plotly-kwargs0].json
  • test/__snapshots__/test_peakmap/test_peakmap_plot[ms_plotly-kwargs1].json
  • test/__snapshots__/test_peakmap/test_peakmap_plot[ms_plotly-kwargs2].json
  • test/__snapshots__/test_peakmap/test_peakmap_plot[ms_plotly-kwargs3].json
  • test/__snapshots__/test_peakmap/test_peakmap_plot[ms_plotly-kwargs4].json
  • test/__snapshots__/test_peakmap3d/test_peakmap_plot[ms_plotly-kwargs0].json
  • test/__snapshots__/test_peakmap3d/test_peakmap_plot[ms_plotly-kwargs1].json
  • test/__snapshots__/test_peakmap3d/test_peakmap_plot[ms_plotly-kwargs2].json
  • test/__snapshots__/test_peakmap_marginals/test_peakmap_marginals[ms_bokeh].html
  • test/__snapshots__/test_peakmap_marginals/test_peakmap_marginals[ms_plotly].json
  • test/__snapshots__/test_peakmap_marginals/test_peakmap_mz_im[ms_bokeh].html
  • test/__snapshots__/test_peakmap_marginals/test_peakmap_mz_im[ms_plotly].json
  • test/__snapshots__/test_spectrum/test_mirror_spectrum[ms_bokeh].html
  • test/__snapshots__/test_spectrum/test_mirror_spectrum[ms_plotly].json
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs0].html
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs1].html
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs2].html
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs3].html
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs4].html
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs0].json
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs1].json
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs2].json
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs3].json
  • test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs4].json
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs0].html
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs1].html
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs2].html
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs3].html
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_plotly-kwargs0].json
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_plotly-kwargs1].json
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_plotly-kwargs2].json
  • test/__snapshots__/test_spectrum/test_spectrum_plot[ms_plotly-kwargs3].json
  • test/__snapshots__/test_spectrum/test_spectrum_plot_with_peak_color[ms_bokeh-kwargs0].html
  • test/__snapshots__/test_spectrum/test_spectrum_plot_with_peak_color[ms_bokeh-kwargs1].html
  • test/__snapshots__/test_spectrum/test_spectrum_plot_with_peak_color[ms_plotly-kwargs0].json
  • test/__snapshots__/test_spectrum/test_spectrum_plot_with_peak_color[ms_plotly-kwargs1].json
  • test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_bokeh-kwargs0].html
  • test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_bokeh-kwargs1].html
  • test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_bokeh-kwargs2].html
  • test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_plotly-kwargs0].json
  • test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_plotly-kwargs1].json
  • test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_plotly-kwargs2].json
🚧 Files skipped from review as they are similar to previous changes (3)
  • test/snapshots/test_spectrum/test_spectrum_with_annotations[ms_bokeh-kwargs2].html
  • test/snapshots/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs1].html
  • test/snapshots/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs3].html
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-01-16T14:23:00.017Z
Learnt from: DimitriPapadopoulos
Repo: OpenMS/pyopenms_viz PR: 137
File: pyproject.toml:7-10
Timestamp: 2026-01-16T14:23:00.017Z
Learning: When reviewing pyproject.toml files that use PEP 621, the authors array may contain entries with only a name field, only an email field, or both fields, and multiple separate entries are valid. Do not flag consolidation of distinct entries unless there is an explicit intention to merge authors. This guideline applies to all pyproject.toml files in the repository that follow PEP 621 authors structure.

Applied to files:

  • pyproject.toml
🧬 Code graph analysis (2)
pyopenms_viz/testing/BokehSnapshotExtension.py (4)
pyopenms_viz/testing/MatplotlibSnapshotExtension.py (1)
  • serialize (40-60)
pyopenms_viz/testing/PlotlySnapshotExtension.py (1)
  • serialize (72-82)
pyopenms_viz/testing/NumpySnapshotExtension.py (1)
  • serialize (59-60)
pyopenms_viz/testing/PandasSnapshotExtension.py (1)
  • serialize (54-55)
pyopenms_viz/testing/PlotlySnapshotExtension.py (4)
pyopenms_viz/testing/BokehSnapshotExtension.py (2)
  • matches (46-66)
  • serialize (142-152)
pyopenms_viz/testing/MatplotlibSnapshotExtension.py (2)
  • matches (19-38)
  • serialize (40-60)
pyopenms_viz/testing/NumpySnapshotExtension.py (2)
  • matches (21-36)
  • serialize (59-60)
pyopenms_viz/testing/PandasSnapshotExtension.py (2)
  • matches (21-32)
  • serialize (54-55)
🪛 Ruff (0.14.11)
pyopenms_viz/testing/BokehSnapshotExtension.py

142-142: Unused method argument: kwargs

(ARG002)

pyopenms_viz/testing/PlotlySnapshotExtension.py

72-72: Unused method argument: kwargs

(ARG002)

pyopenms_viz/testing/MatplotlibSnapshotExtension.py

23-23: Do not catch blind exception: Exception

(BLE001)


29-29: Do not catch blind exception: Exception

(BLE001)


40-40: Unused method argument: kwargs

(ARG002)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CodeQL analysis (python)
🔇 Additional comments (70)
test/__snapshots__/test_peakmap/test_peakmap_plot[ms_bokeh-kwargs0].html (1)

15-33: Snapshot update only — no review comments.

test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs2].json (2)

8-15: Snapshot binary encoding update looks fine.
This aligns with the new snapshot serialization format.


461-472: Plotly scattermap template addition looks good.
No concerns with the template expansion in snapshots.

test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs4].html (1)

15-15: Snapshot regeneration looks consistent.

No issues noticed in the updated snapshot content.

Also applies to: 21-24, 32-33

test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_bokeh-kwargs0].html (2)

15-15: Verify the Bokeh version downgrade is intentional.

The Bokeh version appears to have changed from 3.6.0 to 3.4.1 based on the AI summary. Please confirm this downgrade is expected—perhaps due to a dependency pin or compatibility requirement—rather than an unintended regression in the test environment.


21-33: Snapshot IDs regenerated as expected.

The UUID-based element IDs and JSON document references have been updated, which is standard behavior when regenerating test snapshots. The Bokeh document structure appears valid with proper spectrum renderers, annotations (Labels), legend items, and HoverTool configuration.

Note: This is a Bokeh backend snapshot, but the PR title references a Plotly annotation fix. Please confirm this snapshot regeneration is expected as part of the changes (e.g., due to syrupy dependency update or test infrastructure changes).

test/__snapshots__/test_peakmap/test_peakmap_plot[ms_plotly-kwargs1].json (2)

7-67: Binary-encoded array updates look consistent.

The snapshot serialization changes for marker colors and x/y arrays appear coherent across traces.

Also applies to: 221-281, 435-495, 649-709, 863-923, 1077-1137, 1291-1351


1896-1907: Template expansion looks fine.

The added template entry aligns with the other trace defaults in the layout.

test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs0].json (2)

17-152: Customdata structure is appropriate for stick plot rendering.

The triplet pattern (each unique peak repeated 3 times) correctly supports the baseline→peak→baseline line segments used in mass spectrum stick plots. The 9 unique m/z values × 3 points = 27 entries aligns with the expected data structure.


551-562: Template addition for scattermap trace type looks correct.

The new scattermap entry follows the established pattern used by other scatter-type traces in the template (consistent colorbar configuration). This appears to be a Plotly template update reflecting newer trace types.

test/__snapshots__/test_spectrum/test_spectrum_plot_with_peak_color[ms_plotly-kwargs0].json (2)

9-16: Binary-encoded arrays look consistent with snapshot serialization.

No issues spotted in these changes.

Also applies to: 196-203


616-627: The concern about Plotly version support for scattermap is not applicable. Plotly 5.24.1 (released September 12, 2024) bundles Plotly.js 3.0.0-rc.0, which includes full support for the scattermap trace type (introduced in Plotly.js v2.35.0 on August 29, 2024). The pinned version specification (==5.24.1) ensures consistent behavior across environments.

Likely an incorrect or invalid review comment.

test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs1].json (3)

8-15: Binary x/y snapshot encoding looks fine.


506-517: Template update for scattermap is OK.


17-107: customdata length aligns with decoded x/y points.
The customdata array (18 entries) matches the decoded x and y point counts (both 18), so hovertext alignment is correct.

test/__snapshots__/test_peakmap3d/test_peakmap_plot[ms_plotly-kwargs2].json (1)

2475-2486: Snapshot update aligns with scattermap support.

Line 2475 adds the scattermap defaults; this looks consistent with the heatmapgl → scattermap migration in Plotly templates.

pyproject.toml (1)

28-30: Confirm the testing extra remains compatible across environments.

Line 29 bumps syrupy to >=5.0.0; please ensure the testing extra resolves consistently with the lockfiles and snapshot extensions.

test/__snapshots__/test_peakmap3d/test_peakmap_plot[ms_plotly-kwargs0].json (1)

2475-2486: Snapshot update aligns with scattermap support.

Line 2475 adds scattermap defaults in the template data; this is consistent with the Plotly trace type update.

test/__snapshots__/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs2].html (1)

21-34: LGTM on the ID updates.

The element IDs, doc references, and render items are auto-generated during snapshot creation. These changes are expected when regenerating snapshots with updated test infrastructure.

test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs4].json (2)

8-15: LGTM on the binary data encoding.

The x/y coordinate arrays now use Plotly's typed-array serialization format (dtype: "f8" for 64-bit floats, bdata for base64-encoded binary data). This is the expected format when using syrupy 5.0.0+ with binary serialization mode.


461-472: LGTM on the scattermap trace addition.

The new scattermap trace type in the template data is consistent with the Plotly template structure and follows the same colorbar configuration pattern as other scatter-type traces.

test/__snapshots__/test_peakmap3d/test_peakmap_plot[ms_plotly-kwargs1].json (1)

2022-2033: LGTM on the template update.

The scattermap trace addition to the layout template is consistent with the broader PR changes across Plotly snapshots. The actual scatter3d data traces remain unchanged.

test/__snapshots__/test_spectrum/test_spectrum_plot_with_peak_color[ms_bokeh-kwargs0].html (1)

15-34: LGTM on the snapshot regeneration.

The changes are consistent with the other Bokeh snapshot updates (same version 3.4.1, updated auto-generated IDs). The visualization structure and data remain unchanged.

test/__snapshots__/test_spectrum/test_mirror_spectrum[ms_plotly].json (2)

8-15: LGTM on the binary data encoding.

The x/y coordinate arrays correctly use Plotly's typed-array serialization format for the mirror spectrum traces.


17-152: Correct fix for hover data alignment.

The customdata array now contains 3 entries per data point, matching the expanded x/y line plot format where each spectrum peak is rendered as a triplet (baseline → peak → baseline). This ensures tooltips display correctly at all points of each peak, fixing the annotation bug for single-trace spectra (issue #88).

test/__snapshots__/test_spectrum/test_spectrum_plot[ms_plotly-kwargs2].json (2)

9-16: Binary x/y snapshot update looks consistent.
The encoded x/y payloads align with the new snapshot format while keeping trace metadata intact.

Also applies to: 88-95, 122-129, 171-178


609-620: Template scattermap entry looks fine.
No concerns with the added layout template entry.

test/__snapshots__/test_chromatogram/test_chromatogram_with_annotation[ms_plotly-kwargs0].json (2)

9-16: Binary x/y payloads match the updated snapshot format.
No issues observed with the regenerated trace data blocks.

Also applies to: 424-431, 797-804, 1219-1225, 1772-1778, 2336-2343, 2866-2872


3878-3889: Template scattermap entry looks fine.
No concerns with the added layout template entry.

test/__snapshots__/test_spectrum/test_mirror_spectrum[ms_bokeh].html (1)

15-15: Snapshot-only regeneration.
No actionable review feedback for updated embed artifacts.

Also applies to: 21-23, 32-33

test/__snapshots__/test_spectrum/test_spectrum_plot[ms_plotly-kwargs1].json (2)

9-16: Binary x/y snapshot update looks consistent.
The encoded payloads align with the new snapshot format.

Also applies to: 58-65, 137-144


590-601: Template scattermap entry looks fine.
No concerns with the added layout template entry.

test/__snapshots__/test_spectrum/test_spectrum_plot_with_peak_color[ms_bokeh-kwargs1].html (1)

15-15: Snapshot-only regeneration.
No actionable review feedback for updated embed artifacts.

Also applies to: 21-23, 32-33

test/__snapshots__/test_spectrum/test_spectrum_plot[ms_bokeh-kwargs0].html (1)

21-33: Snapshot IDs regenerated as expected.

The element IDs and document references are dynamically generated during snapshot creation and differ on each regeneration. This is expected behavior for Bokeh HTML snapshots.

test/__snapshots__/test_peakmap/test_peakmap_plot[ms_plotly-kwargs2].json (2)

7-10: Binary-encoded data format is consistent with Plotly's typed array serialization.

The x, y, and color arrays are now serialized using Plotly's binary-encoded format (dtype + bdata). This is the expected output when using plotly.io.to_json() with certain data types.


1728-1739: New scattermap trace type added to layout template.

This addition likely reflects a Plotly library update that includes scattermap as a new trace type in the default template. This is expected behavior when regenerating snapshots with an updated Plotly version.

pyopenms_viz/testing/PlotlySnapshotExtension.py (4)

2-2: LGTM!

The WriteMode import is correctly added to support the binary serialization mode required by syrupy 5.0.0+.


14-14: LGTM!

Setting _write_mode = WriteMode.BINARY aligns with the pattern used in BokehSnapshotExtension and MatplotlibSnapshotExtension as shown in the relevant code snippets.


17-30: LGTM!

The matches() method correctly handles both bytes and string inputs by decoding bytes to UTF-8 strings before JSON parsing. This defensive approach ensures compatibility with the binary write mode.


72-82: LGTM!

The serialize() method correctly returns UTF-8 encoded bytes, consistent with the binary write mode and matching the pattern in BokehSnapshotExtension.serialize().

Regarding the static analysis hint about unused kwargs: this parameter is part of the parent class interface and should be retained for compatibility even if unused.

test/__snapshots__/test_spectrum/test_spectrum_plot[ms_plotly-kwargs3].json (4)

8-15: Binary-encoded data format applied consistently.

The x and y trace data uses Plotly's typed array serialization format, consistent with the other snapshot updates in this PR.


17-152: Customdata expansion supports annotation alignment.

The customdata array now contains 3 entries per data point (matching the expanded x/y data for line rendering: start point, peak, end point). This aligns with the PR objective of fixing annotation rendering for single-trace spectra by ensuring hover data matches the expanded trace coordinates.


1050-1111: Annotations are correctly present in the snapshot.

The annotations array contains the expected peak labels (100.5332, 74.1324, 200.4232, 160.2, 101.545), confirming that the annotation bug fix is working as intended for this single-trace spectrum plot.


551-562: Scattermap trace type added consistently.

The scattermap layout template entry matches the additions in other Plotly snapshots, reflecting the updated Plotly library version.

test/__snapshots__/test_spectrum/test_spectrum_plot_with_peak_color[ms_plotly-kwargs1].json (3)

9-16: Binary-encoded data format for "black" trace.

The x and y arrays for the first trace use Plotly's typed array serialization, consistent with the PR-wide snapshot format updates.


196-203: Binary-encoded data format for "red" trace.

The second trace's x and y arrays also use the typed array format, maintaining consistency across all traces in the snapshot.


616-627: Scattermap trace type added consistently.

The scattermap layout template entry is consistent with the other Plotly snapshot updates in this PR.

test/__snapshots__/test_chromatogram/test_chromatogram_plot[ms_plotly-kwargs1].json (2)

2-3459: Snapshot data serialization looks consistent.

Binary-encoded x/y payloads and refreshed customdata appear coherent for the updated serializer output.


3855-3866: Template update looks fine.

The added scattermap template entry aligns with the new snapshot structure.

test/__snapshots__/test_chromatogram/test_chromatogram_plot[ms_plotly-kwargs2].json (2)

2-3459: Snapshot data serialization looks consistent.

Binary-encoded x/y payloads and refreshed customdata appear coherent for the updated serializer output.


3855-3866: Template update looks fine.

The added scattermap template entry aligns with the new snapshot structure.

test/__snapshots__/test_peakmap/test_peakmap_plot[ms_plotly-kwargs0].json (2)

4-74: Snapshot data serialization looks consistent.

Binary-encoded payloads and shape metadata appear coherent for the updated serializer output.


471-482: Template update looks fine.

The added scattermap template entry aligns with the new snapshot structure.

test/__snapshots__/test_spectrum/test_spectrum_plot[ms_plotly-kwargs0].json (2)

8-153: Snapshot data serialization looks consistent.

Binary-encoded x/y and refreshed customdata align with the updated serializer output.


551-562: Template update looks fine.

The added scattermap template entry aligns with the new snapshot structure.

test/__snapshots__/test_peakmap/test_peakmap_plot[ms_plotly-kwargs3].json (1)

7-10: Snapshot updates align with the new encoded payload format.

No issues noted with the regenerated snapshot data and layout template updates.

Also applies to: 60-67, 221-224, 274-281, 435-438, 488-495, 649-652, 702-709, 863-866, 916-923, 1077-1080, 1130-1137, 1291-1294, 1344-1351, 1896-1907

test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_plotly-kwargs0].json (1)

9-16: Snapshot updates look consistent with encoded trace arrays.

No concerns with the regenerated snapshot data.

Also applies to: 46-53, 83-90, 156-163, 193-200, 230-237, 674-685

test/__snapshots__/test_peakmap/test_peakmap_mz_im[ms_plotly].json (1)

7-10: Snapshot updates for encoded trace/customdata look consistent.

No issues detected in the regenerated snapshot.

Also applies to: 60-67, 69-73, 471-482

pyopenms_viz/testing/BokehSnapshotExtension.py (1)

9-10: Binary snapshot handling is consistent with bytes serialization.

Please verify this against the minimum supported syrupy version in CI.

Also applies to: 43-44, 78-81, 144-152

pyopenms_viz/testing/MatplotlibSnapshotExtension.py (1)

2-4: Binary write mode setup looks aligned with PNG snapshots.

Please verify syrupy WriteMode.BINARY support and PIL availability in the test environment.

Also applies to: 13-17

test/__snapshots__/test_spectrum/test_spectrum_binning[ms_plotly-kwargs3].json (2)

8-15: Binary-encoded data snapshot looks consistent.
No concerns with the updated snapshot encoding.


461-472: Template addition looks fine.
The new scattermap template entry is consistent with regenerated snapshots.

test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs1].html (1)

15-34: Snapshot embed wiring is internally consistent.
IDs and docs_json mapping updates look coherent for the regenerated snapshot.

test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs3].html (1)

15-34: Snapshot embed wiring is internally consistent.
The updated IDs and render_items mapping look correct for the regenerated snapshot.

test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs2].html (1)

15-34: Snapshot embed wiring is internally consistent.
IDs and embedded mapping updates look correct for the snapshot.

test/__snapshots__/test_spectrum/test_spectrum_binning[ms_bokeh-kwargs0].html (1)

15-34: Snapshot embed wiring is internally consistent.
The regenerated IDs and render_items mapping look coherent here.

test/__snapshots__/test_peakmap_marginals/test_peakmap_marginals[ms_plotly].json (1)

1-1772: Snapshot updates align with infrastructure changes.

The changes in this snapshot file reflect:

  1. Migration to binary-encoded data format (dtype/bdata) for coordinates, colors, and customdata - consistent with syrupy 5.0.0 serialization updates
  2. Addition of scattermap trace type in the layout template, mirroring the existing scattermapbox configuration

The JSON structure is valid, and data shapes ("168, 4") are consistent across related fields.

test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_plotly-kwargs2].json (1)

1-1252: Multi-trace annotated spectrum snapshot validates the fix.

This snapshot correctly demonstrates:

  1. Each trace's customdata array length matches its x coordinate length (e.g., 3 entries for 3-point stem segments, 9 entries for multi-peak traces)
  2. Annotations (lines 1173-1234) are properly configured with correct peak positions and ion labels
  3. Binary encoding (dtype/bdata) is consistently applied across all 6 traces

The data alignment ensures hover tooltips display correct information at each point of the spectrum visualization.

test/__snapshots__/test_spectrum/test_spectrum_with_annotations[ms_plotly-kwargs1].json (2)

8-154: Core fix validated: customdata now aligns with trace length for single-trace spectrum.

This snapshot demonstrates the fix for issue #88. The single-trace spectrum data now has:

  • 27 x/y points (9 peaks × 3 points each for stem-plot segments: baseline→peak→baseline)
  • 27 customdata entries (lines 17-152), properly expanded to match

Previously, customdata likely had only 9 entries (one per peak) while x had 27 points, causing the annotation/hover mismatch. The triplication of customdata entries ensures each point in the trace has corresponding hover information.


551-562: Template addition is consistent.

The scattermap entry mirrors the existing scattermapbox configuration and is consistent across all snapshot files in this PR.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

f.write(data)

def serialize(self, data: SerializableData, **kwargs: Any) -> str:
def serialize(self, data: SerializableData, **kwargs: Any) -> bytes:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Silence unused kwargs to avoid Ruff ARG002.

If the signature must remain for the base class, mark it ignored.

💡 Suggested fix
-    def serialize(self, data: SerializableData, **kwargs: Any) -> bytes:
+    def serialize(self, data: SerializableData, **kwargs: Any) -> bytes:  # noqa: ARG002
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def serialize(self, data: SerializableData, **kwargs: Any) -> bytes:
def serialize(self, data: SerializableData, **kwargs: Any) -> bytes: # noqa: ARG002
🧰 Tools
🪛 Ruff (0.14.11)

142-142: Unused method argument: kwargs

(ARG002)

🤖 Prompt for AI Agents
In `@pyopenms_viz/testing/BokehSnapshotExtension.py` at line 142, The serialize
method is currently leaving **kwargs unused which triggers Ruff ARG002; update
the method signature in BokehSnapshotExtension.py (serialize(self, data:
SerializableData, **kwargs: Any) -> bytes) to silence the warning by marking the
var as intentionally unused—e.g., change **kwargs to **_kwargs or explicitly
reference kwargs in the body with a no-op assignment like _ = kwargs or add a
noqa ARG002 comment—so the signature stays compatible but Ruff no longer flags
it.

Comment thread requirements-dev.txt
Comment on lines 1 to +2
pytest
syrupy
syrupy>=5.0.0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Search for syrupy usage in the codebase
rg "syrupy" --type py -A 3 -B 1

Repository: OpenMS/pyopenms_viz

Length of output: 5457


🏁 Script executed:

# Check if there are any other requirements files mentioning syrupy
fd -e txt -e in . | xargs grep -l "syrupy" 2>/dev/null || echo "No other matches found"

Repository: OpenMS/pyopenms_viz

Length of output: 122


🏁 Script executed:

# Check git history to see what version was previously used
git log --oneline -p -- requirements-dev.txt | head -100

Repository: OpenMS/pyopenms_viz

Length of output: 322


🌐 Web query:

syrupy 5.0.0 release notes breaking changes

💡 Result:

Breaking changes in syrupy 5.0.0 (per the release/changelog):

  • License switched to MIT (replacing the previous Apache-2.0). [1][2]
  • Minimum Python requirement raised to Python 3.10 (older 3.8 support dropped). [2]
  • Minimum required pytest version raised to pytest 8. [2]

Sources: PyPI release listing for 5.0.0. [1]; RPM/changelog that lists the 5.0.0 breaking changes. [2]


Ensure pytest version constraint is compatible with syrupy 5.0.0.

Syrupy 5.0.0 requires pytest 8 or later; add pytest>=8 to line 1 to guarantee compatibility with the syrupy dependency on line 2. The custom snapshot extensions correctly use syrupy's stable public extension APIs.

🤖 Prompt for AI Agents
In `@requirements-dev.txt` around lines 1 - 2, Update the pytest requirement to
ensure compatibility with syrupy>=5.0.0 by changing the pytest entry in
requirements-dev.txt from "pytest" to "pytest>=8"; this guarantees pytest
version >=8 for the "pytest" dependency to satisfy "syrupy>=5.0.0".

}
</style>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-3.6.0.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-3.4.1.min.js"></script>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check Bokeh version in project dependencies
rg -n "bokeh" pyproject.toml requirements*.txt setup.py setup.cfg 2>/dev/null || echo "No matches found in standard dependency files"
# Also search broadly for bokeh version specs
find . -type f \( -name "pyproject.toml" -o -name "requirements*.txt" -o -name "setup.py" -o -name "setup.cfg" \) | head -20

Repository: OpenMS/pyopenms_viz

Length of output: 762


Regenerate snapshot to match pinned Bokeh dependency version.

The snapshot file loads bokeh-3.4.1, but requirements.txt pins bokeh==3.6.0. The 3.4.1 version in the snapshot appears to be outdated and should be regenerated to align with the project's pinned dependency.

🤖 Prompt for AI Agents
In `@test/__snapshots__/test_spectrum/test_spectrum_plot`[ms_bokeh-kwargs0].html
at line 15, The snapshot HTML contains a hardcoded script tag referencing
bokeh-3.4.1; update the test snapshot for
test_spectrum_plot[ms_bokeh-kwargs0].html to reference the project's pinned
bokeh version (bokeh-3.6.0) by regenerating the snapshot (re-run the snapshot
generation/test that produces this file) so the embedded script src becomes
https://cdn.bokeh.org/bokeh/release/bokeh-3.6.0.min.js and the test artifacts
match requirements.txt.

}
</style>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-3.6.0.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-3.4.1.min.js"></script>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check Bokeh version in project dependencies
echo "=== Checking project dependencies ==="
find . -maxdepth 2 \( -name "pyproject.toml" -o -name "requirements*.txt" -o -name "setup.py" \) -type f | while read f; do
  if grep -i "bokeh" "$f" 2>/dev/null; then
    echo "--- Found in $f ---"
    grep -i "bokeh" "$f"
  fi
done

echo -e "\n=== Checking snapshot bokeh versions ==="
rg "bokeh-[0-9]+\.[0-9]+\.[0-9]+" test/__snapshots__/ --max-count=10 || echo "No matches found"

Repository: OpenMS/pyopenms_viz

Length of output: 7505


Snapshots use Bokeh 3.4.1 but locked dependencies specify 3.6.0/3.6.2—regenerate with the correct version.

All snapshots consistently reference bokeh-3.4.1.min.js, while the project's pinned dependencies are bokeh==3.6.0 (in requirements.txt and .binder/requirements.txt) and bokeh==3.6.2 (in docs/requirements.txt). The 3.4.1 version is only the minimum supported version in pyproject.toml (bokeh>=3.4.1). This mismatch indicates snapshots were regenerated in an environment with bokeh 3.4.1 installed, creating a disconnect between the tested version and the deployed version. Regenerate snapshots with the correct bokeh version from the locked dependencies.

🤖 Prompt for AI Agents
In `@test/__snapshots__/test_spectrum/test_spectrum_plot`[ms_bokeh-kwargs2].html
at line 15, The snapshot HTML references "bokeh-3.4.1.min.js" but locked deps
require bokeh 3.6.x; re-generate the failing snapshot file (the snapshot named
test_spectrum_plot[ms_bokeh-kwargs2].html that contains the script tag
"bokeh-3.4.1.min.js") in an environment with the pinned bokeh version (install
bokeh==3.6.0 or 3.6.2 per requirements.txt / docs/requirements.txt), run the
test that creates the Bokeh output to update the snapshot (so the script tag
becomes the correct bokeh-3.6.x URL), and commit the regenerated snapshot.
Ensure your test environment uses the same dependency resolution as CI to avoid
future mismatches.

@singjc

singjc commented Jan 16, 2026

Copy link
Copy Markdown
Collaborator Author

Closing in favour of #140

@singjc singjc closed this Jan 16, 2026
auto-merge was automatically disabled January 16, 2026 22:56

Pull request was closed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The output is not displayed properly in SpectrumPlot.

2 participants