Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix missing observer registration when inserting into nested list #1761

Merged
merged 2 commits into from
Dec 7, 2023

Conversation

rhaschke
Copy link
Contributor

@rhaschke rhaschke commented Dec 4, 2023

When using nested list: ta.List(trait=ta.List(trait=ta.Int, items=False), items=False), inserting an inner list failed to register observers on that list. Appending and extending works as expected. The culprit was that the item validation was performed twice, thus probably registering the observers on the wrong object.

Sample code illustrating the behavior:

import traits.api as ta

class Provider(ta.HasTraits):
    l = ta.List(trait=ta.List(trait=ta.Int, items=False), items=False)

    def __init__(self, **kwargs) -> None:
        super().__init__(**kwargs)
        self.observe(self.assigned, "l")
        self.observe(self.outer_changed, "l:items")
        self.observe(self.inner_changed, "l:items:items")

    def assigned(self, event):
        print("assigned", event)

    def outer_changed(self, event):
        print("outer changed", event)

    def inner_changed(self, event):
        print("inner changed", event)


p = Provider()
p.l = [[1, 2], [3], [4]]  # assign list

print()
p.l[0] = [0]  # assign outer item
del p.l[1]  # delete outer item
p.l.append([7, 8])  # append outer item
p.l.insert(-1, [5, 6])  # insert outer item (next-to-last)

# Operating on appended outer item invokes the (inner) observer callbacks:
print()
p.l[-1].append(9)
p.l[-1].extend([10, 11])
p.l[-1][-2] = 20

print()
print(p.l)
print("\nOperating on inserted outer item doesn't invoke the observer callbacks:")
p.l[-2].append(7)
p.l[-2].extend([8, 9, 10])
p.l[-2][-1] = 30
print(p.l)

Expected vs. actual output (without this PR, the green part is missing):

 assigned TraitChangeEvent(object=<__main__.Provider object at 0x7f8f5988e160>, name='l', old=[], new=[[1, 2], [3], [4]])

 outer changed ListChangeEvent(object=[[0], [3], [4]], index=0, removed=[[1, 2]], added=[[0]])
 outer changed ListChangeEvent(object=[[0], [4]], index=1, removed=[[3]], added=[])
 outer changed ListChangeEvent(object=[[0], [4], [7, 8]], index=2, removed=[], added=[[7, 8]])
 outer changed ListChangeEvent(object=[[0], [4], [5, 6], [7, 8]], index=2, removed=[], added=[[5, 6]])

 inner changed ListChangeEvent(object=[7, 8, 9], index=2, removed=[], added=[9])
 inner changed ListChangeEvent(object=[7, 8, 9, 10, 11], index=3, removed=[], added=[10, 11])
 inner changed ListChangeEvent(object=[7, 8, 9, 20, 11], index=3, removed=[10], added=[20])

 [[0], [4], [5, 6], [7, 8, 9, 20, 11]]

 Operating on inserted outer item doesn't invoke the observer callbacks:
+inner changed ListChangeEvent(object=[5, 6, 7], index=2, removed=[], added=[7])
+inner changed ListChangeEvent(object=[5, 6, 7, 8, 9, 10], index=3, removed=[], added=[8, 9, 10])
+inner changed ListChangeEvent(object=[5, 6, 7, 8, 9, 30], index=5, removed=[10], added=[30])
 [[0], [4], [5, 6, 7, 8, 9, 30], [7, 8, 9, 20, 11]]

@mdickinson
Copy link
Member

Nice catch! Thank you for the report, analysis and fix.

I've pushed a regression test to this branch in commit 6c6afbf.

@mdickinson mdickinson merged commit 2b95e24 into enthought:main Dec 7, 2023
32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants