Source code for qcodes.utils.deprecate

import types
import warnings
from contextlib import contextmanager
from typing import Any, Callable, Iterator, List, Optional, cast

import wrapt


[docs]class QCoDeSDeprecationWarning(RuntimeWarning): """ A DeprecationWarning used internally in QCoDeS. This fixes `DeprecationWarning` being suppressed by default. """
def deprecation_message( what: str, reason: Optional[str] = None, alternative: Optional[str] = None ) -> str: msg = f'The {what} is deprecated' if reason is not None: msg += f', because {reason}' msg += '.' if alternative is not None: msg += f' Use \"{alternative}\" as an alternative.' return msg
[docs]def issue_deprecation_warning( what: str, reason: Optional[str] = None, alternative: Optional[str] = None, stacklevel: int = 2, ) -> None: """ Issue a `QCoDeSDeprecationWarning` with a consistently formatted message """ warnings.warn( deprecation_message(what, reason, alternative), QCoDeSDeprecationWarning, stacklevel=stacklevel)
[docs]def deprecate( reason: Optional[str] = None, alternative: Optional[str] = None ) -> Callable[..., Any]: """ A utility function to decorate deprecated functions and classes. Args: reason: The reason of deprecation. alternative: The alternative function or class to put in use instead of the deprecated one. """ @wrapt.decorator # type: ignore[misc] def decorate_callable( func: Callable[..., Any], instance: object, args: Any, kwargs: Any ) -> Any: t, n = ( ("class", instance.__class__.__name__) if func.__name__ == "__init__" else ("function", func.__name__) ) issue_deprecation_warning(f"{t} <{n}>", reason, alternative, stacklevel=3) return func(*args, **kwargs) def actual_decorator(obj: Any) -> Any: if isinstance(obj, (types.FunctionType, types.MethodType)): func = cast(Callable[..., Any], obj) # pylint: disable=no-value-for-parameter return decorate_callable(func) # pyright: ignore[reportGeneralTypeIssues] # pylint: enable=no-value-for-parameter else: # this would need to be recursive for m_name in dir(obj): m = getattr(obj, m_name) if isinstance(m, (types.FunctionType, types.MethodType)): # skip static methods, since they are not wrapped correctly # by wrapt. # if anyone reading this knows how the following line # works please let me know. # wrapt cannot wrap class methods in 3.11.0 # see https://github.com/python/cpython/issues/63272 if isinstance( obj.__dict__.get(m_name, None), (staticmethod, classmethod) ): continue # pylint: disable=no-value-for-parameter setattr( obj, m_name, decorate_callable( m ), # pyright: ignore[reportGeneralTypeIssues] ) # pylint: enable=no-value-for-parameter return obj return actual_decorator
@contextmanager def _catch_deprecation_warnings() -> Iterator[List[warnings.WarningMessage]]: with warnings.catch_warnings(record=True) as ws: warnings.simplefilter("ignore") warnings.filterwarnings("always", category=QCoDeSDeprecationWarning) yield ws @contextmanager def assert_not_deprecated() -> Iterator[None]: with _catch_deprecation_warnings() as ws: yield assert len(ws) == 0 @contextmanager def assert_deprecated(message: str) -> Iterator[None]: with _catch_deprecation_warnings() as ws: yield assert len(ws) == 1 recorded_message = ws[0].message assert isinstance(recorded_message, Warning) assert recorded_message.args[0] == message