import functools import warnings from .error_utils import UnitxtWarning from .settings_utils import get_constants, get_settings constants = get_constants() settings = get_settings() class DeprecationError(Exception): """Custom exception for deprecated versions.""" pass def compare_versions(version1, version2): """Compare two semantic versioning strings and determine their relationship. Parameters: - version1 (str): The first version string to compare. - version2 (str): The second version string to compare. Returns: - int: -1 if version1 < version2, 1 if version1 > version2, 0 if equal. Example: >>> compare_versions("1.2.0", "1.2.3") -1 >>> compare_versions("1.3.0", "1.2.8") 1 >>> compare_versions("1.0.0", "1.0.0") 0 """ parts1 = [int(part) for part in version1.split(".")] parts2 = [int(part) for part in version2.split(".")] length_difference = len(parts1) - len(parts2) if length_difference > 0: parts2.extend([0] * length_difference) elif length_difference < 0: parts1.extend([0] * (-length_difference)) for part1, part2 in zip(parts1, parts2): if part1 < part2: return -1 if part1 > part2: return 1 return 0 def depraction_wrapper(obj, version, alt_text): """A wrapper function for deprecation handling, issuing warnings or errors based on version comparison. Args: obj (callable): The object to be wrapped, typically a function or class method. version (str): The version at which the object becomes deprecated. alt_text (str): Additional text to display, usually suggests an alternative. Returns: callable: A wrapped version of the original object that checks for deprecation. """ @functools.wraps(obj) def wrapper(*args, **kwargs): if constants.version < version: if settings.default_verbosity in ["debug", "info", "warning"]: warnings.warn( f"{obj.__name__} is deprecated.{alt_text}", DeprecationWarning, stacklevel=2, ) elif constants.version >= version: raise DeprecationError(f"{obj.__name__} is no longer supported.{alt_text}") return obj(*args, **kwargs) return wrapper def deprecation(version, alternative=None, msg=None): """Decorator for marking functions or class methods as deprecated. Args: version (str): The version at which the function or method becomes deprecated. alternative (str, optional): Suggested alternative to the deprecated functionality. msg (str, optional): Additional message regarding the deprecation reason or alternatives. Returns: callable: A decorator that can be applied to functions or class methods. """ def decorator(obj): alt_text = f" Use {alternative} instead." if alternative is not None else "" alt_text += msg if msg is not None else "" if callable(obj): func = obj elif hasattr(obj, "__init__"): func = obj.__init__ else: raise ValueError("Unsupported object type for deprecation.") return depraction_wrapper(func, version, alt_text) return decorator def init_warning(msg=""): # Decorator that raises warning when class is initialized def decorator(initiated_class): UnitxtWarning(msg) return initiated_class return decorator def warn_on_call(warning_type=UserWarning, msg=""): def decorator(obj): if isinstance(obj, type): original_init = obj.__init__ @functools.wraps(original_init) def new_init(self, *args, **kwargs): warnings.warn(msg, warning_type, stacklevel=2) original_init(self, *args, **kwargs) obj.__init__ = new_init return obj if callable(obj): @functools.wraps(obj) def wrapper(*args, **kwargs): warnings.warn(msg, warning_type, stacklevel=2) return obj(*args, **kwargs) return wrapper raise TypeError("This decorator can only be applied to classes or functions.") return decorator