From 39fa31c4ca25cf27d3583b5fd733ca8a51473cbb Mon Sep 17 00:00:00 2001 From: Vadzim Hushchanskou Date: Fri, 29 Nov 2024 16:54:59 +0300 Subject: [PATCH] Drop Python 3.7 support --- .github/workflows/tests.yml | 2 +- CHANGELOG.md | 2 + reportportal_client/aio/tasks.py | 6 +-- reportportal_client/helpers.py | 27 +++------- requirements.txt | 4 +- setup.py | 3 +- tests/_internal/services/test_statistics.py | 7 +-- tests/aio/test_aio_client.py | 55 --------------------- tests/aio/test_async_client.py | 8 --- tests/aio/test_batched_client.py | 5 -- tests/aio/test_threaded_client.py | 5 -- tests/core/test_rp_responses.py | 3 -- tests/logs/test_rp_logger.py | 3 -- tox.ini | 2 - 14 files changed, 15 insertions(+), 117 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 121d9d45..bf1f0f73 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -34,7 +34,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ '3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13' ] + python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12', '3.13' ] steps: - name: Checkout repository uses: actions/checkout@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index d7badf82..ca829e02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## [Unreleased] +### Removed +- `Python 3.7` support, by @HardNorth ## [5.5.10] ### Added diff --git a/reportportal_client/aio/tasks.py b/reportportal_client/aio/tasks.py index 0afc77ac..f01b51b5 100644 --- a/reportportal_client/aio/tasks.py +++ b/reportportal_client/aio/tasks.py @@ -14,7 +14,6 @@ """This module contains customized asynchronous Tasks and Task Factories for the ReportPortal client.""" import asyncio -import sys from abc import abstractmethod from asyncio import Future from typing import TypeVar, Generic, Union, Generator, Awaitable, Optional @@ -54,10 +53,7 @@ def __init__( :param name: the name of the task """ self.name = name - if sys.version_info < (3, 8): - super().__init__(coro, loop=loop) - else: - super().__init__(coro, loop=loop, name=name) + super().__init__(coro, loop=loop, name=name) @abstractmethod def blocking_result(self) -> _T: diff --git a/reportportal_client/helpers.py b/reportportal_client/helpers.py index 03118900..960f64b7 100644 --- a/reportportal_client/helpers.py +++ b/reportportal_client/helpers.py @@ -16,7 +16,6 @@ import asyncio import inspect import logging -import sys import threading import time import uuid @@ -203,25 +202,13 @@ def get_package_parameters(package_name: str, parameters: List[str] = None) -> L if not parameters: return result - if sys.version_info < (3, 8): - from pkg_resources import get_distribution, DistributionNotFound - try: - package_info = get_distribution(package_name) - except DistributionNotFound: - return [None] * len(parameters) - for param in parameters: - if param.lower() == 'name': - param = 'project_name' - result.append(getattr(package_info, param, None)) - else: - # noinspection PyCompatibility - from importlib.metadata import distribution, PackageNotFoundError - try: - package_info = distribution(package_name) - except PackageNotFoundError: - return [None] * len(parameters) - for param in parameters: - result.append(package_info.metadata[param.lower()[:1].upper() + param.lower()[1:]]) + from importlib.metadata import distribution, PackageNotFoundError + try: + package_info = distribution(package_name) + except PackageNotFoundError: + return [None] * len(parameters) + for param in parameters: + result.append(package_info.metadata[param.lower()[:1].upper() + param.lower()[1:]]) return result diff --git a/requirements.txt b/requirements.txt index 0641c27c..5871f2d2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ aenum -requests>=2.31.0 -aiohttp>=3.8.6 +requests>=2.32.3 +aiohttp>=3.10.11 certifi>=2024.8.30 diff --git a/setup.py b/setup.py index a62343fc..db5c445a 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ from setuptools import setup, find_packages -__version__ = '5.5.11' +__version__ = '5.6.0' TYPE_STUBS = ['*.pyi'] @@ -40,7 +40,6 @@ def read_file(fname): license='Apache 2.0.', keywords=['testing', 'reporting', 'reportportal', 'client'], classifiers=[ - 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', diff --git a/tests/_internal/services/test_statistics.py b/tests/_internal/services/test_statistics.py index abda1f59..523e1f1e 100644 --- a/tests/_internal/services/test_statistics.py +++ b/tests/_internal/services/test_statistics.py @@ -22,7 +22,6 @@ # limitations under the License import re -import sys from unittest import mock # noinspection PyPackageRequirements @@ -107,13 +106,9 @@ def test_same_client_id(mocked_requests): assert result1 == result2 -MOCKED_AIOHTTP = None -if not sys.version_info < (3, 8): - MOCKED_AIOHTTP = mock.AsyncMock() +MOCKED_AIOHTTP = mock.AsyncMock() -@pytest.mark.skipif(sys.version_info < (3, 8), - reason="the test requires AsyncMock which was introduced in Python 3.8") @mock.patch('reportportal_client._internal.services.statistics.get_client_id', mock.Mock(return_value='555')) @mock.patch('reportportal_client._internal.services.statistics.aiohttp.ClientSession.post', MOCKED_AIOHTTP) diff --git a/tests/aio/test_aio_client.py b/tests/aio/test_aio_client.py index c51028dd..81c93cc0 100644 --- a/tests/aio/test_aio_client.py +++ b/tests/aio/test_aio_client.py @@ -71,9 +71,6 @@ async def test_retries_param(retry_num, expected_class, expected_param): assert getattr(session, '_RetryingClientSession__retry_number') == expected_param -@pytest.mark.skipif(sys.version_info < (3, 8), - reason="For some reasons this does not work on Python 3.7 on Ubuntu, " - "but works on my Mac. Unfortunately GHA use Python 3.7 on Ubuntu.") @pytest.mark.parametrize( 'timeout_param, expected_connect_param, expected_sock_read_param', [ @@ -134,8 +131,6 @@ def test_clone(): EXPECTED_DEBUG_URL = f'http://endpoint/ui/#project/userdebug/all/{LAUNCH_ID}' -@pytest.mark.skipif(sys.version_info < (3, 8), - reason="the test requires AsyncMock which was introduced in Python 3.8") @pytest.mark.parametrize( 'launch_mode, project_name, expected_url', [ @@ -160,9 +155,6 @@ async def get_call(*args, **kwargs): assert await (aio_client.get_launch_ui_url('test_launch_uuid')) == expected_url -@pytest.mark.skipif(sys.version_info < (3, 8), - reason="For some reasons this does not work on Python 3.7 on Ubuntu, " - "but works on my Mac. Unfortunately GHA use Python 3.7 on Ubuntu.") @pytest.mark.parametrize('default', [True, False]) @mock.patch('reportportal_client.aio.client.aiohttp.TCPConnector') @pytest.mark.asyncio @@ -179,9 +171,6 @@ async def test_verify_ssl_default(connector_mock: mock.Mock, default: bool): assert len(ssl_context.get_ca_certs()) > 0 -@pytest.mark.skipif(sys.version_info < (3, 8), - reason="For some reasons this does not work on Python 3.7 on Ubuntu, " - "but works on my Mac. Unfortunately GHA use Python 3.7 on Ubuntu.") @pytest.mark.parametrize('param_value', [False, None]) @mock.patch('reportportal_client.aio.client.aiohttp.TCPConnector') @pytest.mark.asyncio @@ -194,9 +183,6 @@ async def test_verify_ssl_off(connector_mock: mock.Mock, param_value): assert ssl_context is not None and isinstance(ssl_context, bool) and not ssl_context -@pytest.mark.skipif(sys.version_info < (3, 8), - reason="For some reasons this does not work on Python 3.7 on Ubuntu, " - "but works on my Mac. Unfortunately GHA use Python 3.7 on Ubuntu.") @mock.patch('reportportal_client.aio.client.aiohttp.TCPConnector') @pytest.mark.asyncio async def test_verify_ssl_str(connector_mock: mock.Mock): @@ -213,9 +199,6 @@ async def test_verify_ssl_str(connector_mock: mock.Mock): assert certificate['notAfter'] == 'Jun 4 11:04:38 2035 GMT' -@pytest.mark.skipif(sys.version_info < (3, 8), - reason="For some reasons this does not work on Python 3.7 on Ubuntu, " - "but works on my Mac. Unfortunately GHA use Python 3.7 on Ubuntu.") @mock.patch('reportportal_client.aio.client.aiohttp.TCPConnector') @pytest.mark.asyncio async def test_keepalive_timeout(connector_mock: mock.Mock): @@ -229,8 +212,6 @@ async def test_keepalive_timeout(connector_mock: mock.Mock): assert timeout is not None and timeout == keepalive_timeout -@pytest.mark.skipif(sys.version_info < (3, 8), - reason="the test requires AsyncMock which was introduced in Python 3.8") @pytest.mark.asyncio async def test_close(aio_client: Client): # noinspection PyTypeChecker @@ -260,8 +241,6 @@ def verify_attributes(expected_attributes: Optional[dict], actual_attributes: Op assert attribute.get('system') == hidden -@pytest.mark.skipif(sys.version_info < (3, 8), - reason='the test requires AsyncMock which was introduced in Python 3.8') @pytest.mark.asyncio async def test_start_launch(aio_client: Client): # noinspection PyTypeChecker @@ -292,8 +271,6 @@ async def test_start_launch(aio_client: Client): verify_attributes(attributes, actual_attributes) -@pytest.mark.skipif(sys.version_info < (3, 8), - reason='the test requires AsyncMock which was introduced in Python 3.8') @mock.patch('reportportal_client.aio.client.async_send_event') @pytest.mark.asyncio async def test_start_launch_statistics_send(async_send_event): @@ -321,8 +298,6 @@ async def test_start_launch_statistics_send(async_send_event): assert len(kwargs.items()) == 0 -@pytest.mark.skipif(sys.version_info < (3, 8), - reason='the test requires AsyncMock which was introduced in Python 3.8') @mock.patch('reportportal_client.aio.client.getenv') @mock.patch('reportportal_client.aio.client.async_send_event') @pytest.mark.asyncio @@ -343,8 +318,6 @@ async def test_start_launch_no_statistics_send(async_send_event, getenv): async_send_event.assert_not_called() -@pytest.mark.skipif(sys.version_info < (3, 8), - reason="the test requires AsyncMock which was introduced in Python 3.8") @pytest.mark.asyncio async def test_launch_uuid_print(): str_io = StringIO() @@ -358,8 +331,6 @@ async def test_launch_uuid_print(): assert 'ReportPortal Launch UUID: ' in str_io.getvalue() -@pytest.mark.skipif(sys.version_info < (3, 8), - reason="the test requires AsyncMock which was introduced in Python 3.8") @pytest.mark.asyncio async def test_no_launch_uuid_print(): str_io = StringIO() @@ -373,8 +344,6 @@ async def test_no_launch_uuid_print(): assert 'ReportPortal Launch UUID: ' not in str_io.getvalue() -@pytest.mark.skipif(sys.version_info < (3, 8), - reason="the test requires AsyncMock which was introduced in Python 3.8") @pytest.mark.asyncio @mock.patch('reportportal_client.client.sys.stdout', new_callable=StringIO) async def test_launch_uuid_print_default_io(mock_stdout): @@ -386,8 +355,6 @@ async def test_launch_uuid_print_default_io(mock_stdout): assert 'ReportPortal Launch UUID: ' in mock_stdout.getvalue() -@pytest.mark.skipif(sys.version_info < (3, 8), - reason="the test requires AsyncMock which was introduced in Python 3.8") @pytest.mark.asyncio @mock.patch('reportportal_client.client.sys.stdout', new_callable=StringIO) async def test_launch_uuid_print_default_print(mock_stdout): @@ -427,8 +394,6 @@ def request_error(*args, **kwargs): raise ValueError() -@pytest.mark.skipif(sys.version_info < (3, 8), - reason="the test requires AsyncMock which was introduced in Python 3.8") @pytest.mark.parametrize( 'requests_method, client_method, client_params', [ @@ -479,8 +444,6 @@ def verify_parameters(expected_parameters: dict, actual_parameters: List[dict]): assert expected_parameters.get(attribute.get('key')) == attribute.get('value') -@pytest.mark.skipif(sys.version_info < (3, 8), - reason='the test requires AsyncMock which was introduced in Python 3.8') @pytest.mark.parametrize( 'parent_id, expected_uri', [ @@ -533,8 +496,6 @@ async def test_start_test_item(aio_client: Client, parent_id, expected_uri): verify_parameters(parameters, actual_parameters) -@pytest.mark.skipif(sys.version_info < (3, 8), - reason='the test requires AsyncMock which was introduced in Python 3.8') @pytest.mark.asyncio async def test_start_test_item_default_values(aio_client: Client): # noinspection PyTypeChecker @@ -576,8 +537,6 @@ def mock_basic_put_response(session): session.put.return_value = return_object -@pytest.mark.skipif(sys.version_info < (3, 8), - reason='the test requires AsyncMock which was introduced in Python 3.8') @pytest.mark.asyncio async def test_finish_test_item(aio_client: Client): # noinspection PyTypeChecker @@ -618,8 +577,6 @@ async def test_finish_test_item(aio_client: Client): assert entry[1] == expected_issue[entry[0]] -@pytest.mark.skipif(sys.version_info < (3, 8), - reason='the test requires AsyncMock which was introduced in Python 3.8') @pytest.mark.asyncio async def test_finish_test_item_default_values(aio_client: Client): # noinspection PyTypeChecker @@ -649,8 +606,6 @@ async def test_finish_test_item_default_values(aio_client: Client): assert actual_json.get('issue') is None -@pytest.mark.skipif(sys.version_info < (3, 8), - reason='the test requires AsyncMock which was introduced in Python 3.8') @pytest.mark.asyncio async def test_finish_launch(aio_client: Client): # noinspection PyTypeChecker @@ -678,8 +633,6 @@ async def test_finish_launch(aio_client: Client): verify_attributes(attributes, actual_attributes) -@pytest.mark.skipif(sys.version_info < (3, 8), - reason='the test requires AsyncMock which was introduced in Python 3.8') @pytest.mark.asyncio async def test_finish_launch_default_values(aio_client: Client): # noinspection PyTypeChecker @@ -711,8 +664,6 @@ def mock_basic_get_response(session): session.get.return_value = return_object -@pytest.mark.skipif(sys.version_info < (3, 8), - reason='the test requires AsyncMock which was introduced in Python 3.8') @pytest.mark.asyncio async def test_update_item(aio_client: Client): # noinspection PyTypeChecker @@ -738,8 +689,6 @@ async def test_update_item(aio_client: Client): verify_attributes(attributes, actual_attributes) -@pytest.mark.skipif(sys.version_info < (3, 8), - reason='the test requires AsyncMock which was introduced in Python 3.8') @pytest.mark.asyncio async def test_get_item_id_by_uuid(aio_client: Client): # noinspection PyTypeChecker @@ -756,8 +705,6 @@ async def test_get_item_id_by_uuid(aio_client: Client): assert expected_uri == call_args[0][0] -@pytest.mark.skipif(sys.version_info < (3, 8), - reason='the test requires AsyncMock which was introduced in Python 3.8') @pytest.mark.asyncio async def test_get_launch_ui_url(aio_client: Client): # noinspection PyTypeChecker @@ -774,8 +721,6 @@ async def test_get_launch_ui_url(aio_client: Client): assert expected_uri == call_args[0][0] -@pytest.mark.skipif(sys.version_info < (3, 8), - reason='the test requires AsyncMock which was introduced in Python 3.8') @pytest.mark.parametrize( 'method, mock_method, call_method, arguments', [ diff --git a/tests/aio/test_async_client.py b/tests/aio/test_async_client.py index dd12bd88..78d76ef1 100644 --- a/tests/aio/test_async_client.py +++ b/tests/aio/test_async_client.py @@ -64,8 +64,6 @@ def test_clone(): and async_client.current_item() == cloned.current_item() -@pytest.mark.skipif(sys.version_info < (3, 8), - reason='the test requires AsyncMock which was introduced in Python 3.8') @pytest.mark.asyncio async def test_start_launch(): aio_client = mock.AsyncMock() @@ -92,8 +90,6 @@ async def test_start_launch(): assert kwargs.get('rerun_of') == rerun_of -@pytest.mark.skipif(sys.version_info < (3, 8), - reason='the test requires AsyncMock which was introduced in Python 3.8') @pytest.mark.parametrize( 'launch_uuid, method, params', [ @@ -154,8 +150,6 @@ async def test_launch_uuid_usage(launch_uuid, method, params): assert args[i + 1] == param -@pytest.mark.skipif(sys.version_info < (3, 8), - reason='the test requires AsyncMock which was introduced in Python 3.8') @pytest.mark.asyncio async def test_start_item_tracking(async_client: AsyncRPClient): aio_client = async_client.client @@ -174,8 +168,6 @@ async def test_start_item_tracking(async_client: AsyncRPClient): assert async_client.current_item() is None -@pytest.mark.skipif(sys.version_info < (3, 8), - reason='the test requires AsyncMock which was introduced in Python 3.8') @pytest.mark.asyncio async def test_logs_flush_on_close(async_client: AsyncRPClient): # noinspection PyTypeChecker diff --git a/tests/aio/test_batched_client.py b/tests/aio/test_batched_client.py index dd2961ff..f962bdba 100644 --- a/tests/aio/test_batched_client.py +++ b/tests/aio/test_batched_client.py @@ -12,7 +12,6 @@ # limitations under the License import pickle -import sys from unittest import mock # noinspection PyPackageRequirements @@ -80,8 +79,6 @@ def test_clone(): and async_client.current_item() == cloned.current_item() -@pytest.mark.skipif(sys.version_info < (3, 8), - reason='the test requires AsyncMock which was introduced in Python 3.8') @pytest.mark.parametrize( 'launch_uuid, method, params', [ @@ -141,8 +138,6 @@ def test_launch_uuid_usage(launch_uuid, method, params): assert args[i + 1] == param -@pytest.mark.skipif(sys.version_info < (3, 8), - reason='the test requires AsyncMock which was introduced in Python 3.8') def test_logs_flush_on_close(batched_client: BatchedRPClient): batched_client.own_client = True # noinspection PyTypeChecker diff --git a/tests/aio/test_threaded_client.py b/tests/aio/test_threaded_client.py index 25a0ceec..91281ad6 100644 --- a/tests/aio/test_threaded_client.py +++ b/tests/aio/test_threaded_client.py @@ -12,7 +12,6 @@ # limitations under the License import pickle -import sys from unittest import mock import pytest @@ -76,8 +75,6 @@ def test_clone(): and async_client.current_item() == cloned.current_item() -@pytest.mark.skipif(sys.version_info < (3, 8), - reason='the test requires AsyncMock which was introduced in Python 3.8') @pytest.mark.parametrize( 'launch_uuid, method, params', [ @@ -137,8 +134,6 @@ def test_launch_uuid_usage(launch_uuid, method, params): assert args[i + 1] == param -@pytest.mark.skipif(sys.version_info < (3, 8), - reason='the test requires AsyncMock which was introduced in Python 3.8') def test_logs_flush_on_close(batched_client: ThreadedRPClient): batched_client.own_client = True # noinspection PyTypeChecker diff --git a/tests/core/test_rp_responses.py b/tests/core/test_rp_responses.py index e11d860b..2c635bd2 100644 --- a/tests/core/test_rp_responses.py +++ b/tests/core/test_rp_responses.py @@ -12,7 +12,6 @@ # limitations under the License import json -import sys import pytest from unittest import mock @@ -54,8 +53,6 @@ def test_custom_decode_error(error_log, ok, response_code, error_function, expec assert error_log.call_args_list[0][0][0] == expected_message -@pytest.mark.skipif(sys.version_info < (3, 8), - reason='the test requires AsyncMock which was introduced in Python 3.8') @mock.patch('reportportal_client.core.rp_responses.logging.Logger.error') @pytest.mark.asyncio @pytest.mark.parametrize('ok, response_code, error_function, expected_message', [ diff --git a/tests/logs/test_rp_logger.py b/tests/logs/test_rp_logger.py index 7ecaaa40..5e318fda 100644 --- a/tests/logs/test_rp_logger.py +++ b/tests/logs/test_rp_logger.py @@ -73,9 +73,6 @@ def test_log_level_filter(handler_level, log_level, expected_calls): assert mock_client.log.call_count == expected_calls -@pytest.mark.skipif(sys.version_info < (3, 8), - reason='"stacklevel" introduced in Python 3.8, so not ' - 'actual for earlier versions') @mock.patch('reportportal_client.logs.logging.Logger.handle') def test_stacklevel_record_make(logger_handler): logger = RPLogger('test_logger') diff --git a/tox.ini b/tox.ini index 1bdcd57d..433a107e 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,6 @@ isolated_build = True envlist = pep - py37 py38 py39 py310 @@ -27,7 +26,6 @@ commands = pre-commit run --all-files --show-diff-on-failure [gh-actions] python = - 3.7: py37 3.8: py38 3.9: py39 3.10: pep, py310