From a4bc02e41f49de526a793d198d325ac5c0b944cf Mon Sep 17 00:00:00 2001 From: eve Date: Thu, 29 May 2025 07:26:53 +0100 Subject: [PATCH] Move version checking logic into versionutils module --- volatility3/framework/__init__.py | 20 ++++++---------- .../framework/configuration/requirements.py | 10 +++----- volatility3/framework/deprecation.py | 5 ++-- volatility3/framework/versionutils.py | 23 +++++++++++++++++++ 4 files changed, 35 insertions(+), 23 deletions(-) create mode 100644 volatility3/framework/versionutils.py diff --git a/volatility3/framework/__init__.py b/volatility3/framework/__init__.py index c384542a8..1c899f434 100644 --- a/volatility3/framework/__init__.py +++ b/volatility3/framework/__init__.py @@ -13,7 +13,7 @@ import os import traceback from typing import Any, Dict, Generator, List, Optional, Tuple, Type, TypeVar -from volatility3.framework import constants, interfaces +from volatility3.framework import constants, interfaces, versionutils if ( sys.version_info.major != constants.REQUIRED_PYTHON_VERSION[0] @@ -48,19 +48,13 @@ vollog = logging.getLogger(__name__) def require_interface_version(*args) -> None: """Checks the required version of a plugin.""" - if len(args): - if args[0] != interface_version()[0]: - raise RuntimeError( - f"Framework interface version {interface_version()[0]} is incompatible with required version {args[0]}" + if not versionutils.matches_required(args, interface_version()): + raise RuntimeError( + "Framework interface version {} is incompatible with required version {}".format( + ".".join(str(x) for x in interface_version()[0:2]), + ".".join(str(x) for x in args[0:2]), ) - if len(args) > 1: - if args[1] > interface_version()[1]: - raise RuntimeError( - "Framework interface version {} is an older revision than the required version {}".format( - ".".join(str(x) for x in interface_version()[0:2]), - ".".join(str(x) for x in args[0:2]), - ) - ) + ) class NonInheritable: diff --git a/volatility3/framework/configuration/requirements.py b/volatility3/framework/configuration/requirements.py index 166c02fe5..b7971d727 100644 --- a/volatility3/framework/configuration/requirements.py +++ b/volatility3/framework/configuration/requirements.py @@ -14,7 +14,7 @@ import os from typing import Any, ClassVar, Dict, List, Optional, Set, Tuple, Type from urllib import parse, request -from volatility3.framework import constants, interfaces, deprecation +from volatility3.framework import constants, interfaces, deprecation, versionutils vollog = logging.getLogger(__name__) @@ -551,7 +551,7 @@ class VersionRequirement(interfaces.configuration.RequirementInterface): ) -> Dict[str, interfaces.configuration.RequirementInterface]: # Mypy doesn't appreciate our classproperty implementation, self._plugin.version has no type config_path = interfaces.configuration.path_join(config_path, self.name) - if not self.matches_required(self._version, self._component.version): + if not versionutils.matches_required(self._version, self._component.version): return {config_path: self} recurse = True @@ -593,11 +593,7 @@ class VersionRequirement(interfaces.configuration.RequirementInterface): def matches_required( cls, required: Tuple[int, ...], version: Tuple[int, int, int] ) -> bool: - if len(required) > 0 and version[0] != required[0]: - return False - if len(required) > 1 and version[1] < required[1]: - return False - return True + versionutils.matches_required(required, version) @deprecation.renamed_class( diff --git a/volatility3/framework/deprecation.py b/volatility3/framework/deprecation.py index 859a32bad..667ea72a5 100644 --- a/volatility3/framework/deprecation.py +++ b/volatility3/framework/deprecation.py @@ -10,8 +10,7 @@ import inspect from typing import Callable, Tuple -from volatility3.framework import interfaces, exceptions -from volatility3.framework.configuration import requirements +from volatility3.framework import interfaces, exceptions, versionutils def method_being_removed(message: str, removal_date: str): @@ -70,7 +69,7 @@ def deprecated_method( interfaces.configuration.VersionableInterface, ): # SemVer check - if not requirements.VersionRequirement.matches_required( + if not versionutils.matches_required( replacement_version, replacement_base_class.version ): raise exceptions.VersionMismatchException( diff --git a/volatility3/framework/versionutils.py b/volatility3/framework/versionutils.py new file mode 100644 index 000000000..334410ff8 --- /dev/null +++ b/volatility3/framework/versionutils.py @@ -0,0 +1,23 @@ +# This file is Copyright 2025 Volatility Foundation and licensed under the Volatility Software License 1.0 +# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 +# + +from typing import Tuple + + +def matches_required(required: Tuple[int, ...], version: Tuple[int, int, int]) -> bool: + """ + Checks if a version tuple satisfies the required version major and minor constraints. + + Parameters: + required (Tuple[int, ...]): A tuple containing required major and optionally minor version numbers. + version (Tuple[int, int, int]): A tuple containing the full version (major, minor, patch). + + Returns: + bool: True if the version matches the required constraints, False otherwise. + """ + if len(required) > 0 and version[0] != required[0]: + return False + if len(required) > 1 and version[1] < required[1]: + return False + return True