Source code for saltfactories.utils

"""
Utility functions.
"""
import inspect
import pathlib
import random
import string
import sys
import warnings
from functools import lru_cache
from typing import Optional
from typing import Type

import packaging.version

import saltfactories


[docs] def random_string(prefix, size=6, uppercase=True, lowercase=True, digits=True): """ Generates a random string. :keyword str prefix: The prefix for the random string :keyword int size: The size of the random string :keyword bool uppercase: If true, include upper-cased ascii chars in choice sample :keyword bool lowercase: If true, include lower-cased ascii chars in choice sample :keyword bool digits: If true, include digits in choice sample :return str: The random string """ if not any([uppercase, lowercase, digits]): msg = "At least one of 'uppercase', 'lowercase' or 'digits' needs to be true" raise RuntimeError(msg) choices = [] if uppercase: choices.extend(string.ascii_uppercase) if lowercase: choices.extend(string.ascii_lowercase) if digits: choices.extend(string.digits) return prefix + "".join(random.choice(choices) for _ in range(size)) # noqa: S311
[docs] @lru_cache(maxsize=1) def running_username(): """ Return the username that is running the code. """ # Do not move these deferred imports. It allows running against a Salt # onedir build in salt's repo checkout. import salt.utils.user # pylint: disable=import-outside-toplevel return salt.utils.user.get_user()
[docs] def cast_to_pathlib_path(value): """ Cast the passed value to an instance of ``pathlib.Path``. """ if value is None: return value if isinstance(value, pathlib.Path): return value try: return pathlib.Path(value.strpath) except AttributeError: return pathlib.Path(str(value))
[docs] def warn_until( version: str, message: str, category: Type[Warning] = DeprecationWarning, # noqa: FA100 stacklevel: Optional[int] = None, # noqa: FA100 _dont_call_warnings: bool = False, _pkg_version_: Optional[str] = None, # noqa: FA100 ) -> 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. :param version: The version string after which the warning becomes a ``RuntimeError``. For example ``2.1``. :param message: The warning message to be displayed. :param category: The warning class to be thrown, by default ``DeprecationWarning`` :param stacklevel: There should be no need to set the value of ``stacklevel``. :param _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``. """ _version = packaging.version.parse(version) if _pkg_version_ is None: _pkg_version_ = saltfactories.__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 _pytest_is_rewriting = False caller = inspect.getframeinfo(sys._getframe(stacklevel - 1)) # noqa: SLF001 caller_filename = pathlib.Path(caller.filename) if str(caller_filename.as_posix()).endswith("/_pytest/assertion/rewrite.py"): # Pytest is rewriting code, increase stack level so that the right # module triggering the warning is shown. stacklevel += 1 _pytest_is_rewriting = True if _pkg_version >= _version: if _pytest_is_rewriting: # We need to grab the caller with the new stack level caller = inspect.getframeinfo(sys._getframe(stacklevel - 1)) # noqa: SLF001 msg = ( f"The warning triggered on filename '{caller.filename}', line number {caller.lineno}, " f"is supposed to be shown until version {_pkg_version_} is released. Current version " f"is now {version}. Please remove the warning." ) raise RuntimeError(msg) if _dont_call_warnings is False: warnings.warn( message.format(version=version), category, stacklevel=stacklevel, )