Source code for schrodinger.utils.deprecation
"""
Deprecation handling
Copyright Schrodinger LLC, All Rights Reserved.
"""
import warnings
from contextlib import contextmanager
from typing import Callable
from typing import Optional
import decorator
from schrodinger import get_release_name
from schrodinger.test import decorate_fflag
from schrodinger.utils import mmutil
USE_DEPRECATED = "USE_DEPRECATED"
[docs]class DeprecationError(RuntimeError):
"""Exception indicating API deprecation"""
pass
def _valid_release(release: str) -> bool:
split_version = lambda x: (int(y) for y in x.split("-"))
try:
year, quarter = split_version(release)
cur_year, cur_quarter = split_version(get_release_name())
except (AttributeError, ValueError):
return False
# Valid if the current release and onwards
return year > cur_year or (year == cur_year and quarter >= cur_quarter)
[docs]def raise_deprecation_error(msg: str, to_remove_in: str):
"""
Raise a `DeprecationError` unless USE_DEPRECATED feature flag is set, which
will issue a DeprecationWarning only.
:param msg: deprecation message to isse
:param to_remove_in: Schrodinger core suite release version (XXXX-X)
"""
if not _valid_release(to_remove_in):
raise RuntimeError(f"Must provide valid to_remove_in: {to_remove_in}")
# Under feature flag, print deprecation warning only
if mmutil.feature_flag_is_enabled(mmutil.USE_DEPRECATED):
msg += f"\nWARNING: to be completely removed in {to_remove_in} release."
warnings.warn(msg, DeprecationWarning, stacklevel=3)
return
# Otherwise, raise an exception to force callers to update deprecated calls
raise DeprecationError(msg)
[docs]def deprecate_module(module_name: str,
to_remove_in: str,
replacement: str = None):
"""
Raises a deprecation error (or only prints a warning when USE_DEPRECATED
is enabled), indicating to the caller that the import should be removed,
and updated to a new module if applicable.
:param module_name: name of module which is deprecated
:param to_remove_in: Schrodinger core suite release version (XXXX-X)
:param replacement: replacement module name
"""
msg = (f"import deprecated: {module_name}\n"
f"The requested import statement is deprecated in the Schrodinger"
f" suite and is no longer available in this release.")
if replacement:
msg += (f"\nPlease update as:\n import {replacement.__name__}")
raise_deprecation_error(msg, to_remove_in)
[docs]@decorator.decorator
def deprecated(func: Callable,
to_remove_in: Optional[str] = None,
replacement: Optional[Callable] = None,
msg: Optional[str] = None,
*args,
**kwargs):
"""
Raises a deprecation error (or only prints a warning when USE_DEPRECATED
is enabled), indicating to the caller that the decorated call should be
removed, and updated to a new function/method call if applicable.
:param func: deprecated function/method
:param to_remove_in: Schrodinger core suite release version (XXXX-X)
:param replacement: current function/method to use
:param msg: specific message if default message is not desired
"""
def name(x):
return f"{x.__module__}.{x.__name__}"
if not msg:
msg = (f"function deprecated: {name(func)}\n"
f"The requested call is deprecated in the Schrodinger"
f" suite and is no longer available in this release.")
if replacement:
msg += (f"\nPlease update as:\n {name(replacement)}(...)")
raise_deprecation_error(msg, to_remove_in)
# If no exception was raised because the feature flag has been enabled
return func(*args, **kwargs)
[docs]@contextmanager
def enable_deprecated():
"""Enables deprecated imports/functions within managed context"""
with decorate_fflag.enable_feature_flag(USE_DEPRECATED):
yield