mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-06 09:47:38 +02:00
add optional version requirement to deprecated_method
This commit is contained in:
@@ -12,9 +12,11 @@ import logging
|
||||
import os
|
||||
import traceback
|
||||
import functools
|
||||
import warnings
|
||||
from typing import Any, Callable, Dict, Generator, List, Optional, Tuple, Type, TypeVar
|
||||
|
||||
from volatility3.framework import constants, interfaces
|
||||
from volatility3.framework import constants, exceptions, interfaces
|
||||
from volatility3.framework.configuration import requirements
|
||||
|
||||
|
||||
# ##
|
||||
@@ -57,20 +59,64 @@ class Deprecation:
|
||||
"""Deprecation related methods."""
|
||||
|
||||
@staticmethod
|
||||
def deprecated_method(replacement: Callable, additional_information: str = ""):
|
||||
def deprecated_method(
|
||||
replacement: Callable,
|
||||
replacement_base_class_required_version: Tuple[int, int, int] = None,
|
||||
additional_information: str = "",
|
||||
):
|
||||
"""A decorator for marking functions as deprecated.
|
||||
|
||||
Args:
|
||||
replacement: The replacement function overriding the deprecated API, in the form of a Callable (typically a method)
|
||||
replacement_base_class_required_version: The "replacement" base class version that the deprecated method expects before proxying to it. This implies that "replacement" is a method from a class that inherits from VersionableInterface.
|
||||
additional_information: Information appended at the end of the deprecation message
|
||||
"""
|
||||
|
||||
def decorator(deprecated_func):
|
||||
@functools.wraps(deprecated_func)
|
||||
def wrapper(*args, **kwargs):
|
||||
nonlocal replacement, additional_information
|
||||
deprecation_msg = f"Method \"{deprecated_func.__module__ + '.' + deprecated_func.__name__}\" is deprecated, use \"{replacement.__module__ + '.' + replacement.__name__}\" instead. {additional_information}"
|
||||
vollog.warning(deprecation_msg)
|
||||
nonlocal replacement, replacement_base_class_required_version, additional_information
|
||||
# Prevent version mismatches between deprecated (proxy) methods and the ones they proxy
|
||||
if replacement_base_class_required_version is not None and callable(
|
||||
replacement
|
||||
):
|
||||
# example: replacement = volatility3.MyClass.my_dummy_function
|
||||
# "MyClass.my_dummy_function" -> "MyClass"
|
||||
replacement_base_class_name = replacement.__qualname__.split(".")[0]
|
||||
# replacement.__globals__ example: {'MyClass': <class 'volatility3.MyClass'>}
|
||||
replacement_base_class = replacement.__globals__.get(
|
||||
replacement_base_class_name
|
||||
)
|
||||
|
||||
# Verify that the base class inherits from VersionableInterface
|
||||
if inspect.isclass(replacement_base_class) and issubclass(
|
||||
replacement_base_class,
|
||||
interfaces.configuration.VersionableInterface,
|
||||
):
|
||||
# Construct a requirement
|
||||
req = requirements.VersionRequirement(
|
||||
name=replacement_base_class.__name__,
|
||||
component=replacement_base_class,
|
||||
version=replacement_base_class_required_version,
|
||||
)
|
||||
# Verify the requirement
|
||||
if not req.matches_required(
|
||||
req._version, req._component.version
|
||||
):
|
||||
full_unsat_req_path = (
|
||||
deprecated_func.__module__
|
||||
+ "."
|
||||
+ deprecated_func.__qualname__
|
||||
+ "."
|
||||
+ req.name
|
||||
)
|
||||
# Catched by the cli and redirected to process_unsatisfied_exceptions
|
||||
raise exceptions.UnsatisfiedException(
|
||||
{full_unsat_req_path: req}
|
||||
)
|
||||
|
||||
deprecation_msg = f"Method \"{deprecated_func.__module__ + '.' + deprecated_func.__qualname__}\" is deprecated, use \"{replacement.__module__ + '.' + replacement.__qualname__}\" instead. {additional_information}"
|
||||
warnings.warn(deprecation_msg, FutureWarning)
|
||||
# Return the wrapped function with its original arguments
|
||||
return deprecated_func(*args, **kwargs)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user