Skip to content

Commit d2e6a6f

Browse files
committed
Add API warnings infrastructure
1 parent 94573cf commit d2e6a6f

File tree

4 files changed

+117
-1
lines changed

4 files changed

+117
-1
lines changed

elasticsearch/_async/client/utils.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@
2020
_TYPE_HOSTS,
2121
CLIENT_META_SERVICE,
2222
SKIP_IN_PATH,
23+
Stability,
2324
_base64_auth_header,
2425
_quote,
2526
_quote_query,
2627
_rewrite_parameters,
28+
_stability_warning,
2729
client_node_configs,
2830
is_requests_http_auth,
2931
is_requests_node_class,
@@ -37,8 +39,10 @@
3739
"_quote_query",
3840
"_TYPE_HOSTS",
3941
"SKIP_IN_PATH",
42+
"Stability",
4043
"client_node_configs",
4144
"_rewrite_parameters",
45+
"_stability_warning",
4246
"is_requests_http_auth",
4347
"is_requests_node_class",
4448
]

elasticsearch/_sync/client/utils.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import urllib.parse
2121
import warnings
2222
from datetime import date, datetime
23+
from enum import Enum, auto
2324
from functools import wraps
2425
from typing import (
2526
TYPE_CHECKING,
@@ -55,6 +56,8 @@
5556
url_to_node_config,
5657
)
5758

59+
from elasticsearch.exceptions import GeneralAvailabilityWarning
60+
5861
from ..._version import __versionstr__
5962
from ...compat import to_bytes, to_str, warn_stacklevel
6063

@@ -93,6 +96,13 @@
9396
F = TypeVar("F", bound=Callable[..., Any])
9497

9598

99+
class Stability(Enum):
100+
STABLE = auto()
101+
BETA = auto()
102+
EXPERIMENTAL = auto()
103+
DEPRECATED = auto()
104+
105+
96106
def client_node_configs(
97107
hosts: Optional[_TYPE_HOSTS],
98108
cloud_id: Optional[str],
@@ -450,6 +460,42 @@ def wrapped(*args: Any, **kwargs: Any) -> Any:
450460
return wrapper
451461

452462

463+
def _stability_warning(
464+
stability: Stability,
465+
version: Optional[str] = None,
466+
message: Optional[str] = None,
467+
) -> Callable[[F], F]:
468+
def wrapper(api: F) -> F:
469+
@wraps(api)
470+
def wrapped(*args: Any, **kwargs: Any) -> Any:
471+
if stability == Stability.BETA:
472+
warnings.warn(
473+
"This API is in beta and is subject to change. "
474+
"The design and code is less mature than official GA features and is being provided as-is with no warranties. "
475+
"Beta features are not subject to the support SLA of official GA features.",
476+
category=GeneralAvailabilityWarning,
477+
stacklevel=warn_stacklevel(),
478+
)
479+
elif stability == Stability.EXPERIMENTAL:
480+
warnings.warn(
481+
"This API is in technical preview and may be changed or removed in a future release. "
482+
"Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.",
483+
category=GeneralAvailabilityWarning,
484+
stacklevel=warn_stacklevel(),
485+
)
486+
return api(*args, **kwargs)
487+
elif stability == Stability.DEPRECATED:
488+
warnings.warn(
489+
f"This API was deprecated in Elasticsearch {version}. {message}",
490+
category=DeprecationWarning,
491+
stacklevel=warn_stacklevel(),
492+
)
493+
494+
return wrapped # type: ignore[return-value]
495+
496+
return wrapper
497+
498+
453499
def is_requests_http_auth(http_auth: Any) -> bool:
454500
"""Detect if an http_auth value is a custom Requests auth object"""
455501
try:

elasticsearch/exceptions.py

Lines changed: 4 additions & 0 deletions
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/test_client/test_utils.py

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616
# under the License.
1717

1818

19-
from elasticsearch._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
2023

2124

2225
def test_handles_ascii():
@@ -36,3 +39,62 @@ def test_handles_unicode():
3639
def test_handles_unicode2():
3740
string = "中*文,"
3841
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)