Skip to content

Emit Python warnings for beta and tech preview APIs #2675

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions elasticsearch/_async/client/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@
_TYPE_HOSTS,
CLIENT_META_SERVICE,
SKIP_IN_PATH,
Stability,
_base64_auth_header,
_quote,
_quote_query,
_rewrite_parameters,
_stability_warning,
client_node_configs,
is_requests_http_auth,
is_requests_node_class,
Expand All @@ -37,8 +39,10 @@
"_quote_query",
"_TYPE_HOSTS",
"SKIP_IN_PATH",
"Stability",
"client_node_configs",
"_rewrite_parameters",
"_stability_warning",
"is_requests_http_auth",
"is_requests_node_class",
]
48 changes: 48 additions & 0 deletions elasticsearch/_sync/client/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import urllib.parse
import warnings
from datetime import date, datetime
from enum import Enum, auto
from functools import wraps
from typing import (
TYPE_CHECKING,
Expand Down Expand Up @@ -55,6 +56,8 @@
url_to_node_config,
)

from elasticsearch.exceptions import GeneralAvailabilityWarning

from ..._version import __versionstr__
from ...compat import to_bytes, to_str, warn_stacklevel

Expand All @@ -70,6 +73,14 @@
# Default User-Agent used by the client
USER_AGENT = create_user_agent("elasticsearch-py", __versionstr__)


class Stability(Enum):
STABLE = auto()
BETA = auto()
EXPERIMENTAL = auto()
DEPRECATED = auto()


_TYPE_HOSTS = Union[
str, Sequence[Union[str, Mapping[str, Union[str, int]], NodeConfig]]
]
Expand Down Expand Up @@ -450,6 +461,43 @@ def wrapped(*args: Any, **kwargs: Any) -> Any:
return wrapper


def _stability_warning(
stability: Stability,
version: Optional[str] = None,
message: Optional[str] = None,
) -> Callable[[F], F]:
def wrapper(api: F) -> F:
@wraps(api)
def wrapped(*args: Any, **kwargs: Any) -> Any:
if stability == Stability.BETA:
warnings.warn(
"This API is in beta and is subject to change. "
"The design and code is less mature than official GA features and is being provided as-is with no warranties. "
"Beta features are not subject to the support SLA of official GA features.",
category=GeneralAvailabilityWarning,
stacklevel=warn_stacklevel(),
)
elif stability == Stability.EXPERIMENTAL:
warnings.warn(
"This API is in technical preview and may be changed or removed in a future release. "
"Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.",
category=GeneralAvailabilityWarning,
stacklevel=warn_stacklevel(),
)
elif stability == Stability.DEPRECATED:
warnings.warn(
f"This API was deprecated in Elasticsearch {version}. {message}",
category=DeprecationWarning,
stacklevel=warn_stacklevel(),
)

return api(*args, **kwargs)

return wrapped # type: ignore[return-value]

return wrapper


def is_requests_http_auth(http_auth: Any) -> bool:
"""Detect if an http_auth value is a custom Requests auth object"""
try:
Expand Down
4 changes: 4 additions & 0 deletions elasticsearch/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ class ElasticsearchWarning(TransportWarning):
"""


class GeneralAvailabilityWarning(TransportWarning):
"""Warning that is raised when a feature is not yet GA."""


# Aliases for backwards compatibility
ElasticsearchDeprecationWarning = ElasticsearchWarning
RequestError = BadRequestError
Expand Down
64 changes: 63 additions & 1 deletion test_elasticsearch/test_client/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
# under the License.


from elasticsearch._sync.client.utils import _quote
import warnings

from elasticsearch._sync.client.utils import Stability, _quote, _stability_warning
from elasticsearch.exceptions import GeneralAvailabilityWarning


def test_handles_ascii():
Expand All @@ -36,3 +39,62 @@ def test_handles_unicode():
def test_handles_unicode2():
string = "中*文,"
assert "%E4%B8%AD*%E6%96%87," == _quote(string)


class TestStabilityWarning:
def test_default(self):

@_stability_warning(stability=Stability.STABLE)
def func_default(*args, **kwargs):
pass

with warnings.catch_warnings():
warnings.simplefilter("error")
func_default()

def test_beta(self, recwarn):

@_stability_warning(stability=Stability.BETA)
def func_beta(*args, **kwargs):
pass

func_beta()

assert len(recwarn) == 1
user_warning = recwarn.pop(GeneralAvailabilityWarning)
assert user_warning.category == GeneralAvailabilityWarning
assert user_warning.message.args[0].startswith(
"This API is in beta and is subject to change."
)

def test_experimental(self, recwarn):

@_stability_warning(stability=Stability.EXPERIMENTAL)
def func_experimental(*args, **kwargs):
pass

func_experimental()

assert len(recwarn) == 1
user_warning = recwarn.pop(GeneralAvailabilityWarning)
assert user_warning.category == GeneralAvailabilityWarning
assert user_warning.message.args[0].startswith(
"This API is in technical preview and may be changed or removed in a future release."
)

def test_deprecated(self, recwarn):

@_stability_warning(
stability=Stability.DEPRECATED, version="8.4.0", message="Use bar instead."
)
def func_deprecated(*args, **kwargs):
pass

func_deprecated()

assert len(recwarn) == 1
user_warning = recwarn.pop(DeprecationWarning)
assert user_warning.category == DeprecationWarning
assert user_warning.message.args[0] == (
"This API was deprecated in Elasticsearch 8.4.0. Use bar instead."
)