Skip to content

Fix mangled __slots__ attribute lost for subclass instances (#506)#609

Open
uttam12331 wants to merge 1 commit into
qlustered:masterfrom
uttam12331:fix/slots-mangled-subclass-506
Open

Fix mangled __slots__ attribute lost for subclass instances (#506)#609
uttam12331 wants to merge 1 commit into
qlustered:masterfrom
uttam12331:fix/slots-mangled-subclass-506

Conversation

@uttam12331

Copy link
Copy Markdown

Summary

Closes #506.

_dict_from_slots unmangled each slot name using the concrete instance type:

def unmangle(attribute):
    if attribute.startswith('__') and attribute != '__weakref__':
        return '_{type}{attribute}'.format(type=type(object).__name__, attribute=attribute)
    ...

A mangled slot such as __id is stored under the name of the class that declared it (e.g. _Parent__id). For a subclass instance, type(object).__name__ is the subclass, so the lookup used the wrong name (_Child__id), hasattr(...) returned False, and the parent-defined slot was silently dropped:

class A:
    __slots__ = ('__id',)
    def __init__(self, id): self.__id = id

class B(A):
    pass

DeepDiff._dict_from_slots(A(5))   # {'__id': 5}
DeepDiff._dict_from_slots(B(5))   # {}   <-- parent slot lost

This is the root cause behind the unprocessed / dropped-attribute behavior reported when comparing an instance of a class with an instance of its subclass where a slot uses a mangled (dunder-prefixed) attribute — e.g. bson.ObjectId vs a subclass of it.

Fix

Pair each slot with the class in the MRO that declared it, and unmangle using that class's name. Non-mangled slots are unaffected.

Tests

Added test_dict_from_slots_of_subclass_with_mangled_attribute, asserting the parent-declared mangled slot is collected for both the base and the subclass instance. It fails on master ({} for the subclass) and passes with this change. The existing slots tests still pass; flake8 introduces no new warnings; added a CHANGELOG entry.

`_dict_from_slots` unmangled each slot name using the concrete instance
type (`type(object).__name__`). A mangled slot such as `__id` is stored under
the name of the class that *declared* it (e.g. `_Parent__id`), so for a
subclass instance the lookup used the wrong name (`_Child__id`), `hasattr`
returned False, and the parent-defined slot was silently dropped.

Pair each slot with the class in the MRO that declared it and unmangle using
that class's name.

Closes qlustered#506
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Issue with subclasses, slots and mangled attributes

1 participant