Opened 6 years ago
Closed 6 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 , 6 years ago
| Description: | modified (diff) |
|---|
comment:2 by , 6 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 , 6 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 , 6 years ago
| Cc: | added |
|---|---|
| Has patch: | set |
| Patch needs improvement: | set |
comment:5 by , 6 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 , 6 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,BaseValidatoretc. (check basically all__eq__()methods).