Opened 3 years ago

Closed 3 years ago

#32594 closed Bug (fixed)

Signal.disconnect() returns None when passing sender as string

Reported by: Hugo Cachitas Owned by: Hugo Cachitas
Component: Documentation Version: 4.0
Severity: Normal Keywords: signals
Cc: Alex Hill Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: yes UI/UX: no

Description

According to the documentation on Signal.disconnect(receiver=None, sender=None, dispatch_uid=None)

The method returns True if a receiver was disconnected and False if not.

I am having this issue where disconnect() returns None when I specify the sender as a string model reference, i.e., passing sender="myapp.MyModel" instead of sender=MyModel.

The sender may be lazily specified as a string as documented in django.db.models.signals.ModelSignal.
To achieve this functionality, a _lazy_method is implemented where we can find that the return value is not properly set.

if isinstance(sender, str):
    apps = apps or Options.default_apps
    apps.lazy_model_operation(partial_method, make_model_tuple(sender))
else:
    return partial_method(sender)

I already have a failing test at tests/signals/tests.py:LazyModelRefTests that could use your input.

@isolate_apps('signals', kwarg_name='apps')
def test_disconnect_return_value(self, apps):
    """
    Signal.disconnect() return value should be consistent if we
    use the Model or its string reference.
    """

    class Created(models.Model):
        pass

    def receiver(**kwargs):
        pass

    sender = Created
    signals.post_init.connect(receiver, sender=sender, apps=apps)
    self.assertTrue(
        signals.post_init.disconnect(receiver, sender=sender, apps=apps)
    )
    self.assertFalse(
        signals.post_init.disconnect(receiver, sender=sender, apps=apps)
    )

    sender = 'signals.Created'
    signals.post_init.connect(receiver, sender=sender, apps=apps)
    self.assertTrue(
        signals.post_init.disconnect(receiver, sender=sender, apps=apps)
    )
    self.assertFalse(
        signals.post_init.disconnect(receiver, sender=sender, apps=apps)
    )

Change History (8)

comment:1 by Hugo Cachitas, 3 years ago

PR https://github.com/django/django/pull/14187

The solution involved touching the app registry.
Tests are passing, but I am not entirely sure of the implications the new intermediary returned values may have.

comment:2 by Hugo Cachitas, 3 years ago

Has patch: set

comment:3 by Hugo Cachitas, 3 years ago

Keywords: signals added

comment:4 by Mariusz Felisiak, 3 years ago

Cc: Alex Hill added
Triage Stage: UnreviewedAccepted

comment:5 by Mariusz Felisiak <felisiak.mariusz@…>, 3 years ago

In f83214a3:

Refs #32594 -- Added Signal.disconnect() test with a model class.

Co-authored-by: Mariusz Felisiak <felisiak.mariusz@…>

comment:6 by Mariusz Felisiak, 3 years ago

Component: Database layer (models, ORM)Documentation
Needs documentation: set
Patch needs improvement: set

I think we should document the current behavior.

comment:7 by Mariusz Felisiak, 3 years ago

Needs documentation: unset
Patch needs improvement: unset
Triage Stage: AcceptedReady for checkin

comment:8 by Mariusz Felisiak <felisiak.mariusz@…>, 3 years ago

Resolution: fixed
Status: assignedclosed

In 8f6a7a0e:

Fixed #32594 -- Doc'd and tested that Signal.disconnect() with lazy references returns None.

Note: See TracTickets for help on using tickets.
Back to Top