cyberparks_details/.venv/lib/python3.12/site-packages/trio/_deprecate.py

169 lines
5.3 KiB
Python

from __future__ import annotations
import sys
import warnings
from functools import wraps
from typing import TYPE_CHECKING, ClassVar, TypeVar
import attrs
if TYPE_CHECKING:
from collections.abc import Callable
from typing_extensions import ParamSpec
ArgsT = ParamSpec("ArgsT")
RetT = TypeVar("RetT")
# We want our warnings to be visible by default (at least for now), but we
# also want it to be possible to override that using the -W switch. AFAICT
# this means we cannot inherit from DeprecationWarning, because the only way
# to make it visible by default then would be to add our own filter at import
# time, but that would override -W switches...
class TrioDeprecationWarning(FutureWarning):
"""Warning emitted if you use deprecated Trio functionality.
While a relatively mature project, Trio remains committed to refining its
design and improving usability. As part of this, we occasionally deprecate
or remove functionality that proves suboptimal. If you use Trio, we
recommend `subscribing to issue #1
<https://github.com/python-trio/trio/issues/1>`__ to get information about
upcoming deprecations and other backwards compatibility breaking changes.
Despite the name, this class currently inherits from
:class:`FutureWarning`, not :class:`DeprecationWarning`, because until a
1.0 release, we want these warnings to be visible by default. You can hide
them by installing a filter or with the ``-W`` switch: see the
:mod:`warnings` documentation for details.
"""
def _url_for_issue(issue: int) -> str:
return f"https://github.com/python-trio/trio/issues/{issue}"
def _stringify(thing: object) -> str:
if hasattr(thing, "__module__") and hasattr(thing, "__qualname__"):
return f"{thing.__module__}.{thing.__qualname__}"
return str(thing)
def warn_deprecated(
thing: object,
version: str,
*,
issue: int | None,
instead: object,
stacklevel: int = 2,
use_triodeprecationwarning: bool = False,
) -> None:
stacklevel += 1
msg = f"{_stringify(thing)} is deprecated since Trio {version}"
if instead is None:
msg += " with no replacement"
else:
msg += f"; use {_stringify(instead)} instead"
if issue is not None:
msg += f" ({_url_for_issue(issue)})"
if use_triodeprecationwarning:
warning_class: type[Warning] = TrioDeprecationWarning
else:
warning_class = DeprecationWarning
warnings.warn(warning_class(msg), stacklevel=stacklevel)
# @deprecated("0.2.0", issue=..., instead=...)
# def ...
def deprecated(
version: str,
*,
thing: object = None,
issue: int | None,
instead: object,
use_triodeprecationwarning: bool = False,
) -> Callable[[Callable[ArgsT, RetT]], Callable[ArgsT, RetT]]:
def do_wrap(fn: Callable[ArgsT, RetT]) -> Callable[ArgsT, RetT]:
nonlocal thing
@wraps(fn)
def wrapper(*args: ArgsT.args, **kwargs: ArgsT.kwargs) -> RetT:
warn_deprecated(
thing,
version,
instead=instead,
issue=issue,
use_triodeprecationwarning=use_triodeprecationwarning,
)
return fn(*args, **kwargs)
# If our __module__ or __qualname__ get modified, we want to pick up
# on that, so we read them off the wrapper object instead of the (now
# hidden) fn object
if thing is None:
thing = wrapper
if wrapper.__doc__ is not None:
doc = wrapper.__doc__
doc = doc.rstrip()
doc += "\n\n"
doc += f".. deprecated:: {version}\n"
if instead is not None:
doc += f" Use {_stringify(instead)} instead.\n"
if issue is not None:
doc += f" For details, see `issue #{issue} <{_url_for_issue(issue)}>`__.\n"
doc += "\n"
wrapper.__doc__ = doc
return wrapper
return do_wrap
def deprecated_alias(
old_qualname: str,
new_fn: Callable[ArgsT, RetT],
version: str,
*,
issue: int | None,
) -> Callable[ArgsT, RetT]:
@deprecated(version, issue=issue, instead=new_fn)
@wraps(new_fn, assigned=("__module__", "__annotations__"))
def wrapper(*args: ArgsT.args, **kwargs: ArgsT.kwargs) -> RetT:
"""Deprecated alias."""
return new_fn(*args, **kwargs)
wrapper.__qualname__ = old_qualname
wrapper.__name__ = old_qualname.rpartition(".")[-1]
return wrapper
@attrs.frozen(slots=False)
class DeprecatedAttribute:
_not_set: ClassVar[object] = object()
value: object
version: str
issue: int | None
instead: object = _not_set
def deprecate_attributes(
module_name: str, deprecated_attributes: dict[str, DeprecatedAttribute]
) -> None:
def __getattr__(name: str) -> object:
if name in deprecated_attributes:
info = deprecated_attributes[name]
instead = info.instead
if instead is DeprecatedAttribute._not_set:
instead = info.value
thing = f"{module_name}.{name}"
warn_deprecated(thing, info.version, issue=info.issue, instead=instead)
return info.value
msg = "module '{}' has no attribute '{}'"
raise AttributeError(msg.format(module_name, name))
sys.modules[module_name].__getattr__ = __getattr__ # type: ignore[method-assign]