Skip to content

Commit 8b4463e

Browse files
committed
Add API warnings infrastructure
1 parent a5e26b5 commit 8b4463e

File tree

4 files changed

+119
-3
lines changed

4 files changed

+119
-3
lines changed

elasticsearch_serverless/_async/client/utils.py

+4
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@
1919
_TYPE_HOST,
2020
CLIENT_META_SERVICE,
2121
SKIP_IN_PATH,
22+
Stability,
2223
_base64_auth_header,
2324
_quote,
2425
_quote_query,
2526
_rewrite_parameters,
27+
_stability_warning,
2628
client_node_config,
2729
is_requests_http_auth,
2830
is_requests_node_class,
@@ -35,8 +37,10 @@
3537
"_quote_query",
3638
"_TYPE_HOST",
3739
"SKIP_IN_PATH",
40+
"Stability",
3841
"client_node_config",
3942
"_rewrite_parameters",
43+
"_stability_warning",
4044
"is_requests_http_auth",
4145
"is_requests_node_class",
4246
]

elasticsearch_serverless/_sync/client/utils.py

+48
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import inspect
2020
import warnings
2121
from datetime import date, datetime
22+
from enum import Enum, auto
2223
from functools import wraps
2324
from typing import (
2425
TYPE_CHECKING,
@@ -46,6 +47,8 @@
4647
url_to_node_config,
4748
)
4849

50+
from elasticsearch_serverless.exceptions import GeneralAvailabilityWarning
51+
4952
from ..._version import __versionstr__
5053
from ...compat import to_bytes, to_str, warn_stacklevel
5154

@@ -62,6 +65,14 @@
6265
USER_AGENT = create_user_agent("elasticsearch-py", __versionstr__)
6366
ELASTIC_API_VERSION = "2023-10-31"
6467

68+
69+
class Stability(Enum):
70+
STABLE = auto()
71+
BETA = auto()
72+
EXPERIMENTAL = auto()
73+
DEPRECATED = auto()
74+
75+
6576
_TYPE_HOST = Union[str, Mapping[str, Union[str, int]], NodeConfig]
6677

6778
_TYPE_BODY = Union[bytes, str, Dict[str, Any]]
@@ -420,6 +431,43 @@ def wrapped(*args: Any, **kwargs: Any) -> Any:
420431
return wrapper
421432

422433

434+
def _stability_warning(
435+
stability: Stability,
436+
version: Optional[str] = None,
437+
message: Optional[str] = None,
438+
) -> Callable[[F], F]:
439+
def wrapper(api: F) -> F:
440+
@wraps(api)
441+
def wrapped(*args: Any, **kwargs: Any) -> Any:
442+
if stability == Stability.BETA:
443+
warnings.warn(
444+
"This API is in beta and is subject to change. "
445+
"The design and code is less mature than official GA features and is being provided as-is with no warranties. "
446+
"Beta features are not subject to the support SLA of official GA features.",
447+
category=GeneralAvailabilityWarning,
448+
stacklevel=warn_stacklevel(),
449+
)
450+
elif stability == Stability.EXPERIMENTAL:
451+
warnings.warn(
452+
"This API is in technical preview and may be changed or removed in a future release. "
453+
"Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.",
454+
category=GeneralAvailabilityWarning,
455+
stacklevel=warn_stacklevel(),
456+
)
457+
elif stability == Stability.DEPRECATED:
458+
warnings.warn(
459+
f"This API was deprecated in Elasticsearch {version}. {message}",
460+
category=DeprecationWarning,
461+
stacklevel=warn_stacklevel(),
462+
)
463+
464+
return api(*args, **kwargs)
465+
466+
return wrapped # type: ignore[return-value]
467+
468+
return wrapper
469+
470+
423471
def is_requests_http_auth(http_auth: Any) -> bool:
424472
"""Detect if an http_auth value is a custom Requests auth object"""
425473
try:

elasticsearch_serverless/exceptions.py

+4
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ class ElasticsearchWarning(TransportWarning):
115115
"""
116116

117117

118+
class GeneralAvailabilityWarning(TransportWarning):
119+
"""Warning that is raised when a feature is not yet GA."""
120+
121+
118122
# Aliases for backwards compatibility
119123
ElasticsearchDeprecationWarning = ElasticsearchWarning
120124
RequestError = BadRequestError

test_elasticsearch_serverless/test_client/test_utils.py

+63-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# -*- coding: utf-8 -*-
21
# Licensed to Elasticsearch B.V. under one or more contributor
32
# license agreements. See the NOTICE file distributed with
43
# this work for additional information regarding copyright
@@ -16,9 +15,11 @@
1615
# specific language governing permissions and limitations
1716
# under the License.
1817

19-
from __future__ import unicode_literals
2018

21-
from elasticsearch_serverless._sync.client.utils import _quote
19+
import warnings
20+
21+
from elasticsearch._sync.client.utils import Stability, _quote, _stability_warning
22+
from elasticsearch.exceptions import GeneralAvailabilityWarning
2223

2324

2425
def test_handles_ascii():
@@ -38,3 +39,62 @@ def test_handles_unicode():
3839
def test_handles_unicode2():
3940
string = "中*文,"
4041
assert "%E4%B8%AD*%E6%96%87," == _quote(string)
42+
43+
44+
class TestStabilityWarning:
45+
def test_default(self):
46+
47+
@_stability_warning(stability=Stability.STABLE)
48+
def func_default(*args, **kwargs):
49+
pass
50+
51+
with warnings.catch_warnings():
52+
warnings.simplefilter("error")
53+
func_default()
54+
55+
def test_beta(self, recwarn):
56+
57+
@_stability_warning(stability=Stability.BETA)
58+
def func_beta(*args, **kwargs):
59+
pass
60+
61+
func_beta()
62+
63+
assert len(recwarn) == 1
64+
user_warning = recwarn.pop(GeneralAvailabilityWarning)
65+
assert user_warning.category == GeneralAvailabilityWarning
66+
assert user_warning.message.args[0].startswith(
67+
"This API is in beta and is subject to change."
68+
)
69+
70+
def test_experimental(self, recwarn):
71+
72+
@_stability_warning(stability=Stability.EXPERIMENTAL)
73+
def func_experimental(*args, **kwargs):
74+
pass
75+
76+
func_experimental()
77+
78+
assert len(recwarn) == 1
79+
user_warning = recwarn.pop(GeneralAvailabilityWarning)
80+
assert user_warning.category == GeneralAvailabilityWarning
81+
assert user_warning.message.args[0].startswith(
82+
"This API is in technical preview and may be changed or removed in a future release."
83+
)
84+
85+
def test_deprecated(self, recwarn):
86+
87+
@_stability_warning(
88+
stability=Stability.DEPRECATED, version="8.4.0", message="Use bar instead."
89+
)
90+
def func_deprecated(*args, **kwargs):
91+
pass
92+
93+
func_deprecated()
94+
95+
assert len(recwarn) == 1
96+
user_warning = recwarn.pop(DeprecationWarning)
97+
assert user_warning.category == DeprecationWarning
98+
assert user_warning.message.args[0] == (
99+
"This API was deprecated in Elasticsearch 8.4.0. Use bar instead."
100+
)

0 commit comments

Comments
 (0)