diff --git a/elasticsearch/_async/client/license.py b/elasticsearch/_async/client/license.py index 1ccb4305f..669bd0bfe 100644 --- a/elasticsearch/_async/client/license.py +++ b/elasticsearch/_async/client/license.py @@ -97,7 +97,7 @@ async def post_start_basic(self, params=None, headers=None): "POST", "/_license/start_basic", params=params, headers=headers ) - @query_params("acknowledge", "doc_type") + @query_params("acknowledge", "type") async def post_start_trial(self, params=None, headers=None): """ starts a limited time trial license. @@ -106,12 +106,9 @@ async def post_start_trial(self, params=None, headers=None): :arg acknowledge: whether the user has acknowledged acknowledge messages (default: false) - :arg doc_type: The type of trial license to generate (default: + :arg type: The type of trial license to generate (default: "trial") """ - if "doc_type" in params: - params["type"] = params.pop("doc_type") - return await self.transport.perform_request( "POST", "/_license/start_trial", params=params, headers=headers ) diff --git a/elasticsearch/_async/client/license.pyi b/elasticsearch/_async/client/license.pyi index 32e51a00f..31a16ef58 100644 --- a/elasticsearch/_async/client/license.pyi +++ b/elasticsearch/_async/client/license.pyi @@ -134,7 +134,7 @@ class LicenseClient(NamespacedClient): self, *, acknowledge: Optional[bool] = ..., - doc_type: Optional[Any] = ..., + type: Optional[Any] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., diff --git a/elasticsearch/_async/client/nodes.py b/elasticsearch/_async/client/nodes.py index 234aeeee5..78042473c 100644 --- a/elasticsearch/_async/client/nodes.py +++ b/elasticsearch/_async/client/nodes.py @@ -124,7 +124,7 @@ async def stats( ) @query_params( - "doc_type", "ignore_idle_threads", "interval", "snapshots", "threads", "timeout" + "ignore_idle_threads", "interval", "snapshots", "threads", "timeout", "type" ) async def hot_threads(self, node_id=None, params=None, headers=None): """ @@ -136,8 +136,6 @@ async def hot_threads(self, node_id=None, params=None, headers=None): limit the returned information; use `_local` to return information from the node you're connecting to, leave empty to get information from all nodes - :arg doc_type: The type to sample (default: cpu) Valid choices: - cpu, wait, block :arg ignore_idle_threads: Don't show threads that are in known- idle places, such as waiting on a socket select or pulling from an empty task queue (default: true) @@ -147,10 +145,9 @@ async def hot_threads(self, node_id=None, params=None, headers=None): :arg threads: Specify the number of threads to provide information for (default: 3) :arg timeout: Explicit operation timeout + :arg type: The type to sample (default: cpu) Valid choices: + cpu, wait, block """ - if "doc_type" in params: - params["type"] = params.pop("doc_type") - return await self.transport.perform_request( "GET", _make_path("_nodes", node_id, "hot_threads"), diff --git a/elasticsearch/_async/client/nodes.pyi b/elasticsearch/_async/client/nodes.pyi index e959ee41a..54dba4248 100644 --- a/elasticsearch/_async/client/nodes.pyi +++ b/elasticsearch/_async/client/nodes.pyi @@ -100,12 +100,12 @@ class NodesClient(NamespacedClient): self, *, node_id: Optional[Any] = ..., - doc_type: Optional[Any] = ..., ignore_idle_threads: Optional[bool] = ..., interval: Optional[Any] = ..., snapshots: Optional[Any] = ..., threads: Optional[Any] = ..., timeout: Optional[Any] = ..., + type: Optional[Any] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., diff --git a/elasticsearch/client/license.py b/elasticsearch/client/license.py index 413107dbe..fce0f6dc9 100644 --- a/elasticsearch/client/license.py +++ b/elasticsearch/client/license.py @@ -97,7 +97,7 @@ def post_start_basic(self, params=None, headers=None): "POST", "/_license/start_basic", params=params, headers=headers ) - @query_params("acknowledge", "doc_type") + @query_params("acknowledge", "type") def post_start_trial(self, params=None, headers=None): """ starts a limited time trial license. @@ -106,12 +106,9 @@ def post_start_trial(self, params=None, headers=None): :arg acknowledge: whether the user has acknowledged acknowledge messages (default: false) - :arg doc_type: The type of trial license to generate (default: + :arg type: The type of trial license to generate (default: "trial") """ - if "doc_type" in params: - params["type"] = params.pop("doc_type") - return self.transport.perform_request( "POST", "/_license/start_trial", params=params, headers=headers ) diff --git a/elasticsearch/client/license.pyi b/elasticsearch/client/license.pyi index b14bd6e01..14d4d9ecd 100644 --- a/elasticsearch/client/license.pyi +++ b/elasticsearch/client/license.pyi @@ -134,7 +134,7 @@ class LicenseClient(NamespacedClient): self, *, acknowledge: Optional[bool] = ..., - doc_type: Optional[Any] = ..., + type: Optional[Any] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., diff --git a/elasticsearch/client/nodes.py b/elasticsearch/client/nodes.py index a1c137667..e25ddf5cf 100644 --- a/elasticsearch/client/nodes.py +++ b/elasticsearch/client/nodes.py @@ -124,7 +124,7 @@ def stats( ) @query_params( - "doc_type", "ignore_idle_threads", "interval", "snapshots", "threads", "timeout" + "ignore_idle_threads", "interval", "snapshots", "threads", "timeout", "type" ) def hot_threads(self, node_id=None, params=None, headers=None): """ @@ -136,8 +136,6 @@ def hot_threads(self, node_id=None, params=None, headers=None): limit the returned information; use `_local` to return information from the node you're connecting to, leave empty to get information from all nodes - :arg doc_type: The type to sample (default: cpu) Valid choices: - cpu, wait, block :arg ignore_idle_threads: Don't show threads that are in known- idle places, such as waiting on a socket select or pulling from an empty task queue (default: true) @@ -147,10 +145,9 @@ def hot_threads(self, node_id=None, params=None, headers=None): :arg threads: Specify the number of threads to provide information for (default: 3) :arg timeout: Explicit operation timeout + :arg type: The type to sample (default: cpu) Valid choices: + cpu, wait, block """ - if "doc_type" in params: - params["type"] = params.pop("doc_type") - return self.transport.perform_request( "GET", _make_path("_nodes", node_id, "hot_threads"), diff --git a/elasticsearch/client/nodes.pyi b/elasticsearch/client/nodes.pyi index 2e1081c26..6d640fae7 100644 --- a/elasticsearch/client/nodes.pyi +++ b/elasticsearch/client/nodes.pyi @@ -100,12 +100,12 @@ class NodesClient(NamespacedClient): self, *, node_id: Optional[Any] = ..., - doc_type: Optional[Any] = ..., ignore_idle_threads: Optional[bool] = ..., interval: Optional[Any] = ..., snapshots: Optional[Any] = ..., threads: Optional[Any] = ..., timeout: Optional[Any] = ..., + type: Optional[Any] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., diff --git a/elasticsearch/client/utils.py b/elasticsearch/client/utils.py index cd7b138b8..eb83d6ec5 100644 --- a/elasticsearch/client/utils.py +++ b/elasticsearch/client/utils.py @@ -133,6 +133,7 @@ def query_params(*es_query_params, **kwargs): body_only_params = set(body_params or ()) - set(es_query_params) body_name = kwargs.pop("body_name", None) body_required = kwargs.pop("body_required", False) + type_possible_in_params = "type" in es_query_params # There should be no APIs defined with both 'body_params' and a named body. assert not (body_name and body_params) @@ -163,6 +164,27 @@ def _wrapped(*args, **kwargs): using_body_kwarg = kwargs.get("body", None) is not None using_positional_args = args and len(args) > 1 + # The 'doc_type' parameter is deprecated in the query + # string. This was generated and missed in 7.x so to + # push users to use 'type' instead of 'doc_type' in 8.x + # we deprecate it here. + if type_possible_in_params: + doc_type_in_params = params and "doc_type" in params + doc_type_in_kwargs = "doc_type" in kwargs + + if doc_type_in_params or doc_type_in_kwargs: + warnings.warn( + "The 'doc_type' parameter is deprecated, use 'type' for this " + "API instead. See https://github.com/elastic/elasticsearch-py/" + "issues/1698 for more information", + category=DeprecationWarning, + stacklevel=2, + ) + if doc_type_in_params: + params["type"] = params.pop("doc_type") + if doc_type_in_kwargs: + kwargs["type"] = kwargs.pop("doc_type") + if using_body_kwarg or using_positional_args: # If there are any body-only parameters then we raise a 'TypeError' # to alert the user they have to either not use a 'body' parameter diff --git a/test_elasticsearch/test_async/test_server/test_rest_api_spec.py b/test_elasticsearch/test_async/test_server/test_rest_api_spec.py index 666c1fe94..a4657bd61 100644 --- a/test_elasticsearch/test_async/test_server/test_rest_api_spec.py +++ b/test_elasticsearch/test_async/test_server/test_rest_api_spec.py @@ -29,6 +29,7 @@ from elasticsearch.helpers.test import _get_version from ...test_server.test_rest_api_spec import ( + APIS_USING_TYPE_INSTEAD_OF_DOC_TYPE, APIS_WITH_BODY_FIELDS, IMPLEMENTED_FEATURES, PARAMS_RENAMES, @@ -138,6 +139,11 @@ async def run_do(self, action): # some parameters had to be renamed to not clash with python builtins, # compensate for k in PARAMS_RENAMES: + + # Don't do the 'doc_type' rename for APIs that actually want 'type' + if k == "type" and method in APIS_USING_TYPE_INSTEAD_OF_DOC_TYPE: + continue + if k in args: args[PARAMS_RENAMES[k]] = args.pop(k) diff --git a/test_elasticsearch/test_client/test_overrides.py b/test_elasticsearch/test_client/test_overrides.py index 6290b4966..488398ea8 100644 --- a/test_elasticsearch/test_client/test_overrides.py +++ b/test_elasticsearch/test_client/test_overrides.py @@ -16,6 +16,24 @@ # specific language governing permissions and limitations # under the License. +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +import warnings + import pytest from test_elasticsearch.test_cases import ElasticsearchTestCase @@ -184,3 +202,51 @@ def test_clear_scroll(self): assert calls == { ("DELETE", "/_search/scroll"): [({}, {}, {"scroll_id": "scroll-id"})] } + + def test_doc_type_works_for_apis_with_type(self): + with warnings.catch_warnings(record=True) as w: + self.client.license.post_start_trial(type="trial") + + assert w == [] + calls = self.client.transport.calls + assert calls == { + ("POST", "/_license/start_trial"): [({"type": b"trial"}, {}, None)] + } + self.client.transport.calls.pop(("POST", "/_license/start_trial")) + + with warnings.catch_warnings(record=True) as w: + self.client.license.post_start_trial(params={"type": "trial"}) + + assert w == [] + calls = self.client.transport.calls + assert calls == { + ("POST", "/_license/start_trial"): [({"type": "trial"}, {}, None)] + } + self.client.transport.calls.pop(("POST", "/_license/start_trial")) + + # Now we try using 'doc_type' in all the same places and see + # that things still work but we get deprecation warnings. + with pytest.warns(DeprecationWarning) as w: + self.client.license.post_start_trial(doc_type="trial") + + assert str(w[0].message) == ( + "The 'doc_type' parameter is deprecated, use 'type' for this API instead. See " + "https://github.com/elastic/elasticsearch-py/issues/1698 for more information" + ) + calls = self.client.transport.calls + assert calls == { + ("POST", "/_license/start_trial"): [({"type": b"trial"}, {}, None)] + } + self.client.transport.calls.pop(("POST", "/_license/start_trial")) + + with pytest.warns(DeprecationWarning) as w: + self.client.license.post_start_trial(params={"doc_type": "trial"}) + + assert str(w[0].message) == ( + "The 'doc_type' parameter is deprecated, use 'type' for this API instead. See " + "https://github.com/elastic/elasticsearch-py/issues/1698 for more information" + ) + calls = self.client.transport.calls + assert calls == { + ("POST", "/_license/start_trial"): [({"type": "trial"}, {}, None)] + } diff --git a/test_elasticsearch/test_server/test_rest_api_spec.py b/test_elasticsearch/test_server/test_rest_api_spec.py index d8e31a2b2..3fe6af6ec 100644 --- a/test_elasticsearch/test_server/test_rest_api_spec.py +++ b/test_elasticsearch/test_server/test_rest_api_spec.py @@ -42,6 +42,10 @@ # some params had to be changed in python, keep track of them so we can rename # those in the tests accordingly PARAMS_RENAMES = {"type": "doc_type", "from": "from_"} +APIS_USING_TYPE_INSTEAD_OF_DOC_TYPE = { + "nodes.hot_threads", + "license.post_start_trial", +} # mapping from catch values to http status codes CATCH_CODES = {"missing": 404, "conflict": 409, "unauthorized": 401} @@ -235,6 +239,11 @@ def run_do(self, action): # some parameters had to be renamed to not clash with python builtins, # compensate for k in PARAMS_RENAMES: + + # Don't do the 'doc_type' rename for APIs that actually want 'type' + if k == "type" and method in APIS_USING_TYPE_INSTEAD_OF_DOC_TYPE: + continue + if k in args: args[PARAMS_RENAMES[k]] = args.pop(k)