jenkins-bot has submitted this change. ( 
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1337955?usp=email )

Change subject: wikibase: Keep sub-entity indexes aligned after mutations
......................................................................

wikibase: Keep sub-entity indexes aligned after mutations

Deleting or inserting a form or sense leaves shifted entries mapped to
old list positions, causing ID lookups to return the wrong entity or
raise IndexError. Reindex the affected entries after each mutation while
keeping appends constant-time and normalizing Python sequence indices.

Bug: T189321
Change-Id: I1c4b723979e2b4b1b1fa9319f0a3b4fa560da66e
---
M pywikibot/page/_collections.py
M tests/collections_tests.py
2 files changed, 41 insertions(+), 2 deletions(-)

Approvals:
  Xqt: Looks good to me, approved
  jenkins-bot: Verified




diff --git a/pywikibot/page/_collections.py b/pywikibot/page/_collections.py
index 2e6ac13..7566a17 100644
--- a/pywikibot/page/_collections.py
+++ b/pywikibot/page/_collections.py
@@ -6,6 +6,7 @@
 """Structures holding data for Wikibase entities."""
 from __future__ import annotations

+import operator
 import reprlib
 from collections import defaultdict
 from collections.abc import MutableMapping, MutableSequence
@@ -533,9 +534,14 @@
                 index = self._by_key[index]
             except KeyError as e:
                 raise ValueError(f'No entity with id {index} was found') from e
+        index = operator.index(index)
         obj = self._data[index]
+        # Reindex from the nonnegative position after validating the index.
+        if index < 0:
+            index += len(self._data)
         del self._data[index]
         del self._by_key[obj.id]
+        self._reindex(index)

     def __len__(self) -> int:
         return len(self._data)
@@ -543,8 +549,17 @@
     def insert(self, index, obj) -> None:
         """Insert a sub-entity to the collection."""
         self._validate_isinstance(obj)
+        index = operator.index(index)
         self._data.insert(index, obj)
-        self._by_key[obj.id] = index
+        # Normalize the insertion index as list.insert does.
+        length = len(self._data) - 1
+        index = max(0, length + index) if index < 0 else min(index, length)
+        self._reindex(index)
+
+    def _reindex(self, start: int) -> None:
+        """Update ID positions for items shifted by a mutation."""
+        for index in range(start, len(self._data)):
+            self._by_key[self._data[index].id] = index

     @classmethod
     def new_empty(cls, repo):
diff --git a/tests/collections_tests.py b/tests/collections_tests.py
index f8db8af..a1b3470 100755
--- a/tests/collections_tests.py
+++ b/tests/collections_tests.py
@@ -16,7 +16,31 @@
     LanguageDict,
     SiteLinkCollection,
 )
-from tests.aspects import WikidataTestCase
+from pywikibot.page._wikibase import LexemeSense, LexemeSenseCollection
+from tests.aspects import TestCase, WikidataTestCase
+
+
+class TestSubEntityCollection(TestCase):
+
+    """Test sub-entity collection indexing."""
+
+    net = False
+
+    def test_id_index_after_mutation(self) -> None:
+        """Keep ID lookup consistent after deletion and insertion."""
+        first = LexemeSense(None, 'L1-S1')
+        second = LexemeSense(None, 'L1-S2')
+        collection = LexemeSenseCollection(None, [first, second])
+
+        del collection[first.id]
+        self.assertIs(collection[second.id], second)
+
+        collection.insert(0, first)
+        self.assertIs(collection[first.id], first)
+        self.assertIs(collection[second.id], second)
+
+        del collection[-len(collection)]
+        self.assertIs(collection[second.id], second)


 class DataCollectionTestCase(WikidataTestCase):

--
To view, visit 
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1337955?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.wikimedia.org/r/settings?usp=email

Gerrit-MessageType: merged
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Change-Id: I1c4b723979e2b4b1b1fa9319f0a3b4fa560da66e
Gerrit-Change-Number: 1337955
Gerrit-PatchSet: 4
Gerrit-Owner: Mahveotm <[email protected]>
Gerrit-Reviewer: Matěj Suchánek <[email protected]>
Gerrit-Reviewer: Xqt <[email protected]>
Gerrit-Reviewer: jenkins-bot
_______________________________________________
Pywikibot-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to