Opened 5 years ago
Closed 5 years ago
#30651 closed Cleanup/optimization (fixed)
__eq__ should return NotImplemented when equality cannot be checked.
Reported by: | Elizabeth Uselton | Owned by: | Elizabeth Uselton |
---|---|---|---|
Component: | Core (Other) | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | Elizabeth Uselton | Triage Stage: | Ready for checkin |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
Model.__eq__
never returns NotImplemented if it encounters an object it doesn't know how to compare against. Instead, if the object it is comparing to is not a Django Model, it automatically returns False.
https://github.com/django/django/blob/master/django/db/models/base.py#L526
According to the Python 3 data model reference, a __eq__
should return NotImplemented
https://docs.python.org/3/reference/datamodel.html#object.__eq__
If a.__eq__(b)
returns NotImplemented, then b.__eq__(a)
will be tried. If both return NotImplemented, then an is check is performed, and if that fails it returns False.
This may seem like a relatively innocuous difference, but it can cause some nasty bugs. The most obvious is that for testing,
<A Django Model> == mock.ANY returns False, since by not returning NotImplemented it never even looks at the overridden __eq__
on ANY.
Change History (9)
comment:1 by , 5 years ago
Description: | modified (diff) |
---|
comment:2 by , 5 years ago
Component: | Database layer (models, ORM) → Core (Other) |
---|---|
Summary: | Model.__eq__ never returns NotImplemented → __eq__ should return NotImplemented when equality cannot be checked. |
Triage Stage: | Unreviewed → Accepted |
Type: | Bug → Cleanup/optimization |
Version: | 2.2 → master |
comment:3 by , 5 years ago
Great! I'll be sure to do those too. I'm very excited to be making my first Django contribution.
comment:4 by , 5 years ago
Cc: | added |
---|---|
Has patch: | set |
Patch needs improvement: | set |
comment:5 by , 5 years ago
I've added a PR that only has a few of the classes changed, since at a glance there are like 70 classes with __eq__
overwritten, and I'd prefer to get the high value Model
change merged (people seem much more likely to compare Model
in tests than other classes) and get feedback on the tests I added (whether they're in the right place, and whether I'd need to add them for every False
to NotImplemented
change).
comment:6 by , 5 years ago
Patch needs improvement: | unset |
---|---|
Triage Stage: | Accepted → Ready for checkin |
Thanks for this ticket, sounds reasonable. We should be consistent and make this change also for other classes e.g.
Migration
,CheckConstraint
,UniqueConstraint
,BaseValidator
etc. (check basically all__eq__()
methods).