From 1bac9cc81924d0d7c42151b71e3d653ffac7cfde Mon Sep 17 00:00:00 2001 From: Seth Michael Larson Date: Tue, 12 Oct 2021 10:43:36 -0500 Subject: [PATCH] Move synchronous client to 'elasticsearch/_sync/client' for better mirroring --- elasticsearch/_async/client/__init__.py | 184 ++++++--- elasticsearch/_async/client/__init__.pyi | 6 +- elasticsearch/_async/client/_base.py | 151 ++++++++ elasticsearch/_async/client/async_search.py | 3 +- elasticsearch/_async/client/autoscaling.py | 3 +- elasticsearch/_async/client/cat.py | 3 +- elasticsearch/_async/client/ccr.py | 3 +- elasticsearch/_async/client/cluster.py | 3 +- .../_async/client/dangling_indices.py | 3 +- elasticsearch/_async/client/enrich.py | 3 +- elasticsearch/_async/client/eql.py | 3 +- elasticsearch/_async/client/features.py | 3 +- elasticsearch/_async/client/fleet.py | 8 +- elasticsearch/_async/client/graph.py | 3 +- elasticsearch/_async/client/ilm.py | 3 +- elasticsearch/_async/client/indices.py | 14 +- elasticsearch/_async/client/ingest.py | 3 +- elasticsearch/_async/client/license.py | 3 +- elasticsearch/_async/client/logstash.py | 3 +- elasticsearch/_async/client/migration.py | 25 +- elasticsearch/_async/client/migration.pyi | 32 ++ elasticsearch/_async/client/ml.py | 83 ++++- elasticsearch/_async/client/ml.pyi | 39 ++ elasticsearch/_async/client/monitoring.py | 14 +- elasticsearch/_async/client/nodes.py | 9 +- elasticsearch/_async/client/nodes.pyi | 4 +- elasticsearch/_async/client/rollup.py | 3 +- .../_async/client/searchable_snapshots.py | 13 +- elasticsearch/_async/client/security.py | 28 +- elasticsearch/_async/client/shutdown.py | 27 +- elasticsearch/_async/client/slm.py | 3 +- elasticsearch/_async/client/snapshot.py | 3 +- elasticsearch/_async/client/sql.py | 3 +- elasticsearch/_async/client/ssl.py | 3 +- elasticsearch/_async/client/tasks.py | 3 +- elasticsearch/_async/client/text_structure.py | 3 +- elasticsearch/_async/client/transform.py | 3 +- elasticsearch/_async/client/utils.py | 30 +- elasticsearch/_async/client/watcher.py | 3 +- elasticsearch/_async/client/xpack.py | 3 +- elasticsearch/{ => _sync}/client/__init__.py | 184 ++++++--- elasticsearch/{ => _sync}/client/__init__.pyi | 6 +- elasticsearch/_sync/client/_base.py | 151 ++++++++ .../{ => _sync}/client/async_search.py | 3 +- .../{ => _sync}/client/async_search.pyi | 0 .../{ => _sync}/client/autoscaling.py | 3 +- .../{ => _sync}/client/autoscaling.pyi | 0 elasticsearch/{ => _sync}/client/cat.py | 3 +- elasticsearch/{ => _sync}/client/cat.pyi | 0 elasticsearch/{ => _sync}/client/ccr.py | 3 +- elasticsearch/{ => _sync}/client/ccr.pyi | 0 elasticsearch/{ => _sync}/client/cluster.py | 3 +- elasticsearch/{ => _sync}/client/cluster.pyi | 0 .../{ => _sync}/client/dangling_indices.py | 3 +- .../{ => _sync}/client/dangling_indices.pyi | 0 elasticsearch/{ => _sync}/client/enrich.py | 3 +- elasticsearch/{ => _sync}/client/enrich.pyi | 0 elasticsearch/{ => _sync}/client/eql.py | 3 +- elasticsearch/{ => _sync}/client/eql.pyi | 0 elasticsearch/{ => _sync}/client/features.py | 3 +- elasticsearch/{ => _sync}/client/features.pyi | 0 elasticsearch/{ => _sync}/client/fleet.py | 8 +- elasticsearch/{ => _sync}/client/fleet.pyi | 0 elasticsearch/{ => _sync}/client/graph.py | 3 +- elasticsearch/{ => _sync}/client/graph.pyi | 0 elasticsearch/{ => _sync}/client/ilm.py | 3 +- elasticsearch/{ => _sync}/client/ilm.pyi | 0 elasticsearch/{ => _sync}/client/indices.py | 14 +- elasticsearch/{ => _sync}/client/indices.pyi | 0 elasticsearch/{ => _sync}/client/ingest.py | 3 +- elasticsearch/{ => _sync}/client/ingest.pyi | 0 elasticsearch/{ => _sync}/client/license.py | 3 +- elasticsearch/{ => _sync}/client/license.pyi | 0 elasticsearch/{ => _sync}/client/logstash.py | 3 +- elasticsearch/{ => _sync}/client/logstash.pyi | 0 elasticsearch/{ => _sync}/client/migration.py | 25 +- .../{ => _sync}/client/migration.pyi | 32 ++ elasticsearch/{ => _sync}/client/ml.py | 81 +++- elasticsearch/{ => _sync}/client/ml.pyi | 39 ++ .../{ => _sync}/client/monitoring.py | 14 +- .../{ => _sync}/client/monitoring.pyi | 0 elasticsearch/{ => _sync}/client/nodes.py | 9 +- elasticsearch/{ => _sync}/client/nodes.pyi | 4 +- elasticsearch/{ => _sync}/client/rollup.py | 3 +- elasticsearch/{ => _sync}/client/rollup.pyi | 0 .../client/searchable_snapshots.py | 13 +- .../client/searchable_snapshots.pyi | 0 elasticsearch/{ => _sync}/client/security.py | 28 +- elasticsearch/{ => _sync}/client/security.pyi | 0 elasticsearch/{ => _sync}/client/shutdown.py | 27 +- elasticsearch/{ => _sync}/client/shutdown.pyi | 0 elasticsearch/{ => _sync}/client/slm.py | 3 +- elasticsearch/{ => _sync}/client/slm.pyi | 0 elasticsearch/{ => _sync}/client/snapshot.py | 3 +- elasticsearch/{ => _sync}/client/snapshot.pyi | 0 elasticsearch/{ => _sync}/client/sql.py | 3 +- elasticsearch/{ => _sync}/client/sql.pyi | 0 elasticsearch/{ => _sync}/client/ssl.py | 3 +- elasticsearch/{ => _sync}/client/ssl.pyi | 0 elasticsearch/{ => _sync}/client/tasks.py | 3 +- elasticsearch/{ => _sync}/client/tasks.pyi | 0 .../{ => _sync}/client/text_structure.py | 3 +- .../{ => _sync}/client/text_structure.pyi | 0 elasticsearch/{ => _sync}/client/transform.py | 3 +- .../{ => _sync}/client/transform.pyi | 0 elasticsearch/_sync/client/utils.py | 349 ++++++++++++++++++ elasticsearch/{ => _sync}/client/watcher.py | 3 +- elasticsearch/{ => _sync}/client/watcher.pyi | 0 elasticsearch/{ => _sync}/client/xpack.py | 3 +- elasticsearch/{ => _sync}/client/xpack.pyi | 0 .../{_async/client/utils.pyi => client.py} | 16 +- elasticsearch/client/utils.py | 241 ------------ utils/generate-api.py | 5 +- 113 files changed, 1451 insertions(+), 606 deletions(-) create mode 100644 elasticsearch/_async/client/_base.py rename elasticsearch/{ => _sync}/client/__init__.py (95%) rename elasticsearch/{ => _sync}/client/__init__.pyi (99%) create mode 100644 elasticsearch/_sync/client/_base.py rename elasticsearch/{ => _sync}/client/async_search.py (98%) rename elasticsearch/{ => _sync}/client/async_search.pyi (100%) rename elasticsearch/{ => _sync}/client/autoscaling.py (96%) rename elasticsearch/{ => _sync}/client/autoscaling.pyi (100%) rename elasticsearch/{ => _sync}/client/cat.py (99%) rename elasticsearch/{ => _sync}/client/cat.pyi (100%) rename elasticsearch/{ => _sync}/client/ccr.py (98%) rename elasticsearch/{ => _sync}/client/ccr.pyi (100%) rename elasticsearch/{ => _sync}/client/cluster.py (99%) rename elasticsearch/{ => _sync}/client/cluster.pyi (100%) rename elasticsearch/{ => _sync}/client/dangling_indices.py (96%) rename elasticsearch/{ => _sync}/client/dangling_indices.pyi (100%) rename elasticsearch/{ => _sync}/client/enrich.py (97%) rename elasticsearch/{ => _sync}/client/enrich.pyi (100%) rename elasticsearch/{ => _sync}/client/eql.py (97%) rename elasticsearch/{ => _sync}/client/eql.pyi (100%) rename elasticsearch/{ => _sync}/client/features.py (95%) rename elasticsearch/{ => _sync}/client/features.pyi (100%) rename elasticsearch/{ => _sync}/client/fleet.py (89%) rename elasticsearch/{ => _sync}/client/fleet.pyi (100%) rename elasticsearch/{ => _sync}/client/graph.py (93%) rename elasticsearch/{ => _sync}/client/graph.pyi (100%) rename elasticsearch/{ => _sync}/client/ilm.py (98%) rename elasticsearch/{ => _sync}/client/ilm.pyi (100%) rename elasticsearch/{ => _sync}/client/indices.py (99%) rename elasticsearch/{ => _sync}/client/indices.pyi (100%) rename elasticsearch/{ => _sync}/client/ingest.py (97%) rename elasticsearch/{ => _sync}/client/ingest.pyi (100%) rename elasticsearch/{ => _sync}/client/license.py (98%) rename elasticsearch/{ => _sync}/client/license.pyi (100%) rename elasticsearch/{ => _sync}/client/logstash.py (96%) rename elasticsearch/{ => _sync}/client/logstash.pyi (100%) rename elasticsearch/{ => _sync}/client/migration.py (61%) rename elasticsearch/{ => _sync}/client/migration.pyi (54%) rename elasticsearch/{ => _sync}/client/ml.py (96%) rename elasticsearch/{ => _sync}/client/ml.pyi (97%) rename elasticsearch/{ => _sync}/client/monitoring.py (89%) rename elasticsearch/{ => _sync}/client/monitoring.pyi (100%) rename elasticsearch/{ => _sync}/client/nodes.py (97%) rename elasticsearch/{ => _sync}/client/nodes.pyi (98%) rename elasticsearch/{ => _sync}/client/rollup.py (98%) rename elasticsearch/{ => _sync}/client/rollup.pyi (100%) rename elasticsearch/{ => _sync}/client/searchable_snapshots.py (93%) rename elasticsearch/{ => _sync}/client/searchable_snapshots.pyi (100%) rename elasticsearch/{ => _sync}/client/security.py (97%) rename elasticsearch/{ => _sync}/client/security.pyi (100%) rename elasticsearch/{ => _sync}/client/shutdown.py (81%) rename elasticsearch/{ => _sync}/client/shutdown.pyi (100%) rename elasticsearch/{ => _sync}/client/slm.py (98%) rename elasticsearch/{ => _sync}/client/slm.pyi (100%) rename elasticsearch/{ => _sync}/client/snapshot.py (99%) rename elasticsearch/{ => _sync}/client/snapshot.pyi (100%) rename elasticsearch/{ => _sync}/client/sql.py (97%) rename elasticsearch/{ => _sync}/client/sql.pyi (100%) rename elasticsearch/{ => _sync}/client/ssl.py (93%) rename elasticsearch/{ => _sync}/client/ssl.pyi (100%) rename elasticsearch/{ => _sync}/client/tasks.py (97%) rename elasticsearch/{ => _sync}/client/tasks.pyi (100%) rename elasticsearch/{ => _sync}/client/text_structure.py (97%) rename elasticsearch/{ => _sync}/client/text_structure.pyi (100%) rename elasticsearch/{ => _sync}/client/transform.py (98%) rename elasticsearch/{ => _sync}/client/transform.pyi (100%) create mode 100644 elasticsearch/_sync/client/utils.py rename elasticsearch/{ => _sync}/client/watcher.py (98%) rename elasticsearch/{ => _sync}/client/watcher.pyi (100%) rename elasticsearch/{ => _sync}/client/xpack.py (95%) rename elasticsearch/{ => _sync}/client/xpack.pyi (100%) rename elasticsearch/{_async/client/utils.pyi => client.py} (55%) delete mode 100644 elasticsearch/client/utils.py diff --git a/elasticsearch/_async/client/__init__.py b/elasticsearch/_async/client/__init__.py index f6b4db090..0b2b21e49 100644 --- a/elasticsearch/_async/client/__init__.py +++ b/elasticsearch/_async/client/__init__.py @@ -17,8 +17,13 @@ import logging +import warnings +from typing import Optional -from ..transport import AsyncTransport, TransportError +from elastic_transport import AsyncTransport, NotFoundError, TransportError +from elastic_transport.client_utils import DEFAULT + +from ._base import BaseClient from .async_search import AsyncSearchClient from .autoscaling import AutoscalingClient from .cat import CatClient @@ -50,14 +55,22 @@ from .tasks import TasksClient from .text_structure import TextStructureClient from .transform import TransformClient -from .utils import SKIP_IN_PATH, _bulk_body, _make_path, _normalize_hosts, query_params +from .utils import ( + _TYPE_HOSTS, + CLIENT_META_SERVICE, + SKIP_IN_PATH, + _deprecated_options, + _make_path, + client_node_configs, + query_params, +) from .watcher import WatcherClient from .xpack import XPackClient logger = logging.getLogger("elasticsearch") -class AsyncElasticsearch: +class AsyncElasticsearch(BaseClient): """ Elasticsearch low-level client. Provides a straightforward mapping from Python to ES REST endpoints. @@ -74,12 +87,6 @@ class AsyncElasticsearch: preferred (and only supported) way to get access to those classes and their methods. - You can specify your own connection class which should be used by providing - the ``connection_class`` parameter:: - - # create connection to localhost using the ThriftConnection - es = Elasticsearch(connection_class=ThriftConnection) - If you want to turn on :ref:`sniffing` you have several options (described in :class:`~elasticsearch.Transport`):: @@ -111,7 +118,7 @@ class AsyncElasticsearch: detailed description of the options):: es = Elasticsearch( - ['localhost:443', 'other_host:443'], + ['https://localhost:443', 'https://other_host:443'], # turn on SSL use_ssl=True, # make sure we verify SSL certificates @@ -125,7 +132,7 @@ class AsyncElasticsearch: detailed description of the options):: es = Elasticsearch( - ['localhost:443', 'other_host:443'], + ['https://localhost:443', 'https://other_host:443'], # turn on SSL use_ssl=True, # no verify SSL certificates @@ -139,7 +146,7 @@ class AsyncElasticsearch: detailed description of the options):: es = Elasticsearch( - ['localhost:443', 'other_host:443'], + ['https://localhost:443', 'https://other_host:443'], # turn on SSL use_ssl=True, # make sure we verify SSL certificates @@ -163,41 +170,113 @@ class AsyncElasticsearch: verify_certs=True ) - By default, `JSONSerializer - `_ - is used to encode all outgoing requests. + By default, ``JsonSerializer`` is used to encode all outgoing requests. However, you can implement your own custom serializer:: - from elasticsearch.serializer import JSONSerializer + from elasticsearch.serializer import JsonSerializer - class SetEncoder(JSONSerializer): + class SetEncoder(JsonSerializer): def default(self, obj): if isinstance(obj, set): return list(obj) if isinstance(obj, Something): return 'CustomSomethingRepresentation' - return JSONSerializer.default(self, obj) + return JsonSerializer.default(self, obj) es = Elasticsearch(serializer=SetEncoder()) """ - def __init__(self, hosts=None, transport_class=AsyncTransport, **kwargs): - """ - :arg hosts: list of nodes, or a single node, we should connect to. - Node should be a dictionary ({"host": "localhost", "port": 9200}), - the entire dictionary will be passed to the :class:`~elasticsearch.Connection` - class as kwargs, or a string in the format of ``host[:port]`` which will be - translated to a dictionary automatically. If no value is given the - :class:`~elasticsearch.Connection` class defaults will be used. - - :arg transport_class: :class:`~elasticsearch.Transport` subclass to use. + def __init__( + self, + hosts: Optional[_TYPE_HOSTS] = None, + *, + # API + cloud_id: Optional[str] = None, + api_key=None, + basic_auth=None, + bearer_auth=None, + # Node + headers=DEFAULT, + connections_per_node=DEFAULT, + http_compress=DEFAULT, + verify_certs=DEFAULT, + ca_certs=DEFAULT, + client_cert=DEFAULT, + client_key=DEFAULT, + ssl_assert_hostname=DEFAULT, + ssl_assert_fingerprint=DEFAULT, + ssl_version=DEFAULT, + ssl_context=DEFAULT, + ssl_show_warn=DEFAULT, + # Transport + transport_class=AsyncTransport, + request_timeout=DEFAULT, + node_class=DEFAULT, + node_pool_class=DEFAULT, + randomize_nodes_in_pool=DEFAULT, + node_selector_class=DEFAULT, + dead_backoff_factor=DEFAULT, + max_dead_backoff=DEFAULT, + serializers=DEFAULT, + default_mimetype="application/json", + max_retries=DEFAULT, + retry_on_status=DEFAULT, + retry_on_timeout=DEFAULT, + sniff_on_start=DEFAULT, + sniff_before_requests=DEFAULT, + sniff_on_node_failure=DEFAULT, + sniff_timeout=DEFAULT, + min_delay_between_sniffing=DEFAULT, + # Deprecated + timeout=DEFAULT, + # Internal use only + _transport: Optional[AsyncTransport] = None, + ) -> None: + super().__init__() + + if hosts is None and cloud_id is None and _transport is None: + raise ValueError("Either 'hosts' or 'cloud_id' must be specified") + + if timeout is not DEFAULT: + if request_timeout is not DEFAULT: + raise ValueError( + "Can't specify both 'timeout' and 'request_timeout', " + "instead only specify 'request_timeout'" + ) + warnings.warn( + "The 'timeout' parameter is deprecated in favor of 'request_timeout'", + category=DeprecationWarning, + stacklevel=2, + ) + request_timeout = timeout - :arg kwargs: any additional arguments will be passed on to the - :class:`~elasticsearch.Transport` class and, subsequently, to the - :class:`~elasticsearch.Connection` instances. - """ - self.transport = transport_class(_normalize_hosts(hosts), **kwargs) + self.transport: AsyncTransport + if _transport is not None: + self.transport = _transport + else: + node_configs = client_node_configs( + hosts, + cloud_id=cloud_id, + headers=headers, + connections_per_node=connections_per_node, + http_compress=http_compress, + verify_certs=verify_certs, + ca_certs=ca_certs, + client_cert=client_cert, + client_key=client_key, + ssl_assert_hostname=ssl_assert_hostname, + ssl_assert_fingerprint=ssl_assert_fingerprint, + ssl_version=ssl_version, + ssl_context=ssl_context, + ssl_show_warn=ssl_show_warn, + ) + transport_kwargs = {} + self.transport = transport_class( + node_configs, + client_meta_service=CLIENT_META_SERVICE, + **transport_kwargs, + ) # namespaced clients for compatibility with API names self.async_search = AsyncSearchClient(self) @@ -1193,11 +1272,6 @@ async def rank_eval(self, body, index=None, params=None, headers=None): ``_ - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version - :arg body: The ranking evaluation search definition, including search requests, document ratings and ranking metric definition. :arg index: A comma-separated list of index names to search; use @@ -1651,11 +1725,6 @@ async def get_script_context(self, params=None, headers=None): Returns all script contexts. ``_ - - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version """ return await self.transport.perform_request( "GET", "/_script_context", params=params, headers=headers @@ -1667,11 +1736,6 @@ async def get_script_languages(self, params=None, headers=None): Returns available script types, languages and contexts ``_ - - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version """ return await self.transport.perform_request( "GET", "/_script_language", params=params, headers=headers @@ -2050,7 +2114,7 @@ async def close_point_in_time(self, body=None, params=None, headers=None): @query_params( "expand_wildcards", "ignore_unavailable", "keep_alive", "preference", "routing" ) - async def open_point_in_time(self, index=None, params=None, headers=None): + async def open_point_in_time(self, index, params=None, headers=None): """ Open a point in time that can be used in subsequent searches @@ -2069,6 +2133,9 @@ async def open_point_in_time(self, index=None, params=None, headers=None): be performed on (default: random) :arg routing: Specific routing value """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument 'index'.") + return await self.transport.perform_request( "POST", _make_path(index, "_pit"), params=params, headers=headers ) @@ -2082,11 +2149,6 @@ async def terms_enum(self, index, body=None, params=None, headers=None): ``_ - .. warning:: - - This API is **beta** so may include breaking changes - or be removed in a future version - :arg index: A comma-separated list of index names to search; use `_all` or empty string to perform the operation on all indices :arg body: field name, string which is the prefix expected in @@ -2103,7 +2165,14 @@ async def terms_enum(self, index, body=None, params=None, headers=None): body=body, ) - @query_params("exact_bounds", "extent", "grid_precision", "grid_type", "size") + @query_params( + "exact_bounds", + "extent", + "grid_precision", + "grid_type", + "size", + "track_total_hits", + ) async def search_mvt( self, index, field, zoom, x, y, body=None, params=None, headers=None ): @@ -2133,9 +2202,12 @@ async def search_mvt( :arg grid_precision: Additional zoom levels available through the aggs layer. Accepts 0-8. Default: 8 :arg grid_type: Determines the geometry type for features in the - aggs layer. Valid choices: grid, point Default: grid + aggs layer. Valid choices: grid, point, centroid Default: grid :arg size: Maximum number of features to return in the hits layer. Accepts 0-10000. Default: 10000 + :arg track_total_hits: Indicate if the number of documents that + match the query should be tracked. A number can also be specified, to + accurately track the total hit count up to the number. """ for param in (index, field, zoom, x, y): if param in SKIP_IN_PATH: diff --git a/elasticsearch/_async/client/__init__.pyi b/elasticsearch/_async/client/__init__.pyi index 2ced1ca8f..2782160de 100644 --- a/elasticsearch/_async/client/__init__.pyi +++ b/elasticsearch/_async/client/__init__.pyi @@ -18,7 +18,8 @@ import logging from typing import Any, Collection, MutableMapping, Optional, Tuple, Type, Union -from ..transport import AsyncTransport +from elastic_transport import AsyncTransport + from .async_search import AsyncSearchClient from .autoscaling import AutoscalingClient from .cat import CatClient @@ -1110,8 +1111,8 @@ class AsyncElasticsearch: ) -> Any: ... async def open_point_in_time( self, + index: Any, *, - index: Optional[Any] = ..., expand_wildcards: Optional[Any] = ..., ignore_unavailable: Optional[Any] = ..., keep_alive: Optional[Any] = ..., @@ -1162,6 +1163,7 @@ class AsyncElasticsearch: grid_precision: Optional[Any] = ..., grid_type: Optional[Any] = ..., size: Optional[Any] = ..., + track_total_hits: Optional[Any] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., diff --git a/elasticsearch/_async/client/_base.py b/elasticsearch/_async/client/_base.py new file mode 100644 index 000000000..107831743 --- /dev/null +++ b/elasticsearch/_async/client/_base.py @@ -0,0 +1,151 @@ +# 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 urllib.parse +from typing import Any + +from elastic_transport import AsyncTransport, HttpHeaders +from elastic_transport.client_utils import DEFAULT +from ..exceptions import UnsupportedProductError + + +class BaseClient: + transport: AsyncTransport + + def __init__(self): + self._headers = HttpHeaders({"content-type": "application/json"}) + self._request_timeout = DEFAULT + self._ignore_status = DEFAULT + self._max_retries = DEFAULT + self._retry_on_timeout = DEFAULT + self._retry_on_status = DEFAULT + + async def _perform_request( + self, method: str, target: str, headers=None, params=None, body=None + ) -> Any: + # Handle the passing of 'params' as additional query parameters. + # This behavior is deprecated and should be removed in 9.0.0. + if params: + if "?" in target: + raise ValueError("Can't add query to a target that already has a query") + target = f"{target}?{urllib.parse.urlencode(params, quote_via=urllib.parse.quote)}" + + if headers: + request_headers = self._headers.copy() + request_headers.update(headers) + else: + request_headers = self._headers + + meta, response = await self.transport.perform_request( + method, target, headers=request_headers, body=body + ) + + # 'X-Elastic-Product: Elasticsearch' should be on every response. + if meta.headers.get("x-elastic-product", "") != "Elasticsearch": + raise UnsupportedProductError( + "The client noticed that the server is not Elasticsearch " + "and we do not support this unknown product" + ) + + return response + + def options( + self, + *, + opaque_id=DEFAULT, + api_key=DEFAULT, + basic_auth=DEFAULT, + bearer_auth=DEFAULT, + headers=DEFAULT, + request_timeout=DEFAULT, + ignore_status=DEFAULT, + max_retries=DEFAULT, + retry_on_status=DEFAULT, + retry_on_timeout=DEFAULT, + ): + client = type(self)(_transport=self.transport) + + new_headers = self._headers.copy() + if headers is not DEFAULT: + new_headers.update(headers) + if opaque_id is not DEFAULT: + new_headers["x-opaque-id"] = opaque_id + if ( + api_key is not DEFAULT + or basic_auth is not DEFAULT + or bearer_auth is not DEFAULT + ): + pass # TODO + + if request_timeout is not DEFAULT: + client._request_timeout = DEFAULT + if ignore_status is not DEFAULT: + client._ignore_status = DEFAULT + if max_retries is not DEFAULT: + client._max_retries = DEFAULT + if retry_on_timeout is not DEFAULT: + client._retry_on_timeout = DEFAULT + if retry_on_status is not DEFAULT: + client._retry_on_status = DEFAULT + return client + + +class NamespacedClient: + def __init__(self, client: "BaseClient") -> None: + self._client = client + + async def _perform_request( + self, method: str, target: str, headers=None, params=None, body=None + ) -> Any: + # Use the internal clients .perform_request() implementation + # so we take advantage of their transport options. + return await self._client._perform_request( + method, target, headers=headers, params=params, body=body + ) + + @property + def transport(self) -> AsyncTransport: + return self._client.transport + + def options( + self, + *, + opaque_id=DEFAULT, + api_key=DEFAULT, + basic_auth=DEFAULT, + bearer_auth=DEFAULT, + headers=DEFAULT, + request_timeout=DEFAULT, + ignore_status=DEFAULT, + max_retries=DEFAULT, + retry_on_status=DEFAULT, + retry_on_timeout=DEFAULT, + ) -> "NamespacedClient": + return type(self)( + self._client.options( + opaque_id=opaque_id, + api_key=api_key, + basic_auth=basic_auth, + bearer_auth=bearer_auth, + headers=headers, + request_timeout=request_timeout, + ignore_status=ignore_status, + max_retries=max_retries, + retry_on_status=retry_on_status, + retry_on_timeout=retry_on_timeout, + ) + ) diff --git a/elasticsearch/_async/client/async_search.py b/elasticsearch/_async/client/async_search.py index 9143b8d22..39f3cc010 100644 --- a/elasticsearch/_async/client/async_search.py +++ b/elasticsearch/_async/client/async_search.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class AsyncSearchClient(NamespacedClient): diff --git a/elasticsearch/_async/client/autoscaling.py b/elasticsearch/_async/client/autoscaling.py index 8fdf3ad51..b828f8da1 100644 --- a/elasticsearch/_async/client/autoscaling.py +++ b/elasticsearch/_async/client/autoscaling.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class AutoscalingClient(NamespacedClient): diff --git a/elasticsearch/_async/client/cat.py b/elasticsearch/_async/client/cat.py index 6b1f03a84..df9e8a43d 100644 --- a/elasticsearch/_async/client/cat.py +++ b/elasticsearch/_async/client/cat.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import _deprecated_options, _make_path, query_params class CatClient(NamespacedClient): diff --git a/elasticsearch/_async/client/ccr.py b/elasticsearch/_async/client/ccr.py index 8a4f7ffae..aa4f25c75 100644 --- a/elasticsearch/_async/client/ccr.py +++ b/elasticsearch/_async/client/ccr.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class CcrClient(NamespacedClient): diff --git a/elasticsearch/_async/client/cluster.py b/elasticsearch/_async/client/cluster.py index 0a3b61019..b3fc5f867 100644 --- a/elasticsearch/_async/client/cluster.py +++ b/elasticsearch/_async/client/cluster.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class ClusterClient(NamespacedClient): diff --git a/elasticsearch/_async/client/dangling_indices.py b/elasticsearch/_async/client/dangling_indices.py index 4641875fa..04d9c6bb5 100644 --- a/elasticsearch/_async/client/dangling_indices.py +++ b/elasticsearch/_async/client/dangling_indices.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class DanglingIndicesClient(NamespacedClient): diff --git a/elasticsearch/_async/client/enrich.py b/elasticsearch/_async/client/enrich.py index 707832527..a9f1041f2 100644 --- a/elasticsearch/_async/client/enrich.py +++ b/elasticsearch/_async/client/enrich.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class EnrichClient(NamespacedClient): diff --git a/elasticsearch/_async/client/eql.py b/elasticsearch/_async/client/eql.py index 1f6f3f5f5..bf46414a4 100644 --- a/elasticsearch/_async/client/eql.py +++ b/elasticsearch/_async/client/eql.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class EqlClient(NamespacedClient): diff --git a/elasticsearch/_async/client/features.py b/elasticsearch/_async/client/features.py index ab793536b..9adb1107c 100644 --- a/elasticsearch/_async/client/features.py +++ b/elasticsearch/_async/client/features.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import NamespacedClient, query_params +from ._base import NamespacedClient +from .utils import _deprecated_options, query_params class FeaturesClient(NamespacedClient): diff --git a/elasticsearch/_async/client/fleet.py b/elasticsearch/_async/client/fleet.py index 9149de482..467b8326b 100644 --- a/elasticsearch/_async/client/fleet.py +++ b/elasticsearch/_async/client/fleet.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class FleetClient(NamespacedClient): @@ -25,10 +26,7 @@ async def global_checkpoints(self, index, params=None, headers=None): Returns the current global checkpoints for an index. This API is design for internal use by the fleet server project. - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version + ``_ :arg index: The name of the index. :arg checkpoints: Comma separated list of checkpoints diff --git a/elasticsearch/_async/client/graph.py b/elasticsearch/_async/client/graph.py index 62f9b5e23..d01e78471 100644 --- a/elasticsearch/_async/client/graph.py +++ b/elasticsearch/_async/client/graph.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class GraphClient(NamespacedClient): diff --git a/elasticsearch/_async/client/ilm.py b/elasticsearch/_async/client/ilm.py index cd0284a55..92c5037dd 100644 --- a/elasticsearch/_async/client/ilm.py +++ b/elasticsearch/_async/client/ilm.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class IlmClient(NamespacedClient): diff --git a/elasticsearch/_async/client/indices.py b/elasticsearch/_async/client/indices.py index fabbac454..1a4fda12a 100644 --- a/elasticsearch/_async/client/indices.py +++ b/elasticsearch/_async/client/indices.py @@ -15,7 +15,10 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from elastic_transport import NotFoundError + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class IndicesClient(NamespacedClient): @@ -274,8 +277,8 @@ async def delete(self, index, params=None, headers=None): :arg allow_no_indices: Ignore if a wildcard expression resolves to no concrete indices (default: false) :arg expand_wildcards: Whether wildcard expressions should get - expanded to open or closed indices (default: open) Valid choices: open, - closed, hidden, none, all Default: open + expanded to open, closed, or hidden indices Valid choices: open, + closed, hidden, none, all Default: open,closed :arg ignore_unavailable: Ignore unavailable indexes (default: false) :arg master_timeout: Specify timeout for connection to master @@ -1408,11 +1411,6 @@ async def resolve_index(self, name, params=None, headers=None): ``_ - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version - :arg name: A comma-separated list of names or wildcard expressions :arg expand_wildcards: Whether wildcard expressions should get diff --git a/elasticsearch/_async/client/ingest.py b/elasticsearch/_async/client/ingest.py index 2eff9ae21..e3b20b516 100644 --- a/elasticsearch/_async/client/ingest.py +++ b/elasticsearch/_async/client/ingest.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class IngestClient(NamespacedClient): diff --git a/elasticsearch/_async/client/license.py b/elasticsearch/_async/client/license.py index f3dac519d..88dff02ba 100644 --- a/elasticsearch/_async/client/license.py +++ b/elasticsearch/_async/client/license.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import NamespacedClient, query_params +from ._base import NamespacedClient +from .utils import _deprecated_options, query_params class LicenseClient(NamespacedClient): diff --git a/elasticsearch/_async/client/logstash.py b/elasticsearch/_async/client/logstash.py index a1df13f6d..bd35a45ef 100644 --- a/elasticsearch/_async/client/logstash.py +++ b/elasticsearch/_async/client/logstash.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class LogstashClient(NamespacedClient): diff --git a/elasticsearch/_async/client/migration.py b/elasticsearch/_async/client/migration.py index 1f5181560..6d65ed2b4 100644 --- a/elasticsearch/_async/client/migration.py +++ b/elasticsearch/_async/client/migration.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import _deprecated_options, _make_path, query_params class MigrationClient(NamespacedClient): @@ -36,3 +37,25 @@ async def deprecations(self, index=None, params=None, headers=None): params=params, headers=headers, ) + + @query_params() + async def get_feature_upgrade_status(self, params=None, headers=None): + """ + Find out whether system features need to be upgraded or not + + ``_ + """ + return await self.transport.perform_request( + "GET", "/_migration/system_features", params=params, headers=headers + ) + + @query_params() + async def post_feature_upgrade(self, params=None, headers=None): + """ + Begin upgrades for system features + + ``_ + """ + return await self.transport.perform_request( + "POST", "/_migration/system_features", params=params, headers=headers + ) diff --git a/elasticsearch/_async/client/migration.pyi b/elasticsearch/_async/client/migration.pyi index d4b7623b3..3ac423489 100644 --- a/elasticsearch/_async/client/migration.pyi +++ b/elasticsearch/_async/client/migration.pyi @@ -37,3 +37,35 @@ class MigrationClient(NamespacedClient): params: Optional[MutableMapping[str, Any]] = ..., headers: Optional[MutableMapping[str, str]] = ..., ) -> Any: ... + async def get_feature_upgrade_status( + self, + *, + pretty: Optional[bool] = ..., + human: Optional[bool] = ..., + error_trace: Optional[bool] = ..., + format: Optional[str] = ..., + filter_path: Optional[Union[str, Collection[str]]] = ..., + request_timeout: Optional[Union[int, float]] = ..., + ignore: Optional[Union[int, Collection[int]]] = ..., + opaque_id: Optional[str] = ..., + http_auth: Optional[Union[str, Tuple[str, str]]] = ..., + api_key: Optional[Union[str, Tuple[str, str]]] = ..., + params: Optional[MutableMapping[str, Any]] = ..., + headers: Optional[MutableMapping[str, str]] = ..., + ) -> Any: ... + async def post_feature_upgrade( + self, + *, + pretty: Optional[bool] = ..., + human: Optional[bool] = ..., + error_trace: Optional[bool] = ..., + format: Optional[str] = ..., + filter_path: Optional[Union[str, Collection[str]]] = ..., + request_timeout: Optional[Union[int, float]] = ..., + ignore: Optional[Union[int, Collection[int]]] = ..., + opaque_id: Optional[str] = ..., + http_auth: Optional[Union[str, Tuple[str, str]]] = ..., + api_key: Optional[Union[str, Tuple[str, str]]] = ..., + params: Optional[MutableMapping[str, Any]] = ..., + headers: Optional[MutableMapping[str, str]] = ..., + ) -> Any: ... diff --git a/elasticsearch/_async/client/ml.py b/elasticsearch/_async/client/ml.py index 27983d923..3642e1c69 100644 --- a/elasticsearch/_async/client/ml.py +++ b/elasticsearch/_async/client/ml.py @@ -15,7 +15,14 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _bulk_body, _make_path, query_params +from ._base import NamespacedClient +from .utils import ( + SKIP_IN_PATH, + _bulk_body, + _deprecated_options, + _make_path, + query_params, +) class MlClient(NamespacedClient): @@ -1366,7 +1373,7 @@ async def get_trained_models_stats(self, model_id=None, params=None, headers=Non headers=headers, ) - @query_params() + @query_params("defer_definition_decompression") async def put_trained_model(self, model_id, body, params=None, headers=None): """ Creates an inference trained model. @@ -1375,6 +1382,9 @@ async def put_trained_model(self, model_id, body, params=None, headers=None): :arg model_id: The ID of the trained models to store :arg body: The trained model configuration + :arg defer_definition_decompression: If set to `true` and a + `compressed_definition` is provided, the request defers definition + decompression and skips relevant validations. """ for param in (model_id, body): if param in SKIP_IN_PATH: @@ -1748,7 +1758,7 @@ async def reset_job(self, job_id, params=None, headers=None): headers=headers, ) - @query_params("timeout") + @query_params("timeout", "wait_for") async def start_trained_model_deployment(self, model_id, params=None, headers=None): """ Start a trained model deployment. @@ -1763,6 +1773,8 @@ async def start_trained_model_deployment(self, model_id, params=None, headers=No :arg model_id: The unique identifier of the trained model. :arg timeout: Controls the amount of time to wait for the model to deploy. Default: 20s + :arg wait_for: The allocation status for which to wait Valid + choices: starting, started, fully_allocated Default: started """ if model_id in SKIP_IN_PATH: raise ValueError("Empty value passed for a required argument 'model_id'.") @@ -1807,11 +1819,6 @@ async def get_trained_model_deployment_stats( ``_ - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version - :arg model_id: The ID of the trained model deployment stats to fetch """ @@ -1824,3 +1831,63 @@ async def get_trained_model_deployment_stats( params=params, headers=headers, ) + + @query_params() + async def put_trained_model_definition_part( + self, model_id, part, body, params=None, headers=None + ): + """ + Creates part of a trained model definition + + ``_ + + .. warning:: + + This API is **experimental** so may include breaking changes + or be removed in a future version + + :arg model_id: The ID of the trained model for this definition + part + :arg part: The part number + :arg body: The trained model definition part + """ + for param in (model_id, part, body): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") + + return await self.transport.perform_request( + "PUT", + _make_path("_ml", "trained_models", model_id, "definition", part), + params=params, + headers=headers, + body=body, + ) + + @query_params() + async def put_trained_model_vocabulary( + self, model_id, body, params=None, headers=None + ): + """ + Creates a trained model vocabulary + + ``_ + + .. warning:: + + This API is **experimental** so may include breaking changes + or be removed in a future version + + :arg model_id: The ID of the trained model for this vocabulary + :arg body: The trained model vocabulary + """ + for param in (model_id, body): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") + + return await self.transport.perform_request( + "PUT", + _make_path("_ml", "trained_models", model_id, "vocabulary"), + params=params, + headers=headers, + body=body, + ) diff --git a/elasticsearch/_async/client/ml.pyi b/elasticsearch/_async/client/ml.pyi index cd4a39135..ae3657592 100644 --- a/elasticsearch/_async/client/ml.pyi +++ b/elasticsearch/_async/client/ml.pyi @@ -1038,6 +1038,7 @@ class MlClient(NamespacedClient): model_id: Any, *, body: Any, + defer_definition_decompression: Optional[Any] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., @@ -1307,6 +1308,7 @@ class MlClient(NamespacedClient): model_id: Any, *, timeout: Optional[Any] = ..., + wait_for: Optional[Any] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., @@ -1354,3 +1356,40 @@ class MlClient(NamespacedClient): params: Optional[MutableMapping[str, Any]] = ..., headers: Optional[MutableMapping[str, str]] = ..., ) -> Any: ... + async def put_trained_model_definition_part( + self, + model_id: Any, + part: Any, + *, + body: Any, + pretty: Optional[bool] = ..., + human: Optional[bool] = ..., + error_trace: Optional[bool] = ..., + format: Optional[str] = ..., + filter_path: Optional[Union[str, Collection[str]]] = ..., + request_timeout: Optional[Union[int, float]] = ..., + ignore: Optional[Union[int, Collection[int]]] = ..., + opaque_id: Optional[str] = ..., + http_auth: Optional[Union[str, Tuple[str, str]]] = ..., + api_key: Optional[Union[str, Tuple[str, str]]] = ..., + params: Optional[MutableMapping[str, Any]] = ..., + headers: Optional[MutableMapping[str, str]] = ..., + ) -> Any: ... + async def put_trained_model_vocabulary( + self, + model_id: Any, + *, + body: Any, + pretty: Optional[bool] = ..., + human: Optional[bool] = ..., + error_trace: Optional[bool] = ..., + format: Optional[str] = ..., + filter_path: Optional[Union[str, Collection[str]]] = ..., + request_timeout: Optional[Union[int, float]] = ..., + ignore: Optional[Union[int, Collection[int]]] = ..., + opaque_id: Optional[str] = ..., + http_auth: Optional[Union[str, Tuple[str, str]]] = ..., + api_key: Optional[Union[str, Tuple[str, str]]] = ..., + params: Optional[MutableMapping[str, Any]] = ..., + headers: Optional[MutableMapping[str, str]] = ..., + ) -> Any: ... diff --git a/elasticsearch/_async/client/monitoring.py b/elasticsearch/_async/client/monitoring.py index d3405f6be..0b436a828 100644 --- a/elasticsearch/_async/client/monitoring.py +++ b/elasticsearch/_async/client/monitoring.py @@ -15,7 +15,14 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _bulk_body, _make_path, query_params +from ._base import NamespacedClient +from .utils import ( + SKIP_IN_PATH, + _bulk_body, + _deprecated_options, + _make_path, + query_params, +) class MonitoringClient(NamespacedClient): @@ -26,11 +33,6 @@ async def bulk(self, body, doc_type=None, params=None, headers=None): ``_ - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version - :arg body: The operation definition and data (action-data pairs), separated by newlines :arg doc_type: Default document type for items which don't diff --git a/elasticsearch/_async/client/nodes.py b/elasticsearch/_async/client/nodes.py index e87f4cf8c..aebbe95b9 100644 --- a/elasticsearch/_async/client/nodes.py +++ b/elasticsearch/_async/client/nodes.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class NodesClient(NamespacedClient): @@ -153,7 +154,7 @@ async def stats( metric to the specific index metrics. Isn't used if `indices` (or `all`) metric isn't specified. Valid choices: _all, completion, docs, fielddata, query_cache, flush, get, indexing, merge, request_cache, - refresh, search, segments, store, warmer, bulk, shards + refresh, search, segments, store, warmer, bulk, shard_stats :arg completion_fields: A comma-separated list of fields for the `completion` index metric (supports wildcards) :arg fielddata_fields: A comma-separated list of fields for the @@ -182,7 +183,7 @@ async def stats( ) @query_params() - async def clear_metering_archive( + async def clear_repositories_metering_archive( self, node_id, max_archive_version, params=None, headers=None ): """ @@ -214,7 +215,7 @@ async def clear_metering_archive( ) @query_params() - async def get_metering_info(self, node_id, params=None, headers=None): + async def get_repositories_metering_info(self, node_id, params=None, headers=None): """ Returns cluster repositories metering information. diff --git a/elasticsearch/_async/client/nodes.pyi b/elasticsearch/_async/client/nodes.pyi index 6af1c7631..5f50315e9 100644 --- a/elasticsearch/_async/client/nodes.pyi +++ b/elasticsearch/_async/client/nodes.pyi @@ -129,7 +129,7 @@ class NodesClient(NamespacedClient): params: Optional[MutableMapping[str, Any]] = ..., headers: Optional[MutableMapping[str, str]] = ..., ) -> Any: ... - async def clear_metering_archive( + async def clear_repositories_metering_archive( self, node_id: Any, max_archive_version: Any, @@ -147,7 +147,7 @@ class NodesClient(NamespacedClient): params: Optional[MutableMapping[str, Any]] = ..., headers: Optional[MutableMapping[str, str]] = ..., ) -> Any: ... - async def get_metering_info( + async def get_repositories_metering_info( self, node_id: Any, *, diff --git a/elasticsearch/_async/client/rollup.py b/elasticsearch/_async/client/rollup.py index c07f734d3..2d9ee55b9 100644 --- a/elasticsearch/_async/client/rollup.py +++ b/elasticsearch/_async/client/rollup.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class RollupClient(NamespacedClient): diff --git a/elasticsearch/_async/client/searchable_snapshots.py b/elasticsearch/_async/client/searchable_snapshots.py index 89c8d22ec..340fc4181 100644 --- a/elasticsearch/_async/client/searchable_snapshots.py +++ b/elasticsearch/_async/client/searchable_snapshots.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class SearchableSnapshotsClient(NamespacedClient): @@ -56,11 +57,6 @@ async def mount(self, repository, snapshot, body, params=None, headers=None): ``_ - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version - :arg repository: The name of the repository containing the snapshot of the index to mount :arg snapshot: The name of the snapshot of the index to mount @@ -92,11 +88,6 @@ async def stats(self, index=None, params=None, headers=None): ``_ - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version - :arg index: A comma-separated list of index names :arg level: Return stats aggregated at cluster, index or shard level Valid choices: cluster, indices, shards Default: indices diff --git a/elasticsearch/_async/client/security.py b/elasticsearch/_async/client/security.py index bebbca7c8..8a2fdd9f2 100644 --- a/elasticsearch/_async/client/security.py +++ b/elasticsearch/_async/client/security.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class SecurityClient(NamespacedClient): @@ -611,11 +612,6 @@ async def clear_cached_service_tokens( ``_ - .. warning:: - - This API is **beta** so may include breaking changes - or be removed in a future version - :arg namespace: An identifier for the namespace :arg service: An identifier for the service name :arg name: A comma-separated list of service token names @@ -650,11 +646,6 @@ async def create_service_token( ``_ - .. warning:: - - This API is **beta** so may include breaking changes - or be removed in a future version - :arg namespace: An identifier for the namespace :arg service: An identifier for the service name :arg name: An identifier for the token name @@ -685,11 +676,6 @@ async def delete_service_token( ``_ - .. warning:: - - This API is **beta** so may include breaking changes - or be removed in a future version - :arg namespace: An identifier for the namespace :arg service: An identifier for the service name :arg name: An identifier for the token name @@ -720,11 +706,6 @@ async def get_service_accounts( ``_ - .. warning:: - - This API is **beta** so may include breaking changes - or be removed in a future version - :arg namespace: An identifier for the namespace :arg service: An identifier for the service name """ @@ -744,11 +725,6 @@ async def get_service_credentials( ``_ - .. warning:: - - This API is **beta** so may include breaking changes - or be removed in a future version - :arg namespace: An identifier for the namespace :arg service: An identifier for the service name """ diff --git a/elasticsearch/_async/client/shutdown.py b/elasticsearch/_async/client/shutdown.py index ec6df2123..33c0c9b46 100644 --- a/elasticsearch/_async/client/shutdown.py +++ b/elasticsearch/_async/client/shutdown.py @@ -15,22 +15,19 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class ShutdownClient(NamespacedClient): @query_params() async def delete_node(self, node_id, params=None, headers=None): """ - Removes a node from the shutdown list + Removes a node from the shutdown list. Designed for indirect use by ECE/ESS and + ECK. Direct use is not supported. ``_ - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version - :arg node_id: The node id of node to be removed from the shutdown state """ @@ -47,15 +44,11 @@ async def delete_node(self, node_id, params=None, headers=None): @query_params() async def get_node(self, node_id=None, params=None, headers=None): """ - Retrieve status of a node or nodes that are currently marked as shutting down + Retrieve status of a node or nodes that are currently marked as shutting down. + Designed for indirect use by ECE/ESS and ECK. Direct use is not supported. ``_ - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version - :arg node_id: Which node for which to retrieve the shutdown status """ @@ -69,15 +62,11 @@ async def get_node(self, node_id=None, params=None, headers=None): @query_params() async def put_node(self, node_id, body, params=None, headers=None): """ - Adds a node to be shut down + Adds a node to be shut down. Designed for indirect use by ECE/ESS and ECK. + Direct use is not supported. ``_ - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version - :arg node_id: The node id of node to be shut down :arg body: The shutdown type definition to register """ diff --git a/elasticsearch/_async/client/slm.py b/elasticsearch/_async/client/slm.py index 0fe710624..7653d7df8 100644 --- a/elasticsearch/_async/client/slm.py +++ b/elasticsearch/_async/client/slm.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class SlmClient(NamespacedClient): diff --git a/elasticsearch/_async/client/snapshot.py b/elasticsearch/_async/client/snapshot.py index cfacdd9d7..23d0c5ec2 100644 --- a/elasticsearch/_async/client/snapshot.py +++ b/elasticsearch/_async/client/snapshot.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class SnapshotClient(NamespacedClient): diff --git a/elasticsearch/_async/client/sql.py b/elasticsearch/_async/client/sql.py index 373a52d1e..c54d04d44 100644 --- a/elasticsearch/_async/client/sql.py +++ b/elasticsearch/_async/client/sql.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class SqlClient(NamespacedClient): diff --git a/elasticsearch/_async/client/ssl.py b/elasticsearch/_async/client/ssl.py index 6eba54b18..5c8889a07 100644 --- a/elasticsearch/_async/client/ssl.py +++ b/elasticsearch/_async/client/ssl.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import NamespacedClient, query_params +from ._base import NamespacedClient +from .utils import _deprecated_options, query_params class SslClient(NamespacedClient): diff --git a/elasticsearch/_async/client/tasks.py b/elasticsearch/_async/client/tasks.py index ec43c26d6..f35c66c5f 100644 --- a/elasticsearch/_async/client/tasks.py +++ b/elasticsearch/_async/client/tasks.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class TasksClient(NamespacedClient): diff --git a/elasticsearch/_async/client/text_structure.py b/elasticsearch/_async/client/text_structure.py index 439027627..47616e4a4 100644 --- a/elasticsearch/_async/client/text_structure.py +++ b/elasticsearch/_async/client/text_structure.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _bulk_body, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _bulk_body, _deprecated_options, query_params class TextStructureClient(NamespacedClient): diff --git a/elasticsearch/_async/client/transform.py b/elasticsearch/_async/client/transform.py index ce8e6c2fa..a7f3114d8 100644 --- a/elasticsearch/_async/client/transform.py +++ b/elasticsearch/_async/client/transform.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class TransformClient(NamespacedClient): diff --git a/elasticsearch/_async/client/utils.py b/elasticsearch/_async/client/utils.py index 26e4f2eb9..76ae2b5f2 100644 --- a/elasticsearch/_async/client/utils.py +++ b/elasticsearch/_async/client/utils.py @@ -15,10 +15,26 @@ # specific language governing permissions and limitations # under the License. -from ...client.utils import SKIP_IN_PATH # noqa -from ...client.utils import _bulk_body # noqa -from ...client.utils import _escape # noqa -from ...client.utils import _make_path # noqa -from ...client.utils import _normalize_hosts # noqa -from ...client.utils import query_params # noqa -from ...client.utils import NamespacedClient as NamespacedClient # noqa +from ...client.utils import ( + _TYPE_HOSTS, + CLIENT_META_SERVICE, + SKIP_IN_PATH, + _bulk_body, + _deprecated_options, + _escape, + _make_path, + client_node_configs, + query_params, +) + +__all__ = [ + "CLIENT_META_SERVICE", + "_deprecated_options", + "_TYPE_HOSTS", + "SKIP_IN_PATH", + "_bulk_body", + "_escape", + "_make_path", + "query_params", + "client_node_configs", +] diff --git a/elasticsearch/_async/client/watcher.py b/elasticsearch/_async/client/watcher.py index 6be771a85..5cad8789f 100644 --- a/elasticsearch/_async/client/watcher.py +++ b/elasticsearch/_async/client/watcher.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class WatcherClient(NamespacedClient): diff --git a/elasticsearch/_async/client/xpack.py b/elasticsearch/_async/client/xpack.py index 883cb3c93..15d6591c3 100644 --- a/elasticsearch/_async/client/xpack.py +++ b/elasticsearch/_async/client/xpack.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import NamespacedClient, query_params +from ._base import NamespacedClient +from .utils import _deprecated_options, query_params class XPackClient(NamespacedClient): diff --git a/elasticsearch/client/__init__.py b/elasticsearch/_sync/client/__init__.py similarity index 95% rename from elasticsearch/client/__init__.py rename to elasticsearch/_sync/client/__init__.py index 928b3f6b8..64a0fd453 100644 --- a/elasticsearch/client/__init__.py +++ b/elasticsearch/_sync/client/__init__.py @@ -17,8 +17,13 @@ import logging +import warnings +from typing import Optional -from ..transport import Transport, TransportError +from elastic_transport import Transport, NotFoundError, TransportError +from elastic_transport.client_utils import DEFAULT + +from ._base import BaseClient from .async_search import AsyncSearchClient from .autoscaling import AutoscalingClient from .cat import CatClient @@ -50,14 +55,22 @@ from .tasks import TasksClient from .text_structure import TextStructureClient from .transform import TransformClient -from .utils import SKIP_IN_PATH, _bulk_body, _make_path, _normalize_hosts, query_params +from .utils import ( + _TYPE_HOSTS, + CLIENT_META_SERVICE, + SKIP_IN_PATH, + _deprecated_options, + _make_path, + client_node_configs, + query_params, +) from .watcher import WatcherClient from .xpack import XPackClient logger = logging.getLogger("elasticsearch") -class Elasticsearch: +class Elasticsearch(BaseClient): """ Elasticsearch low-level client. Provides a straightforward mapping from Python to ES REST endpoints. @@ -74,12 +87,6 @@ class Elasticsearch: preferred (and only supported) way to get access to those classes and their methods. - You can specify your own connection class which should be used by providing - the ``connection_class`` parameter:: - - # create connection to localhost using the ThriftConnection - es = Elasticsearch(connection_class=ThriftConnection) - If you want to turn on :ref:`sniffing` you have several options (described in :class:`~elasticsearch.Transport`):: @@ -111,7 +118,7 @@ class Elasticsearch: detailed description of the options):: es = Elasticsearch( - ['localhost:443', 'other_host:443'], + ['https://localhost:443', 'https://other_host:443'], # turn on SSL use_ssl=True, # make sure we verify SSL certificates @@ -125,7 +132,7 @@ class Elasticsearch: detailed description of the options):: es = Elasticsearch( - ['localhost:443', 'other_host:443'], + ['https://localhost:443', 'https://other_host:443'], # turn on SSL use_ssl=True, # no verify SSL certificates @@ -139,7 +146,7 @@ class Elasticsearch: detailed description of the options):: es = Elasticsearch( - ['localhost:443', 'other_host:443'], + ['https://localhost:443', 'https://other_host:443'], # turn on SSL use_ssl=True, # make sure we verify SSL certificates @@ -163,41 +170,113 @@ class Elasticsearch: verify_certs=True ) - By default, `JSONSerializer - `_ - is used to encode all outgoing requests. + By default, ``JsonSerializer`` is used to encode all outgoing requests. However, you can implement your own custom serializer:: - from elasticsearch.serializer import JSONSerializer + from elasticsearch.serializer import JsonSerializer - class SetEncoder(JSONSerializer): + class SetEncoder(JsonSerializer): def default(self, obj): if isinstance(obj, set): return list(obj) if isinstance(obj, Something): return 'CustomSomethingRepresentation' - return JSONSerializer.default(self, obj) + return JsonSerializer.default(self, obj) es = Elasticsearch(serializer=SetEncoder()) """ - def __init__(self, hosts=None, transport_class=Transport, **kwargs): - """ - :arg hosts: list of nodes, or a single node, we should connect to. - Node should be a dictionary ({"host": "localhost", "port": 9200}), - the entire dictionary will be passed to the :class:`~elasticsearch.Connection` - class as kwargs, or a string in the format of ``host[:port]`` which will be - translated to a dictionary automatically. If no value is given the - :class:`~elasticsearch.Connection` class defaults will be used. - - :arg transport_class: :class:`~elasticsearch.Transport` subclass to use. + def __init__( + self, + hosts: Optional[_TYPE_HOSTS] = None, + *, + # API + cloud_id: Optional[str] = None, + api_key=None, + basic_auth=None, + bearer_auth=None, + # Node + headers=DEFAULT, + connections_per_node=DEFAULT, + http_compress=DEFAULT, + verify_certs=DEFAULT, + ca_certs=DEFAULT, + client_cert=DEFAULT, + client_key=DEFAULT, + ssl_assert_hostname=DEFAULT, + ssl_assert_fingerprint=DEFAULT, + ssl_version=DEFAULT, + ssl_context=DEFAULT, + ssl_show_warn=DEFAULT, + # Transport + transport_class=Transport, + request_timeout=DEFAULT, + node_class=DEFAULT, + node_pool_class=DEFAULT, + randomize_nodes_in_pool=DEFAULT, + node_selector_class=DEFAULT, + dead_backoff_factor=DEFAULT, + max_dead_backoff=DEFAULT, + serializers=DEFAULT, + default_mimetype="application/json", + max_retries=DEFAULT, + retry_on_status=DEFAULT, + retry_on_timeout=DEFAULT, + sniff_on_start=DEFAULT, + sniff_before_requests=DEFAULT, + sniff_on_node_failure=DEFAULT, + sniff_timeout=DEFAULT, + min_delay_between_sniffing=DEFAULT, + # Deprecated + timeout=DEFAULT, + # Internal use only + _transport: Optional[Transport] = None, + ) -> None: + super().__init__() + + if hosts is None and cloud_id is None and _transport is None: + raise ValueError("Either 'hosts' or 'cloud_id' must be specified") + + if timeout is not DEFAULT: + if request_timeout is not DEFAULT: + raise ValueError( + "Can't specify both 'timeout' and 'request_timeout', " + "instead only specify 'request_timeout'" + ) + warnings.warn( + "The 'timeout' parameter is deprecated in favor of 'request_timeout'", + category=DeprecationWarning, + stacklevel=2, + ) + request_timeout = timeout - :arg kwargs: any additional arguments will be passed on to the - :class:`~elasticsearch.Transport` class and, subsequently, to the - :class:`~elasticsearch.Connection` instances. - """ - self.transport = transport_class(_normalize_hosts(hosts), **kwargs) + self.transport: Transport + if _transport is not None: + self.transport = _transport + else: + node_configs = client_node_configs( + hosts, + cloud_id=cloud_id, + headers=headers, + connections_per_node=connections_per_node, + http_compress=http_compress, + verify_certs=verify_certs, + ca_certs=ca_certs, + client_cert=client_cert, + client_key=client_key, + ssl_assert_hostname=ssl_assert_hostname, + ssl_assert_fingerprint=ssl_assert_fingerprint, + ssl_version=ssl_version, + ssl_context=ssl_context, + ssl_show_warn=ssl_show_warn, + ) + transport_kwargs = {} + self.transport = transport_class( + node_configs, + client_meta_service=CLIENT_META_SERVICE, + **transport_kwargs, + ) # namespaced clients for compatibility with API names self.async_search = AsyncSearchClient(self) @@ -1193,11 +1272,6 @@ def rank_eval(self, body, index=None, params=None, headers=None): ``_ - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version - :arg body: The ranking evaluation search definition, including search requests, document ratings and ranking metric definition. :arg index: A comma-separated list of index names to search; use @@ -1649,11 +1723,6 @@ def get_script_context(self, params=None, headers=None): Returns all script contexts. ``_ - - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version """ return self.transport.perform_request( "GET", "/_script_context", params=params, headers=headers @@ -1665,11 +1734,6 @@ def get_script_languages(self, params=None, headers=None): Returns available script types, languages and contexts ``_ - - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version """ return self.transport.perform_request( "GET", "/_script_language", params=params, headers=headers @@ -2048,7 +2112,7 @@ def close_point_in_time(self, body=None, params=None, headers=None): @query_params( "expand_wildcards", "ignore_unavailable", "keep_alive", "preference", "routing" ) - def open_point_in_time(self, index=None, params=None, headers=None): + def open_point_in_time(self, index, params=None, headers=None): """ Open a point in time that can be used in subsequent searches @@ -2067,6 +2131,9 @@ def open_point_in_time(self, index=None, params=None, headers=None): be performed on (default: random) :arg routing: Specific routing value """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument 'index'.") + return self.transport.perform_request( "POST", _make_path(index, "_pit"), params=params, headers=headers ) @@ -2080,11 +2147,6 @@ def terms_enum(self, index, body=None, params=None, headers=None): ``_ - .. warning:: - - This API is **beta** so may include breaking changes - or be removed in a future version - :arg index: A comma-separated list of index names to search; use `_all` or empty string to perform the operation on all indices :arg body: field name, string which is the prefix expected in @@ -2101,7 +2163,14 @@ def terms_enum(self, index, body=None, params=None, headers=None): body=body, ) - @query_params("exact_bounds", "extent", "grid_precision", "grid_type", "size") + @query_params( + "exact_bounds", + "extent", + "grid_precision", + "grid_type", + "size", + "track_total_hits", + ) def search_mvt( self, index, field, zoom, x, y, body=None, params=None, headers=None ): @@ -2131,9 +2200,12 @@ def search_mvt( :arg grid_precision: Additional zoom levels available through the aggs layer. Accepts 0-8. Default: 8 :arg grid_type: Determines the geometry type for features in the - aggs layer. Valid choices: grid, point Default: grid + aggs layer. Valid choices: grid, point, centroid Default: grid :arg size: Maximum number of features to return in the hits layer. Accepts 0-10000. Default: 10000 + :arg track_total_hits: Indicate if the number of documents that + match the query should be tracked. A number can also be specified, to + accurately track the total hit count up to the number. """ for param in (index, field, zoom, x, y): if param in SKIP_IN_PATH: diff --git a/elasticsearch/client/__init__.pyi b/elasticsearch/_sync/client/__init__.pyi similarity index 99% rename from elasticsearch/client/__init__.pyi rename to elasticsearch/_sync/client/__init__.pyi index c32b8e0a8..d99cd4722 100644 --- a/elasticsearch/client/__init__.pyi +++ b/elasticsearch/_sync/client/__init__.pyi @@ -18,7 +18,8 @@ import logging from typing import Any, Collection, MutableMapping, Optional, Tuple, Type, Union -from ..transport import Transport +from elastic_transport import Transport + from .async_search import AsyncSearchClient from .autoscaling import AutoscalingClient from .cat import CatClient @@ -1110,8 +1111,8 @@ class Elasticsearch: ) -> Any: ... def open_point_in_time( self, + index: Any, *, - index: Optional[Any] = ..., expand_wildcards: Optional[Any] = ..., ignore_unavailable: Optional[Any] = ..., keep_alive: Optional[Any] = ..., @@ -1162,6 +1163,7 @@ class Elasticsearch: grid_precision: Optional[Any] = ..., grid_type: Optional[Any] = ..., size: Optional[Any] = ..., + track_total_hits: Optional[Any] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., diff --git a/elasticsearch/_sync/client/_base.py b/elasticsearch/_sync/client/_base.py new file mode 100644 index 000000000..969a75470 --- /dev/null +++ b/elasticsearch/_sync/client/_base.py @@ -0,0 +1,151 @@ +# 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 urllib.parse +from typing import Any + +from elastic_transport import Transport, HttpHeaders +from elastic_transport.client_utils import DEFAULT +from ..exceptions import UnsupportedProductError + + +class BaseClient: + transport: Transport + + def __init__(self): + self._headers = HttpHeaders({"content-type": "application/json"}) + self._request_timeout = DEFAULT + self._ignore_status = DEFAULT + self._max_retries = DEFAULT + self._retry_on_timeout = DEFAULT + self._retry_on_status = DEFAULT + + def _perform_request( + self, method: str, target: str, headers=None, params=None, body=None + ) -> Any: + # Handle the passing of 'params' as additional query parameters. + # This behavior is deprecated and should be removed in 9.0.0. + if params: + if "?" in target: + raise ValueError("Can't add query to a target that already has a query") + target = f"{target}?{urllib.parse.urlencode(params, quote_via=urllib.parse.quote)}" + + if headers: + request_headers = self._headers.copy() + request_headers.update(headers) + else: + request_headers = self._headers + + meta, response = self.transport.perform_request( + method, target, headers=request_headers, body=body + ) + + # 'X-Elastic-Product: Elasticsearch' should be on every response. + if meta.headers.get("x-elastic-product", "") != "Elasticsearch": + raise UnsupportedProductError( + "The client noticed that the server is not Elasticsearch " + "and we do not support this unknown product" + ) + + return response + + def options( + self, + *, + opaque_id=DEFAULT, + api_key=DEFAULT, + basic_auth=DEFAULT, + bearer_auth=DEFAULT, + headers=DEFAULT, + request_timeout=DEFAULT, + ignore_status=DEFAULT, + max_retries=DEFAULT, + retry_on_status=DEFAULT, + retry_on_timeout=DEFAULT, + ): + client = type(self)(_transport=self.transport) + + new_headers = self._headers.copy() + if headers is not DEFAULT: + new_headers.update(headers) + if opaque_id is not DEFAULT: + new_headers["x-opaque-id"] = opaque_id + if ( + api_key is not DEFAULT + or basic_auth is not DEFAULT + or bearer_auth is not DEFAULT + ): + pass # TODO + + if request_timeout is not DEFAULT: + client._request_timeout = DEFAULT + if ignore_status is not DEFAULT: + client._ignore_status = DEFAULT + if max_retries is not DEFAULT: + client._max_retries = DEFAULT + if retry_on_timeout is not DEFAULT: + client._retry_on_timeout = DEFAULT + if retry_on_status is not DEFAULT: + client._retry_on_status = DEFAULT + return client + + +class NamespacedClient: + def __init__(self, client: "BaseClient") -> None: + self._client = client + + def _perform_request( + self, method: str, target: str, headers=None, params=None, body=None + ) -> Any: + # Use the internal clients .perform_request() implementation + # so we take advantage of their transport options. + return self._client._perform_request( + method, target, headers=headers, params=params, body=body + ) + + @property + def transport(self) -> Transport: + return self._client.transport + + def options( + self, + *, + opaque_id=DEFAULT, + api_key=DEFAULT, + basic_auth=DEFAULT, + bearer_auth=DEFAULT, + headers=DEFAULT, + request_timeout=DEFAULT, + ignore_status=DEFAULT, + max_retries=DEFAULT, + retry_on_status=DEFAULT, + retry_on_timeout=DEFAULT, + ) -> "NamespacedClient": + return type(self)( + self._client.options( + opaque_id=opaque_id, + api_key=api_key, + basic_auth=basic_auth, + bearer_auth=bearer_auth, + headers=headers, + request_timeout=request_timeout, + ignore_status=ignore_status, + max_retries=max_retries, + retry_on_status=retry_on_status, + retry_on_timeout=retry_on_timeout, + ) + ) diff --git a/elasticsearch/client/async_search.py b/elasticsearch/_sync/client/async_search.py similarity index 98% rename from elasticsearch/client/async_search.py rename to elasticsearch/_sync/client/async_search.py index 7769fec05..097ca6580 100644 --- a/elasticsearch/client/async_search.py +++ b/elasticsearch/_sync/client/async_search.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class AsyncSearchClient(NamespacedClient): diff --git a/elasticsearch/client/async_search.pyi b/elasticsearch/_sync/client/async_search.pyi similarity index 100% rename from elasticsearch/client/async_search.pyi rename to elasticsearch/_sync/client/async_search.pyi diff --git a/elasticsearch/client/autoscaling.py b/elasticsearch/_sync/client/autoscaling.py similarity index 96% rename from elasticsearch/client/autoscaling.py rename to elasticsearch/_sync/client/autoscaling.py index e5b31ef52..986a8c2ea 100644 --- a/elasticsearch/client/autoscaling.py +++ b/elasticsearch/_sync/client/autoscaling.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class AutoscalingClient(NamespacedClient): diff --git a/elasticsearch/client/autoscaling.pyi b/elasticsearch/_sync/client/autoscaling.pyi similarity index 100% rename from elasticsearch/client/autoscaling.pyi rename to elasticsearch/_sync/client/autoscaling.pyi diff --git a/elasticsearch/client/cat.py b/elasticsearch/_sync/client/cat.py similarity index 99% rename from elasticsearch/client/cat.py rename to elasticsearch/_sync/client/cat.py index fb55ffe7e..d35c1dd4e 100644 --- a/elasticsearch/client/cat.py +++ b/elasticsearch/_sync/client/cat.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import _deprecated_options, _make_path, query_params class CatClient(NamespacedClient): diff --git a/elasticsearch/client/cat.pyi b/elasticsearch/_sync/client/cat.pyi similarity index 100% rename from elasticsearch/client/cat.pyi rename to elasticsearch/_sync/client/cat.pyi diff --git a/elasticsearch/client/ccr.py b/elasticsearch/_sync/client/ccr.py similarity index 98% rename from elasticsearch/client/ccr.py rename to elasticsearch/_sync/client/ccr.py index a0e3f1a08..7e2c89d4c 100644 --- a/elasticsearch/client/ccr.py +++ b/elasticsearch/_sync/client/ccr.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class CcrClient(NamespacedClient): diff --git a/elasticsearch/client/ccr.pyi b/elasticsearch/_sync/client/ccr.pyi similarity index 100% rename from elasticsearch/client/ccr.pyi rename to elasticsearch/_sync/client/ccr.pyi diff --git a/elasticsearch/client/cluster.py b/elasticsearch/_sync/client/cluster.py similarity index 99% rename from elasticsearch/client/cluster.py rename to elasticsearch/_sync/client/cluster.py index b75409455..0438addeb 100644 --- a/elasticsearch/client/cluster.py +++ b/elasticsearch/_sync/client/cluster.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class ClusterClient(NamespacedClient): diff --git a/elasticsearch/client/cluster.pyi b/elasticsearch/_sync/client/cluster.pyi similarity index 100% rename from elasticsearch/client/cluster.pyi rename to elasticsearch/_sync/client/cluster.pyi diff --git a/elasticsearch/client/dangling_indices.py b/elasticsearch/_sync/client/dangling_indices.py similarity index 96% rename from elasticsearch/client/dangling_indices.py rename to elasticsearch/_sync/client/dangling_indices.py index e87db8d9c..74fe95262 100644 --- a/elasticsearch/client/dangling_indices.py +++ b/elasticsearch/_sync/client/dangling_indices.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class DanglingIndicesClient(NamespacedClient): diff --git a/elasticsearch/client/dangling_indices.pyi b/elasticsearch/_sync/client/dangling_indices.pyi similarity index 100% rename from elasticsearch/client/dangling_indices.pyi rename to elasticsearch/_sync/client/dangling_indices.pyi diff --git a/elasticsearch/client/enrich.py b/elasticsearch/_sync/client/enrich.py similarity index 97% rename from elasticsearch/client/enrich.py rename to elasticsearch/_sync/client/enrich.py index 7953362be..f2478e7c8 100644 --- a/elasticsearch/client/enrich.py +++ b/elasticsearch/_sync/client/enrich.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class EnrichClient(NamespacedClient): diff --git a/elasticsearch/client/enrich.pyi b/elasticsearch/_sync/client/enrich.pyi similarity index 100% rename from elasticsearch/client/enrich.pyi rename to elasticsearch/_sync/client/enrich.pyi diff --git a/elasticsearch/client/eql.py b/elasticsearch/_sync/client/eql.py similarity index 97% rename from elasticsearch/client/eql.py rename to elasticsearch/_sync/client/eql.py index 7b0e7f75d..db1da69f8 100644 --- a/elasticsearch/client/eql.py +++ b/elasticsearch/_sync/client/eql.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class EqlClient(NamespacedClient): diff --git a/elasticsearch/client/eql.pyi b/elasticsearch/_sync/client/eql.pyi similarity index 100% rename from elasticsearch/client/eql.pyi rename to elasticsearch/_sync/client/eql.pyi diff --git a/elasticsearch/client/features.py b/elasticsearch/_sync/client/features.py similarity index 95% rename from elasticsearch/client/features.py rename to elasticsearch/_sync/client/features.py index 0efc60901..536123088 100644 --- a/elasticsearch/client/features.py +++ b/elasticsearch/_sync/client/features.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import NamespacedClient, query_params +from ._base import NamespacedClient +from .utils import _deprecated_options, query_params class FeaturesClient(NamespacedClient): diff --git a/elasticsearch/client/features.pyi b/elasticsearch/_sync/client/features.pyi similarity index 100% rename from elasticsearch/client/features.pyi rename to elasticsearch/_sync/client/features.pyi diff --git a/elasticsearch/client/fleet.py b/elasticsearch/_sync/client/fleet.py similarity index 89% rename from elasticsearch/client/fleet.py rename to elasticsearch/_sync/client/fleet.py index 633cdbb60..ea90e49fa 100644 --- a/elasticsearch/client/fleet.py +++ b/elasticsearch/_sync/client/fleet.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class FleetClient(NamespacedClient): @@ -25,10 +26,7 @@ def global_checkpoints(self, index, params=None, headers=None): Returns the current global checkpoints for an index. This API is design for internal use by the fleet server project. - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version + ``_ :arg index: The name of the index. :arg checkpoints: Comma separated list of checkpoints diff --git a/elasticsearch/client/fleet.pyi b/elasticsearch/_sync/client/fleet.pyi similarity index 100% rename from elasticsearch/client/fleet.pyi rename to elasticsearch/_sync/client/fleet.pyi diff --git a/elasticsearch/client/graph.py b/elasticsearch/_sync/client/graph.py similarity index 93% rename from elasticsearch/client/graph.py rename to elasticsearch/_sync/client/graph.py index 8e6ed2631..8788c0a06 100644 --- a/elasticsearch/client/graph.py +++ b/elasticsearch/_sync/client/graph.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class GraphClient(NamespacedClient): diff --git a/elasticsearch/client/graph.pyi b/elasticsearch/_sync/client/graph.pyi similarity index 100% rename from elasticsearch/client/graph.pyi rename to elasticsearch/_sync/client/graph.pyi diff --git a/elasticsearch/client/ilm.py b/elasticsearch/_sync/client/ilm.py similarity index 98% rename from elasticsearch/client/ilm.py rename to elasticsearch/_sync/client/ilm.py index c5562e80e..1545b22ad 100644 --- a/elasticsearch/client/ilm.py +++ b/elasticsearch/_sync/client/ilm.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class IlmClient(NamespacedClient): diff --git a/elasticsearch/client/ilm.pyi b/elasticsearch/_sync/client/ilm.pyi similarity index 100% rename from elasticsearch/client/ilm.pyi rename to elasticsearch/_sync/client/ilm.pyi diff --git a/elasticsearch/client/indices.py b/elasticsearch/_sync/client/indices.py similarity index 99% rename from elasticsearch/client/indices.py rename to elasticsearch/_sync/client/indices.py index 5f3e09e0e..807448f86 100644 --- a/elasticsearch/client/indices.py +++ b/elasticsearch/_sync/client/indices.py @@ -15,7 +15,10 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from elastic_transport import NotFoundError + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class IndicesClient(NamespacedClient): @@ -274,8 +277,8 @@ def delete(self, index, params=None, headers=None): :arg allow_no_indices: Ignore if a wildcard expression resolves to no concrete indices (default: false) :arg expand_wildcards: Whether wildcard expressions should get - expanded to open or closed indices (default: open) Valid choices: open, - closed, hidden, none, all Default: open + expanded to open, closed, or hidden indices Valid choices: open, + closed, hidden, none, all Default: open,closed :arg ignore_unavailable: Ignore unavailable indexes (default: false) :arg master_timeout: Specify timeout for connection to master @@ -1406,11 +1409,6 @@ def resolve_index(self, name, params=None, headers=None): ``_ - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version - :arg name: A comma-separated list of names or wildcard expressions :arg expand_wildcards: Whether wildcard expressions should get diff --git a/elasticsearch/client/indices.pyi b/elasticsearch/_sync/client/indices.pyi similarity index 100% rename from elasticsearch/client/indices.pyi rename to elasticsearch/_sync/client/indices.pyi diff --git a/elasticsearch/client/ingest.py b/elasticsearch/_sync/client/ingest.py similarity index 97% rename from elasticsearch/client/ingest.py rename to elasticsearch/_sync/client/ingest.py index 695e97460..784fdd68e 100644 --- a/elasticsearch/client/ingest.py +++ b/elasticsearch/_sync/client/ingest.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class IngestClient(NamespacedClient): diff --git a/elasticsearch/client/ingest.pyi b/elasticsearch/_sync/client/ingest.pyi similarity index 100% rename from elasticsearch/client/ingest.pyi rename to elasticsearch/_sync/client/ingest.pyi diff --git a/elasticsearch/client/license.py b/elasticsearch/_sync/client/license.py similarity index 98% rename from elasticsearch/client/license.py rename to elasticsearch/_sync/client/license.py index 9f1a094c1..532e99e7e 100644 --- a/elasticsearch/client/license.py +++ b/elasticsearch/_sync/client/license.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import NamespacedClient, query_params +from ._base import NamespacedClient +from .utils import _deprecated_options, query_params class LicenseClient(NamespacedClient): diff --git a/elasticsearch/client/license.pyi b/elasticsearch/_sync/client/license.pyi similarity index 100% rename from elasticsearch/client/license.pyi rename to elasticsearch/_sync/client/license.pyi diff --git a/elasticsearch/client/logstash.py b/elasticsearch/_sync/client/logstash.py similarity index 96% rename from elasticsearch/client/logstash.py rename to elasticsearch/_sync/client/logstash.py index ca3e0e88c..eebc7b834 100644 --- a/elasticsearch/client/logstash.py +++ b/elasticsearch/_sync/client/logstash.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class LogstashClient(NamespacedClient): diff --git a/elasticsearch/client/logstash.pyi b/elasticsearch/_sync/client/logstash.pyi similarity index 100% rename from elasticsearch/client/logstash.pyi rename to elasticsearch/_sync/client/logstash.pyi diff --git a/elasticsearch/client/migration.py b/elasticsearch/_sync/client/migration.py similarity index 61% rename from elasticsearch/client/migration.py rename to elasticsearch/_sync/client/migration.py index 0fd4612ff..61d5c836f 100644 --- a/elasticsearch/client/migration.py +++ b/elasticsearch/_sync/client/migration.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import _deprecated_options, _make_path, query_params class MigrationClient(NamespacedClient): @@ -36,3 +37,25 @@ def deprecations(self, index=None, params=None, headers=None): params=params, headers=headers, ) + + @query_params() + def get_feature_upgrade_status(self, params=None, headers=None): + """ + Find out whether system features need to be upgraded or not + + ``_ + """ + return self.transport.perform_request( + "GET", "/_migration/system_features", params=params, headers=headers + ) + + @query_params() + def post_feature_upgrade(self, params=None, headers=None): + """ + Begin upgrades for system features + + ``_ + """ + return self.transport.perform_request( + "POST", "/_migration/system_features", params=params, headers=headers + ) diff --git a/elasticsearch/client/migration.pyi b/elasticsearch/_sync/client/migration.pyi similarity index 54% rename from elasticsearch/client/migration.pyi rename to elasticsearch/_sync/client/migration.pyi index 0fc1842bf..b0ae288b9 100644 --- a/elasticsearch/client/migration.pyi +++ b/elasticsearch/_sync/client/migration.pyi @@ -37,3 +37,35 @@ class MigrationClient(NamespacedClient): params: Optional[MutableMapping[str, Any]] = ..., headers: Optional[MutableMapping[str, str]] = ..., ) -> Any: ... + def get_feature_upgrade_status( + self, + *, + pretty: Optional[bool] = ..., + human: Optional[bool] = ..., + error_trace: Optional[bool] = ..., + format: Optional[str] = ..., + filter_path: Optional[Union[str, Collection[str]]] = ..., + request_timeout: Optional[Union[int, float]] = ..., + ignore: Optional[Union[int, Collection[int]]] = ..., + opaque_id: Optional[str] = ..., + http_auth: Optional[Union[str, Tuple[str, str]]] = ..., + api_key: Optional[Union[str, Tuple[str, str]]] = ..., + params: Optional[MutableMapping[str, Any]] = ..., + headers: Optional[MutableMapping[str, str]] = ..., + ) -> Any: ... + def post_feature_upgrade( + self, + *, + pretty: Optional[bool] = ..., + human: Optional[bool] = ..., + error_trace: Optional[bool] = ..., + format: Optional[str] = ..., + filter_path: Optional[Union[str, Collection[str]]] = ..., + request_timeout: Optional[Union[int, float]] = ..., + ignore: Optional[Union[int, Collection[int]]] = ..., + opaque_id: Optional[str] = ..., + http_auth: Optional[Union[str, Tuple[str, str]]] = ..., + api_key: Optional[Union[str, Tuple[str, str]]] = ..., + params: Optional[MutableMapping[str, Any]] = ..., + headers: Optional[MutableMapping[str, str]] = ..., + ) -> Any: ... diff --git a/elasticsearch/client/ml.py b/elasticsearch/_sync/client/ml.py similarity index 96% rename from elasticsearch/client/ml.py rename to elasticsearch/_sync/client/ml.py index af02f5a25..b86b84597 100644 --- a/elasticsearch/client/ml.py +++ b/elasticsearch/_sync/client/ml.py @@ -15,7 +15,14 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _bulk_body, _make_path, query_params +from ._base import NamespacedClient +from .utils import ( + SKIP_IN_PATH, + _bulk_body, + _deprecated_options, + _make_path, + query_params, +) class MlClient(NamespacedClient): @@ -1350,7 +1357,7 @@ def get_trained_models_stats(self, model_id=None, params=None, headers=None): headers=headers, ) - @query_params() + @query_params("defer_definition_decompression") def put_trained_model(self, model_id, body, params=None, headers=None): """ Creates an inference trained model. @@ -1359,6 +1366,9 @@ def put_trained_model(self, model_id, body, params=None, headers=None): :arg model_id: The ID of the trained models to store :arg body: The trained model configuration + :arg defer_definition_decompression: If set to `true` and a + `compressed_definition` is provided, the request defers definition + decompression and skips relevant validations. """ for param in (model_id, body): if param in SKIP_IN_PATH: @@ -1726,7 +1736,7 @@ def reset_job(self, job_id, params=None, headers=None): headers=headers, ) - @query_params("timeout") + @query_params("timeout", "wait_for") def start_trained_model_deployment(self, model_id, params=None, headers=None): """ Start a trained model deployment. @@ -1741,6 +1751,8 @@ def start_trained_model_deployment(self, model_id, params=None, headers=None): :arg model_id: The unique identifier of the trained model. :arg timeout: Controls the amount of time to wait for the model to deploy. Default: 20s + :arg wait_for: The allocation status for which to wait Valid + choices: starting, started, fully_allocated Default: started """ if model_id in SKIP_IN_PATH: raise ValueError("Empty value passed for a required argument 'model_id'.") @@ -1783,11 +1795,6 @@ def get_trained_model_deployment_stats(self, model_id, params=None, headers=None ``_ - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version - :arg model_id: The ID of the trained model deployment stats to fetch """ @@ -1800,3 +1807,61 @@ def get_trained_model_deployment_stats(self, model_id, params=None, headers=None params=params, headers=headers, ) + + @query_params() + def put_trained_model_definition_part( + self, model_id, part, body, params=None, headers=None + ): + """ + Creates part of a trained model definition + + ``_ + + .. warning:: + + This API is **experimental** so may include breaking changes + or be removed in a future version + + :arg model_id: The ID of the trained model for this definition + part + :arg part: The part number + :arg body: The trained model definition part + """ + for param in (model_id, part, body): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") + + return self.transport.perform_request( + "PUT", + _make_path("_ml", "trained_models", model_id, "definition", part), + params=params, + headers=headers, + body=body, + ) + + @query_params() + def put_trained_model_vocabulary(self, model_id, body, params=None, headers=None): + """ + Creates a trained model vocabulary + + ``_ + + .. warning:: + + This API is **experimental** so may include breaking changes + or be removed in a future version + + :arg model_id: The ID of the trained model for this vocabulary + :arg body: The trained model vocabulary + """ + for param in (model_id, body): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") + + return self.transport.perform_request( + "PUT", + _make_path("_ml", "trained_models", model_id, "vocabulary"), + params=params, + headers=headers, + body=body, + ) diff --git a/elasticsearch/client/ml.pyi b/elasticsearch/_sync/client/ml.pyi similarity index 97% rename from elasticsearch/client/ml.pyi rename to elasticsearch/_sync/client/ml.pyi index e02429d38..1ef0420ed 100644 --- a/elasticsearch/client/ml.pyi +++ b/elasticsearch/_sync/client/ml.pyi @@ -1038,6 +1038,7 @@ class MlClient(NamespacedClient): model_id: Any, *, body: Any, + defer_definition_decompression: Optional[Any] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., @@ -1307,6 +1308,7 @@ class MlClient(NamespacedClient): model_id: Any, *, timeout: Optional[Any] = ..., + wait_for: Optional[Any] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., @@ -1354,3 +1356,40 @@ class MlClient(NamespacedClient): params: Optional[MutableMapping[str, Any]] = ..., headers: Optional[MutableMapping[str, str]] = ..., ) -> Any: ... + def put_trained_model_definition_part( + self, + model_id: Any, + part: Any, + *, + body: Any, + pretty: Optional[bool] = ..., + human: Optional[bool] = ..., + error_trace: Optional[bool] = ..., + format: Optional[str] = ..., + filter_path: Optional[Union[str, Collection[str]]] = ..., + request_timeout: Optional[Union[int, float]] = ..., + ignore: Optional[Union[int, Collection[int]]] = ..., + opaque_id: Optional[str] = ..., + http_auth: Optional[Union[str, Tuple[str, str]]] = ..., + api_key: Optional[Union[str, Tuple[str, str]]] = ..., + params: Optional[MutableMapping[str, Any]] = ..., + headers: Optional[MutableMapping[str, str]] = ..., + ) -> Any: ... + def put_trained_model_vocabulary( + self, + model_id: Any, + *, + body: Any, + pretty: Optional[bool] = ..., + human: Optional[bool] = ..., + error_trace: Optional[bool] = ..., + format: Optional[str] = ..., + filter_path: Optional[Union[str, Collection[str]]] = ..., + request_timeout: Optional[Union[int, float]] = ..., + ignore: Optional[Union[int, Collection[int]]] = ..., + opaque_id: Optional[str] = ..., + http_auth: Optional[Union[str, Tuple[str, str]]] = ..., + api_key: Optional[Union[str, Tuple[str, str]]] = ..., + params: Optional[MutableMapping[str, Any]] = ..., + headers: Optional[MutableMapping[str, str]] = ..., + ) -> Any: ... diff --git a/elasticsearch/client/monitoring.py b/elasticsearch/_sync/client/monitoring.py similarity index 89% rename from elasticsearch/client/monitoring.py rename to elasticsearch/_sync/client/monitoring.py index b606df257..7c96c4a70 100644 --- a/elasticsearch/client/monitoring.py +++ b/elasticsearch/_sync/client/monitoring.py @@ -15,7 +15,14 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _bulk_body, _make_path, query_params +from ._base import NamespacedClient +from .utils import ( + SKIP_IN_PATH, + _bulk_body, + _deprecated_options, + _make_path, + query_params, +) class MonitoringClient(NamespacedClient): @@ -26,11 +33,6 @@ def bulk(self, body, doc_type=None, params=None, headers=None): ``_ - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version - :arg body: The operation definition and data (action-data pairs), separated by newlines :arg doc_type: Default document type for items which don't diff --git a/elasticsearch/client/monitoring.pyi b/elasticsearch/_sync/client/monitoring.pyi similarity index 100% rename from elasticsearch/client/monitoring.pyi rename to elasticsearch/_sync/client/monitoring.pyi diff --git a/elasticsearch/client/nodes.py b/elasticsearch/_sync/client/nodes.py similarity index 97% rename from elasticsearch/client/nodes.py rename to elasticsearch/_sync/client/nodes.py index dd1cbb03f..fad85cf66 100644 --- a/elasticsearch/client/nodes.py +++ b/elasticsearch/_sync/client/nodes.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class NodesClient(NamespacedClient): @@ -153,7 +154,7 @@ def stats( metric to the specific index metrics. Isn't used if `indices` (or `all`) metric isn't specified. Valid choices: _all, completion, docs, fielddata, query_cache, flush, get, indexing, merge, request_cache, - refresh, search, segments, store, warmer, bulk, shards + refresh, search, segments, store, warmer, bulk, shard_stats :arg completion_fields: A comma-separated list of fields for the `completion` index metric (supports wildcards) :arg fielddata_fields: A comma-separated list of fields for the @@ -182,7 +183,7 @@ def stats( ) @query_params() - def clear_metering_archive( + def clear_repositories_metering_archive( self, node_id, max_archive_version, params=None, headers=None ): """ @@ -214,7 +215,7 @@ def clear_metering_archive( ) @query_params() - def get_metering_info(self, node_id, params=None, headers=None): + def get_repositories_metering_info(self, node_id, params=None, headers=None): """ Returns cluster repositories metering information. diff --git a/elasticsearch/client/nodes.pyi b/elasticsearch/_sync/client/nodes.pyi similarity index 98% rename from elasticsearch/client/nodes.pyi rename to elasticsearch/_sync/client/nodes.pyi index 772682556..bdb5c5636 100644 --- a/elasticsearch/client/nodes.pyi +++ b/elasticsearch/_sync/client/nodes.pyi @@ -129,7 +129,7 @@ class NodesClient(NamespacedClient): params: Optional[MutableMapping[str, Any]] = ..., headers: Optional[MutableMapping[str, str]] = ..., ) -> Any: ... - def clear_metering_archive( + def clear_repositories_metering_archive( self, node_id: Any, max_archive_version: Any, @@ -147,7 +147,7 @@ class NodesClient(NamespacedClient): params: Optional[MutableMapping[str, Any]] = ..., headers: Optional[MutableMapping[str, str]] = ..., ) -> Any: ... - def get_metering_info( + def get_repositories_metering_info( self, node_id: Any, *, diff --git a/elasticsearch/client/rollup.py b/elasticsearch/_sync/client/rollup.py similarity index 98% rename from elasticsearch/client/rollup.py rename to elasticsearch/_sync/client/rollup.py index 66ce2e132..ef35b876b 100644 --- a/elasticsearch/client/rollup.py +++ b/elasticsearch/_sync/client/rollup.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class RollupClient(NamespacedClient): diff --git a/elasticsearch/client/rollup.pyi b/elasticsearch/_sync/client/rollup.pyi similarity index 100% rename from elasticsearch/client/rollup.pyi rename to elasticsearch/_sync/client/rollup.pyi diff --git a/elasticsearch/client/searchable_snapshots.py b/elasticsearch/_sync/client/searchable_snapshots.py similarity index 93% rename from elasticsearch/client/searchable_snapshots.py rename to elasticsearch/_sync/client/searchable_snapshots.py index 9bff0c715..d269be8f6 100644 --- a/elasticsearch/client/searchable_snapshots.py +++ b/elasticsearch/_sync/client/searchable_snapshots.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class SearchableSnapshotsClient(NamespacedClient): @@ -56,11 +57,6 @@ def mount(self, repository, snapshot, body, params=None, headers=None): ``_ - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version - :arg repository: The name of the repository containing the snapshot of the index to mount :arg snapshot: The name of the snapshot of the index to mount @@ -92,11 +88,6 @@ def stats(self, index=None, params=None, headers=None): ``_ - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version - :arg index: A comma-separated list of index names :arg level: Return stats aggregated at cluster, index or shard level Valid choices: cluster, indices, shards Default: indices diff --git a/elasticsearch/client/searchable_snapshots.pyi b/elasticsearch/_sync/client/searchable_snapshots.pyi similarity index 100% rename from elasticsearch/client/searchable_snapshots.pyi rename to elasticsearch/_sync/client/searchable_snapshots.pyi diff --git a/elasticsearch/client/security.py b/elasticsearch/_sync/client/security.py similarity index 97% rename from elasticsearch/client/security.py rename to elasticsearch/_sync/client/security.py index aa2742dc4..759cbd34b 100644 --- a/elasticsearch/client/security.py +++ b/elasticsearch/_sync/client/security.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class SecurityClient(NamespacedClient): @@ -609,11 +610,6 @@ def clear_cached_service_tokens( ``_ - .. warning:: - - This API is **beta** so may include breaking changes - or be removed in a future version - :arg namespace: An identifier for the namespace :arg service: An identifier for the service name :arg name: A comma-separated list of service token names @@ -648,11 +644,6 @@ def create_service_token( ``_ - .. warning:: - - This API is **beta** so may include breaking changes - or be removed in a future version - :arg namespace: An identifier for the namespace :arg service: An identifier for the service name :arg name: An identifier for the token name @@ -681,11 +672,6 @@ def delete_service_token(self, namespace, service, name, params=None, headers=No ``_ - .. warning:: - - This API is **beta** so may include breaking changes - or be removed in a future version - :arg namespace: An identifier for the namespace :arg service: An identifier for the service name :arg name: An identifier for the token name @@ -716,11 +702,6 @@ def get_service_accounts( ``_ - .. warning:: - - This API is **beta** so may include breaking changes - or be removed in a future version - :arg namespace: An identifier for the namespace :arg service: An identifier for the service name """ @@ -738,11 +719,6 @@ def get_service_credentials(self, namespace, service, params=None, headers=None) ``_ - .. warning:: - - This API is **beta** so may include breaking changes - or be removed in a future version - :arg namespace: An identifier for the namespace :arg service: An identifier for the service name """ diff --git a/elasticsearch/client/security.pyi b/elasticsearch/_sync/client/security.pyi similarity index 100% rename from elasticsearch/client/security.pyi rename to elasticsearch/_sync/client/security.pyi diff --git a/elasticsearch/client/shutdown.py b/elasticsearch/_sync/client/shutdown.py similarity index 81% rename from elasticsearch/client/shutdown.py rename to elasticsearch/_sync/client/shutdown.py index 4a868fe7e..d3fcc26a8 100644 --- a/elasticsearch/client/shutdown.py +++ b/elasticsearch/_sync/client/shutdown.py @@ -15,22 +15,19 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class ShutdownClient(NamespacedClient): @query_params() def delete_node(self, node_id, params=None, headers=None): """ - Removes a node from the shutdown list + Removes a node from the shutdown list. Designed for indirect use by ECE/ESS and + ECK. Direct use is not supported. ``_ - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version - :arg node_id: The node id of node to be removed from the shutdown state """ @@ -47,15 +44,11 @@ def delete_node(self, node_id, params=None, headers=None): @query_params() def get_node(self, node_id=None, params=None, headers=None): """ - Retrieve status of a node or nodes that are currently marked as shutting down + Retrieve status of a node or nodes that are currently marked as shutting down. + Designed for indirect use by ECE/ESS and ECK. Direct use is not supported. ``_ - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version - :arg node_id: Which node for which to retrieve the shutdown status """ @@ -69,15 +62,11 @@ def get_node(self, node_id=None, params=None, headers=None): @query_params() def put_node(self, node_id, body, params=None, headers=None): """ - Adds a node to be shut down + Adds a node to be shut down. Designed for indirect use by ECE/ESS and ECK. + Direct use is not supported. ``_ - .. warning:: - - This API is **experimental** so may include breaking changes - or be removed in a future version - :arg node_id: The node id of node to be shut down :arg body: The shutdown type definition to register """ diff --git a/elasticsearch/client/shutdown.pyi b/elasticsearch/_sync/client/shutdown.pyi similarity index 100% rename from elasticsearch/client/shutdown.pyi rename to elasticsearch/_sync/client/shutdown.pyi diff --git a/elasticsearch/client/slm.py b/elasticsearch/_sync/client/slm.py similarity index 98% rename from elasticsearch/client/slm.py rename to elasticsearch/_sync/client/slm.py index dfc5fd7b3..03aa2debb 100644 --- a/elasticsearch/client/slm.py +++ b/elasticsearch/_sync/client/slm.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class SlmClient(NamespacedClient): diff --git a/elasticsearch/client/slm.pyi b/elasticsearch/_sync/client/slm.pyi similarity index 100% rename from elasticsearch/client/slm.pyi rename to elasticsearch/_sync/client/slm.pyi diff --git a/elasticsearch/client/snapshot.py b/elasticsearch/_sync/client/snapshot.py similarity index 99% rename from elasticsearch/client/snapshot.py rename to elasticsearch/_sync/client/snapshot.py index baec9f5f5..b113a1e19 100644 --- a/elasticsearch/client/snapshot.py +++ b/elasticsearch/_sync/client/snapshot.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class SnapshotClient(NamespacedClient): diff --git a/elasticsearch/client/snapshot.pyi b/elasticsearch/_sync/client/snapshot.pyi similarity index 100% rename from elasticsearch/client/snapshot.pyi rename to elasticsearch/_sync/client/snapshot.pyi diff --git a/elasticsearch/client/sql.py b/elasticsearch/_sync/client/sql.py similarity index 97% rename from elasticsearch/client/sql.py rename to elasticsearch/_sync/client/sql.py index 45dff9332..906e8b96e 100644 --- a/elasticsearch/client/sql.py +++ b/elasticsearch/_sync/client/sql.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class SqlClient(NamespacedClient): diff --git a/elasticsearch/client/sql.pyi b/elasticsearch/_sync/client/sql.pyi similarity index 100% rename from elasticsearch/client/sql.pyi rename to elasticsearch/_sync/client/sql.pyi diff --git a/elasticsearch/client/ssl.py b/elasticsearch/_sync/client/ssl.py similarity index 93% rename from elasticsearch/client/ssl.py rename to elasticsearch/_sync/client/ssl.py index 5f24181f8..1a61ba887 100644 --- a/elasticsearch/client/ssl.py +++ b/elasticsearch/_sync/client/ssl.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import NamespacedClient, query_params +from ._base import NamespacedClient +from .utils import _deprecated_options, query_params class SslClient(NamespacedClient): diff --git a/elasticsearch/client/ssl.pyi b/elasticsearch/_sync/client/ssl.pyi similarity index 100% rename from elasticsearch/client/ssl.pyi rename to elasticsearch/_sync/client/ssl.pyi diff --git a/elasticsearch/client/tasks.py b/elasticsearch/_sync/client/tasks.py similarity index 97% rename from elasticsearch/client/tasks.py rename to elasticsearch/_sync/client/tasks.py index 129b5c846..8cb3f8c0c 100644 --- a/elasticsearch/client/tasks.py +++ b/elasticsearch/_sync/client/tasks.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class TasksClient(NamespacedClient): diff --git a/elasticsearch/client/tasks.pyi b/elasticsearch/_sync/client/tasks.pyi similarity index 100% rename from elasticsearch/client/tasks.pyi rename to elasticsearch/_sync/client/tasks.pyi diff --git a/elasticsearch/client/text_structure.py b/elasticsearch/_sync/client/text_structure.py similarity index 97% rename from elasticsearch/client/text_structure.py rename to elasticsearch/_sync/client/text_structure.py index a2331a28d..12ded2c87 100644 --- a/elasticsearch/client/text_structure.py +++ b/elasticsearch/_sync/client/text_structure.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _bulk_body, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _bulk_body, _deprecated_options, query_params class TextStructureClient(NamespacedClient): diff --git a/elasticsearch/client/text_structure.pyi b/elasticsearch/_sync/client/text_structure.pyi similarity index 100% rename from elasticsearch/client/text_structure.pyi rename to elasticsearch/_sync/client/text_structure.pyi diff --git a/elasticsearch/client/transform.py b/elasticsearch/_sync/client/transform.py similarity index 98% rename from elasticsearch/client/transform.py rename to elasticsearch/_sync/client/transform.py index 72af1fb3b..887b29fe2 100644 --- a/elasticsearch/client/transform.py +++ b/elasticsearch/_sync/client/transform.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class TransformClient(NamespacedClient): diff --git a/elasticsearch/client/transform.pyi b/elasticsearch/_sync/client/transform.pyi similarity index 100% rename from elasticsearch/client/transform.pyi rename to elasticsearch/_sync/client/transform.pyi diff --git a/elasticsearch/_sync/client/utils.py b/elasticsearch/_sync/client/utils.py new file mode 100644 index 000000000..92a5b39f4 --- /dev/null +++ b/elasticsearch/_sync/client/utils.py @@ -0,0 +1,349 @@ +# 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 base64 +import warnings +from datetime import date, datetime +from functools import wraps +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Collection, + List, + Mapping, + Optional, + Tuple, + TypeVar, + Union, +) + +from elastic_transport import NodeConfig, Transport +from elastic_transport.client_utils import ( + DEFAULT, + client_meta_version, + parse_cloud_id, + url_to_node_config, +) + +from .._version import __versionstr__ +from ..compat import quote, string_types, to_bytes, to_str +from ..serializer import Serializer + +if TYPE_CHECKING: + from ..client import Elasticsearch + +# parts of URL to be omitted +SKIP_IN_PATH: Collection[Any] = (None, "", b"", [], ()) + +# To be passed to 'client_meta_service' on the Transport +CLIENT_META_SERVICE = ("es", client_meta_version(__versionstr__)) + +_TYPE_HOSTS = Union[str, List[Union[str, Mapping[str, Union[str, int]], NodeConfig]]] + + +def client_node_configs( + hosts: _TYPE_HOSTS, cloud_id: str, **kwargs: Any +) -> List[NodeConfig]: + if cloud_id is not None: + if hosts is not None: + raise ValueError( + "The 'cloud_id' and 'hosts' parameters are mutually exclusive" + ) + node_configs = cloud_id_to_node_configs(cloud_id) + else: + node_configs = hosts_to_node_configs(hosts) + + # Remove all values which are 'DEFAULT' to avoid overwriting actual defaults. + node_options = {k: v for k, v in kwargs.items() if v is not DEFAULT} + return [node_config.replace(**node_options) for node_config in node_configs] + + +def hosts_to_node_configs(hosts: _TYPE_HOSTS) -> List[NodeConfig]: + """Transforms the many formats of 'hosts' into NodeConfigs""" + + # To make the logic here simpler we reroute everything to be List[X] + if not isinstance(hosts, (tuple, list)): + return hosts_to_node_configs([hosts]) + + node_configs: List[NodeConfig] = [] + for host in hosts: + if isinstance(host, NodeConfig): + node_configs.append(host) + + elif isinstance(host, str): + node_configs.append(url_to_node_config(host)) + + elif isinstance(host, Mapping): + node_configs.append(host_mapping_to_node_config(host)) + else: + raise ValueError( + "'hosts' must be a list of URLs, NodeConfigs, or dictionaries" + ) + + return node_configs + + +def host_mapping_to_node_config(host: Mapping[str, Union[str, int]]) -> NodeConfig: + """Converts an old-style dictionary host specification to a NodeConfig""" + + allow_hosts_keys = { + "scheme", + "host", + "port", + "path_prefix", + "url_prefix", + "use_ssl", + } + disallowed_keys = set(host.keys()).difference(allow_hosts_keys) + if disallowed_keys: + raise ValueError("Can't specify") + + options = dict(host) + + # Handle the deprecated option 'use_ssl' + if "use_ssl" in options: + use_ssl = options.pop("use_ssl") + if not isinstance(use_ssl, bool): + raise TypeError("'use_ssl' must be of type 'bool'") + + # Ensure the user isn't specifying scheme=http use_ssl=True or vice-versa + if "scheme" in options and (options["scheme"] == "https") != use_ssl: + raise ValueError( + f"Cannot specify conflicting options 'scheme={options['scheme']}' " + f"and 'use_ssl={use_ssl}'. Use 'scheme' only instead" + ) + + warnings.warn( + "The 'use_ssl' option is no longer needed as specifying a 'scheme' is now required", + category=DeprecationWarning, + stacklevel=3, + ) + options.setdefault("scheme", "https" if use_ssl else "http") + + # Handle the deprecated option 'url_prefix' + if "url_prefix" in options: + if "path_prefix" in options: + raise ValueError( + "Cannot specify conflicting options 'url_prefix' and " + "'path_prefix'. Use 'path_prefix' only instead" + ) + + warnings.warn( + "The 'url_prefix' option is deprecated in favor of 'path_prefix'", + category=DeprecationWarning, + stacklevel=3, + ) + options["path_prefix"] = options.pop("url_prefix") + + return NodeConfig(**options) + + +def cloud_id_to_node_configs(cloud_id: str) -> List[NodeConfig]: + """Transforms an Elastic Cloud ID into a NodeConfig""" + host, port = parse_cloud_id(cloud_id).es_address + if not host or not port: + raise ValueError("Cloud ID missing host and port information for Elasticsearch") + return [ + NodeConfig( + scheme="https", + host=host, + port=port, + http_compress=True, + # TODO: Set TLSv1.2+ + ) + ] + + +def _escape(value: Any) -> str: + """ + Escape a single value of a URL string or a query parameter. If it is a list + or tuple, turn it into a comma-separated string first. + """ + + # make sequences into comma-separated stings + if isinstance(value, (list, tuple)): + value = ",".join(value) + + # dates and datetimes into isoformat + elif isinstance(value, (date, datetime)): + value = value.isoformat() + + # make bools into true/false strings + elif isinstance(value, bool): + value = str(value).lower() + + elif isinstance(value, bytes): + return value.decode("utf-8") + + return str(value) + + +def _make_path(*parts: Any) -> str: + """ + Create a URL string from parts, omit all `None` values and empty strings. + Convert lists and tuples to comma separated values. + """ + # TODO: maybe only allow some parts to be lists/tuples ? + return "/" + "/".join( + # preserve ',' and '*' in url for nicer URLs in logs + quote(_escape(p), b",*") + for p in parts + if p not in SKIP_IN_PATH + ) + + +# parameters that apply to all methods +GLOBAL_PARAMS: Tuple[str, ...] = ( + "pretty", + "human", + "error_trace", + "format", + "filter_path", +) +T = TypeVar("T") + + +def query_params( + *es_query_params: str, +) -> Callable[[T], T]: + """ + Decorator that pops all accepted parameters from method's kwargs and puts + them in the params argument. + """ + + def _wrapper(func: Any) -> Any: + @wraps(func) + def _wrapped(*args: Any, **kwargs: Any) -> Any: + params = (kwargs.pop("params", None) or {}).copy() + headers = { + k.lower(): v + for k, v in (kwargs.pop("headers", None) or {}).copy().items() + } + + if "opaque_id" in kwargs: + headers["x-opaque-id"] = kwargs.pop("opaque_id") + + http_auth = kwargs.pop("http_auth", None) + api_key = kwargs.pop("api_key", None) + + if http_auth is not None and api_key is not None: + raise ValueError( + "Only one of 'http_auth' and 'api_key' may be passed at a time" + ) + elif http_auth is not None: + headers["authorization"] = f"Basic {_base64_auth_header(http_auth)}" + elif api_key is not None: + headers["authorization"] = f"ApiKey {_base64_auth_header(api_key)}" + + for p in es_query_params + GLOBAL_PARAMS: + if p in kwargs: + v = kwargs.pop(p) + if v is not None: + params[p] = _escape(v) + + # don't treat ignore, request_timeout, and opaque_id as other params to avoid escaping + for p in ("ignore", "request_timeout"): + if p in kwargs: + params[p] = kwargs.pop(p) + return func(*args, params=params, headers=headers, **kwargs) + + return _wrapped + + return _wrapper + + +def _bulk_body( + serializer: Serializer, body: Union[str, bytes, Collection[Any]] +) -> Union[str, bytes]: + # if not passed in a string, serialize items and join by newline + if not isinstance(body, string_types): + body = "\n".join(map(serializer.dumps, body)) + + # bulk body must end with a newline + if isinstance(body, bytes): + if not body.endswith(b"\n"): + body += b"\n" + elif isinstance(body, str) and not body.endswith("\n"): + body += "\n" + + return body + + +def _base64_auth_header( + auth_value: Union[List[str], Tuple[str, ...], str, bytes] +) -> str: + """Takes either a 2-tuple or a base64-encoded string + and returns a base64-encoded string to be used + as an HTTP authorization header. + """ + if isinstance(auth_value, (list, tuple)): + auth_value = base64.b64encode(to_bytes(":".join(auth_value))) + return to_str(auth_value) + + +def _deprecated_options( + client: "Elasticsearch", + params: Optional[Mapping[str, Any]], +) -> Tuple["Elasticsearch", Optional[Mapping[str, Any]]]: + """Applies the deprecated logic for per-request options. When passed deprecated options + this function will convert them into a Elasticsearch.options() or encoded params""" + if params: + warnings.warn( + "Passing options via 'params' is deprecated, instead use API parameters directly.", + category=DeprecationWarning, + stacklevel=2, + ) + + options_kwargs = {} + opaque_id = params.pop("opaque_id", None) + api_key = params.pop("api_key", None) + http_auth = params.pop("http_auth", None) + headers = {} + if opaque_id is not None: + headers["x-opaque-id"] = opaque_id + if http_auth is not None and api_key is not None: + raise ValueError( + "Only one of 'http_auth' and 'api_key' may be passed at a time" + ) + elif api_key is not None: + headers["authorization"] = f"ApiKey {_base64_auth_header(api_key)}" + elif http_auth is not None: + if isinstance(http_auth, str): + headers["authorization"] = f"Bearer {_base64_auth_header(http_auth)}" + else: + headers["authorization"] = f"Basic {_base64_auth_header(http_auth)}" + if headers: + options_kwargs["headers"] = headers + + request_timeout = params.pop("request_timeout", None) + if request_timeout is not None: + options_kwargs["request_timeout"] = request_timeout + + ignore = params.pop("ignore", None) + if ignore is not None: + options_kwargs["ignore_status"] = ignore + + if options_kwargs: + warnings.warn( + "Passing transport options in the API method is deprecated. Use 'Elasticsearch.options()' instead.", + category=DeprecationWarning, + ) + client = client.options(**options_kwargs) + + return client, params or None diff --git a/elasticsearch/client/watcher.py b/elasticsearch/_sync/client/watcher.py similarity index 98% rename from elasticsearch/client/watcher.py rename to elasticsearch/_sync/client/watcher.py index 75fb33e5d..36d19ce61 100644 --- a/elasticsearch/client/watcher.py +++ b/elasticsearch/_sync/client/watcher.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import SKIP_IN_PATH, NamespacedClient, _make_path, query_params +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _deprecated_options, _make_path, query_params class WatcherClient(NamespacedClient): diff --git a/elasticsearch/client/watcher.pyi b/elasticsearch/_sync/client/watcher.pyi similarity index 100% rename from elasticsearch/client/watcher.pyi rename to elasticsearch/_sync/client/watcher.pyi diff --git a/elasticsearch/client/xpack.py b/elasticsearch/_sync/client/xpack.py similarity index 95% rename from elasticsearch/client/xpack.py rename to elasticsearch/_sync/client/xpack.py index 348825f0c..0a8fe51ee 100644 --- a/elasticsearch/client/xpack.py +++ b/elasticsearch/_sync/client/xpack.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from .utils import NamespacedClient, query_params +from ._base import NamespacedClient +from .utils import _deprecated_options, query_params class XPackClient(NamespacedClient): diff --git a/elasticsearch/client/xpack.pyi b/elasticsearch/_sync/client/xpack.pyi similarity index 100% rename from elasticsearch/client/xpack.pyi rename to elasticsearch/_sync/client/xpack.pyi diff --git a/elasticsearch/_async/client/utils.pyi b/elasticsearch/client.py similarity index 55% rename from elasticsearch/_async/client/utils.pyi rename to elasticsearch/client.py index ec9e7905b..cc376076d 100644 --- a/elasticsearch/_async/client/utils.pyi +++ b/elasticsearch/client.py @@ -15,17 +15,7 @@ # specific language governing permissions and limitations # under the License. -from ...client.utils import SKIP_IN_PATH as SKIP_IN_PATH # noqa -from ...client.utils import _bulk_body as _bulk_body # noqa -from ...client.utils import _escape as _escape # noqa -from ...client.utils import _make_path as _make_path # noqa -from ...client.utils import _normalize_hosts as _normalize_hosts # noqa -from ...client.utils import query_params as query_params # noqa -from ..client import AsyncElasticsearch -from ..transport import AsyncTransport +from ._async import AsyncElasticsearch +from ._sync import Elasticsearch -class NamespacedClient: - client: AsyncElasticsearch - def __init__(self, client: AsyncElasticsearch) -> None: ... - @property - def transport(self) -> AsyncTransport: ... +__all__ = ["AsyncElasticsearch", "Elasticsearch"] diff --git a/elasticsearch/client/utils.py b/elasticsearch/client/utils.py deleted file mode 100644 index aaaa8c824..000000000 --- a/elasticsearch/client/utils.py +++ /dev/null @@ -1,241 +0,0 @@ -# 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 base64 -import weakref -from datetime import date, datetime -from functools import wraps -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Collection, - Dict, - List, - Optional, - Tuple, - TypeVar, - Union, -) - -from ..compat import quote, string_types, to_bytes, to_str, unquote, urlparse -from ..serializer import Serializer -from ..transport import Transport - -if TYPE_CHECKING: - from ..client import Elasticsearch - -# parts of URL to be omitted -SKIP_IN_PATH: Collection[Any] = (None, "", b"", [], ()) - - -def _normalize_hosts( - hosts: Optional[Union[str, Collection[Union[str, Dict[str, Any]]]]] -) -> List[Dict[str, Any]]: - """ - Helper function to transform hosts argument to - :class:`~elasticsearch.Elasticsearch` to a list of dicts. - """ - # if hosts are empty, just defer to defaults down the line - if hosts is None: - return [{}] - - # passed in just one string - if isinstance(hosts, string_types): - hosts = [hosts] - - out: List[Dict[str, Any]] = [] - # normalize hosts to dicts - for host in hosts: - if isinstance(host, string_types): - if "://" not in host: - host = f"//{host}" - - parsed_url = urlparse(host) - h: Dict[str, Any] = {"host": parsed_url.hostname} - - if parsed_url.port: - h["port"] = parsed_url.port - - if parsed_url.scheme == "https": - h["port"] = parsed_url.port or 443 - h["use_ssl"] = True - - if parsed_url.username or parsed_url.password: - h["http_auth"] = "{}:{}".format( - unquote(parsed_url.username or ""), - unquote(parsed_url.password or ""), - ) - - if parsed_url.path and parsed_url.path != "/": - h["url_prefix"] = parsed_url.path - - out.append(h) - else: - out.append(host) # type: ignore - return out - - -def _escape(value: Any) -> Union[str, bytes]: - """ - Escape a single value of a URL string or a query parameter. If it is a list - or tuple, turn it into a comma-separated string first. - """ - - # make sequences into comma-separated stings - if isinstance(value, (list, tuple)): - value = ",".join(value) - - # dates and datetimes into isoformat - elif isinstance(value, (date, datetime)): - value = value.isoformat() - - # make bools into true/false strings - elif isinstance(value, bool): - value = str(value).lower() - - # don't decode bytestrings - elif isinstance(value, bytes): - return value - - # encode strings to utf-8 - if not isinstance(value, str): - return str(value).encode("utf-8") - return value.encode("utf-8") - - -def _make_path(*parts: Any) -> str: - """ - Create a URL string from parts, omit all `None` values and empty strings. - Convert lists and tuples to comma separated values. - """ - # TODO: maybe only allow some parts to be lists/tuples ? - return "/" + "/".join( - # preserve ',' and '*' in url for nicer URLs in logs - quote(_escape(p), b",*") - for p in parts - if p not in SKIP_IN_PATH - ) - - -# parameters that apply to all methods -GLOBAL_PARAMS: Tuple[str, ...] = ( - "pretty", - "human", - "error_trace", - "format", - "filter_path", -) -T = TypeVar("T") - - -def query_params( - *es_query_params: str, -) -> Callable[[Callable[..., T]], Callable[..., T]]: - """ - Decorator that pops all accepted parameters from method's kwargs and puts - them in the params argument. - """ - - def _wrapper(func: Any) -> Any: - @wraps(func) - def _wrapped(*args: Any, **kwargs: Any) -> Any: - params = (kwargs.pop("params", None) or {}).copy() - headers = { - k.lower(): v - for k, v in (kwargs.pop("headers", None) or {}).copy().items() - } - - if "opaque_id" in kwargs: - headers["x-opaque-id"] = kwargs.pop("opaque_id") - - http_auth = kwargs.pop("http_auth", None) - api_key = kwargs.pop("api_key", None) - - if http_auth is not None and api_key is not None: - raise ValueError( - "Only one of 'http_auth' and 'api_key' may be passed at a time" - ) - elif http_auth is not None: - headers["authorization"] = f"Basic {_base64_auth_header(http_auth)}" - elif api_key is not None: - headers["authorization"] = f"ApiKey {_base64_auth_header(api_key)}" - - for p in es_query_params + GLOBAL_PARAMS: - if p in kwargs: - v = kwargs.pop(p) - if v is not None: - params[p] = _escape(v) - - # don't treat ignore, request_timeout, and opaque_id as other params to avoid escaping - for p in ("ignore", "request_timeout"): - if p in kwargs: - params[p] = kwargs.pop(p) - return func(*args, params=params, headers=headers, **kwargs) - - return _wrapped - - return _wrapper - - -def _bulk_body( - serializer: Serializer, body: Union[str, bytes, Collection[Any]] -) -> Union[str, bytes]: - # if not passed in a string, serialize items and join by newline - if not isinstance(body, string_types): - body = "\n".join(map(serializer.dumps, body)) - - # bulk body must end with a newline - if isinstance(body, bytes): - if not body.endswith(b"\n"): - body += b"\n" - elif isinstance(body, str) and not body.endswith("\n"): - body += "\n" - - return body - - -def _base64_auth_header( - auth_value: Union[List[str], Tuple[str, ...], str, bytes] -) -> str: - """Takes either a 2-tuple or a base64-encoded string - and returns a base64-encoded string to be used - as an HTTP authorization header. - """ - if isinstance(auth_value, (list, tuple)): - auth_value = base64.b64encode(to_bytes(":".join(auth_value))) - return to_str(auth_value) - - -class NamespacedClient: - client: "Elasticsearch" - - def __init__(self, client: "Elasticsearch") -> None: - self.client = client - - @property - def transport(self) -> Transport: - return self.client.transport - - -class AddonClient(NamespacedClient): - @classmethod - def infect_client(cls, client: "Elasticsearch") -> "Elasticsearch": - addon = cls(weakref.proxy(client)) - setattr(client, cls.namespace, addon) # type: ignore - return client diff --git a/utils/generate-api.py b/utils/generate-api.py index 32fa565da..7f1b5ceff 100644 --- a/utils/generate-api.py +++ b/utils/generate-api.py @@ -344,6 +344,9 @@ def download_artifact(version): for name in zip.namelist(): if not name.endswith(".json") or name == "schema.json": continue + # Skip compatibility APIs/tests + if "/compat" in name: + continue with (tmp / name.replace("rest-api-spec/api/", "")).open("wb") as f: f.write(zip.read(name)) @@ -396,7 +399,7 @@ def dump_modules(modules): rules = [ unasync.Rule( fromdir="/elasticsearch/_async/client/", - todir="/elasticsearch/client/", + todir="/elasticsearch/_sync/client/", additional_replacements=additional_replacements, ), ]