factory_boy icon indicating copy to clipboard operation
factory_boy copied to clipboard

Add functionality to mute specific receivers of signals (not just all signals)

Open khpeek opened this issue 7 years ago • 5 comments

The problem

As described at https://factoryboy.readthedocs.io/en/latest/orms.html#disabling-signals, it is possible to mute all signals of a certain type using the factory.django.mute_signals decorator. In my use case, hwoever, I have a specific receiver of post_save signals which calls an external API, and I would like to mute only this receiver (and not all post_save signals).

Proposed solution

We could write a mute_receivers decorator/context manager along the lines of mute_signals which does this.

khpeek avatar Aug 15 '18 22:08 khpeek

Hi,

I understand the interest of that suggestion; however, I feel that the mute_signals helper is already quite complex, and would rather avoid adding more of those helpers in the codebase.

Do you have some example of what such a mute_receivers decorator/context manager would look like?

rbarrois avatar Aug 19 '18 11:08 rbarrois

I had this problem too and solved it by overriding _create factory method:

@receiver(post_save, sender=MyModel)
def signal_handler(sender, instance, **kwargs):
    ...

class MyModelFactory(factory.DjangoModelFactory):

    class Meta:
        model = MyModel

    @classmethod
    def _create(cls, *args, **kwargs):
        post_save.disconnect(signal_handler, sender=MyModel)
        try:
            instance = super()._create(*args, **kwargs)
        finally:
            post_save.connect(signal_handler, sender=MyModel)
        return instance

xuhcc avatar Nov 06 '18 16:11 xuhcc

Is there a way to utilize the above _create overwrite but at the same time using Param? Can I access a Param in _create?

khmob avatar Oct 12 '23 09:10 khmob

I'd to implement a context manager to mute a specific receiver and restore it after the block of code is executed.

from contextlib import contextmanager
from django.dispatch import Signal
from typing import Any, Type


@contextmanager
def muted_receiver(
    signal: Type[Signal],
    receiver: callable,
    sender: Any,
    dispatch_uid=None,
    weak: bool = True,
    failsafe=False,
):
    disconnected = signal.disconnect(
        receiver=receiver,
        sender=sender,
        dispatch_uid=dispatch_uid,
    )
    if not disconnected and not failsafe:
        raise ValueError(f"Couldn't disconnect {signal}.")

    yield

    # restore the signal
    signal.connect(
        receiver=receiver,
        sender=sender,
        weak=weak,
        dispatch_uid=dispatch_uid,
    )
    assert receiver in signal._live_receivers(sender=sender)

premchalmeti avatar Mar 14 '24 15:03 premchalmeti