Skip to content
Back to top

# Source code for pytestshellutils.utils

    # Copyright 2021-2024 VMware, Inc.
    # SPDX-License-Identifier: Apache-2.0
    #
    import inspect
    import pathlib
    import sys
    import warnings
    from typing import Any
    from typing import Callable
    from typing import Dict
    from typing import Optional
    from typing import Tuple
    from typing import Type
    from typing import TYPE_CHECKING
    from typing import Union

    import packaging.version

    import pytestshellutils

    if TYPE_CHECKING:
        from packaging.version import Version

    def resolved_pathlib_path(path: Union[str, pathlib.Path]) -> pathlib.Path:
        """
        Return a resolved ``pathlib.Path``.
        """
        if isinstance(path, str):
            path = pathlib.Path(path)
        return path.resolve()

    def format_callback_to_string(
        callback: Union[str, Callable[..., Any]],
        args: Optional[Tuple[Any, ...]] = None,
        kwargs: Optional[Dict[str, Any]] = None,
    ) -> str:
        """
        Convert a callback, its arguments and keyword arguments to a string suitable for logging purposes.

        Arguments:
            callback:
                The callback function

        Keyword Arguments:
            args:
                The callback arguments
            kwargs:
                The callback keyword arguments

        Returns:
            str: The formatted callback string
        """
        callback_str: str
        if not isinstance(callback, str):
            try:
                callback_str = f"{callback.__qualname__}("
            except AttributeError:  # pragma: no cover
                callback_str = f"{callback.__name__}("
        else:
            callback_str = f"{callback}("
        if args:
            callback_str += ", ".join([repr(arg) for arg in args])
        if kwargs:
            if args:
                callback_str += ", "
            callback_str += ", ".join([f"{k}={v!r}" for (k, v) in kwargs.items()])
        callback_str += ")"
        return callback_str

    def warn_until(  # pragma: no cover
        version: str,
        message: str,
        category: Type[Warning] = DeprecationWarning,
        stacklevel: Optional[int] = None,
        _dont_call_warnings: bool = False,
        _pkg_version_: Optional[str] = None,
    ) -> None:
        """
        Show a deprecation warning.

        Helper function to raise a warning, by default, a ``DeprecationWarning``,
        until the provided ``version``, after which, a ``RuntimeError`` will
        be raised to remind the developers to remove the warning because the
        target version has been reached.

        Arguments:
            version:
                The version string after which the warning becomes a ``RuntimeError``.
                For example ``2.1``.
            message:
                The warning message to be displayed.

        Keyword Arguments:
            category:
                The warning class to be thrown, by default ``DeprecationWarning``
            stacklevel:
                There should be no need to set the value of ``stacklevel``.
            _dont_call_warnings:
                This parameter is used just to get the functionality until the actual
                error is to be issued. When we're only after the version checks to
                raise a ``RuntimeError``.

        Returns:
            Nothing.
        """
        _version = packaging.version.parse(version)
        if _pkg_version_ is None:
            _pkg_version_ = pytestshellutils.__version__  # type: ignore[attr-defined]
        _pkg_version = packaging.version.parse(_pkg_version_)

        if stacklevel is None:
            # Attribute the warning to the calling function, not to warn_until()
            stacklevel = 3

        if _pkg_version >= _version:
            caller = inspect.getframeinfo(sys._getframe(stacklevel - 1))
            raise RuntimeError(
                "The warning triggered on filename '{filename}', line number "
                "{lineno}, is supposed to be shown until version "
                "{until_version} is released. Current version is now "
                "{version}. Please remove the warning.".format(
                    filename=caller.filename,
                    lineno=caller.lineno,
                    until_version=_pkg_version_,
                    version=version,
                ),
            )

        if _dont_call_warnings is False:
            warnings.warn(
                message.format(version=version),
                category,
                stacklevel=stacklevel,
            )