Skip to content

Commit 7bae67e

Browse files
committed
Accept body in _rewrite_parameters
1 parent f16d183 commit 7bae67e

File tree

5 files changed

+106
-25
lines changed

5 files changed

+106
-25
lines changed

docs/guide/migration.asciidoc

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ with the `security.grant_api_key` API:
167167

168168
[source,python]
169169
------------------------------------
170-
# 8.0+ SUPPORTED USAGES:
170+
# 8.0+ SUPPORTED USAGE:
171171
resp = (
172172
client.options(
173173
# This is the API key being used for the request
@@ -183,13 +183,36 @@ resp = (
183183
)
184184
)
185185
186-
# 7.x DEPRECATED USAGES (Don't do this!):
186+
# 7.x DEPRECATED USAGE (Don't do this!):
187187
resp = (
188188
# This is the API key being used for the request
189189
client.security.grant_api_key(
190190
api_key=("request-id", "request-api-key"),
191-
# This is the API key being granted
192191
body={
192+
# This is the API key being granted
193+
"api_key": {
194+
"name": "granted-api-key"
195+
},
196+
"grant_type": "password",
197+
"username": "elastic",
198+
"password": "changeme"
199+
}
200+
)
201+
)
202+
------------------------------------
203+
204+
Starting with the 8.12 client, using a body parameter is fully supported again, meaning you can also use `grant_api_key` like this:
205+
206+
[source,python]
207+
------------------------------------
208+
# 8.12+ SUPPORTED USAGE:
209+
resp = (
210+
client.options(
211+
# This is the API key being used for the request
212+
api_key=("request-id", "request-api-key")
213+
).security.grant_api_key(
214+
body={
215+
# This is the API key being granted
193216
"api_key": {
194217
"name": "granted-api-key"
195218
},
@@ -295,7 +318,7 @@ from elasticsearch import TransportError, Elasticsearch
295318
try:
296319
client.indices.get(index="index-that-does-not-exist")
297320
298-
# In elasticsearch-python v7.x this would capture the resulting
321+
# In elasticsearch-py v7.x this would capture the resulting
299322
# 'NotFoundError' that would be raised above. But in 8.0.0 this
300323
# 'except TransportError' won't capture 'NotFoundError'.
301324
except TransportError as err:

elasticsearch/_async/helpers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ def normalize_from_keyword(kw: MutableMapping[str, Any]) -> None:
445445
search_kwargs = kwargs.copy()
446446
search_kwargs["scroll"] = scroll
447447
search_kwargs["size"] = size
448-
resp = await client.search(body=query, **search_kwargs) # type: ignore[call-arg]
448+
resp = await client.search(body=query, **search_kwargs)
449449

450450
scroll_id: Optional[str] = resp.get("_scroll_id")
451451
scroll_transport_kwargs = pop_transport_kwargs(scroll_kwargs)

elasticsearch/_sync/client/utils.py

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@
7474
str, Sequence[Union[str, Mapping[str, Union[str, int]], NodeConfig]]
7575
]
7676

77+
_TYPE_BODY = Union[bytes, str, Dict[str, Any]]
78+
7779
_TYPE_ASYNC_SNIFF_CALLBACK = Callable[
7880
[AsyncTransport, SniffOptions], Awaitable[List[NodeConfig]]
7981
]
@@ -289,14 +291,40 @@ def _merge_kwargs_no_duplicates(kwargs: Dict[str, Any], values: Dict[str, Any])
289291
if key in kwargs:
290292
raise ValueError(
291293
f"Received multiple values for '{key}', specify parameters "
292-
"directly instead of using 'body' or 'params'"
294+
"directly instead of using 'params'"
293295
)
294296
kwargs[key] = val
295297

296298

299+
def _merge_body_fields_no_duplicates(
300+
body: _TYPE_BODY, kwargs: Dict[str, Any], body_fields: Tuple[str, ...]
301+
) -> None:
302+
for key in list(kwargs.keys()):
303+
if key in body_fields:
304+
if isinstance(body, (str, bytes)):
305+
raise ValueError(
306+
"Couldn't merge 'body' with other parameters as it wasn't a mapping."
307+
)
308+
309+
if key in body:
310+
raise ValueError(
311+
f"Received multiple values for '{key}', specify parameters "
312+
"using either body or parameters, not both."
313+
)
314+
315+
warnings.warn(
316+
f"Received '{key}' via a specific parameter in the presence of a "
317+
"'body' parameter, which is deprecated and will be removed in a future "
318+
"version. Instead, use only 'body' or only specific paremeters.",
319+
category=DeprecationWarning,
320+
stacklevel=warn_stacklevel(),
321+
)
322+
body[key] = kwargs.pop(key)
323+
324+
297325
def _rewrite_parameters(
298326
body_name: Optional[str] = None,
299-
body_fields: bool = False,
327+
body_fields: Optional[Tuple[str, ...]] = None,
300328
parameter_aliases: Optional[Dict[str, str]] = None,
301329
ignore_deprecated_options: Optional[Set[str]] = None,
302330
) -> Callable[[F], F]:
@@ -372,7 +400,7 @@ def wrapped(*args: Any, **kwargs: Any) -> Any:
372400
if "body" in kwargs and (
373401
not ignore_deprecated_options or "body" not in ignore_deprecated_options
374402
):
375-
body = kwargs.pop("body")
403+
body: Optional[_TYPE_BODY] = kwargs.pop("body")
376404
if body is not None:
377405
if body_name:
378406
if body_name in kwargs:
@@ -384,13 +412,9 @@ def wrapped(*args: Any, **kwargs: Any) -> Any:
384412
)
385413
kwargs[body_name] = body
386414

387-
elif body_fields:
388-
if not hasattr(body, "items"):
389-
raise ValueError(
390-
"Couldn't merge 'body' with other parameters as it wasn't a mapping. "
391-
"Instead of using 'body' use individual API parameters"
392-
)
393-
_merge_kwargs_no_duplicates(kwargs, body)
415+
elif body_fields is not None:
416+
_merge_body_fields_no_duplicates(body, kwargs, body_fields)
417+
kwargs["body"] = body
394418

395419
if parameter_aliases:
396420
for alias, rename_to in parameter_aliases.items():

elasticsearch/helpers/actions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -714,7 +714,7 @@ def normalize_from_keyword(kw: MutableMapping[str, Any]) -> None:
714714
search_kwargs = kwargs.copy()
715715
search_kwargs["scroll"] = scroll
716716
search_kwargs["size"] = size
717-
resp = client.search(body=query, **search_kwargs) # type: ignore[call-arg]
717+
resp = client.search(body=query, **search_kwargs)
718718

719719
scroll_id = resp.get("_scroll_id")
720720
scroll_transport_kwargs = pop_transport_kwargs(scroll_kwargs)

test_elasticsearch/test_client/test_rewrite_parameters.py

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,19 @@ def wrapped_func_default(self, *args, **kwargs):
4242
def wrapped_func_body_name(self, *args, **kwargs):
4343
self.calls.append((args, kwargs))
4444

45-
@_rewrite_parameters(body_fields=True)
45+
@_rewrite_parameters(body_fields=("query", "source"))
4646
def wrapped_func_body_fields(self, *args, **kwargs):
4747
self.calls.append((args, kwargs))
4848

4949
@_rewrite_parameters(
50-
body_fields=True, ignore_deprecated_options={"api_key", "body", "params"}
50+
body_fields=("query",), ignore_deprecated_options={"api_key", "body", "params"}
5151
)
5252
def wrapped_func_ignore(self, *args, **kwargs):
5353
self.calls.append((args, kwargs))
5454

55-
@_rewrite_parameters(body_fields=True, parameter_aliases={"_source": "source"})
55+
@_rewrite_parameters(
56+
body_fields=("source",), parameter_aliases={"_source": "source"}
57+
)
5658
def wrapped_func_aliases(self, *args, **kwargs):
5759
self.calls.append((args, kwargs))
5860

@@ -81,6 +83,16 @@ def test_default(self):
8183
((), {"query": {"match_all": {}}, "key": "value"}),
8284
]
8385

86+
def test_default_params_conflict(self):
87+
with pytest.raises(ValueError) as e:
88+
self.wrapped_func_default(
89+
query={"match_all": {}},
90+
params={"query": {"match_all": {}}},
91+
)
92+
assert str(e.value) == (
93+
"Received multiple values for 'query', specify parameters directly instead of using 'params'"
94+
)
95+
8496
def test_body_name_using_body(self):
8597
with warnings.catch_warnings(record=True) as w:
8698
self.wrapped_func_body_name(
@@ -142,18 +154,21 @@ def test_body_fields(self):
142154

143155
assert self.calls == [
144156
((), {"api_key": ("id", "api_key")}),
145-
((), {"query": {"match_all": {}}}),
157+
((), {"body": {"query": {"match_all": {}}}}),
146158
]
147159

148160
@pytest.mark.parametrize(
149-
"body", ['{"query": {"match_all": {}}}', b'{"query": {"match_all": {}}}']
161+
"body, kwargs",
162+
[
163+
('{"query": {"match_all": {}}}', {"query": {"match_all": {}}}),
164+
(b'{"query": {"match_all": {}}}', {"query": {"match_all": {}}}),
165+
],
150166
)
151-
def test_error_on_body_merge(self, body):
167+
def test_error_on_body_merge(self, body, kwargs):
152168
with pytest.raises(ValueError) as e:
153-
self.wrapped_func_body_fields(body=body)
169+
self.wrapped_func_body_fields(body=body, **kwargs)
154170
assert str(e.value) == (
155-
"Couldn't merge 'body' with other parameters as it wasn't a mapping. Instead of "
156-
"using 'body' use individual API parameters"
171+
"Couldn't merge 'body' with other parameters as it wasn't a mapping."
157172
)
158173

159174
@pytest.mark.parametrize(
@@ -167,6 +182,25 @@ def test_error_on_params_merge(self, params):
167182
"using 'params' use individual API parameters"
168183
)
169184

185+
def test_body_fields_merge(self):
186+
with warnings.catch_warnings(record=True) as w:
187+
self.wrapped_func_body_fields(source=False, body={"query": {}})
188+
189+
assert len(w) == 1
190+
assert w[0].category == DeprecationWarning
191+
assert str(w[0].message) == (
192+
"Received 'source' via a specific parameter in the presence of a "
193+
"'body' parameter, which is deprecated and will be removed in a future "
194+
"version. Instead, use only 'body' or only specific paremeters."
195+
)
196+
197+
def test_body_fields_conflict(self):
198+
with pytest.raises(ValueError) as e:
199+
self.wrapped_func_body_fields(query={"match_all": {}}, body={"query": {}})
200+
assert str(e.value) == (
201+
"Received multiple values for 'query', specify parameters using either body or parameters, not both."
202+
)
203+
170204
def test_ignore_deprecated_options(self):
171205
with warnings.catch_warnings(record=True) as w:
172206
self.wrapped_func_ignore(

0 commit comments

Comments
 (0)