diff --git a/elasticsearch/__init__.py b/elasticsearch/__init__.py index ea05b5b2d..d0831f8d2 100644 --- a/elasticsearch/__init__.py +++ b/elasticsearch/__init__.py @@ -16,7 +16,6 @@ # under the License. # flake8: noqa -from __future__ import absolute_import import logging import re @@ -25,9 +24,9 @@ from ._version import __versionstr__ -_major, _minor, _patch = [ +_major, _minor, _patch = ( int(x) for x in re.search(r"^(\d+)\.(\d+)\.(\d+)", __versionstr__).groups() -] +) VERSION = __version__ = (_major, _minor, _patch) logger = logging.getLogger("elasticsearch") @@ -88,10 +87,6 @@ ] try: - # Asyncio only supported on Python 3.6+ - if sys.version_info < (3, 6): - raise ImportError - from ._async.client import AsyncElasticsearch from ._async.http_aiohttp import AIOHttpConnection, AsyncConnection from ._async.transport import AsyncTransport @@ -102,5 +97,5 @@ "AsyncTransport", "AsyncElasticsearch", ] -except (ImportError, SyntaxError): +except ImportError: # pragma: nocover pass diff --git a/elasticsearch/__init__.pyi b/elasticsearch/__init__.pyi index 373f623c4..ccf439279 100644 --- a/elasticsearch/__init__.pyi +++ b/elasticsearch/__init__.pyi @@ -45,13 +45,10 @@ from .serializer import JSONSerializer as JSONSerializer from .transport import Transport as Transport try: - if sys.version_info < (3, 6): - raise ImportError - from ._async.client import AsyncElasticsearch as AsyncElasticsearch from ._async.http_aiohttp import AIOHttpConnection as AIOHttpConnection from ._async.transport import AsyncTransport as AsyncTransport -except (ImportError, SyntaxError): +except ImportError: pass VERSION: Tuple[int, int, int] diff --git a/elasticsearch/_async/_extra_imports.py b/elasticsearch/_async/_extra_imports.py index 9877b3686..9796340bd 100644 --- a/elasticsearch/_async/_extra_imports.py +++ b/elasticsearch/_async/_extra_imports.py @@ -37,7 +37,7 @@ # See AIOHttpConnection.request() for more information why. try: import yarl -except ImportError: +except ImportError: # pragma: nocover yarl = False __all__ = ["aiohttp", "aiohttp_exceptions", "yarl"] diff --git a/elasticsearch/_async/client/__init__.py b/elasticsearch/_async/client/__init__.py index a340d8a43..d103ccca4 100644 --- a/elasticsearch/_async/client/__init__.py +++ b/elasticsearch/_async/client/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # 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 @@ -16,7 +15,6 @@ # specific language governing permissions and limitations # under the License. -from __future__ import unicode_literals import logging @@ -59,7 +57,7 @@ logger = logging.getLogger("elasticsearch") -class AsyncElasticsearch(object): +class AsyncElasticsearch: """ Elasticsearch low-level client. Provides a straightforward mapping from Python to ES REST endpoints. @@ -244,10 +242,10 @@ def __repr__(self): # truncate to 5 if there are too many if len(cons) > 5: cons = cons[:5] + ["..."] - return "<{cls}({cons})>".format(cls=self.__class__.__name__, cons=cons) + return f"<{self.__class__.__name__}({cons})>" except Exception: # probably operating on custom transport and connection_pool, ignore - return super(AsyncElasticsearch, self).__repr__() + return super().__repr__() async def __aenter__(self): if hasattr(self.transport, "_async_call"): diff --git a/elasticsearch/_async/client/__init__.pyi b/elasticsearch/_async/client/__init__.pyi index d7b17058a..71203fba4 100644 --- a/elasticsearch/_async/client/__init__.pyi +++ b/elasticsearch/_async/client/__init__.pyi @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # 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 @@ -16,8 +15,6 @@ # specific language governing permissions and limitations # under the License. -from __future__ import unicode_literals - import logging from typing import Any, Collection, MutableMapping, Optional, Tuple, Type, Union @@ -59,7 +56,7 @@ from .xpack import XPackClient logger: logging.Logger -class AsyncElasticsearch(object): +class AsyncElasticsearch: transport: AsyncTransport async_search: AsyncSearchClient diff --git a/elasticsearch/_async/helpers.py b/elasticsearch/_async/helpers.py index b6511285d..40c018c74 100644 --- a/elasticsearch/_async/helpers.py +++ b/elasticsearch/_async/helpers.py @@ -18,7 +18,6 @@ import asyncio import logging -from ..compat import map from ..exceptions import NotFoundError, TransportError from ..helpers.actions import ( _ActionChunker, @@ -57,7 +56,7 @@ async def _process_bulk_chunk( raise_on_error=True, ignore_status=(), *args, - **kwargs + **kwargs, ): """ Send a bulk request to elasticsearch and process the output. @@ -127,7 +126,7 @@ async def async_streaming_bulk( yield_ok=True, ignore_status=(), *args, - **kwargs + **kwargs, ): """ @@ -287,7 +286,7 @@ async def async_scan( request_timeout=None, clear_scroll=True, scroll_kwargs=None, - **kwargs + **kwargs, ): """ Simple abstraction on top of the diff --git a/elasticsearch/_async/helpers.pyi b/elasticsearch/_async/helpers.pyi index fca332f94..58aa348fc 100644 --- a/elasticsearch/_async/helpers.pyi +++ b/elasticsearch/_async/helpers.pyi @@ -50,7 +50,7 @@ def _process_bulk_chunk( raise_on_error: bool = ..., ignore_status: Optional[Union[int, Collection[int]]] = ..., *args: Any, - **kwargs: Any + **kwargs: Any, ) -> AsyncGenerator[Tuple[bool, Any], None]: ... def aiter(x: Union[Iterable[T], AsyncIterable[T]]) -> AsyncGenerator[T, None]: ... def azip( @@ -70,7 +70,7 @@ def async_streaming_bulk( yield_ok: bool = ..., ignore_status: Optional[Union[int, Collection[int]]] = ..., *args: Any, - **kwargs: Any + **kwargs: Any, ) -> AsyncGenerator[Tuple[bool, Any], None]: ... async def async_bulk( client: AsyncElasticsearch, @@ -78,7 +78,7 @@ async def async_bulk( stats_only: bool = ..., ignore_status: Optional[Union[int, Collection[int]]] = ..., *args: Any, - **kwargs: Any + **kwargs: Any, ) -> Tuple[int, Union[int, List[Any]]]: ... def async_scan( client: AsyncElasticsearch, @@ -90,7 +90,7 @@ def async_scan( request_timeout: Optional[Union[float, int]] = ..., clear_scroll: bool = ..., scroll_kwargs: Optional[Mapping[str, Any]] = ..., - **kwargs: Any + **kwargs: Any, ) -> AsyncGenerator[int, None]: ... async def async_reindex( client: AsyncElasticsearch, diff --git a/elasticsearch/_async/http_aiohttp.py b/elasticsearch/_async/http_aiohttp.py index 2b5110108..8c65d0a6f 100644 --- a/elasticsearch/_async/http_aiohttp.py +++ b/elasticsearch/_async/http_aiohttp.py @@ -275,7 +275,7 @@ async def perform_request( else: url = self.url_prefix + url if query_string: - url = "%s?%s" % (url, query_string) + url = f"{url}?{query_string}" url = self.host + url timeout = aiohttp.ClientTimeout( diff --git a/elasticsearch/_async/transport.py b/elasticsearch/_async/transport.py index bdb5a3e0d..907f813ed 100644 --- a/elasticsearch/_async/transport.py +++ b/elasticsearch/_async/transport.py @@ -64,7 +64,7 @@ def __init__( retry_on_timeout=False, send_get_body_as="GET", meta_header=True, - **kwargs + **kwargs, ): """ :arg hosts: list of dictionaries, each containing keyword arguments to @@ -110,7 +110,7 @@ def __init__( self._async_init_called = False self._sniff_on_start_event = None # type: asyncio.Event - super(AsyncTransport, self).__init__( + super().__init__( hosts=[], connection_class=connection_class, connection_pool_class=connection_pool_class, diff --git a/elasticsearch/_async/transport.pyi b/elasticsearch/_async/transport.pyi index 447de8316..86b7ce672 100644 --- a/elasticsearch/_async/transport.pyi +++ b/elasticsearch/_async/transport.pyi @@ -21,7 +21,7 @@ from ..connection import Connection from ..connection_pool import ConnectionPool from ..serializer import Deserializer, Serializer -class AsyncTransport(object): +class AsyncTransport: DEFAULT_CONNECTION_CLASS: Type[Connection] connection_pool: ConnectionPool deserializer: Deserializer @@ -64,7 +64,7 @@ class AsyncTransport(object): retry_on_timeout: bool = ..., send_get_body_as: str = ..., meta_header: bool = ..., - **kwargs: Any + **kwargs: Any, ) -> None: ... def add_connection(self, host: Any) -> None: ... def set_connections(self, hosts: Collection[Any]) -> None: ... diff --git a/elasticsearch/client/__init__.py b/elasticsearch/client/__init__.py index 0355a3367..af565c91a 100644 --- a/elasticsearch/client/__init__.py +++ b/elasticsearch/client/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # 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 @@ -16,7 +15,6 @@ # specific language governing permissions and limitations # under the License. -from __future__ import unicode_literals import logging @@ -59,7 +57,7 @@ logger = logging.getLogger("elasticsearch") -class Elasticsearch(object): +class Elasticsearch: """ Elasticsearch low-level client. Provides a straightforward mapping from Python to ES REST endpoints. @@ -244,10 +242,10 @@ def __repr__(self): # truncate to 5 if there are too many if len(cons) > 5: cons = cons[:5] + ["..."] - return "<{cls}({cons})>".format(cls=self.__class__.__name__, cons=cons) + return f"<{self.__class__.__name__}({cons})>" except Exception: # probably operating on custom transport and connection_pool, ignore - return super(Elasticsearch, self).__repr__() + return super().__repr__() def __enter__(self): if hasattr(self.transport, "_async_call"): diff --git a/elasticsearch/client/__init__.pyi b/elasticsearch/client/__init__.pyi index 48e3b1936..8a43574c4 100644 --- a/elasticsearch/client/__init__.pyi +++ b/elasticsearch/client/__init__.pyi @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # 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 @@ -16,8 +15,6 @@ # specific language governing permissions and limitations # under the License. -from __future__ import unicode_literals - import logging from typing import Any, Collection, MutableMapping, Optional, Tuple, Type, Union @@ -59,7 +56,7 @@ from .xpack import XPackClient logger: logging.Logger -class Elasticsearch(object): +class Elasticsearch: transport: Transport async_search: AsyncSearchClient diff --git a/elasticsearch/client/utils.py b/elasticsearch/client/utils.py index bbf40efd6..b15a9ce47 100644 --- a/elasticsearch/client/utils.py +++ b/elasticsearch/client/utils.py @@ -15,14 +15,13 @@ # specific language governing permissions and limitations # under the License. -from __future__ import unicode_literals import base64 import weakref from datetime import date, datetime from functools import wraps -from ..compat import PY2, quote, string_types, to_bytes, to_str, unquote, urlparse +from ..compat import quote, string_types, to_bytes, to_str, unquote, urlparse # parts of URL to be omitted SKIP_IN_PATH = (None, "", b"", [], ()) @@ -46,7 +45,7 @@ def _normalize_hosts(hosts): for host in hosts: if isinstance(host, string_types): if "://" not in host: - host = "//%s" % host + host = f"//{host}" parsed_url = urlparse(host) h = {"host": parsed_url.hostname} @@ -59,7 +58,7 @@ def _normalize_hosts(hosts): h["use_ssl"] = True if parsed_url.username or parsed_url.password: - h["http_auth"] = "%s:%s" % ( + h["http_auth"] = "{}:{}".format( unquote(parsed_url.username), unquote(parsed_url.password), ) @@ -96,13 +95,9 @@ def _escape(value): return value # encode strings to utf-8 - if isinstance(value, string_types): - if PY2 and isinstance(value, unicode): # noqa: F821 - return value.encode("utf-8") - if not PY2 and isinstance(value, str): - return value.encode("utf-8") - - return str(value) + if not isinstance(value, str): + value = str(value) + return value.encode("utf-8") def _make_path(*parts): @@ -149,11 +144,9 @@ def _wrapped(*args, **kwargs): "Only one of 'http_auth' and 'api_key' may be passed at a time" ) elif http_auth is not None: - headers["authorization"] = "Basic %s" % ( - _base64_auth_header(http_auth), - ) + headers["authorization"] = f"Basic {_base64_auth_header(http_auth)}" elif api_key is not None: - headers["authorization"] = "ApiKey %s" % (_base64_auth_header(api_key),) + headers["authorization"] = f"ApiKey {_base64_auth_header(api_key)}" for p in es_query_params + GLOBAL_PARAMS: if p in kwargs: @@ -197,7 +190,7 @@ def _base64_auth_header(auth_value): return to_str(auth_value) -class NamespacedClient(object): +class NamespacedClient: def __init__(self, client): self.client = client diff --git a/elasticsearch/client/utils.pyi b/elasticsearch/client/utils.pyi index e7ec3cad5..ebbd25ef2 100644 --- a/elasticsearch/client/utils.pyi +++ b/elasticsearch/client/utils.pyi @@ -15,8 +15,6 @@ # specific language governing permissions and limitations # under the License. -from __future__ import unicode_literals - from typing import ( Any, Callable, diff --git a/elasticsearch/compat.py b/elasticsearch/compat.py index 99425ce6c..2255790db 100644 --- a/elasticsearch/compat.py +++ b/elasticsearch/compat.py @@ -15,41 +15,22 @@ # specific language governing permissions and limitations # under the License. -import sys +from queue import Queue +from urllib.parse import quote, quote_plus, unquote, urlencode, urlparse -PY2 = sys.version_info[0] == 2 +string_types = str, bytes -if PY2: - string_types = (basestring,) # noqa: F821 - from itertools import imap as map - from urllib import quote, quote_plus, unquote, urlencode - from Queue import Queue - from urlparse import urlparse +def to_str(x, encoding="ascii"): + if not isinstance(x, str): + return x.decode(encoding) + return x - def to_str(x, encoding="ascii"): - if not isinstance(x, str): - return x.encode(encoding) - return x - to_bytes = to_str - -else: - string_types = str, bytes - from urllib.parse import quote, quote_plus, unquote, urlencode, urlparse - - map = map - from queue import Queue - - def to_str(x, encoding="ascii"): - if not isinstance(x, str): - return x.decode(encoding) - return x - - def to_bytes(x, encoding="ascii"): - if not isinstance(x, bytes): - return x.encode(encoding) - return x +def to_bytes(x, encoding="ascii"): + if not isinstance(x, bytes): + return x.encode(encoding) + return x try: @@ -58,10 +39,7 @@ def to_bytes(x, encoding="ascii"): from collections import Mapping -try: - reraise_exceptions = (RecursionError,) -except NameError: - reraise_exceptions = () +reraise_exceptions = (RecursionError,) try: import asyncio @@ -79,7 +57,6 @@ def to_bytes(x, encoding="ascii"): "urlencode", "unquote", "urlparse", - "map", "Queue", "Mapping", ] diff --git a/elasticsearch/compat.pyi b/elasticsearch/compat.pyi index 249ff0415..631ce649a 100644 --- a/elasticsearch/compat.pyi +++ b/elasticsearch/compat.pyi @@ -15,31 +15,17 @@ # specific language governing permissions and limitations # under the License. -import sys from typing import Callable, Tuple, Type, Union -PY2: bool string_types: Tuple[type, ...] to_str: Callable[[Union[str, bytes]], str] to_bytes: Callable[[Union[str, bytes]], bytes] reraise_exceptions: Tuple[Type[Exception], ...] -if sys.version_info[0] == 2: - from itertools import imap as map - from urllib import quote as quote - from urllib import quote_plus as quote_plus - from urllib import unquote as unquote - from urllib import urlencode as urlencode - - from Queue import Queue as Queue - from urlparse import urlparse as urlparse -else: - from urllib.parse import quote as quote - from urllib.parse import quote_plus as quote_plus - from urllib.parse import unquote as unquote - from urllib.parse import urlencode as urlencode - from urllib.parse import urlparse as urlparse - - map = map - from queue import Queue as Queue +from queue import Queue as Queue +from urllib.parse import quote as quote +from urllib.parse import quote_plus as quote_plus +from urllib.parse import unquote as unquote +from urllib.parse import urlencode as urlencode +from urllib.parse import urlparse as urlparse diff --git a/elasticsearch/connection/base.py b/elasticsearch/connection/base.py index 8fb19267a..00d71df89 100644 --- a/elasticsearch/connection/base.py +++ b/elasticsearch/connection/base.py @@ -18,17 +18,13 @@ import binascii import gzip import io +import json import logging import os import re import warnings from platform import python_version -try: - import simplejson as json -except ImportError: - import json - from .. import __version__, __versionstr__ from ..exceptions import ( HTTP_EXCEPTIONS, @@ -49,7 +45,7 @@ _WARNING_RE = re.compile(r"\"([^\"]*)\"") -class Connection(object): +class Connection: """ Class responsible for maintaining a connection to an Elasticsearch node. It holds persistent connection pool to it and it's main interface @@ -83,7 +79,7 @@ def __init__( api_key=None, opaque_id=None, meta_header=True, - **kwargs + **kwargs, ): if cloud_id: @@ -101,7 +97,7 @@ def __init__( except (ValueError, IndexError): raise ImproperlyConfigured("'cloud_id' is not properly formatted") - host = "%s.%s" % (es_uuid, parent_dn) + host = f"{es_uuid}.{parent_dn}" use_ssl = True if http_compress is None: http_compress = True @@ -149,11 +145,11 @@ def __init__( self.hostname = host self.port = port if ":" in host: # IPv6 - self.host = "%s://[%s]" % (scheme, host) + self.host = f"{scheme}://[{host}]" else: - self.host = "%s://%s" % (scheme, host) + self.host = f"{scheme}://{host}" if self.port is not None: - self.host += ":%s" % self.port + self.host += f":{self.port}" if url_prefix: url_prefix = "/" + url_prefix.strip("/") self.url_prefix = url_prefix @@ -164,11 +160,11 @@ def __init__( self.meta_header = meta_header def __repr__(self): - return "<%s: %s>" % (self.__class__.__name__, self.host) + return f"<{self.__class__.__name__}: {self.host}>" def __eq__(self, other): if not isinstance(other, Connection): - raise TypeError("Unsupported equality check for %s and %s" % (self, other)) + raise TypeError(f"Unsupported equality check for {self} and {other}") return self.__hash__() == other.__hash__() def __hash__(self): @@ -332,7 +328,7 @@ def _raise_error(self, status_code, raw_data): ) def _get_default_user_agent(self): - return "elasticsearch-py/%s (Python %s)" % (__versionstr__, python_version()) + return f"elasticsearch-py/{__versionstr__} (Python {python_version()})" def _get_api_key_header_val(self, api_key): """ @@ -341,6 +337,6 @@ def _get_api_key_header_val(self, api_key): :arg api_key, either a tuple or a base64 encoded string """ if isinstance(api_key, (tuple, list)): - s = "{0}:{1}".format(api_key[0], api_key[1]).encode("utf-8") + s = f"{api_key[0]}:{api_key[1]}".encode("utf-8") return "ApiKey " + binascii.b2a_base64(s).rstrip(b"\r\n").decode("utf-8") return "ApiKey " + api_key diff --git a/elasticsearch/connection/base.pyi b/elasticsearch/connection/base.pyi index f0149bc0b..5fd17cf72 100644 --- a/elasticsearch/connection/base.pyi +++ b/elasticsearch/connection/base.pyi @@ -33,7 +33,7 @@ from typing import ( logger: logging.Logger tracer: logging.Logger -class Connection(object): +class Connection: headers: Dict[str, str] use_ssl: bool http_compress: bool @@ -57,7 +57,7 @@ class Connection(object): api_key: Optional[Union[Tuple[str, str], List[str], str]] = ..., opaque_id: Optional[str] = ..., meta_header: bool = ..., - **kwargs: Any + **kwargs: Any, ) -> None: ... def __repr__(self) -> str: ... def __eq__(self, other: object) -> bool: ... diff --git a/elasticsearch/connection/http_requests.py b/elasticsearch/connection/http_requests.py index e45cbde81..b90a47686 100644 --- a/elasticsearch/connection/http_requests.py +++ b/elasticsearch/connection/http_requests.py @@ -80,7 +80,7 @@ def __init__( cloud_id=None, api_key=None, opaque_id=None, - **kwargs + **kwargs, ): if not REQUESTS_AVAILABLE: raise ImproperlyConfigured( @@ -92,7 +92,7 @@ def __init__( for key in list(self.session.headers): self.session.headers.pop(key) - super(RequestsHttpConnection, self).__init__( + super().__init__( host=host, port=port, use_ssl=use_ssl, @@ -101,7 +101,7 @@ def __init__( cloud_id=cloud_id, api_key=api_key, opaque_id=opaque_id, - **kwargs + **kwargs, ) if not self.http_compress: @@ -115,10 +115,7 @@ def __init__( http_auth = tuple(http_auth.split(":", 1)) self.session.auth = http_auth - self.base_url = "%s%s" % ( - self.host, - self.url_prefix, - ) + self.base_url = f"{self.host}{self.url_prefix}" self.session.verify = verify_certs if not client_key: self.session.cert = client_cert @@ -147,7 +144,7 @@ def perform_request( url = self.base_url + url headers = headers or {} if params: - url = "%s?%s" % (url, urlencode(params)) + url = f"{url}?{urlencode(params)}" orig_body = body if self.http_compress and body: diff --git a/elasticsearch/connection/http_requests.pyi b/elasticsearch/connection/http_requests.pyi index fe3351a57..5f5581692 100644 --- a/elasticsearch/connection/http_requests.pyi +++ b/elasticsearch/connection/http_requests.pyi @@ -40,5 +40,5 @@ class RequestsHttpConnection(Connection): api_key: Optional[Any] = ..., opaque_id: Optional[str] = ..., meta_header: bool = ..., - **kwargs: Any + **kwargs: Any, ) -> None: ... diff --git a/elasticsearch/connection/http_urllib3.py b/elasticsearch/connection/http_urllib3.py index cac8659bf..08e1f079e 100644 --- a/elasticsearch/connection/http_urllib3.py +++ b/elasticsearch/connection/http_urllib3.py @@ -122,12 +122,12 @@ def __init__( cloud_id=None, api_key=None, opaque_id=None, - **kwargs + **kwargs, ): # Initialize headers before calling super().__init__(). self.headers = urllib3.make_headers(keep_alive=True) - super(Urllib3HttpConnection, self).__init__( + super().__init__( host=host, port=port, use_ssl=use_ssl, @@ -136,7 +136,7 @@ def __init__( cloud_id=cloud_id, api_key=api_key, opaque_id=opaque_id, - **kwargs + **kwargs, ) if http_auth is not None: if isinstance(http_auth, (tuple, list)): @@ -222,7 +222,7 @@ def perform_request( ): url = self.url_prefix + url if params: - url = "%s?%s" % (url, urlencode(params)) + url = f"{url}?{urlencode(params)}" full_url = self.host + url diff --git a/elasticsearch/connection/http_urllib3.pyi b/elasticsearch/connection/http_urllib3.pyi index 2e1094cce..e9dd191ac 100644 --- a/elasticsearch/connection/http_urllib3.pyi +++ b/elasticsearch/connection/http_urllib3.pyi @@ -54,5 +54,5 @@ class Urllib3HttpConnection(Connection): api_key: Optional[Any] = ..., opaque_id: Optional[str] = ..., meta_header: bool = ..., - **kwargs: Any + **kwargs: Any, ) -> None: ... diff --git a/elasticsearch/connection/pooling.py b/elasticsearch/connection/pooling.py index ea178c510..088ca246c 100644 --- a/elasticsearch/connection/pooling.py +++ b/elasticsearch/connection/pooling.py @@ -15,12 +15,9 @@ # specific language governing permissions and limitations # under the License. -from .base import Connection +import queue -try: - import queue -except ImportError: - import Queue as queue # type: ignore +from .base import Connection class PoolingConnection(Connection): @@ -33,7 +30,7 @@ class PoolingConnection(Connection): def __init__(self, *args, **kwargs): self._free_connections = queue.Queue() - super(PoolingConnection, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def _make_connection(self): raise NotImplementedError diff --git a/elasticsearch/connection_pool.py b/elasticsearch/connection_pool.py index 5a60efa53..45bfa53e4 100644 --- a/elasticsearch/connection_pool.py +++ b/elasticsearch/connection_pool.py @@ -19,18 +19,14 @@ import random import threading import time +from queue import Empty, PriorityQueue from .exceptions import ImproperlyConfigured -try: - from Queue import Empty, PriorityQueue -except ImportError: - from queue import Empty, PriorityQueue - logger = logging.getLogger("elasticsearch") -class ConnectionSelector(object): +class ConnectionSelector: """ Simple class used to select a connection from a list of currently live connection instances. In init time it is passed a dictionary containing all @@ -79,7 +75,7 @@ class RoundRobinSelector(ConnectionSelector): """ def __init__(self, opts): - super(RoundRobinSelector, self).__init__(opts) + super().__init__(opts) self.data = threading.local() def select(self, connections): @@ -88,7 +84,7 @@ def select(self, connections): return connections[self.data.rr] -class ConnectionPool(object): +class ConnectionPool: """ Container holding the :class:`~elasticsearch.Connection` instances, managing the selection process (via a @@ -118,7 +114,7 @@ def __init__( timeout_cutoff=5, selector_class=RoundRobinSelector, randomize_hosts=True, - **kwargs + **kwargs, ): """ :arg connections: list of tuples containing the @@ -273,7 +269,7 @@ def close(self): conn.close() def __repr__(self): - return "<%s: %r>" % (type(self).__name__, self.connections) + return f"<{type(self).__name__}: {self.connections!r}>" class DummyConnectionPool(ConnectionPool): diff --git a/elasticsearch/connection_pool.pyi b/elasticsearch/connection_pool.pyi index a05810f8d..ab0ee752c 100644 --- a/elasticsearch/connection_pool.pyi +++ b/elasticsearch/connection_pool.pyi @@ -16,18 +16,14 @@ # under the License. import logging -from typing import Any, Dict, List, Optional, Sequence, Tuple, Type, Union +from queue import PriorityQueue +from typing import Any, Dict, Optional, Sequence, Tuple, Type from .connection import Connection -try: - from Queue import PriorityQueue # type: ignore -except ImportError: - from queue import PriorityQueue - logger: logging.Logger -class ConnectionSelector(object): +class ConnectionSelector: connection_opts: Sequence[Tuple[Connection, Any]] def __init__(self, opts: Sequence[Tuple[Connection, Any]]) -> None: ... def select(self, connections: Sequence[Connection]) -> Connection: ... @@ -35,11 +31,11 @@ class ConnectionSelector(object): class RandomSelector(ConnectionSelector): ... class RoundRobinSelector(ConnectionSelector): ... -class ConnectionPool(object): +class ConnectionPool: connections_opts: Sequence[Tuple[Connection, Any]] connections: Sequence[Connection] orig_connections: Tuple[Connection, ...] - dead: PriorityQueue + dead: PriorityQueue[Connection] dead_count: Dict[Connection, int] dead_timeout: float timeout_cutoff: int @@ -51,7 +47,7 @@ class ConnectionPool(object): timeout_cutoff: int = ..., selector_class: Type[ConnectionSelector] = ..., randomize_hosts: bool = ..., - **kwargs: Any + **kwargs: Any, ) -> None: ... def mark_dead(self, connection: Connection, now: Optional[float] = ...) -> None: ... def mark_live(self, connection: Connection) -> None: ... diff --git a/elasticsearch/exceptions.py b/elasticsearch/exceptions.py index 9ff5c8b2e..3d3aa5935 100644 --- a/elasticsearch/exceptions.py +++ b/elasticsearch/exceptions.py @@ -107,7 +107,7 @@ def __str__(self): except LookupError: pass msg = ", ".join(filter(None, [str(self.status_code), repr(self.error), cause])) - return "%s(%s)" % (self.__class__.__name__, msg) + return f"{self.__class__.__name__}({msg})" class ConnectionError(TransportError): @@ -118,7 +118,7 @@ class ConnectionError(TransportError): """ def __str__(self): - return "ConnectionError(%s) caused by: %s(%s)" % ( + return "ConnectionError({}) caused by: {}({})".format( self.error, self.info.__class__.__name__, self.info, @@ -133,7 +133,7 @@ class ConnectionTimeout(ConnectionError): """A network timeout. Doesn't cause a node retry by default.""" def __str__(self): - return "ConnectionTimeout caused by - %s(%s)" % ( + return "ConnectionTimeout caused by - {}({})".format( self.info.__class__.__name__, self.info, ) diff --git a/elasticsearch/helpers/__init__.py b/elasticsearch/helpers/__init__.py index a8478be7f..ae2febfef 100644 --- a/elasticsearch/helpers/__init__.py +++ b/elasticsearch/helpers/__init__.py @@ -15,8 +15,6 @@ # specific language governing permissions and limitations # under the License. -import sys - from .actions import ( _chunk_actions, _process_bulk_chunk, @@ -44,10 +42,6 @@ try: - # Asyncio only supported on Python 3.6+ - if sys.version_info < (3, 6): - raise ImportError - from .._async.helpers import ( async_bulk, async_reindex, @@ -56,5 +50,5 @@ ) __all__ += ["async_scan", "async_bulk", "async_reindex", "async_streaming_bulk"] -except (ImportError, SyntaxError): +except ImportError: # pragma: nocover pass diff --git a/elasticsearch/helpers/__init__.pyi b/elasticsearch/helpers/__init__.pyi index 648056ab0..3a0083230 100644 --- a/elasticsearch/helpers/__init__.pyi +++ b/elasticsearch/helpers/__init__.pyi @@ -29,13 +29,9 @@ from .errors import BulkIndexError as BulkIndexError from .errors import ScanError as ScanError try: - # Asyncio only supported on Python 3.6+ - if sys.version_info < (3, 6): - raise ImportError - from .._async.helpers import async_bulk as async_bulk from .._async.helpers import async_reindex as async_reindex from .._async.helpers import async_scan as async_scan from .._async.helpers import async_streaming_bulk as async_streaming_bulk -except (ImportError, SyntaxError): +except ImportError: pass diff --git a/elasticsearch/helpers/actions.py b/elasticsearch/helpers/actions.py index d9242d735..b944a6816 100644 --- a/elasticsearch/helpers/actions.py +++ b/elasticsearch/helpers/actions.py @@ -19,7 +19,7 @@ import time from operator import methodcaller -from ..compat import Mapping, Queue, map, string_types +from ..compat import Mapping, Queue, string_types from ..exceptions import NotFoundError, TransportError from .errors import BulkIndexError, ScanError @@ -225,7 +225,7 @@ def _process_bulk_chunk( raise_on_error=True, ignore_status=(), *args, - **kwargs + **kwargs, ): """ Send a bulk request to elasticsearch and process the output. @@ -253,8 +253,7 @@ def _process_bulk_chunk( ignore_status=ignore_status, raise_on_error=raise_on_error, ) - for item in gen: - yield item + yield from gen def _add_helper_meta_to_kwargs(kwargs, helper_meta): @@ -278,7 +277,7 @@ def streaming_bulk( yield_ok=True, ignore_status=(), *args, - **kwargs + **kwargs, ): """ @@ -336,7 +335,7 @@ def streaming_bulk( raise_on_error, ignore_status, *args, - **kwargs + **kwargs, ), ): @@ -431,7 +430,7 @@ def parallel_bulk( expand_action_callback=expand_action, ignore_status=(), *args, - **kwargs + **kwargs, ): """ Parallel version of the bulk helper run in multiple threads at once. @@ -460,7 +459,7 @@ def parallel_bulk( class BlockingPool(ThreadPool): def _setup_queues(self): - super(BlockingPool, self)._setup_queues() # type: ignore + super()._setup_queues() # type: ignore # The queue must be at least the size of the number of threads to # prevent hanging when inserting sentinel values during teardown. self._inqueue = Queue(max(queue_size, thread_count)) @@ -477,15 +476,14 @@ def _setup_queues(self): bulk_chunk[0], ignore_status=ignore_status, *args, - **kwargs + **kwargs, ) ), _chunk_actions( actions, chunk_size, max_chunk_bytes, client.transport.serializer ), ): - for item in result: - yield item + yield from result finally: pool.close() @@ -502,7 +500,7 @@ def scan( request_timeout=None, clear_scroll=True, scroll_kwargs=None, - **kwargs + **kwargs, ): """ Simple abstraction on top of the @@ -572,8 +570,7 @@ def scan( try: while scroll_id and resp["hits"]["hits"]: - for hit in resp["hits"]["hits"]: - yield hit + yield from resp["hits"]["hits"] # Default to 0 if the value isn't included in the response shards_successful = resp["_shards"].get("successful", 0) @@ -610,7 +607,7 @@ def scan( body={"scroll_id": [scroll_id]}, ignore=(404,), params={"__elastic_client_meta": (("h", "s"),)}, - **transport_kwargs + **transport_kwargs, ) @@ -699,5 +696,5 @@ def _change_doc_index(hits, index, op_type): target_client, _change_doc_index(docs, target_index, op_type), chunk_size=chunk_size, - **kwargs + **kwargs, ) diff --git a/elasticsearch/helpers/actions.pyi b/elasticsearch/helpers/actions.pyi index f5c14985d..43c8f729c 100644 --- a/elasticsearch/helpers/actions.pyi +++ b/elasticsearch/helpers/actions.pyi @@ -47,7 +47,7 @@ def _process_bulk_chunk( raise_on_exception: bool = ..., raise_on_error: bool = ..., *args: Any, - **kwargs: Any + **kwargs: Any, ) -> Generator[Tuple[bool, Any], None, None]: ... def streaming_bulk( client: Elasticsearch, @@ -63,7 +63,7 @@ def streaming_bulk( yield_ok: bool = ..., ignore_status: Optional[Union[int, Collection[int]]] = ..., *args: Any, - **kwargs: Any + **kwargs: Any, ) -> Generator[Tuple[bool, Any], None, None]: ... def bulk( client: Elasticsearch, @@ -71,7 +71,7 @@ def bulk( stats_only: bool = ..., ignore_status: Optional[Union[int, Collection[int]]] = ..., *args: Any, - **kwargs: Any + **kwargs: Any, ) -> Tuple[int, Union[int, List[Any]]]: ... def parallel_bulk( client: Elasticsearch, @@ -83,7 +83,7 @@ def parallel_bulk( expand_action_callback: Callable[[Any], Tuple[Dict[str, Any], Optional[Any]]] = ..., ignore_status: Optional[Union[int, Collection[int]]] = ..., *args: Any, - **kwargs: Any + **kwargs: Any, ) -> Generator[Tuple[bool, Any], None, None]: ... def scan( client: Elasticsearch, @@ -95,7 +95,7 @@ def scan( request_timeout: Optional[Union[float, int]] = ..., clear_scroll: bool = ..., scroll_kwargs: Optional[Mapping[str, Any]] = ..., - **kwargs: Any + **kwargs: Any, ) -> Generator[Any, None, None]: ... def reindex( client: Elasticsearch, diff --git a/elasticsearch/helpers/errors.py b/elasticsearch/helpers/errors.py index 8137abc6b..381309ca2 100644 --- a/elasticsearch/helpers/errors.py +++ b/elasticsearch/helpers/errors.py @@ -27,5 +27,5 @@ def errors(self): class ScanError(ElasticsearchException): def __init__(self, scroll_id, *args, **kwargs): - super(ScanError, self).__init__(*args, **kwargs) # type: ignore + super().__init__(*args, **kwargs) # type: ignore self.scroll_id = scroll_id diff --git a/elasticsearch/serializer.py b/elasticsearch/serializer.py index afb0cba2b..219e69f51 100644 --- a/elasticsearch/serializer.py +++ b/elasticsearch/serializer.py @@ -15,11 +15,7 @@ # specific language governing permissions and limitations # under the License. -try: - import simplejson as json -except ImportError: - import json - +import json import uuid from datetime import date, datetime from decimal import Decimal @@ -63,7 +59,7 @@ pd = None -class Serializer(object): +class Serializer: mimetype = "" def loads(self, s): @@ -83,7 +79,7 @@ def dumps(self, data): if isinstance(data, string_types): return data - raise SerializationError("Cannot serialize %r into text." % data) + raise SerializationError(f"Cannot serialize {data!r} into text.") class JSONSerializer(Serializer): @@ -113,7 +109,7 @@ def default(self, data): elif data is getattr(pd, "NA", None): return None - raise TypeError("Unable to serialize %r (type: %s)" % (data, type(data))) + raise TypeError(f"Unable to serialize {data!r} (type: {type(data)})") def loads(self, s): try: @@ -140,13 +136,13 @@ def dumps(self, data): } -class Deserializer(object): +class Deserializer: def __init__(self, serializers, default_mimetype="application/json"): try: self.default = serializers[default_mimetype] except KeyError: raise ImproperlyConfigured( - "Cannot find default serializer (%s)" % default_mimetype + f"Cannot find default serializer ({default_mimetype})" ) self.serializers = serializers @@ -164,7 +160,7 @@ def loads(self, s, mimetype=None): deserializer = self.serializers[mimetype] except KeyError: raise SerializationError( - "Unknown mimetype, unable to deserialize: %s" % mimetype + f"Unknown mimetype, unable to deserialize: {mimetype}" ) return deserializer.loads(s) diff --git a/elasticsearch/serializer.pyi b/elasticsearch/serializer.pyi index d6a7157c3..038728516 100644 --- a/elasticsearch/serializer.pyi +++ b/elasticsearch/serializer.pyi @@ -17,7 +17,7 @@ from typing import Any, Dict, Optional -class Serializer(object): +class Serializer: mimetype: str def loads(self, s: str) -> Any: ... def dumps(self, data: Any) -> str: ... @@ -35,7 +35,7 @@ class JSONSerializer(Serializer): DEFAULT_SERIALIZERS: Dict[str, Serializer] -class Deserializer(object): +class Deserializer: def __init__( self, serializers: Dict[str, Serializer], diff --git a/elasticsearch/transport.py b/elasticsearch/transport.py index e8fc6b5c0..875dd0852 100644 --- a/elasticsearch/transport.py +++ b/elasticsearch/transport.py @@ -53,7 +53,7 @@ def get_host_info(node_info, host): return host -class Transport(object): +class Transport: """ Encapsulation of transport-related to logic. Handles instantiation of the individual connections as well as creating a connection pool to hold them. @@ -81,7 +81,7 @@ def __init__( retry_on_timeout=False, send_get_body_as="GET", meta_header=True, - **kwargs + **kwargs, ): """ :arg hosts: list of dictionaries, each containing keyword arguments to @@ -492,7 +492,7 @@ def _resolve_request_args(self, method, headers, params, body): headers = headers or {} client_meta = self._client_meta + client_meta headers["x-elastic-client-meta"] = ",".join( - "%s=%s" % (k, v) for k, v in client_meta + f"{k}={v}" for k, v in client_meta ) return method, headers, params, body, ignore, timeout diff --git a/elasticsearch/transport.pyi b/elasticsearch/transport.pyi index 5265b51e3..0bd55dc74 100644 --- a/elasticsearch/transport.pyi +++ b/elasticsearch/transport.pyi @@ -25,7 +25,7 @@ def get_host_info( node_info: Dict[str, Any], host: Optional[Dict[str, Any]] ) -> Optional[Dict[str, Any]]: ... -class Transport(object): +class Transport: DEFAULT_CONNECTION_CLASS: Type[Connection] connection_pool: ConnectionPool deserializer: Deserializer @@ -68,7 +68,7 @@ class Transport(object): retry_on_timeout: bool = ..., send_get_body_as: str = ..., meta_header: bool = ..., - **kwargs: Any + **kwargs: Any, ) -> None: ... def add_connection(self, host: Any) -> None: ... def set_connections(self, hosts: Collection[Any]) -> None: ... diff --git a/noxfile.py b/noxfile.py index cc2e397f1..f89e13432 100644 --- a/noxfile.py +++ b/noxfile.py @@ -29,7 +29,7 @@ ) -@nox.session(python=["2.7", "3.4", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10"]) +@nox.session(python=["3.6", "3.7", "3.8", "3.9", "3.10"]) def test(session): session.install(".") session.install("-r", "dev-requirements.txt") @@ -39,7 +39,7 @@ def test(session): pytest_argv = [ "pytest", "--cov=elasticsearch", - "--junitxml=%s" % junit_xml, + f"--junitxml={junit_xml}", "--log-level=DEBUG", "--cache-clear", "-vv", @@ -53,10 +53,11 @@ def test(session): @nox.session() def format(session): - session.install("black", "isort") + session.install("black", "isort", "flynt") session.run("isort", "--profile=black", *SOURCE_FILES) - session.run("black", "--target-version=py27", *SOURCE_FILES) + session.run("flynt", *SOURCE_FILES) + session.run("black", "--target-version=py36", *SOURCE_FILES) session.run("python", "utils/license-headers.py", "fix", *SOURCE_FILES) lint(session) @@ -67,7 +68,7 @@ def lint(session): session.install("flake8", "black", "mypy", "isort", "types-requests") session.run("isort", "--check", "--profile=black", *SOURCE_FILES) - session.run("black", "--target-version=py27", "--check", *SOURCE_FILES) + session.run("black", "--target-version=py36", "--check", *SOURCE_FILES) session.run("flake8", *SOURCE_FILES) session.run("python", "utils/license-headers.py", "check", *SOURCE_FILES) diff --git a/setup.py b/setup.py index 66363ef30..7c3c5ad25 100644 --- a/setup.py +++ b/setup.py @@ -52,7 +52,7 @@ ] install_requires = [ - "urllib3>=1.21.1, <2", + "urllib3>=1.26, <2", "certifi", ] async_requires = ["aiohttp>=3,<4"] @@ -84,19 +84,16 @@ "Intended Audience :: Developers", "Operating System :: OS Independent", "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.4", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", ], - python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4", + python_requires=">=3.6, <4", install_requires=install_requires, extras_require={ "requests": ["requests>=2.4.0, <3.0.0"], diff --git a/test_elasticsearch/test_async/test_connection.py b/test_elasticsearch/test_async/test_connection.py index d0646e1c7..1691d045b 100644 --- a/test_elasticsearch/test_async/test_connection.py +++ b/test_elasticsearch/test_async/test_connection.py @@ -318,7 +318,7 @@ async def test_surrogatepass_into_bytes(self): buf = b"\xe4\xbd\xa0\xe5\xa5\xbd\xed\xa9\xaa" con = await self._get_mock_connection(response_body=buf) status, headers, data = await con.perform_request("GET", "/") - assert u"你好\uda6a" == data + assert "你好\uda6a" == data @pytest.mark.parametrize("exception_cls", reraise_exceptions) async def test_recursion_error_reraised(self, exception_cls): diff --git a/test_elasticsearch/test_async/test_server/test_rest_api_spec.py b/test_elasticsearch/test_async/test_server/test_rest_api_spec.py index de8adde9c..259e922c2 100644 --- a/test_elasticsearch/test_async/test_server/test_rest_api_spec.py +++ b/test_elasticsearch/test_async/test_server/test_rest_api_spec.py @@ -106,7 +106,7 @@ async def run_code(self, test): if hasattr(self, "run_" + action_type): await await_if_coro(getattr(self, "run_" + action_type)(action)) else: - raise RuntimeError("Invalid action type %r" % (action_type,)) + raise RuntimeError(f"Invalid action type {action_type!r}") async def run_do(self, action): api = self.client @@ -155,7 +155,7 @@ async def run_do(self, action): else: if catch: raise AssertionError( - "Failed to catch %r in %r." % (catch, self.last_response) + f"Failed to catch {catch!r} in {self.last_response!r}." ) # Filter out warnings raised by other components. @@ -182,7 +182,7 @@ async def run_skip(self, skip): for feature in features: if feature in IMPLEMENTED_FEATURES: continue - pytest.skip("feature '%s' is not supported" % feature) + pytest.skip(f"feature '{feature}' is not supported") if "version" in skip: version, reason = skip["version"], skip["reason"] diff --git a/test_elasticsearch/test_client/test_utils.py b/test_elasticsearch/test_client/test_utils.py index f02969776..5b3ada1d2 100644 --- a/test_elasticsearch/test_client/test_utils.py +++ b/test_elasticsearch/test_client/test_utils.py @@ -21,7 +21,6 @@ import pytest from elasticsearch.client.utils import _bulk_body, _escape, _make_path, query_params -from elasticsearch.compat import PY2 class TestQueryParams: @@ -118,13 +117,6 @@ def test_handles_unicode(self): "some-index", "type", id ) - @pytest.mark.skipif(not PY2, reason="Only relevant for Python 2") - def test_handles_utf_encoded_string(self): - id = "中文".encode("utf-8") - assert "/some-index/type/%E4%B8%AD%E6%96%87" == _make_path( - "some-index", "type", id - ) - class TestEscape: def test_handles_ascii(self): diff --git a/test_elasticsearch/test_connection.py b/test_elasticsearch/test_connection.py index c4cc90733..9ab2885e2 100644 --- a/test_elasticsearch/test_connection.py +++ b/test_elasticsearch/test_connection.py @@ -32,7 +32,6 @@ from urllib3._collections import HTTPHeaderDict from elasticsearch import __versionstr__ -from elasticsearch.compat import reraise_exceptions from elasticsearch.connection import ( Connection, RequestsHttpConnection, @@ -418,11 +417,8 @@ def test_surrogatepass_into_bytes(self): buf = b"\xe4\xbd\xa0\xe5\xa5\xbd\xed\xa9\xaa" conn = self.get_mock_urllib3_connection(response_body=buf) status, headers, data = conn.perform_request("GET", "/") - assert u"你好\uda6a" == data + assert "你好\uda6a" == data - @pytest.mark.skipif( - not reraise_exceptions, reason="RecursionError isn't defined in Python <3.5" - ) def test_recursion_error_reraised(self): conn = Urllib3HttpConnection() @@ -802,11 +798,8 @@ def test_surrogatepass_into_bytes(self): buf = b"\xe4\xbd\xa0\xe5\xa5\xbd\xed\xa9\xaa" conn = self.get_mock_requests_connection(response_body=buf) status, headers, data = conn.perform_request("GET", "/") - assert u"你好\uda6a" == data + assert "你好\uda6a" == data - @pytest.mark.skipif( - not reraise_exceptions, reason="RecursionError isn't defined in Python <3.5" - ) def test_recursion_error_reraised(self): conn = RequestsHttpConnection() diff --git a/test_elasticsearch/test_helpers.py b/test_elasticsearch/test_helpers.py index 7c824a100..95e6e00d9 100644 --- a/test_elasticsearch/test_helpers.py +++ b/test_elasticsearch/test_helpers.py @@ -75,7 +75,7 @@ def test_chunk_sent_from_different_threads(self, _process_bulk_chunk): class TestChunkActions: def setup_method(self, _): - self.actions = [({"index": {}}, {"some": u"datá", "i": i}) for i in range(100)] + self.actions = [({"index": {}}, {"some": "datá", "i": i}) for i in range(100)] def test_expand_action(self): assert helpers.expand_action({}) == ({"index": {}}, {}) @@ -173,7 +173,7 @@ def test_chunks_are_chopped_by_byte_size_properly(self): ) assert 25 == len(chunks) for chunk_data, chunk_actions in chunks: - chunk = u"".join(chunk_actions) + chunk = "".join(chunk_actions) chunk = chunk if isinstance(chunk, str) else chunk.encode("utf-8") assert len(chunk) <= max_byte_size diff --git a/test_elasticsearch/test_server/test_rest_api_spec.py b/test_elasticsearch/test_server/test_rest_api_spec.py index 8869f60f7..643ce6ab0 100644 --- a/test_elasticsearch/test_server/test_rest_api_spec.py +++ b/test_elasticsearch/test_server/test_rest_api_spec.py @@ -205,7 +205,7 @@ def run_code(self, test): if hasattr(self, "run_" + action_type): getattr(self, "run_" + action_type)(action) else: - raise RuntimeError("Invalid action type %r" % (action_type,)) + raise RuntimeError(f"Invalid action type {action_type!r}") def run_do(self, action): api = self.client @@ -254,7 +254,7 @@ def run_do(self, action): else: if catch: raise AssertionError( - "Failed to catch %r in %r." % (catch, self.last_response) + f"Failed to catch {catch!r} in {self.last_response!r}." ) # Filter out warnings raised by other components. @@ -284,7 +284,7 @@ def run_catch(self, catch, exception): elif catch[0] == "/" and catch[-1] == "/": assert ( re.search(catch[1:-1], exception.error + " " + repr(exception.info)), - "%s not in %r" % (catch, exception.info), + f"{catch} not in {exception.info!r}", ) is not None self.last_response = exception.info @@ -298,7 +298,7 @@ def run_skip(self, skip): for feature in features: if feature in IMPLEMENTED_FEATURES: continue - pytest.skip("feature '%s' is not supported" % feature) + pytest.skip(f"feature '{feature}' is not supported") if "version" in skip: version, reason = skip["version"], skip["reason"] @@ -364,10 +364,7 @@ def run_match(self, action): and expected.endswith("/") ): expected = re.compile(expected[1:-1], re.VERBOSE | re.MULTILINE) - assert expected.search(value), "%r does not match %r" % ( - value, - expected, - ) + assert expected.search(value), f"{value!r} does not match {expected!r}" else: self._assert_match_equals(value, expected) @@ -377,7 +374,7 @@ def run_contains(self, action): expected = self._resolve(expected) # dict[str, str] if expected not in value: - raise AssertionError("%s is not contained by %s" % (expected, value)) + raise AssertionError(f"{expected} is not contained by {value}") def run_transform_and_set(self, action): for key, value in action.items(): @@ -460,7 +457,7 @@ def _assert_match_equals(self, a, b): if isinstance(b, string_types) and isinstance(a, float) and "e" in repr(a): a = repr(a).replace("e+", "E") - assert a == b, "%r does not match %r" % (a, b) + assert a == b, f"{a!r} does not match {b!r}" @pytest.fixture(scope="function") @@ -505,7 +502,7 @@ def remove_implicit_resolver(cls, tag_to_remove): # Now talk to the artifacts API with the 'STACK_VERSION' environment variable resp = http.request( "GET", - "https://artifacts-api.elastic.co/v1/versions/%s" % (version_number,), + f"https://artifacts-api.elastic.co/v1/versions/{version_number}", ) resp = json.loads(resp.data.decode("utf-8")) @@ -528,7 +525,7 @@ def remove_implicit_resolver(cls, tag_to_remove): break else: raise RuntimeError( - "Could not find the package 'rest-resources-zip-*.zip' in build %r" % build + f"Could not find the package 'rest-resources-zip-*.zip' in build {build!r}" ) # Download the zip and start reading YAML from the files in memory @@ -582,7 +579,7 @@ def remove_implicit_resolver(cls, tag_to_remove): YAML_TEST_SPECS.append(pytest.param(pytest_param, id=pytest_param_id)) except Exception as e: - warnings.warn("Could not load REST API tests: %s" % (str(e),)) + warnings.warn(f"Could not load REST API tests: {str(e)}") if not RUN_ASYNC_REST_API_TESTS: diff --git a/test_elasticsearch/utils.py b/test_elasticsearch/utils.py index 8468b3541..0371dae2c 100644 --- a/test_elasticsearch/utils.py +++ b/test_elasticsearch/utils.py @@ -225,7 +225,7 @@ def wipe_xpack_templates(client): try: client.indices.delete_template(name=template) except NotFoundError as e: - if "index_template [%s] missing" % template in str(e.info): + if f"index_template [{template}] missing" in str(e.info): client.indices.delete_index_template(name=template) # Delete component templates, need to retry because sometimes diff --git a/utils/build-dists.py b/utils/build-dists.py index 854add1dc..23c79ea4a 100644 --- a/utils/build-dists.py +++ b/utils/build-dists.py @@ -222,7 +222,7 @@ def main(): # Rename the module to fit the suffix. shutil.move( os.path.join(base_dir, "elasticsearch"), - os.path.join(base_dir, "elasticsearch%s" % suffix), + os.path.join(base_dir, f"elasticsearch{suffix}"), ) # Ensure that the version within 'elasticsearch/_version.py' is correct. @@ -231,7 +231,7 @@ def main(): version_data = f.read() version_data = re.sub( r"__versionstr__ = \"[^\"]+\"", - '__versionstr__ = "%s"' % version, + f'__versionstr__ = "{version}"', version_data, ) with open(version_path, "w") as f: @@ -248,7 +248,7 @@ def main(): f.write( setup_py.replace( 'package_name = "elasticsearch"', - 'package_name = "elasticsearch%s"' % suffix, + f'package_name = "elasticsearch{suffix}"', ) ) @@ -258,7 +258,7 @@ def main(): # Clean up everything. run("git", "checkout", "--", "setup.py", "elasticsearch/") if suffix: - run("rm", "-rf", "elasticsearch%s/" % suffix) + run("rm", "-rf", f"elasticsearch{suffix}/") # Test everything that got created dists = os.listdir(os.path.join(base_dir, "dist"))