Skip to content

Commit

Permalink
provide more flexibility for comparing different versions of the specs
Browse files Browse the repository at this point in the history
  • Loading branch information
NicolasGensollen committed Aug 7, 2024
1 parent 77fea0f commit 4e9492f
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 8 deletions.
77 changes: 69 additions & 8 deletions clinica/utils/caps.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import json
from datetime import datetime
from enum import Enum
from pathlib import Path
from typing import IO, List, MutableSequence, NewType, Optional
from typing import IO, List, MutableSequence, NewType, Optional, Union

from attrs import define, fields
from cattr.gen import make_dict_structure_fn, make_dict_unstructure_fn, override
Expand All @@ -17,6 +18,8 @@
__all__ = [
"CAPS_VERSION",
"CAPSDatasetDescription",
"VersionComparisonPolicy",
"are_versions_compatible",
"write_caps_dataset_description",
"build_caps_dataset_description",
]
Expand All @@ -27,6 +30,60 @@
IsoDate = NewType("IsoDate", datetime)


class VersionComparisonPolicy(str, Enum):
"""Defines the different ways we can compare version numbers in Clinica.
STRICT: version numbers have to match exactly.
MINOR : version numbers have to have the same major and minor numbers.
MAJOR: version numbers only need to share the same major number.
"""

STRICT = "strict"
MINOR = "minor"
MAJOR = "major"


def are_versions_compatible(
version1: Union[str, Version],
version2: Union[str, Version],
policy: Optional[Union[str, VersionComparisonPolicy]] = None,
) -> bool:
"""Returns whether the two provided versions are compatible or not depending on the policy.
Parameters
----------
version1 : str or Version
The first version number to compare.
version2 : str or Version
The second version number to compare.
policy : str or VersionComparisonPolicy, optional
The policy under which to compare version1 with version2.
By default, a strict policy is used, meaning that version
numbers have to match exactly.
Returns
-------
bool :
True if version1 is 'compatible' with version2, False otherwise.
"""
if isinstance(version1, str):
version1 = Version(version1)
if isinstance(version2, str):
version2 = Version(version2)
if policy is None:
policy = VersionComparisonPolicy.STRICT
else:
policy = VersionComparisonPolicy(policy)
if policy == VersionComparisonPolicy.STRICT:
return version1 == version2
if policy == VersionComparisonPolicy.MINOR:
return version1.major == version2.major and version1.minor == version2.minor
if policy == VersionComparisonPolicy.MAJOR:
return version1.major == version2.major


@define
class CAPSProcessingDescription:
"""This class models a CAPS processing pipeline metadata.
Expand Down Expand Up @@ -209,12 +266,14 @@ def from_dict(cls, values: dict):
processing,
)

def is_compatible_with(self, other) -> bool:
if self.bids_version != other.bids_version:
return False
if self.caps_version != other.caps_version:
return False
return True
def is_compatible_with(
self, other, policy: Optional[Union[str, VersionComparisonPolicy]] = None
) -> bool:
return are_versions_compatible(
self.bids_version, other.bids_version, policy=policy
) and are_versions_compatible(
self.caps_version, other.caps_version, policy=policy
)


def _get_username() -> str:
Expand Down Expand Up @@ -452,7 +511,9 @@ def build_caps_dataset_description(
previous_desc = CAPSDatasetDescription.from_file(
output_dir / "dataset_description.json"
)
if not previous_desc.is_compatible_with(new_desc):
if not previous_desc.is_compatible_with(
new_desc, VersionComparisonPolicy.STRICT
):
msg = (
f"Impossible to write the 'dataset_description.json' file in {output_dir} "
"because it already exists and it contains incompatible metadata."
Expand Down
23 changes: 23 additions & 0 deletions test/unittests/utils/test_caps.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,3 +346,26 @@ def test_write_caps_dataset_description_multiple_processing(tmp_path, mocker):
},
],
}


@pytest.mark.parametrize(
"version1,version2,policy,expected",
[
("1.2.3", "1.2.3", "strict", True),
("1.2.3", "1.2.3", "minor", True),
("1.2.3", "1.2.3", "major", True),
("1.2.3", "1.2.4", "strict", False),
("1.2.3", "1.2.4", "minor", True),
("1.2.3", "1.2.4", "major", True),
("1.2.3", "1.3.3", "strict", False),
("1.2.3", "1.3.3", "minor", False),
("1.2.3", "1.3.3", "major", True),
("1.2.3", "2.2.3", "strict", False),
("1.2.3", "2.2.3", "minor", False),
("1.2.3", "2.2.3", "major", False),
],
)
def test_are_versions_compatible(version1, version2, policy, expected):
from clinica.utils.caps import are_versions_compatible

assert are_versions_compatible(version1, version2, policy=policy) is expected

0 comments on commit 4e9492f

Please sign in to comment.