-
Notifications
You must be signed in to change notification settings - Fork 2
Backward compatibility testing for IO module. #51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
45ddcd9
Don't ignore tiff files for testing.
YooSunYoung 415ac97
Backward compatibility testing for scitiff IO module.
YooSunYoung 0fe2ed2
Rename context manager for compatibility issues.
YooSunYoung 258e078
Fix typo in the copyright statement.
YooSunYoung bec133e
Add documentation about backward compatibility.
YooSunYoung File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -48,4 +48,5 @@ docs/generated/ | |
| *.nxspe | ||
| *.tiff | ||
| *.tif | ||
| !tests/_regression_test_files/*.tiff | ||
| *.mtz | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| # SPDX-License-Identifier: BSD-3-Clause | ||
| # Copyright (c) 2026 Scipp(ESS) contributors (https://github.com/scipp) | ||
| import json | ||
| import pathlib | ||
| from contextlib import contextmanager | ||
| from dataclasses import dataclass | ||
| from datetime import UTC, datetime | ||
|
|
||
| import pydantic | ||
| import pytest | ||
| import requests | ||
| from packaging.version import Version | ||
|
|
||
| from scitiff.io import load_scitiff | ||
|
|
||
| _LOWER_BOUND_VERSION = Version('25.1.0') | ||
| _SCITIFF_TEST_CACHE = pathlib.Path.home() / '.cache' / 'scitiff-test' | ||
| _SCITIFF_TEST_CACHE.mkdir(parents=True, exist_ok=True) | ||
| _CACHED_PACKAGE_INFO_PATH = _SCITIFF_TEST_CACHE / 'scitiff-package-info.json' | ||
|
|
||
|
|
||
| def _utc_now() -> datetime: | ||
| return datetime.now(tz=UTC) | ||
|
|
||
|
|
||
| class ScitiffPackageInfoCache(pydantic.BaseModel): | ||
| last_updated: datetime = pydantic.Field(default_factory=_utc_now) | ||
| versions: tuple[str, ...] | ||
|
|
||
| @property | ||
| def testing_versions(self) -> tuple[str, ...]: | ||
| return tuple( | ||
| _v.base_version | ||
| for _v in sorted( | ||
| Version(version) | ||
| for version in self.versions | ||
| if Version(version) >= _LOWER_BOUND_VERSION | ||
| ) | ||
| ) | ||
|
|
||
| @classmethod | ||
| def from_pypi(cls) -> 'ScitiffPackageInfoCache': | ||
| url = "https://pypi.org/pypi/scitiff/json" | ||
| response = requests.get(url, timeout=1) | ||
| data = response.json() | ||
| pacakge_info = ScitiffPackageInfoCache(versions=tuple(data['releases'].keys())) | ||
| # Save the info if possible | ||
| try: | ||
| _CACHED_PACKAGE_INFO_PATH.write_text( | ||
| data=pacakge_info.model_dump_json(indent=True) | ||
| ) | ||
| except Exception as err: | ||
| import warnings | ||
|
|
||
| warnings.warn( | ||
| 'Could not save scitiff package info into ' | ||
| f'{_CACHED_PACKAGE_INFO_PATH.as_posix()}.\n' | ||
| f'An error raised: {err}\n' | ||
| 'Skipping saving file... use `from_pypi` instead ' | ||
| 'if you do not need to save the info.', | ||
| RuntimeWarning, | ||
| stacklevel=3, | ||
| ) | ||
|
|
||
| return pacakge_info | ||
|
|
||
| @classmethod | ||
| def maybe_from_cache(cls) -> 'ScitiffPackageInfoCache': | ||
| if _CACHED_PACKAGE_INFO_PATH.exists(): | ||
| try: | ||
| latest = ScitiffPackageInfoCache( | ||
| **json.loads(_CACHED_PACKAGE_INFO_PATH.read_text()) | ||
| ) | ||
| except Exception: | ||
| ... | ||
| else: | ||
| if (_utc_now() - latest.last_updated).seconds <= 300: | ||
| return latest | ||
|
|
||
| return cls.from_pypi() | ||
|
|
||
|
|
||
| SCITIFF_PACKAGE_INFO = ScitiffPackageInfoCache.maybe_from_cache() | ||
|
|
||
|
|
||
| @dataclass | ||
| class KnownError: | ||
| error_type: type | ||
| """Type of error. i.e. RuntimeError.""" | ||
| error_match: str | ||
| """Match description of the error for pytest.""" | ||
|
|
||
|
|
||
| _KNOWN_ERRORS: dict[str, tuple[KnownError]] = {} # No known errors yet | ||
|
|
||
|
|
||
| @contextmanager | ||
| def known_backward_compatibility_issues(_errors: tuple[KnownError, ...]): | ||
| if len(_errors) == 1: | ||
| with pytest.raises(_errors[0].error_type, match=_errors[0].error_match): | ||
| yield | ||
| elif len(_errors) >= 1: | ||
| with pytest.raises(_errors[0].error_type, match=_errors[0].error_match): | ||
| with known_backward_compatibility_issues(_errors[1:]): | ||
| yield | ||
| else: | ||
| yield | ||
|
|
||
|
|
||
| def _get_scitiff_example_file_path(version: str) -> pathlib.Path: | ||
| _test_files_dir = pathlib.Path(__file__).parent / '_regression_test_files' | ||
| return _test_files_dir / f'scitiff-{version}.tiff' | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
| argnames=('scitiff_version'), | ||
| argvalues=SCITIFF_PACKAGE_INFO.testing_versions, | ||
| ) | ||
| def test_example_files_for_all_releases_exist(scitiff_version) -> None: | ||
| cur_version_file = _get_scitiff_example_file_path(scitiff_version) | ||
| if not cur_version_file.exists(): | ||
| raise RuntimeError( | ||
| f"Example file for version {scitiff_version} does not exist. " | ||
| "Use `tools/dump_scitiff_example.py` to create a new one " | ||
| "with the missing release version.\n" | ||
| "Creating an example file for a new release is not automated.\n" | ||
| "Therefore a developer will have to create a new scitiff file " | ||
| "and push it to the repo manually." | ||
| ) | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
| argnames=('scitiff_version'), | ||
| argvalues=SCITIFF_PACKAGE_INFO.testing_versions, | ||
| ) | ||
| def test_loading_old_version_files(scitiff_version) -> None: | ||
| _known_erros = _KNOWN_ERRORS.get(scitiff_version, ()) | ||
| with known_backward_compatibility_issues(_known_erros): | ||
| load_scitiff(_get_scitiff_example_file_path(scitiff_version)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| # SPDX-License-Identifier: BSD-3-Clause | ||
| # Copyright (c) 2026 Scipp(ESS) contributors (https://github.com/scipp) | ||
| import logging | ||
| import pathlib | ||
|
|
||
| import scipp as sc | ||
|
|
||
| from scitiff.data import hyperstack_example | ||
| from scitiff.io import save_scitiff | ||
|
|
||
|
|
||
| def _require_rich() -> None: | ||
| try: | ||
| import rich # noqa: F401 - just for checking | ||
| except ImportError as e: | ||
| raise ImportError( | ||
| "You need `rich` to run this script.\n" | ||
| "Please install `rich` or `scitiff` with `GUI` " | ||
| "optional dependencies.\n" | ||
| "Recommended command: pip install scitiff[gui]." | ||
| ) from e | ||
|
|
||
|
|
||
| def _get_rich_logger() -> logging.Logger: | ||
| _require_rich() | ||
| from rich.logging import RichHandler | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
| if not logger.handlers: | ||
| logger.addHandler(RichHandler()) | ||
| logger.setLevel(logging.INFO) | ||
| return logger | ||
|
|
||
|
|
||
| def _get_scitiff_version() -> str: | ||
| from scitiff import __version__ | ||
|
|
||
| if 'dev' in __version__ or __version__.startswith('0.'): | ||
| raise RuntimeError( | ||
| "Only release versions must be used for dumping an example image." | ||
| ) | ||
| else: | ||
| return __version__ | ||
|
|
||
|
|
||
| def _example_image_after_2610() -> sc.DataGroup: | ||
| from scitiff._schema import DAQMetadata | ||
| from scitiff.data import hyperstack_example_with_variances_and_mask | ||
|
|
||
| # Trimmed the example image | ||
| example_image = hyperstack_example_with_variances_and_mask()['x', :10]['y', :10] | ||
| daq_metadata = DAQMetadata( | ||
| facility='scitiff-dev', | ||
| instrument='computer', | ||
| detector_type='computer', | ||
| simulated=True, | ||
| ) | ||
| extra = { | ||
| 'string-value': 'string-value', | ||
| 'int-value': 1, | ||
| 'float-value': 1.2, | ||
| 'scipp-scalar-number': sc.scalar(1, unit='count'), | ||
| 'scipp-scalar-datetime': sc.datetime('now'), | ||
| } | ||
| return sc.DataGroup(image=example_image, daq=daq_metadata, extra=extra) | ||
|
|
||
|
|
||
| def _example_image(version: str) -> sc.DataArray | sc.DataGroup: | ||
| from packaging.version import Version | ||
|
|
||
| cur_version = Version(version) | ||
| if cur_version < Version('25.12.0'): # When saving mask and stdev was introduced. | ||
| return hyperstack_example()['x', :10]['y', :10] | ||
| elif cur_version < Version('26.1.0'): # When saving data group was introduced. | ||
| from scitiff.data import hyperstack_example_with_variances_and_mask | ||
|
|
||
| return hyperstack_example_with_variances_and_mask()['x', :10]['y', :10] | ||
| else: | ||
| return _example_image_after_2610() | ||
|
|
||
|
|
||
| def dump_example_scitiff(): | ||
| """Dump an example scitiff file with all possible metadata fields.""" | ||
|
|
||
| logger = _get_rich_logger() | ||
| version = _get_scitiff_version() | ||
| default_dir = pathlib.Path(__file__).parent.parent / pathlib.Path( | ||
| 'tests/_regression_test_files' | ||
| ) | ||
| prefix = 'scitiff_' | ||
| suffix = '.tiff' | ||
| new_file_name = ''.join([prefix, version, suffix]) | ||
| new_file_path = default_dir / new_file_name | ||
| logger.info("Dumping new example scitiff at %s", new_file_path.as_posix()) | ||
| image = _example_image(version=version) | ||
| logger.info(image) | ||
| logger.info("Dumping image for version %s", version) | ||
| save_scitiff(dg=image, file_path=new_file_path) | ||
| logger.info( | ||
| "Successfully saved image for version %s in %s", | ||
| version, | ||
| new_file_path.as_posix(), | ||
| ) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| dump_example_scitiff() |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a script that I used for dumping all example scitiff images manually.
I could use it for all released versions.