From e7dce593dd66aa1b67033c217ceeeba2b66005c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Thu, 15 Sep 2022 12:05:46 +0200 Subject: [PATCH] Add diff method to AwesomeVersion to list out changes between 2 objects (#185) * Add diff method to AwesomeVersion to list out changes between 2 objects * Make it an object * lint * Add substraction --- awesomeversion/__init__.py | 3 +- awesomeversion/awesomeversion.py | 64 +++++++++++++++++++++++++++++++- tests/test_awesomeversion.py | 24 ++++++++++++ 3 files changed, 88 insertions(+), 3 deletions(-) diff --git a/awesomeversion/__init__.py b/awesomeversion/__init__.py index 6c336fc..a49a8a7 100644 --- a/awesomeversion/__init__.py +++ b/awesomeversion/__init__.py @@ -1,5 +1,5 @@ """Initialize the AwesomeVersion package.""" -from .awesomeversion import AwesomeVersion +from .awesomeversion import AwesomeVersion, AwesomeVersionDiff from .exceptions import ( AwesomeVersionCompareException, AwesomeVersionException, @@ -10,6 +10,7 @@ __all__ = [ "AwesomeVersion", "AwesomeVersionCompareException", + "AwesomeVersionDiff", "AwesomeVersionException", "AwesomeVersionStrategy", "AwesomeVersionStrategyException", diff --git a/awesomeversion/awesomeversion.py b/awesomeversion/awesomeversion.py index c7452ac..387b5bc 100644 --- a/awesomeversion/awesomeversion.py +++ b/awesomeversion/awesomeversion.py @@ -1,7 +1,7 @@ """AwesomeVersion.""" from __future__ import annotations -from typing import Any +from typing import TYPE_CHECKING, Any, Dict from warnings import warn from .comparehandlers.container import compare_handler_container @@ -15,7 +15,6 @@ AwesomeVersionStrategy, AwesomeVersionStrategyDescription, ) -from .typing import EnsureStrategyIterableType, EnsureStrategyType, VersionType from .utils.regex import ( RE_DIGIT, RE_MODIFIER, @@ -24,6 +23,9 @@ generate_full_string_regex, ) +if TYPE_CHECKING: + from .typing import EnsureStrategyIterableType, EnsureStrategyType, VersionType + class _AwesomeVersionBase(str): """Base class for AwesomeVersion to allow the usage of the default JSON encoder.""" @@ -202,6 +204,25 @@ def __le__(self, compareto: object) -> bool: def __ge__(self, compareto: object) -> bool: return self.__eq__(compareto) or self.__gt__(compareto) + def __sub__(self, compareto: object) -> AwesomeVersionDiff: + return self.diff(compareto) + + def diff(self, compareto: VersionType) -> AwesomeVersionDiff: + """Return a dictionary with differences between 2 AwesomeVersion objects.""" + if isinstance(compareto, (str, float, int)): + compareto = AwesomeVersion(compareto) + if not isinstance(compareto, AwesomeVersion): + raise AwesomeVersionCompareException("Not a valid AwesomeVersion object") + return AwesomeVersionDiff( + { + "major": self.major != compareto.major, + "minor": self.minor != compareto.minor, + "patch": self.patch != compareto.patch, + "modifier": self.modifier != compareto.modifier, + "strategy": self.strategy != compareto.strategy, + } + ) + def section(self, idx: int) -> int: """Return the value of the specified section of the version.""" if self.sections >= (idx + 1): @@ -430,3 +451,42 @@ def simple(self) -> bool: generate_full_string_regex(RE_SIMPLE).match(self.string) is not None ) return self._simple + + +class AwesomeVersionDiff: + """Structured output of AwesomeVersion.diff""" + + def __init__(self, changes: Dict[str, bool]) -> None: + """Initialize the AwesomeVersionDiff.""" + self._changes = changes + + def __repr__(self) -> str: + return ( + f"AwesomeVersionDiff(major={self.major}, minor={self.minor}, " + f"patch={self.patch}, modifier={self.modifier}, strategy={self.strategy})" + ) + + @property + def major(self) -> bool: + """Return True if the major version has changed.""" + return self._changes["major"] + + @property + def minor(self) -> bool: + """Return True if the minor version has changed.""" + return self._changes["minor"] + + @property + def patch(self) -> bool: + """Return True if the patch version has changed.""" + return self._changes["patch"] + + @property + def modifier(self) -> bool: + """Return True if the modifier version has changed.""" + return self._changes["modifier"] + + @property + def strategy(self) -> bool: + """Return True if the strategy has changed.""" + return self._changes["strategy"] diff --git a/tests/test_awesomeversion.py b/tests/test_awesomeversion.py index e55abef..8bfd9b1 100644 --- a/tests/test_awesomeversion.py +++ b/tests/test_awesomeversion.py @@ -9,6 +9,7 @@ AwesomeVersionStrategy, AwesomeVersionStrategyException, ) +from awesomeversion.exceptions import AwesomeVersionException from awesomeversion.strategy import COMPARABLE_STRATEGIES from awesomeversion.typing import VersionType @@ -222,3 +223,26 @@ def test_find_first_match_exception() -> None: assert "Can not use find_first_match without ensure_strategy" in str( warning_list[-1].message ) + + +def test_diff() -> None: + """Test .diff""" + version = AwesomeVersion("2020.12.1") + + assert version.diff("2021.12.1").major + assert not version.diff("2021.12.1").minor + assert not version.diff("2021.12.1").patch + assert not version.diff("2021.12.1").modifier + assert not version.diff("2021.12.1").strategy + + assert version.diff("2021.11.1").minor + assert version.diff("2021.12.2").patch + assert version.diff("2021.12.1dev2").modifier + assert version.diff("2.11.1").strategy + + diff = version - "2021.12.1" + assert diff.major + assert diff.__repr__().startswith("AwesomeVersionDiff(") + + with pytest.raises(AwesomeVersionException): + version.diff(None)