diff --git a/.ci/test-matrix.yml b/.ci/test-matrix.yml index c76a1bff..d63b5793 100755 --- a/.ci/test-matrix.yml +++ b/.ci/test-matrix.yml @@ -2,9 +2,6 @@ TEST_SUITE: - oss PYTHON_VERSION: - - "2.7" - - "3.4" - - "3.5" - "3.6" - "3.7" - "3.8" diff --git a/CHANGELOG.md b/CHANGELOG.md index 843abeac..eaae9670 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Deprecated - Deprecated point-in-time APIs (list_all_point_in_time, create_point_in_time, delete_point_in_time) and Security Client APIs (health_check and update_audit_config) ([#502](https://github.com/opensearch-project/opensearch-py/pull/502)) ### Removed +- Removed leftover support for Python 2.7 ([#548](https://github.com/opensearch-project/opensearch-py/pull/548)) ### Fixed ### Security ### Dependencies diff --git a/dev-requirements.txt b/dev-requirements.txt index 76e1acdb..04cfb3e8 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -12,14 +12,13 @@ pytz numpy; python_version<"3.10" pandas; python_version<"3.10" -pyyaml>=5.4; python_version>="3.6" -pyyaml==5.3.1; python_version<"3.6" +pyyaml>=5.4 isort -black; python_version>="3.6" +black twine # Requirements for testing [async] extra -aiohttp; python_version>="3.6" -pytest-asyncio<=0.21.1; python_version>="3.6" -unasync; python_version>="3.6" +aiohttp +pytest-asyncio<=0.21.1 +unasync diff --git a/noxfile.py b/noxfile.py index 80b4e400..6b734b48 100644 --- a/noxfile.py +++ b/noxfile.py @@ -36,7 +36,7 @@ ) -@nox.session(python=["2.7", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11"]) +@nox.session(python=["3.6", "3.7", "3.8", "3.9", "3.10", "3.11"]) def test(session): session.install(".") session.install("-r", "dev-requirements.txt") diff --git a/opensearchpy/__init__.py b/opensearchpy/__init__.py index 6f26bc53..a0ea9f60 100644 --- a/opensearchpy/__init__.py +++ b/opensearchpy/__init__.py @@ -248,22 +248,15 @@ "tokenizer", ] -try: - # Asyncio only supported on Python 3.6+ - if sys.version_info < (3, 6): - raise ImportError +from ._async.client import AsyncOpenSearch +from ._async.http_aiohttp import AIOHttpConnection, AsyncConnection +from ._async.transport import AsyncTransport +from .connection import AsyncHttpConnection - from ._async.client import AsyncOpenSearch - from ._async.http_aiohttp import AIOHttpConnection, AsyncConnection - from ._async.transport import AsyncTransport - from .connection import AsyncHttpConnection - - __all__ += [ - "AIOHttpConnection", - "AsyncConnection", - "AsyncTransport", - "AsyncOpenSearch", - "AsyncHttpConnection", - ] -except (ImportError, SyntaxError): - pass +__all__ += [ + "AIOHttpConnection", + "AsyncConnection", + "AsyncTransport", + "AsyncOpenSearch", + "AsyncHttpConnection", +] diff --git a/opensearchpy/__init__.pyi b/opensearchpy/__init__.pyi index 01fccaec..0fa4afcf 100644 --- a/opensearchpy/__init__.pyi +++ b/opensearchpy/__init__.pyi @@ -27,6 +27,10 @@ import sys from typing import Tuple +from ._async.client import AsyncOpenSearch as AsyncOpenSearch +from ._async.http_aiohttp import AIOHttpConnection as AIOHttpConnection +from ._async.http_aiohttp import AsyncConnection as AsyncConnection +from ._async.transport import AsyncTransport as AsyncTransport from .client import OpenSearch as OpenSearch from .connection import AsyncHttpConnection as AsyncHttpConnection from .connection import Connection as Connection @@ -54,6 +58,8 @@ from .exceptions import SSLError as SSLError from .exceptions import TransportError as TransportError from .exceptions import UnknownDslObject as UnknownDslObject from .exceptions import ValidationException as ValidationException +from .helpers import AWSV4SignerAsyncAuth as AWSV4SignerAsyncAuth +from .helpers import AWSV4SignerAuth as AWSV4SignerAuth from .helpers.aggs import A as A from .helpers.analysis import Analyzer, CharFilter, Normalizer, TokenFilter, Tokenizer from .helpers.document import Document as Document @@ -120,19 +126,6 @@ from .helpers.wrappers import Range as Range 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 AsyncOpenSearch as AsyncOpenSearch - from ._async.http_aiohttp import AIOHttpConnection as AIOHttpConnection - from ._async.http_aiohttp import AsyncConnection as AsyncConnection - from ._async.transport import AsyncTransport as AsyncTransport - from .helpers import AWSV4SignerAsyncAuth as AWSV4SignerAsyncAuth - from .helpers import AWSV4SignerAuth as AWSV4SignerAuth -except (ImportError, SyntaxError): - pass - VERSION: Tuple[int, int, int] __version__: Tuple[int, int, int] __versionstr__: str diff --git a/opensearchpy/connection/__init__.py b/opensearchpy/connection/__init__.py index 1b9ad2cd..6e331a54 100644 --- a/opensearchpy/connection/__init__.py +++ b/opensearchpy/connection/__init__.py @@ -25,9 +25,8 @@ # under the License. -import sys - from .base import Connection +from .http_async import AsyncHttpConnection from .http_requests import RequestsHttpConnection from .http_urllib3 import Urllib3HttpConnection, create_ssl_context @@ -36,17 +35,5 @@ "RequestsHttpConnection", "Urllib3HttpConnection", "create_ssl_context", + "AsyncHttpConnection", ] - -try: - # Asyncio only supported on Python 3.6+ - if sys.version_info < (3, 6): - raise ImportError - - from .http_async import AsyncHttpConnection - - __all__ += [ - "AsyncHttpConnection", - ] -except (ImportError, SyntaxError): - pass diff --git a/opensearchpy/helpers/__init__.py b/opensearchpy/helpers/__init__.py index 80dbf8bf..8057de7e 100644 --- a/opensearchpy/helpers/__init__.py +++ b/opensearchpy/helpers/__init__.py @@ -25,8 +25,12 @@ # under the License. -import sys - +from .._async.helpers.actions import ( + async_bulk, + async_reindex, + async_scan, + async_streaming_bulk, +) from .actions import ( _chunk_actions, _process_bulk_chunk, @@ -56,16 +60,8 @@ "AWSV4SignerAsyncAuth", "RequestsAWSV4SignerAuth", "Urllib3AWSV4SignerAuth", + "async_scan", + "async_bulk", + "async_reindex", + "async_streaming_bulk", ] - - -# Asyncio only supported on Python 3.6+ -if sys.version_info >= (3, 6): - from .._async.helpers.actions import ( - async_bulk, - async_reindex, - async_scan, - async_streaming_bulk, - ) - - __all__ += ["async_scan", "async_bulk", "async_reindex", "async_streaming_bulk"] diff --git a/opensearchpy/helpers/__init__.pyi b/opensearchpy/helpers/__init__.pyi index a4711989..01d4973c 100644 --- a/opensearchpy/helpers/__init__.pyi +++ b/opensearchpy/helpers/__init__.pyi @@ -26,6 +26,10 @@ import sys +from .._async.helpers.actions import async_bulk as async_bulk +from .._async.helpers.actions import async_reindex as async_reindex +from .._async.helpers.actions import async_scan as async_scan +from .._async.helpers.actions import async_streaming_bulk as async_streaming_bulk from .actions import _chunk_actions as _chunk_actions from .actions import _process_bulk_chunk as _process_bulk_chunk from .actions import bulk as bulk @@ -34,20 +38,8 @@ from .actions import parallel_bulk as parallel_bulk from .actions import reindex as reindex from .actions import scan as scan from .actions import streaming_bulk as streaming_bulk +from .asyncsigner import AWSV4SignerAsyncAuth as AWSV4SignerAsyncAuth 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.actions import async_bulk as async_bulk - from .._async.helpers.actions import async_reindex as async_reindex - from .._async.helpers.actions import async_scan as async_scan - from .._async.helpers.actions import async_streaming_bulk as async_streaming_bulk - from .asyncsigner import AWSV4SignerAsyncAuth as AWSV4SignerAsyncAuth - from .signer import AWSV4SignerAuth as AWSV4SignerAuth - from .signer import RequestsAWSV4SignerAuth, Urllib3AWSV4SignerAuth -except (ImportError, SyntaxError): - pass +from .signer import AWSV4SignerAuth as AWSV4SignerAuth +from .signer import RequestsAWSV4SignerAuth, Urllib3AWSV4SignerAuth diff --git a/setup.py b/setup.py index 8bde5f40..f4163840 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,6 @@ import re -import sys from os.path import abspath, dirname, join from setuptools import find_packages, setup @@ -54,8 +53,7 @@ "requests>=2.4.0, <3.0.0", "six", "python-dateutil", - # ipaddress is included in stdlib since python 3.3 - 'ipaddress; python_version<"3.3"', + "certifi>=2022.12.07", ] tests_require = [ "requests>=2.0.0, <3.0.0", @@ -65,11 +63,9 @@ "pytest>=3.0.0", "pytest-cov", "pytz", - "botocore;python_version>='3.6'", + "botocore", + "pytest-mock<4.0.0", ] -if sys.version_info >= (3, 6): - tests_require.append("pytest-mock<4.0.0") - install_requires.append("certifi>=2022.12.07") async_require = ["aiohttp>=3,<4"] @@ -103,11 +99,6 @@ "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", diff --git a/test_opensearchpy/run_tests.py b/test_opensearchpy/run_tests.py index e0461af7..ca9db82a 100755 --- a/test_opensearchpy/run_tests.py +++ b/test_opensearchpy/run_tests.py @@ -116,27 +116,19 @@ def run_all(argv=None): if test_pattern: argv.append("-k %s" % test_pattern) else: - ignores = [] - # Python 3.6+ is required for async - if sys.version_info < (3, 6): - ignores.append("test_opensearchpy/test_async/") - - ignores.extend( - [ - "test_opensearchpy/test_server/", - "test_opensearchpy/test_server_secured/", - "test_opensearchpy/test_async/test_server/", - "test_opensearchpy/test_async/test_server_secured/", - ] - ) + ignores = [ + "test_opensearchpy/test_server/", + "test_opensearchpy/test_server_secured/", + "test_opensearchpy/test_async/test_server/", + "test_opensearchpy/test_async/test_server_secured/", + ] # Jenkins/Github actions, only run server tests if environ.get("TEST_TYPE") == "server": test_dir = abspath(dirname(__file__)) if secured: argv.append(join(test_dir, "test_server_secured")) - if sys.version_info >= (3, 6): - argv.append(join(test_dir, "test_async/test_server_secured")) + argv.append(join(test_dir, "test_async/test_server_secured")) ignores.extend( [ "test_opensearchpy/test_server/", @@ -145,8 +137,7 @@ def run_all(argv=None): ) else: argv.append(join(test_dir, "test_server")) - if sys.version_info >= (3, 6): - argv.append(join(test_dir, "test_async/test_server")) + argv.append(join(test_dir, "test_async/test_server")) ignores.extend( [ "test_opensearchpy/test_server_secured/", diff --git a/test_opensearchpy/test_async/test_signer.py b/test_opensearchpy/test_async/test_signer.py index 5b92c89d..84458c9e 100644 --- a/test_opensearchpy/test_async/test_signer.py +++ b/test_opensearchpy/test_async/test_signer.py @@ -8,7 +8,6 @@ # Modifications Copyright OpenSearch Contributors. See # GitHub history for details. -import sys import uuid import pytest @@ -42,9 +41,6 @@ async def test_aws_signer_async_as_http_auth(self): assert "X-Amz-Date" in headers assert "X-Amz-Security-Token" in headers - @pytest.mark.skipif( - sys.version_info < (3, 6), reason="AWSV4SignerAuth requires python3.6+" - ) async def test_aws_signer_async_when_region_is_null(self): session = self.mock_session() @@ -58,9 +54,6 @@ async def test_aws_signer_async_when_region_is_null(self): AWSV4SignerAsyncAuth(session, "") assert str(e.value) == "Region cannot be empty" - @pytest.mark.skipif( - sys.version_info < (3, 6), reason="AWSV4SignerAuth requires python3.6+" - ) async def test_aws_signer_async_when_credentials_is_null(self): region = "us-west-1" @@ -70,9 +63,6 @@ async def test_aws_signer_async_when_credentials_is_null(self): AWSV4SignerAsyncAuth(None, region) assert str(e.value) == "Credentials cannot be empty" - @pytest.mark.skipif( - sys.version_info < (3, 6), reason="AWSV4SignerAsyncAuth requires python3.6+" - ) async def test_aws_signer_async_when_service_is_specified(self): region = "us-west-2" service = "aoss" @@ -100,9 +90,6 @@ def mock_session(self, disable_get_frozen=True): return dummy_session - @pytest.mark.skipif( - sys.version_info < (3, 6), reason="AWSV4SignerAsyncAuth requires python3.6+" - ) async def test_aws_signer_async_frozen_credentials_as_http_auth(self): region = "us-west-2" diff --git a/test_opensearchpy/test_connection/test_requests_http_connection.py b/test_opensearchpy/test_connection/test_requests_http_connection.py index db426fa5..c85d2efd 100644 --- a/test_opensearchpy/test_connection/test_requests_http_connection.py +++ b/test_opensearchpy/test_connection/test_requests_http_connection.py @@ -28,7 +28,6 @@ import json import re -import sys import uuid import warnings @@ -36,7 +35,6 @@ from mock import Mock, patch from requests.auth import AuthBase -from opensearchpy.compat import reraise_exceptions from opensearchpy.connection import Connection, RequestsHttpConnection from opensearchpy.exceptions import ( ConflictError, @@ -44,12 +42,10 @@ RequestError, TransportError, ) +from test_opensearchpy.TestHttpServer import TestHTTPServer from ..test_cases import TestCase -if sys.version_info > (3, 0): - from test_opensearchpy.TestHttpServer import TestHTTPServer - class TestRequestsHttpConnection(TestCase): def _get_mock_connection( @@ -393,9 +389,6 @@ def test_surrogatepass_into_bytes(self): status, headers, data = con.perform_request("GET", "/") self.assertEqual(u"你好\uda6a", data) # fmt: skip - @pytest.mark.skipif( - not reraise_exceptions, reason="RecursionError isn't defined in Python <3.5" - ) def test_recursion_error_reraised(self): conn = RequestsHttpConnection() @@ -420,9 +413,6 @@ def mock_session(self): return dummy_session - @pytest.mark.skipif( - sys.version_info < (3, 6), reason="RequestsAWSV4SignerAuth requires python3.6+" - ) def test_aws_signer_as_http_auth(self): region = "us-west-2" @@ -440,9 +430,6 @@ def test_aws_signer_as_http_auth(self): self.assertIn("X-Amz-Security-Token", prepared_request.headers) self.assertIn("X-Amz-Content-SHA256", prepared_request.headers) - @pytest.mark.skipif( - sys.version_info < (3, 6), reason="RequestsAWSV4SignerAuth requires python3.6+" - ) def test_aws_signer_when_service_is_specified(self): region = "us-west-1" service = "aoss" @@ -460,9 +447,6 @@ def test_aws_signer_when_service_is_specified(self): self.assertIn("X-Amz-Date", prepared_request.headers) self.assertIn("X-Amz-Security-Token", prepared_request.headers) - @pytest.mark.skipif( - sys.version_info < (3, 6), reason="RequestsAWSV4SignerAuth requires python3.6+" - ) @patch("opensearchpy.helpers.signer.AWSV4Signer.sign") def test_aws_signer_signs_with_query_string(self, mock_sign): region = "us-west-1" @@ -484,10 +468,6 @@ def test_aws_signer_signs_with_query_string(self, mock_sign): ) -@pytest.mark.skipif( - sys.version_info < (3, 0), - reason="http_server is only available from python 3.x", -) class TestRequestsConnectionRedirect: @classmethod def setup_class(cls): @@ -537,9 +517,6 @@ def mock_session(self): return dummy_session - @pytest.mark.skipif( - sys.version_info < (3, 6), reason="RequestsAWSV4SignerAuth requires python3.6+" - ) def test_requests_http_connection_aws_signer_frozen_credentials_as_http_auth(self): region = "us-west-2" diff --git a/test_opensearchpy/test_connection/test_urllib3_http_connection.py b/test_opensearchpy/test_connection/test_urllib3_http_connection.py index 854e22e4..929258fd 100644 --- a/test_opensearchpy/test_connection/test_urllib3_http_connection.py +++ b/test_opensearchpy/test_connection/test_urllib3_http_connection.py @@ -27,7 +27,6 @@ import ssl -import sys import uuid import warnings from gzip import GzipFile @@ -40,7 +39,6 @@ from urllib3._collections import HTTPHeaderDict from opensearchpy import __versionstr__ -from opensearchpy.compat import reraise_exceptions from opensearchpy.connection import Connection, Urllib3HttpConnection from ..test_cases import SkipTest, TestCase @@ -179,9 +177,6 @@ def test_http_auth_list(self): con.headers, ) - @pytest.mark.skipif( - sys.version_info < (3, 6), reason="Urllib3AWSV4SignerAuth requires python3.6+" - ) @patch( "urllib3.HTTPConnectionPool.urlopen", return_value=Mock(status=200, headers=HTTPHeaderDict({}), data=b"{}"), @@ -202,9 +197,6 @@ def test_aws_signer_as_http_auth_adds_headers(self, mock_open): self.assertIn("X-Amz-Security-Token", headers) self.assertIn("X-Amz-Content-SHA256", headers) - @pytest.mark.skipif( - sys.version_info < (3, 6), reason="Urllib3AWSV4SignerAuth requires python3.6+" - ) def test_aws_signer_as_http_auth(self): region = "us-west-2" @@ -217,9 +209,6 @@ def test_aws_signer_as_http_auth(self): self.assertIn("X-Amz-Security-Token", headers) self.assertIn("X-Amz-Content-SHA256", headers) - @pytest.mark.skipif( - sys.version_info < (3, 6), reason="Urllib3AWSV4SignerAuth requires python3.6+" - ) def test_aws_signer_when_region_is_null(self): session = self.mock_session() @@ -233,9 +222,6 @@ def test_aws_signer_when_region_is_null(self): Urllib3AWSV4SignerAuth(session, "") assert str(e.value) == "Region cannot be empty" - @pytest.mark.skipif( - sys.version_info < (3, 6), reason="Urllib3AWSV4SignerAuth requires python3.6+" - ) def test_aws_signer_when_credentials_is_null(self): region = "us-west-1" @@ -249,9 +235,6 @@ def test_aws_signer_when_credentials_is_null(self): Urllib3AWSV4SignerAuth("", region) assert str(e.value) == "Credentials cannot be empty" - @pytest.mark.skipif( - sys.version_info < (3, 6), reason="Urllib3AWSV4SignerAuth requires python3.6+" - ) def test_aws_signer_when_service_is_specified(self): region = "us-west-1" service = "aoss" @@ -358,9 +341,6 @@ def test_surrogatepass_into_bytes(self): status, headers, data = con.perform_request("GET", "/") self.assertEqual(u"你好\uda6a", data) # fmt: skip - @pytest.mark.skipif( - not reraise_exceptions, reason="RecursionError isn't defined in Python <3.5" - ) def test_recursion_error_reraised(self): conn = Urllib3HttpConnection() @@ -387,9 +367,6 @@ def mock_session(self): return dummy_session - @pytest.mark.skipif( - sys.version_info < (3, 6), reason="Urllib3AWSV4SignerAuth requires python3.6+" - ) def test_urllib3_http_connection_aws_signer_frozen_credentials_as_http_auth(self): region = "us-west-2" diff --git a/test_opensearchpy/test_helpers/test_field.py b/test_opensearchpy/test_helpers/test_field.py index 15b51c52..288eab3a 100644 --- a/test_opensearchpy/test_helpers/test_field.py +++ b/test_opensearchpy/test_helpers/test_field.py @@ -25,7 +25,6 @@ # under the License. import base64 -import sys from datetime import datetime from ipaddress import ip_address @@ -150,7 +149,6 @@ def test_scaled_float(): assert f.to_dict() == {"scaling_factor": 123, "type": "scaled_float"} -@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") def test_ipaddress(): f = field.Ip() assert f.deserialize("127.0.0.1") == ip_address("127.0.0.1") diff --git a/test_opensearchpy/test_server/test_rest_api_spec.py b/test_opensearchpy/test_server/test_rest_api_spec.py index b5d890ab..306993f2 100644 --- a/test_opensearchpy/test_server/test_rest_api_spec.py +++ b/test_opensearchpy/test_server/test_rest_api_spec.py @@ -33,7 +33,6 @@ import io import os import re -import sys import warnings import zipfile @@ -135,8 +134,7 @@ OPENSEARCH_VERSION = None RUN_ASYNC_REST_API_TESTS = ( - sys.version_info >= (3, 6) - and os.environ.get("PYTHON_CONNECTION_CLASS") == "RequestsHttpConnection" + os.environ.get("PYTHON_CONNECTION_CLASS") == "RequestsHttpConnection" ) FALSEY_VALUES = ("", None, False, 0, 0.0)