Skip to content

Make fmt.version extensions backward compatible in ome-zarr-py dependents. - #491

Closed
melonora wants to merge 7 commits into
ome:masterfrom
melonora:backward_compatible
Closed

melonora wants to merge 7 commits into
ome:masterfrom
melonora:backward_compatible

Conversation

@melonora

@melonora melonora commented Sep 6, 2025 •

Copy link
Copy Markdown
Contributor

In SpatialData we use RasterFormat versions for writing images and labels. In discussion with @joshmoore and @bogovicj the version for RasterFormatV02 is 0.4-dev-spatialdata (PR here). This leads to problems with newer ome-zarr-py versions that support zarr v3. Reason for this is checks in writer.py that check things like the following:
fmt.version not in ("0.1", "0.2", "0.3", "0.4"):, which leads ome metadata to be written which is specifically for Formats supporting zarr v3. This PR uses .startswith to check for the version in the string.

In SpatialData I already checked whether this re-enables compatibility with zarr, which it does.

Note: In SpatialData so far all formats were based on FormatV04. Therefore, there are no issues with the other formats. Changing from FormatV04 to FormatV05 gives the problem at the moment and then only when it was using RasterFormatV02, given that the version here is not 0.4, but 0.4-dev-spatialdata. RasterFormatV01 does not have this issue as the version here was specificied as 0.4.

Alternatives

  • Follow Wills suggestion if "ome" in attrs: attrs = attrs.get("ome", {}). This is not explicit regarding versions.
  • Make an exception only for 0.4 and "0.5" (later when there is a "0.6) which to the best of my knowledge is the only case in which an alternative version name was accepted that previously worked. Comment the reason for this exception and that this will not be supported for other versions. Reason for 0.5 as well is that we do not expect to be entirely compliant yet.
  • Another option is to use a Global if present. Then we can handle the matching on our side.

@melonora

melonora commented Sep 6, 2025 •

Copy link
Copy Markdown
Contributor Author

cc @LucaMarconato @will-moore @joshmoore

I know there are other ways of doing this, but I think any other change would require a change in both ome-zarr-py and dependent libraries (though SpatialData could be the only one in this case effected)

@codecov

codecov Bot commented Sep 6, 2025 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.02%. Comparing base (b49986e) to head (b5b85c5).
⚠️ Report is 28 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #491   +/-   ##
=======================================
  Coverage   87.02%   87.02%           
=======================================
  Files          13       13           
  Lines        1772     1772           
=======================================
  Hits         1542     1542           
  Misses        230      230           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@will-moore will-moore left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good, thanks.

@melonora

melonora commented Sep 8, 2025

Copy link
Copy Markdown
Contributor Author

Thanks for approving! Would there be any timeline for a release? Right now zarr v3 in SpatialData is blocked by this as we can't have backward compatibility without this.

@will-moore

Copy link
Copy Markdown
Member

Just to understand your usage a bit better... Your RasterFormatV02 is a subclass of FormatV04?

I'm wondering if doing if fmt.version.zarr_format == 2 might be a nicer way of checking for versions 0.1-0.4 in the future?

@melonora

melonora commented Sep 8, 2025 •

Copy link
Copy Markdown
Contributor Author

correct, so that would seemingly be an alternative right now, though I would not be able to predict if in the future we have a similar situation while the format is then still zarr_format == 3. Hopefully not, but not certain, so I think the current solution is better.

@will-moore

Copy link
Copy Markdown
Member

One possible issue in the future is that "0.10" startswith "0.1", although I expect we'll remove support for writing "0.1" by the time we get to "0.10".

As far as I can understand, following your PR link above, all your Format classes are based on CurrentFormat(), which is now based on zarr V3?

But in your RasterFormatV02 are you also nesting your metadata within an ome block?
It sounds like you don't want to unwrap the ome nesting as is happening here:

if not fmt.version.startswith(("0.1", "0.2", "0.3", "0.4")):
        attrs = attrs.get("ome", {})

So you have zarr.json and attributes: {} but this doesn't contain ome?

If so, then my suggestion of checking for fmt.zarr_format would certainly not work for you.

I'm just trying to work out the best way of not breaking your usage in future, but I don't know which of the checks was the one that broke your usage, or exactly why.
Would it be worth adding a test that checks the behaviour works for you?

Or maybe I'm over-thinking this and we can just go with the fixes as they are now.

@melonora

melonora commented Sep 8, 2025 •

Copy link
Copy Markdown
Contributor Author

Not all Format classes are based on current format. RasterFormatV02 is inheriting from RasterFormatV01 which is based on ome-zarr-py FormatV04. This does not contain the ome key in attrs. The main reason for supporting this raster format is for allowing users to read the data that they have in zarr v2 and convert it to zarr v3 by then writing it back to disk with the current format. All our current formats would be based on FormatV05, but can contain additional metadata besides ome.

TLDR We need this to allow our users to update their zarr v2 data to zarr v3. I don't foresee any problems once we have given the time for our users to update.

The main reason why it is broken now is that our RasterFormatV02 which was used with zarr v2 does not write metadata to ome. However, ome-zarr-py recognizes it as being of format ' 0.5' which assumes the ome key. This because our format version is 0.4-spatialdata-dev. This does not match either of the versions in the conditional.

@melonora

melonora commented Sep 8, 2025

Copy link
Copy Markdown
Contributor Author

Also, just for the record, thank you for thinking along:)

Comment thread ome_zarr/writer.py

attrs = group.attrs
if fmt.version not in ("0.1", "0.2", "0.3", "0.4"):
if not fmt.version.startswith(("0.1", "0.2", "0.3", "0.4")):

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@will-moore This is the check that was really the main dealbreaker for allowing backward compatibility for now in SpatialData. If fmt.version does not exactly match here, the ome key is assumed in attrs.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

maybe we should just do:

if "ome" in attrs:
    attrs = attrs.get("ome", {})

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

it would be less explicit regarding versions though, so I am not certain whether that would be favorable.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

FWIW version 0.4 of the OME-Zarr specification places no restrictions on version numbers, saying:

Each "multiscales" dictionary SHOULD contain the field "name". It SHOULD contain the field "version", which indicates the version of the multiscale metadata of this image (current version is 0.4).

(and similar SHOULD statements for other metadata), so it would possibly be fine to not be explicit regarding the version?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ahh - but here we're not reading the version from an OME-Zarr (where the version could be missing), we're writing the OME-Zarr according to a format version that we specify.

And I realise that checking for "ome" in attrs for set_metadata() won't always work as the group may not have any existing attributes.
So let's go with the proposed changes and we can think about a more fancy version parser in future as needed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Great, would be happy to contribute if it is required.

@BioinfoTongLI

Copy link
Copy Markdown

Hi there! I wonder if there's any progress on this? Or is there anything that I can help with?
This seems to be a blocker to enable zarr V3 support in spatialdata, which we'd like to try out ASAP.

@melonora

Copy link
Copy Markdown
Contributor Author

I will close this PR, after talking yesterday together with @joshmoore , @jo-mueller and @LucaMarconato to discuss the potential implications and concerns of @sbesson, we have chosen for the option of overwriting the version metadata with the version as indicated by SpatialData. We know which Format is used with which element format so we can do the mapping on our side.

Prior to reading we then also check the metadata for the version and then request the corresponding ome-zarr version when calling ZarrLocation.

This leaves still the issue that there should be a way to deviate in case of ome development versions and that there should be a way to indicate something like:

  1. which format is used for reading the data
  2. if the version string is an exact match, we expect that the data gets validated by NGFFvalidator, if only partial match then not.

However, this is a RFC process.

@melonora melonora closed this Sep 17, 2025
@sbesson

sbesson commented Sep 24, 2025

Copy link
Copy Markdown
Member

Thanks @melonora for the summary. Reflecting a bit on the discussions I had with various OME members here, I believe what happened here was the outcome of a classical conflict between:

  • the reference expectations for a library such as this one which mandates strict compliance with the specification especially on write
  • the needs of the spatialdata library especially in terms of expanding the current specification

From OME's previous experience with formats, this type of tension is not specific to OME-Zarr by any means. In my opinion, the approach described in #491 (comment) i.e. use this library with a canonical OME-Zarr version and then let the SpatialData library override the metadata/binary data wherever applicable is a solid workaround which respects the requirements/needs of each library.

To the last point, I am very supportive of defining development OME-Zarr versions formally moving forward. The main concern was to effectively make these decisions implicitly by encoding them in a implementation. I think this topic deserves its own RFC. This might be very timely given the current discussions around the OME-Zarr 1.0 roadmap as I suspect another point to codift over the next few months will be the expectations in terms of compatibility between the different OME-Zarr versions.

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.

5 participants