Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hotfix #166

Merged
merged 4 commits into from
Jan 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

|build-status| |coverage| |license| |semgrep| |pyversion| |pyimp| |ocbackerbadge| |ocsponsorbadge|

:Version: 1.0.0 (TBD)
:Version: 1.0.0a12
:Web: https://pytest-celery.readthedocs.io/en/latest/
:Download: https://pypi.org/project/pytest-celery/
:Source: https://github.com/celery/pytest-celery/
Expand Down
3 changes: 3 additions & 0 deletions examples/myworker/tests/test_myworker.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@ def celery_worker_cluster(
celery_worker: CeleryTestWorker,
myworker_worker: CeleryTestWorker,
) -> CeleryWorkerCluster:
"""Add myworker worker to the workers cluster alongside the parametrize
plugin worker."""
cluster = CeleryWorkerCluster(celery_worker, myworker_worker) # type: ignore
yield cluster
cluster.teardown()


def test_ping(celery_setup: CeleryTestSetup):
"""Test ping task for each worker node."""
worker: CeleryTestWorker
for worker in celery_setup.worker_cluster:
sig: Signature = ping.s()
Expand Down
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ version = "1.0.0a12"
search = '__version__ = "{current_version}"'
replace = '__version__ = "{new_version}"'

[tool.poetry_bumpversion.file."README.rst"]
search = ':Version: {current_version}'
replace = ':Version: {new_version}'

[tool.poetry.dependencies]
python = ">= 3.8,<4.0"
celery = { version = "^5", extras = ["redis", "pymemcache"] }
Expand Down
22 changes: 6 additions & 16 deletions src/pytest_celery/api/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@

from pytest_celery.api.base import CeleryTestCluster
from pytest_celery.api.base import CeleryTestNode
from pytest_celery.api.container import CeleryTestContainer
from pytest_celery.defaults import DEFAULT_WORKER_ENV


class CeleryTestBackend(CeleryTestNode):
"""CeleryTestBackend is specialized node type for handling celery backends
nodes. It is used to encapsulate a backend instance.
"""This is specialized node type for handling celery backends nodes. It is
used to encapsulate a backend instance.

Responsibility Scope:
Handling backend specific requirements and configuration.
Expand All @@ -22,6 +21,8 @@ def default_config(cls) -> dict:
}

def restart(self, reload_container: bool = True, force: bool = False) -> None:
"""Override restart method to update the app result backend with new
container values."""
super().restart(reload_container, force)
if self.app:
self.app.conf.update(
Expand All @@ -30,24 +31,13 @@ def restart(self, reload_container: bool = True, force: bool = False) -> None:


class CeleryBackendCluster(CeleryTestCluster):
"""CeleryBackendCluster is a specialized cluster type for handling celery
backends. It is used to define which backend instances are available for
the test.
"""This is a specialized cluster type for handling celery backends. It is
used to define which backend instances are available for the test.

Responsibility Scope:
Provude useful methods for managing a cluster of celery backends.
"""

def __init__(self, *backends: tuple[CeleryTestBackend | CeleryTestContainer]) -> None:
super().__init__(*backends)

def _set_nodes(
self,
*nodes: tuple[CeleryTestNode | CeleryTestContainer],
node_cls: type[CeleryTestNode] = CeleryTestBackend,
) -> tuple[CeleryTestNode]:
return super()._set_nodes(*nodes, node_cls=node_cls)

@classmethod
def default_config(cls) -> dict:
return {
Expand Down
26 changes: 16 additions & 10 deletions src/pytest_celery/api/base.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from __future__ import annotations

from abc import abstractmethod
from typing import Any
from typing import Iterator

import pytest_docker_tools
Expand All @@ -14,9 +13,9 @@


class CeleryTestNode:
"""CeleryTestNode is the logical representation of a container instance. It
is used to provide a common interface for interacting with the container
regardless of the underlying implementation.
"""This is the logical representation of a container instance. It is used
to provide a common interface for interacting with the container regardless
of the underlying implementation.

Responsibility Scope:
The node's responsibility is to wrap the container and provide
Expand Down Expand Up @@ -44,6 +43,8 @@
return self._app

def __eq__(self, other: object) -> bool:
"""Two nodes are equal if they have the same container and Celery
app."""
if isinstance(other, CeleryTestNode):
return all(
(
Expand Down Expand Up @@ -152,8 +153,8 @@


class CeleryTestCluster:
"""CeleryTestCluster is a collection of CeleryTestNodes. It is used to
collect the test nodes into a single object for easier management.
"""This is a collection of CeleryTestNodes. It is used to collect the test
nodes into a single object for easier management.

Responsibility Scope:
The cluster's responsibility is to define which nodes will be used for
Expand Down Expand Up @@ -194,19 +195,24 @@
self._nodes = self._set_nodes(*nodes) # type: ignore

def __iter__(self) -> Iterator[CeleryTestNode]:
"""Iterate over the nodes of the cluster."""
return iter(self.nodes)

def __getitem__(self, index: Any) -> CeleryTestNode:
def __getitem__(self, index: int) -> CeleryTestNode:

Check warning on line 201 in src/pytest_celery/api/base.py

View check run for this annotation

Codecov / codecov/patch

src/pytest_celery/api/base.py#L201

Added line #L201 was not covered by tests
"""Get a node from the cluster by index."""
return self.nodes[index]

def __len__(self) -> int:
"""Get the number of nodes in the cluster."""
return len(self.nodes)

def __eq__(self, other: object) -> bool:
"""Two clusters are equal if they have the same nodes."""
if isinstance(other, CeleryTestCluster):
for node in self:
if node not in other:
return False
if len(self) == len(other):
for node in self:
if node not in other:
return False

Check warning on line 215 in src/pytest_celery/api/base.py

View check run for this annotation

Codecov / codecov/patch

src/pytest_celery/api/base.py#L215

Added line #L215 was not covered by tests
return False

@classmethod
Expand Down
22 changes: 6 additions & 16 deletions src/pytest_celery/api/broker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@

from pytest_celery.api.base import CeleryTestCluster
from pytest_celery.api.base import CeleryTestNode
from pytest_celery.api.container import CeleryTestContainer
from pytest_celery.defaults import DEFAULT_WORKER_ENV


class CeleryTestBroker(CeleryTestNode):
"""CeleryTestBroker is specialized node type for handling celery brokers
nodes. It is used to encapsulate a broker instance.
"""This is specialized node type for handling celery brokers nodes. It is
used to encapsulate a broker instance.

Responsibility Scope:
Handling broker specific requirements and configuration.
Expand All @@ -22,6 +21,8 @@ def default_config(cls) -> dict:
}

def restart(self, reload_container: bool = True, force: bool = False) -> None:
"""Override restart method to update the app broker url with new
container values."""
super().restart(reload_container, force)
if self.app:
self.app.conf.update(
Expand All @@ -30,24 +31,13 @@ def restart(self, reload_container: bool = True, force: bool = False) -> None:


class CeleryBrokerCluster(CeleryTestCluster):
"""CeleryBrokerCluster is a specialized cluster type for handling celery
brokers. It is used to define which broker instances are available for the
test.
"""This is a specialized cluster type for handling celery brokers. It is
used to define which broker instances are available for the test.

Responsibility Scope:
Provude useful methods for managing a cluster of celery brokers.
"""

def __init__(self, *brokers: tuple[CeleryTestBroker | CeleryTestContainer]) -> None:
super().__init__(*brokers)

def _set_nodes(
self,
*nodes: tuple[CeleryTestNode | CeleryTestContainer],
node_cls: type[CeleryTestNode] = CeleryTestBroker,
) -> tuple[CeleryTestNode]:
return super()._set_nodes(*nodes, node_cls=node_cls)

@classmethod
def default_config(cls) -> dict:
return {
Expand Down
2 changes: 2 additions & 0 deletions src/pytest_celery/api/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def __init__(
self.ping = ping

def __len__(self) -> int:
"""The total number of nodes in the setup."""
return len(self._worker_cluster) + len(self._broker_cluster) + len(self._backend_cluster)

@property
Expand Down Expand Up @@ -88,6 +89,7 @@ def worker(self) -> CeleryTestWorker:

@classmethod
def name(cls) -> str:
"""The name of the setup."""
# TODO: Possibly not needed/required refactoring
return DEFAULT_WORKER_APP_NAME

Expand Down
19 changes: 4 additions & 15 deletions src/pytest_celery/api/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@


class CeleryTestWorker(CeleryTestNode):
"""CeleryTestWorker is specialized node type for handling celery worker
nodes. It is used to encapsulate a worker instance.
"""This is specialized node type for handling celery worker nodes. It is
used to encapsulate a worker instance.

Responsibility Scope:
Managing a celery worker.
Expand Down Expand Up @@ -98,24 +98,13 @@ def get_running_processes_info(


class CeleryWorkerCluster(CeleryTestCluster):
"""CeleryWorkerCluster is a specialized cluster type for handling celery
workers. It is used to define which worker instances are available for the
test.
"""This is a specialized cluster type for handling celery workers. It is
used to define which worker instances are available for the test.

Responsibility Scope:
Provude useful methods for managing a cluster of celery workers.
"""

def __init__(self, *workers: tuple[CeleryTestWorker | CeleryTestContainer]) -> None:
super().__init__(*workers)

def _set_nodes(
self,
*nodes: tuple[CeleryTestNode | CeleryTestContainer],
node_cls: type[CeleryTestNode] = CeleryTestWorker,
) -> tuple[CeleryTestNode]:
return super()._set_nodes(*nodes, node_cls=node_cls)

@property
def versions(self) -> set[str]:
"""Celery versions of all workers in this cluster."""
Expand Down
6 changes: 3 additions & 3 deletions src/pytest_celery/fixtures/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def celery_setup( # type: ignore
@pytest.fixture
def celery_setup_name(celery_setup_cls: type[CeleryTestSetup]) -> str: # type: ignore
"""Fixture interface to the API."""
yield celery_setup_cls.name()
return celery_setup_cls.name()


@pytest.fixture
Expand All @@ -64,7 +64,7 @@ def celery_setup_config(
celery_worker_cluster_config: dict,
) -> dict:
"""Fixture interface to the API."""
yield celery_setup_cls.config(
return celery_setup_cls.config(
celery_worker_cluster_config=celery_worker_cluster_config,
)

Expand All @@ -76,7 +76,7 @@ def celery_setup_app(
celery_setup_name: str,
) -> Celery:
"""Fixture interface to the API."""
yield celery_setup_cls.create_setup_app(
return celery_setup_cls.create_setup_app(
celery_setup_config=celery_setup_config,
celery_setup_app_name=celery_setup_name,
)
6 changes: 3 additions & 3 deletions src/pytest_celery/vendors/memcached/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@

@pytest.fixture
def default_memcached_backend_env(default_memcached_backend_cls: type[MemcachedContainer]) -> dict:
yield default_memcached_backend_cls.env()
return default_memcached_backend_cls.env()

Check warning on line 44 in src/pytest_celery/vendors/memcached/fixtures.py

View check run for this annotation

Codecov / codecov/patch

src/pytest_celery/vendors/memcached/fixtures.py#L44

Added line #L44 was not covered by tests


@pytest.fixture
def default_memcached_backend_image(default_memcached_backend_cls: type[MemcachedContainer]) -> str:
yield default_memcached_backend_cls.image()
return default_memcached_backend_cls.image()

Check warning on line 49 in src/pytest_celery/vendors/memcached/fixtures.py

View check run for this annotation

Codecov / codecov/patch

src/pytest_celery/vendors/memcached/fixtures.py#L49

Added line #L49 was not covered by tests


@pytest.fixture
def default_memcached_backend_ports(default_memcached_backend_cls: type[MemcachedContainer]) -> dict:
yield default_memcached_backend_cls.ports()
return default_memcached_backend_cls.ports()

Check warning on line 54 in src/pytest_celery/vendors/memcached/fixtures.py

View check run for this annotation

Codecov / codecov/patch

src/pytest_celery/vendors/memcached/fixtures.py#L54

Added line #L54 was not covered by tests
6 changes: 3 additions & 3 deletions src/pytest_celery/vendors/rabbitmq/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@

@pytest.fixture
def default_rabbitmq_broker_env(default_rabbitmq_broker_cls: type[RabbitMQContainer]) -> dict:
yield default_rabbitmq_broker_cls.env()
return default_rabbitmq_broker_cls.env()

Check warning on line 44 in src/pytest_celery/vendors/rabbitmq/fixtures.py

View check run for this annotation

Codecov / codecov/patch

src/pytest_celery/vendors/rabbitmq/fixtures.py#L44

Added line #L44 was not covered by tests


@pytest.fixture
def default_rabbitmq_broker_image(default_rabbitmq_broker_cls: type[RabbitMQContainer]) -> str:
yield default_rabbitmq_broker_cls.image()
return default_rabbitmq_broker_cls.image()

Check warning on line 49 in src/pytest_celery/vendors/rabbitmq/fixtures.py

View check run for this annotation

Codecov / codecov/patch

src/pytest_celery/vendors/rabbitmq/fixtures.py#L49

Added line #L49 was not covered by tests


@pytest.fixture
def default_rabbitmq_broker_ports(default_rabbitmq_broker_cls: type[RabbitMQContainer]) -> dict:
yield default_rabbitmq_broker_cls.ports()
return default_rabbitmq_broker_cls.ports()

Check warning on line 54 in src/pytest_celery/vendors/rabbitmq/fixtures.py

View check run for this annotation

Codecov / codecov/patch

src/pytest_celery/vendors/rabbitmq/fixtures.py#L54

Added line #L54 was not covered by tests
2 changes: 1 addition & 1 deletion src/pytest_celery/vendors/redis/backend/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ def teardown(self) -> None:
# and the Redis container. The AsyncResult object tries
# to release the connection but the Redis container has already
# exited. This causes a warning to be logged. To avoid this
# warning we force a garbage collection here.
# warning to our best effort we force a garbage collection here.
gc.collect(1)
super().teardown()
6 changes: 3 additions & 3 deletions src/pytest_celery/vendors/redis/backend/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@

@pytest.fixture
def default_redis_backend_env(default_redis_backend_cls: type[RedisContainer]) -> dict:
yield default_redis_backend_cls.env()
return default_redis_backend_cls.env()

Check warning on line 45 in src/pytest_celery/vendors/redis/backend/fixtures.py

View check run for this annotation

Codecov / codecov/patch

src/pytest_celery/vendors/redis/backend/fixtures.py#L45

Added line #L45 was not covered by tests


@pytest.fixture
def default_redis_backend_image(default_redis_backend_cls: type[RedisContainer]) -> str:
yield default_redis_backend_cls.image()
return default_redis_backend_cls.image()

Check warning on line 50 in src/pytest_celery/vendors/redis/backend/fixtures.py

View check run for this annotation

Codecov / codecov/patch

src/pytest_celery/vendors/redis/backend/fixtures.py#L50

Added line #L50 was not covered by tests


@pytest.fixture
def default_redis_backend_ports(default_redis_backend_cls: type[RedisContainer]) -> dict:
yield default_redis_backend_cls.ports()
return default_redis_backend_cls.ports()

Check warning on line 55 in src/pytest_celery/vendors/redis/backend/fixtures.py

View check run for this annotation

Codecov / codecov/patch

src/pytest_celery/vendors/redis/backend/fixtures.py#L55

Added line #L55 was not covered by tests
6 changes: 3 additions & 3 deletions src/pytest_celery/vendors/redis/broker/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@

@pytest.fixture
def default_redis_broker_env(default_redis_broker_cls: type[RedisContainer]) -> dict:
yield default_redis_broker_cls.env()
return default_redis_broker_cls.env()

Check warning on line 45 in src/pytest_celery/vendors/redis/broker/fixtures.py

View check run for this annotation

Codecov / codecov/patch

src/pytest_celery/vendors/redis/broker/fixtures.py#L45

Added line #L45 was not covered by tests


@pytest.fixture
def default_redis_broker_image(default_redis_broker_cls: type[RedisContainer]) -> str:
yield default_redis_broker_cls.image()
return default_redis_broker_cls.image()

Check warning on line 50 in src/pytest_celery/vendors/redis/broker/fixtures.py

View check run for this annotation

Codecov / codecov/patch

src/pytest_celery/vendors/redis/broker/fixtures.py#L50

Added line #L50 was not covered by tests


@pytest.fixture
def default_redis_broker_ports(default_redis_broker_cls: type[RedisContainer]) -> dict:
yield default_redis_broker_cls.ports()
return default_redis_broker_cls.ports()

Check warning on line 55 in src/pytest_celery/vendors/redis/broker/fixtures.py

View check run for this annotation

Codecov / codecov/patch

src/pytest_celery/vendors/redis/broker/fixtures.py#L55

Added line #L55 was not covered by tests
Loading