From 67a8023fa0f2d1207aa9e3f71d5f9bd80435d4c2 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Sun, 8 Oct 2023 22:29:36 +0200 Subject: [PATCH 1/5] Drop Python 3.7 (end-of-life), require at least 3.8 Signed-off-by: Sergey Vasilyev --- .github/ISSUE_TEMPLATE/bug-report.yaml | 2 +- .github/workflows/ci.yaml | 4 ++-- .github/workflows/thorough.yaml | 4 ++-- DEVELOPMENT.md | 2 +- README.md | 2 +- docs/deployment.rst | 2 +- docs/install.rst | 2 +- docs/walkthrough/prerequisites.rst | 2 +- kopf/_cogs/helpers/typedefs.py | 2 +- setup.py | 3 +-- 10 files changed, 12 insertions(+), 13 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yaml b/.github/ISSUE_TEMPLATE/bug-report.yaml index 0a7a299a..c7455ed7 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/bug-report.yaml @@ -43,7 +43,7 @@ body: id: python-version attributes: label: Python version - placeholder: e.g. 3.9 or pypy-3.7-7.3.3 + placeholder: e.g. 3.10 or pypy-3.10-7.3.13 - type: textarea id: code diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 37deb32c..9830c3ed 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -38,7 +38,7 @@ jobs: fail-fast: false matrix: install-extras: [ "", "full-auth" ] - python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11" ] + python-version: [ "3.8", "3.9", "3.10", "3.11" ] include: - install-extras: "uvloop" python-version: "3.11" @@ -81,7 +81,7 @@ jobs: fail-fast: false matrix: install-extras: [ "", "full-auth" ] - python-version: [ "pypy-3.7", "pypy-3.8", "pypy-3.9" ] + python-version: [ "pypy-3.8", "pypy-3.9" ] name: Python ${{ matrix.python-version }} ${{ matrix.install-extras }} runs-on: ubuntu-22.04 timeout-minutes: 10 diff --git a/.github/workflows/thorough.yaml b/.github/workflows/thorough.yaml index 930e566c..74be61d7 100644 --- a/.github/workflows/thorough.yaml +++ b/.github/workflows/thorough.yaml @@ -42,7 +42,7 @@ jobs: fail-fast: false matrix: install-extras: [ "", "full-auth" ] - python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11" ] + python-version: [ "3.8", "3.9", "3.10", "3.11" ] include: - install-extras: "uvloop" python-version: "3.11" @@ -85,7 +85,7 @@ jobs: fail-fast: false matrix: install-extras: [ "", "full-auth" ] - python-version: [ "pypy-3.7", "pypy-3.8", "pypy-3.9" ] + python-version: [ "pypy-3.8", "pypy-3.9" ] name: Python ${{ matrix.python-version }} ${{ matrix.install-extras }} runs-on: ubuntu-22.04 timeout-minutes: 10 diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index b2c051d5..b8338b33 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -82,7 +82,7 @@ If you use PyCharm, create a Run/Debug Configuration as follows: * Mode: `module name` * Module name: `kopf` * Arguments: `run examples/01-minimal/example.py --verbose` -* Python Interpreter: anything with Python>=3.7 +* Python Interpreter: anything with Python>=3.8 Stop the console operator, and start the IDE debug session. Put a breakpoint in the used operator script on the first line of the function. diff --git a/README.md b/README.md index 836102b9..d79b5674 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ That easy! For more features, see the [documentation](https://kopf.readthedocs.i ## Usage -Python 3.7+ is required: +Python 3.8+ is required: [CPython](https://www.python.org/) and [PyPy](https://www.pypy.org/) are officially supported and tested; other Python implementations can work too. diff --git a/docs/deployment.rst b/docs/deployment.rst index 4569125b..aa4ad9fc 100644 --- a/docs/deployment.rst +++ b/docs/deployment.rst @@ -10,7 +10,7 @@ But normally, the operators are usually deployed directly to the clusters. Docker image ============ -First of all, the operator must be packaged as a docker image with Python 3.7 or newer: +First of all, the operator must be packaged as a docker image with Python 3.8 or newer: .. code-block:: dockerfile :caption: Dockerfile diff --git a/docs/install.rst b/docs/install.rst index bf0cb508..4148add6 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -6,7 +6,7 @@ Installation Prerequisites: -* Python >= 3.7 (CPython and PyPy are officially tested and supported). +* Python >= 3.8 (CPython and PyPy are officially tested and supported). To install Kopf:: diff --git a/docs/walkthrough/prerequisites.rst b/docs/walkthrough/prerequisites.rst index b799cd17..bf717a08 100644 --- a/docs/walkthrough/prerequisites.rst +++ b/docs/walkthrough/prerequisites.rst @@ -6,7 +6,7 @@ We need a running Kubernetes cluster and some tools for our experiments. If you have a cluster already preconfigured, you can skip this section. Otherwise, let's install minikube locally (e.g. for MacOS): -* Python >= 3.7 (running in a venv is recommended, though is not necessary). +* Python >= 3.8 (running in a venv is recommended, though is not necessary). * `Install kubectl `_ * :doc:`Install minikube ` (a local Kubernetes cluster) * :doc:`Install Kopf ` diff --git a/kopf/_cogs/helpers/typedefs.py b/kopf/_cogs/helpers/typedefs.py index 43c6f49d..ab90633e 100644 --- a/kopf/_cogs/helpers/typedefs.py +++ b/kopf/_cogs/helpers/typedefs.py @@ -2,7 +2,7 @@ Rudimentary type [re-]definitions for cross-versioned Python & mypy. The problem is that new mypy versions often bring type-sheds with StdLib types -defined as generics, while the old Python runtime (down to 3.7) +defined as generics, while the old Python runtime (down to 3.8 & 3.9 & 3.10) does not support the usual syntax. Examples: asyncio.Task, asyncio.Future, logging.LoggerAdapter, and others. diff --git a/setup.py b/setup.py index 4db2ee12..255446a7 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,6 @@ 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', @@ -53,7 +52,7 @@ ], }, - python_requires='>=3.7', + python_requires='>=3.8', setup_requires=[ 'setuptools_scm', ], From c3bbcc5250f042c316fcd4d384f0dd3cc2ce7711 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Sun, 8 Oct 2023 22:35:59 +0200 Subject: [PATCH 2/5] Drop 3.7-style named task creation, use asyncio directly Signed-off-by: Sergey Vasilyev --- kopf/_cogs/aiokits/aioenums.py | 4 +-- kopf/_cogs/aiokits/aiotasks.py | 26 +++++-------------- kopf/_cogs/clients/watching.py | 2 +- kopf/_core/engines/daemons.py | 2 +- kopf/_core/reactor/running.py | 9 +++---- .../utilities/aiotasks/test_task_creation.py | 24 ----------------- .../utilities/aiotasks/test_task_guarding.py | 6 ++--- .../utilities/aiotasks/test_task_selection.py | 6 ++--- .../utilities/aiotasks/test_task_stopping.py | 22 ++++++++-------- tests/utilities/aiotasks/test_task_waiting.py | 4 +-- 10 files changed, 33 insertions(+), 72 deletions(-) delete mode 100644 tests/utilities/aiotasks/test_task_creation.py diff --git a/kopf/_cogs/aiokits/aioenums.py b/kopf/_cogs/aiokits/aioenums.py index a36d2809..4051830d 100644 --- a/kopf/_cogs/aiokits/aioenums.py +++ b/kopf/_cogs/aiokits/aioenums.py @@ -4,8 +4,6 @@ import time from typing import Awaitable, Generator, Generic, Optional, TypeVar -from kopf._cogs.aiokits import aiotasks - FlagReasonT = TypeVar('FlagReasonT', bound=enum.Flag) @@ -168,7 +166,7 @@ def __init__(self, waiter: AsyncFlagWaiter[FlagReasonT], *, timeout: Optional[fl def __await__(self) -> Generator[None, None, AsyncFlagWaiter[FlagReasonT]]: name = f"time-limited waiting for the daemon stopper {self._setter!r}" coro = asyncio.wait_for(self._setter.async_event.wait(), timeout=self._timeout) - task = aiotasks.create_task(coro, name=name) + task = asyncio.create_task(coro, name=name) try: yield from task except asyncio.TimeoutError: diff --git a/kopf/_cogs/aiokits/aiotasks.py b/kopf/_cogs/aiokits/aiotasks.py index fbeaf117..69ab4863 100644 --- a/kopf/_cogs/aiokits/aiotasks.py +++ b/kopf/_cogs/aiokits/aiotasks.py @@ -10,9 +10,8 @@ so there is no added overhead; instead, the implicit overhead is made explicit. """ import asyncio -import sys -from typing import TYPE_CHECKING, Any, Awaitable, Callable, Collection, Coroutine, \ - Generator, NamedTuple, Optional, Set, Tuple, TypeVar, Union +from typing import TYPE_CHECKING, Any, Callable, Collection, Coroutine, \ + NamedTuple, Optional, Set, Tuple, TypeVar from kopf._cogs.helpers import typedefs @@ -27,17 +26,6 @@ Future = asyncio.Future Task = asyncio.Task -# Accept `name=` always, but simulate it for Python 3.7 to do nothing. -if sys.version_info >= (3, 8): - create_task = asyncio.create_task -else: - def create_task( - coro: Union[Generator[Any, None, _T], Awaitable[_T]], - *, - name: Optional[str] = None, # noqa: W613 # pylint: disable=unused-argument - ) -> Task: - return asyncio.create_task(coro) - async def cancel_coro( coro: Coroutine[Any, Any, Any], @@ -65,7 +53,7 @@ async def cancel_coro( coro.close() # OR: coro.throw(asyncio.CancelledError()) except AttributeError: # The official way is to create an extra task object, thus to waste some memory. - corotask = create_task(coro=coro, name=name) + corotask = asyncio.create_task(coro=coro, name=name) corotask.cancel() try: await corotask @@ -133,7 +121,7 @@ def create_guarded_task( This is only a shortcut for named task creation (name is used in 2 places). """ - return create_task( + return asyncio.create_task( name=name, coro=guard( name=name, @@ -303,8 +291,8 @@ def __init__( self._pending_coros: asyncio.Queue[SchedulerJob] = asyncio.Queue() self._running_tasks: Set[Task] = set() self._cleaning_queue: asyncio.Queue[Task] = asyncio.Queue() - self._cleaning_task = create_task(self._task_cleaner(), name=f"task cleaner of {self!r}") - self._spawning_task = create_task(self._task_spawner(), name=f"task spawner of {self!r}") + self._cleaning_task = asyncio.create_task(self._task_cleaner(), name=f"cleaner of {self!r}") + self._spawning_task = asyncio.create_task(self._task_spawner(), name=f"spawner of {self!r}") def empty(self) -> bool: """ Check if the scheduler has nothing to do. """ @@ -371,7 +359,7 @@ async def _task_spawner(self) -> None: # when they are finished --- to be awaited and released "passively". while self._can_spawn(): coro, name = self._pending_coros.get_nowait() # guaranteed by the predicate - task = create_task(coro=coro, name=name) + task = asyncio.create_task(coro=coro, name=name) task.add_done_callback(self._task_done_callback) self._running_tasks.add(task) if self._closed: diff --git a/kopf/_cogs/clients/watching.py b/kopf/_cogs/clients/watching.py index 3144ccee..d16d4ba8 100644 --- a/kopf/_cogs/clients/watching.py +++ b/kopf/_cogs/clients/watching.py @@ -145,7 +145,7 @@ async def streaming_block( # Create the signalling future for when paused again. operator_pause_waiter: aiotasks.Future if operator_paused is not None: - operator_pause_waiter = aiotasks.create_task( + operator_pause_waiter = asyncio.create_task( operator_paused.wait_for(True), name=f"pause-waiter for {resource}") else: diff --git a/kopf/_core/engines/daemons.py b/kopf/_core/engines/daemons.py index 6d1de9ad..2302eb62 100644 --- a/kopf/_core/engines/daemons.py +++ b/kopf/_core/engines/daemons.py @@ -99,7 +99,7 @@ async def spawn_daemons( stopper=stopper, # for stopping (outside of causes) handler=handler, logger=loggers.LocalObjectLogger(body=cause.body, settings=settings), - task=aiotasks.create_task(_runner( + task=asyncio.create_task(_runner( settings=settings, daemons=daemons, # for self-garbage-collection handler=handler, diff --git a/kopf/_core/reactor/running.py b/kopf/_core/reactor/running.py index a8a7102b..96186ef8 100644 --- a/kopf/_core/reactor/running.py +++ b/kopf/_core/reactor/running.py @@ -219,17 +219,17 @@ async def spawn_tasks( posting.settings_var.set(settings) # A few common background forever-running infrastructural tasks (irregular root tasks). - tasks.append(aiotasks.create_task( + tasks.append(asyncio.create_task( name="stop-flag checker", coro=_stop_flag_checker( signal_flag=signal_flag, stop_flag=stop_flag))) - tasks.append(aiotasks.create_task( + tasks.append(asyncio.create_task( name="ultimate termination", coro=_ultimate_termination( settings=settings, stop_flag=stop_flag))) - tasks.append(aiotasks.create_task( + tasks.append(asyncio.create_task( name="startup/cleanup activities", coro=_startup_cleanup_activities( root_tasks=tasks, # used as a "live" view, populated later. @@ -433,8 +433,7 @@ async def _stop_flag_checker( if signal_flag is not None: flags.append(signal_flag) if stop_flag is not None: - flags.append(aiotasks.create_task(aioadapters.wait_flag(stop_flag), - name="stop-flag waiter")) + flags.append(asyncio.create_task(aioadapters.wait_flag(stop_flag), name="stop-flag waiter")) # Wait until one of the stoppers is set/raised. try: diff --git a/tests/utilities/aiotasks/test_task_creation.py b/tests/utilities/aiotasks/test_task_creation.py deleted file mode 100644 index 5c35f97b..00000000 --- a/tests/utilities/aiotasks/test_task_creation.py +++ /dev/null @@ -1,24 +0,0 @@ -import asyncio - -import pytest - -from kopf._cogs.aiokits.aiotasks import create_task - - -async def sample() -> None: - pass - - -@pytest.mark.skipif('sys.version_info < (3, 8)') -def test_py38_create_task_is_the_native_one(): - assert create_task is asyncio.create_task - - -@pytest.mark.skipif('sys.version_info >= (3, 8)') -async def test_py37_create_task_accepts_name(mocker): - real_create_task = mocker.patch('asyncio.create_task') - coro = sample() - task = create_task(coro, name='unused') - assert real_create_task.called - assert task is real_create_task.return_value - await coro # to prevent "never awaited" errors diff --git a/tests/utilities/aiotasks/test_task_guarding.py b/tests/utilities/aiotasks/test_task_guarding.py index 4a3e0265..239c1393 100644 --- a/tests/utilities/aiotasks/test_task_guarding.py +++ b/tests/utilities/aiotasks/test_task_guarding.py @@ -3,7 +3,7 @@ import pytest -from kopf._cogs.aiokits.aiotasks import create_guarded_task, create_task, reraise +from kopf._cogs.aiokits.aiotasks import create_guarded_task, reraise class Error(Exception): @@ -97,14 +97,14 @@ async def test_guard_waits_for_the_flag(): async def test_reraise_escalates_errors(): - task = create_task(fail("boo!")) + task = asyncio.create_task(fail("boo!")) await asyncio.wait([task], timeout=0.01) # let it start & react with pytest.raises(Error): await reraise([task]) async def test_reraise_skips_cancellations(): - task = create_task(asyncio.Event().wait()) + task = asyncio.create_task(asyncio.Event().wait()) done, pending = await asyncio.wait([task], timeout=0.01) # let it start assert not done task.cancel() diff --git a/tests/utilities/aiotasks/test_task_selection.py b/tests/utilities/aiotasks/test_task_selection.py index a3bf17b8..8de7d9e1 100644 --- a/tests/utilities/aiotasks/test_task_selection.py +++ b/tests/utilities/aiotasks/test_task_selection.py @@ -1,12 +1,12 @@ import asyncio -from kopf._cogs.aiokits.aiotasks import all_tasks, create_task +from kopf._cogs.aiokits.aiotasks import all_tasks async def test_alltasks_exclusion(): flag = asyncio.Event() - task1 = create_task(flag.wait()) - task2 = create_task(flag.wait()) + task1 = asyncio.create_task(flag.wait()) + task2 = asyncio.create_task(flag.wait()) done, pending = await asyncio.wait([task1, task2], timeout=0.01) assert not done diff --git a/tests/utilities/aiotasks/test_task_stopping.py b/tests/utilities/aiotasks/test_task_stopping.py index 3717bd46..c61bfe9c 100644 --- a/tests/utilities/aiotasks/test_task_stopping.py +++ b/tests/utilities/aiotasks/test_task_stopping.py @@ -3,7 +3,7 @@ import pytest -from kopf._cogs.aiokits.aiotasks import create_task, stop +from kopf._cogs.aiokits.aiotasks import stop async def simple() -> None: @@ -38,8 +38,8 @@ async def test_stop_with_no_tasks_when_quiet(assert_logs, caplog): async def test_stop_immediately_with_finishing(assert_logs, caplog): logger = logging.getLogger() caplog.set_level(0) - task1 = create_task(simple()) - task2 = create_task(simple()) + task1 = asyncio.create_task(simple()) + task2 = asyncio.create_task(simple()) done, pending = await stop([task1, task2], title='sample', logger=logger, cancelled=False) assert done assert not pending @@ -51,8 +51,8 @@ async def test_stop_immediately_with_finishing(assert_logs, caplog): async def test_stop_immediately_with_cancelling(assert_logs, caplog): logger = logging.getLogger() caplog.set_level(0) - task1 = create_task(simple()) - task2 = create_task(simple()) + task1 = asyncio.create_task(simple()) + task2 = asyncio.create_task(simple()) done, pending = await stop([task1, task2], title='sample', logger=logger, cancelled=True) assert done assert not pending @@ -65,9 +65,9 @@ async def test_stop_immediately_with_cancelling(assert_logs, caplog): async def test_stop_iteratively(assert_logs, caplog, cancelled): logger = logging.getLogger() caplog.set_level(0) - task1 = create_task(simple()) - task2 = create_task(stuck()) - stask = create_task(stop([task1, task2], title='sample', logger=logger, interval=0.01, cancelled=cancelled)) + task1 = asyncio.create_task(simple()) + task2 = asyncio.create_task(stuck()) + stask = asyncio.create_task(stop([task1, task2], title='sample', logger=logger, interval=0.01, cancelled=cancelled)) done, pending = await asyncio.wait({stask}, timeout=0.011) assert not done @@ -91,9 +91,9 @@ async def test_stop_iteratively(assert_logs, caplog, cancelled): async def test_stop_itself_is_cancelled(assert_logs, caplog, cancelled): logger = logging.getLogger() caplog.set_level(0) - task1 = create_task(simple()) - task2 = create_task(stuck()) - stask = create_task(stop([task1, task2], title='sample', logger=logger, interval=0.01, cancelled=cancelled)) + task1 = asyncio.create_task(simple()) + task2 = asyncio.create_task(stuck()) + stask = asyncio.create_task(stop([task1, task2], title='sample', logger=logger, interval=0.01, cancelled=cancelled)) done, pending = await asyncio.wait({stask}, timeout=0.011) assert not done diff --git a/tests/utilities/aiotasks/test_task_waiting.py b/tests/utilities/aiotasks/test_task_waiting.py index 75949d5d..3a76ed84 100644 --- a/tests/utilities/aiotasks/test_task_waiting.py +++ b/tests/utilities/aiotasks/test_task_waiting.py @@ -1,6 +1,6 @@ import asyncio -from kopf._cogs.aiokits.aiotasks import create_task, wait +from kopf._cogs.aiokits.aiotasks import wait async def test_wait_with_no_tasks(): @@ -11,7 +11,7 @@ async def test_wait_with_no_tasks(): async def test_wait_with_timeout(): flag = asyncio.Event() - task = create_task(flag.wait()) + task = asyncio.create_task(flag.wait()) done, pending = await wait([task], timeout=0.01) assert not done assert pending == {task} From de2466ccce2a657011617d7bf8c949155ec8a7e6 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Sun, 8 Oct 2023 22:41:58 +0200 Subject: [PATCH 3/5] Switch to stdlib's mock instead of a backported one Signed-off-by: Sergey Vasilyev --- pytest.ini | 2 -- requirements.txt | 3 --- tests/admission/test_admission_server.py | 2 +- tests/admission/test_serving_handler_selection.py | 3 ++- tests/admission/test_serving_kwargs_passthrough.py | 3 ++- tests/apis/test_iterjsonlines.py | 2 +- tests/basic-structs/test_memories.py | 2 +- tests/causation/test_kwargs.py | 2 +- tests/conftest.py | 2 +- tests/handling/conftest.py | 2 +- tests/handling/daemons/conftest.py | 2 +- tests/handling/subhandling/test_subhandling.py | 2 +- tests/handling/test_parametrization.py | 3 +-- tests/hierarchies/conftest.py | 3 ++- tests/hierarchies/test_owner_referencing.py | 2 +- tests/invocations/test_callbacks.py | 2 +- tests/persistence/test_states.py | 2 +- tests/reactor/conftest.py | 2 +- tests/registries/test_matching_of_callbacks.py | 2 +- tests/registries/test_matching_of_resources.py | 2 +- tests/settings/test_executor.py | 3 +-- tests/timing/test_throttling.py | 2 +- tests/utilities/aiotasks/test_coro_cancellation.py | 2 +- tests/utilities/aiotasks/test_scheduler.py | 2 +- 24 files changed, 25 insertions(+), 29 deletions(-) diff --git a/pytest.ini b/pytest.ini index da506c41..afd6b82b 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,6 +1,4 @@ [pytest] -; The standalone `mock` instead of stdlib `unittest.mock` is only for AsyncMock in Python 3.7. -mock_use_standalone_module = true asyncio_mode = auto addopts = --strict-markers diff --git a/requirements.txt b/requirements.txt index b97f81ba..1b8a2404 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,9 +12,6 @@ freezegun import-linter isort lxml -# Generally, `unittest.mock` is enough, but it lacks `AsyncMock` for Py 3.7. -# TODO: Once 3.7 is removed (Jun 2023), roll back to unittest.mock. -mock # Mypy requires typed-ast, which is broken on PyPy 3.7 (could work in PyPy 3.8). mypy==1.2.0; implementation_name == "cpython" pre-commit diff --git a/tests/admission/test_admission_server.py b/tests/admission/test_admission_server.py index bc490b80..636b9b1f 100644 --- a/tests/admission/test_admission_server.py +++ b/tests/admission/test_admission_server.py @@ -1,7 +1,7 @@ import contextlib +from unittest.mock import Mock import pytest -from mock import Mock import kopf from kopf._cogs.aiokits.aiovalues import Container diff --git a/tests/admission/test_serving_handler_selection.py b/tests/admission/test_serving_handler_selection.py index cdf287b1..b704a8ac 100644 --- a/tests/admission/test_serving_handler_selection.py +++ b/tests/admission/test_serving_handler_selection.py @@ -1,5 +1,6 @@ +from unittest.mock import Mock + import pytest -from mock import Mock import kopf from kopf._cogs.structs.ids import HandlerId diff --git a/tests/admission/test_serving_kwargs_passthrough.py b/tests/admission/test_serving_kwargs_passthrough.py index 25fdc39d..b93199ad 100644 --- a/tests/admission/test_serving_kwargs_passthrough.py +++ b/tests/admission/test_serving_kwargs_passthrough.py @@ -1,5 +1,6 @@ +from unittest.mock import Mock + import pytest -from mock import Mock import kopf from kopf._core.engines.admission import serve_admission_request diff --git a/tests/apis/test_iterjsonlines.py b/tests/apis/test_iterjsonlines.py index 91dc10ac..b120a844 100644 --- a/tests/apis/test_iterjsonlines.py +++ b/tests/apis/test_iterjsonlines.py @@ -1,4 +1,4 @@ -from mock import Mock +from unittest.mock import Mock from kopf._cogs.clients.api import iter_jsonlines diff --git a/tests/basic-structs/test_memories.py b/tests/basic-structs/test_memories.py index 65406804..9f0c3f45 100644 --- a/tests/basic-structs/test_memories.py +++ b/tests/basic-structs/test_memories.py @@ -1,4 +1,4 @@ -from mock import Mock +from unittest.mock import Mock from kopf._cogs.structs.bodies import Body from kopf._cogs.structs.ephemera import Memo diff --git a/tests/causation/test_kwargs.py b/tests/causation/test_kwargs.py index 6ee55c85..43fe6e6e 100644 --- a/tests/causation/test_kwargs.py +++ b/tests/causation/test_kwargs.py @@ -1,9 +1,9 @@ import dataclasses import logging from typing import Type +from unittest.mock import Mock import pytest -from mock import Mock from kopf._cogs.configs.configuration import OperatorSettings from kopf._cogs.structs import diffs diff --git a/tests/conftest.py b/tests/conftest.py index 683abc10..f7625ca3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,10 +8,10 @@ import sys import time from typing import Set +from unittest.mock import AsyncMock, Mock import aiohttp.web import pytest -from mock import AsyncMock, Mock import kopf from kopf._cogs.clients.auth import APIContext diff --git a/tests/handling/conftest.py b/tests/handling/conftest.py index 72d0fdc4..a9cec2b5 100644 --- a/tests/handling/conftest.py +++ b/tests/handling/conftest.py @@ -35,9 +35,9 @@ """ import dataclasses from typing import Callable +from unittest.mock import Mock import pytest -from mock import Mock import kopf from kopf._core.intents.causes import ChangingCause diff --git a/tests/handling/daemons/conftest.py b/tests/handling/daemons/conftest.py index 623d0d43..bdd0f846 100644 --- a/tests/handling/daemons/conftest.py +++ b/tests/handling/daemons/conftest.py @@ -1,10 +1,10 @@ import asyncio import contextlib import time +from unittest.mock import MagicMock, patch import freezegun import pytest -from mock import MagicMock, patch import kopf from kopf._cogs.aiokits.aiotoggles import ToggleSet diff --git a/tests/handling/subhandling/test_subhandling.py b/tests/handling/subhandling/test_subhandling.py index 9b2832d2..b83604fb 100644 --- a/tests/handling/subhandling/test_subhandling.py +++ b/tests/handling/subhandling/test_subhandling.py @@ -1,8 +1,8 @@ import asyncio import logging +from unittest.mock import Mock import pytest -from mock import Mock import kopf from kopf._cogs.structs.ephemera import Memo diff --git a/tests/handling/test_parametrization.py b/tests/handling/test_parametrization.py index df4209c2..4f73249a 100644 --- a/tests/handling/test_parametrization.py +++ b/tests/handling/test_parametrization.py @@ -1,6 +1,5 @@ import asyncio - -from mock import Mock +from unittest.mock import Mock import kopf from kopf._cogs.structs.ephemera import Memo diff --git a/tests/hierarchies/conftest.py b/tests/hierarchies/conftest.py index 11f2865d..8e340d34 100644 --- a/tests/hierarchies/conftest.py +++ b/tests/hierarchies/conftest.py @@ -1,5 +1,6 @@ +from unittest.mock import Mock + import pytest -from mock import Mock class CustomIterable: diff --git a/tests/hierarchies/test_owner_referencing.py b/tests/hierarchies/test_owner_referencing.py index e6b42b79..1e124960 100644 --- a/tests/hierarchies/test_owner_referencing.py +++ b/tests/hierarchies/test_owner_referencing.py @@ -1,7 +1,7 @@ import copy +from unittest.mock import call import pytest -from mock import call import kopf from kopf._cogs.structs.bodies import Body, RawBody, RawMeta diff --git a/tests/invocations/test_callbacks.py b/tests/invocations/test_callbacks.py index 175c5d6b..6f15e244 100644 --- a/tests/invocations/test_callbacks.py +++ b/tests/invocations/test_callbacks.py @@ -1,9 +1,9 @@ import functools import logging import traceback +from unittest.mock import Mock import pytest -from mock import Mock from kopf._cogs.structs.bodies import Body from kopf._cogs.structs.patches import Patch diff --git a/tests/persistence/test_states.py b/tests/persistence/test_states.py index c73a8195..32714230 100644 --- a/tests/persistence/test_states.py +++ b/tests/persistence/test_states.py @@ -1,8 +1,8 @@ import datetime +from unittest.mock import Mock import freezegun import pytest -from mock import Mock from kopf._cogs.configs.progress import SmartProgressStorage, StatusProgressStorage from kopf._cogs.structs.bodies import Body diff --git a/tests/reactor/conftest.py b/tests/reactor/conftest.py index 99adc261..af8e07db 100644 --- a/tests/reactor/conftest.py +++ b/tests/reactor/conftest.py @@ -1,8 +1,8 @@ import asyncio import functools +from unittest.mock import AsyncMock import pytest -from mock import AsyncMock from kopf._cogs.clients.watching import infinite_watch from kopf._core.reactor.queueing import watcher, worker as original_worker diff --git a/tests/registries/test_matching_of_callbacks.py b/tests/registries/test_matching_of_callbacks.py index 4aaff10c..e76cebd5 100644 --- a/tests/registries/test_matching_of_callbacks.py +++ b/tests/registries/test_matching_of_callbacks.py @@ -1,7 +1,7 @@ import dataclasses +from unittest.mock import Mock import pytest -from mock import Mock from kopf._cogs.structs.bodies import Body from kopf._cogs.structs.dicts import parse_field diff --git a/tests/registries/test_matching_of_resources.py b/tests/registries/test_matching_of_resources.py index db09c9a1..afa5d8ec 100644 --- a/tests/registries/test_matching_of_resources.py +++ b/tests/registries/test_matching_of_resources.py @@ -1,4 +1,4 @@ -from mock import Mock +from unittest.mock import Mock from kopf._cogs.structs.references import Resource, Selector from kopf._core.intents.registries import _matches_resource diff --git a/tests/settings/test_executor.py b/tests/settings/test_executor.py index 5494f617..f1dd522b 100644 --- a/tests/settings/test_executor.py +++ b/tests/settings/test_executor.py @@ -1,7 +1,6 @@ import concurrent.futures import threading - -from mock import MagicMock +from unittest.mock import MagicMock import kopf from kopf._core.actions.invocation import invoke diff --git a/tests/timing/test_throttling.py b/tests/timing/test_throttling.py index a77282a9..e6dc8537 100644 --- a/tests/timing/test_throttling.py +++ b/tests/timing/test_throttling.py @@ -1,8 +1,8 @@ import asyncio import logging +from unittest.mock import call import pytest -from mock import call from kopf._core.actions.throttlers import Throttler, throttled diff --git a/tests/utilities/aiotasks/test_coro_cancellation.py b/tests/utilities/aiotasks/test_coro_cancellation.py index f71ddc9b..97f8fef0 100644 --- a/tests/utilities/aiotasks/test_coro_cancellation.py +++ b/tests/utilities/aiotasks/test_coro_cancellation.py @@ -1,9 +1,9 @@ import asyncio import gc import warnings +from unittest.mock import AsyncMock, Mock import pytest -from mock import AsyncMock, Mock from kopf._cogs.aiokits.aiotasks import cancel_coro diff --git a/tests/utilities/aiotasks/test_scheduler.py b/tests/utilities/aiotasks/test_scheduler.py index 4a5a933c..2670748c 100644 --- a/tests/utilities/aiotasks/test_scheduler.py +++ b/tests/utilities/aiotasks/test_scheduler.py @@ -1,7 +1,7 @@ import asyncio +from unittest.mock import Mock import pytest -from mock import Mock from kopf._cogs.aiokits.aiotasks import Scheduler From 85d0bc5d676d6efd12d9d8acc8508c4187580b41 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Sun, 8 Oct 2023 22:44:25 +0200 Subject: [PATCH 4/5] Propagate CancelledError natively as since Python 3.8 Signed-off-by: Sergey Vasilyev --- kopf/_core/actions/throttlers.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/kopf/_core/actions/throttlers.py b/kopf/_core/actions/throttlers.py index c591b14b..2106cf97 100644 --- a/kopf/_core/actions/throttlers.py +++ b/kopf/_core/actions/throttlers.py @@ -43,11 +43,6 @@ async def throttled( try: yield should_run - except asyncio.CancelledError: - # CancelledError is a BaseException in 3.8 & 3.9, but a regular Exception in 3.7. - # Behave as if it is a BaseException -- to enabled tests with async-timeout. - raise - except Exception as e: # If it is not an error-of-interest, escalate normally. BaseExceptions are escalated always. From 1ca37ca217512c7e9dc0cb6022989b3069254132 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Sun, 8 Oct 2023 22:56:01 +0200 Subject: [PATCH 5/5] Re-enable mypy in PyPy too (not used in CI though) Signed-off-by: Sergey Vasilyev --- requirements.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 1b8a2404..b708fd8c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,8 +12,7 @@ freezegun import-linter isort lxml -# Mypy requires typed-ast, which is broken on PyPy 3.7 (could work in PyPy 3.8). -mypy==1.2.0; implementation_name == "cpython" +mypy==1.2.0 pre-commit pyngrok pytest>=6.0.0