jenkins-bot has submitted this change. (
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1358296?usp=email )
Change subject: lexeme: Serialize term removals and empty feature sets
......................................................................
lexeme: Serialize term removals and empty feature sets
Use explicit removal markers for representations and glosses, whose API
validation rejects empty strings. Compare grammatical features as sets
and emit an empty list when clearing them.
Initialize new subentities before callers populate them so subsequent
lazy attribute access cannot erase fields during serialization.
Bug: T189321
Change-Id: I53d4d6834fe91db5a89479e761c8da58a389e08a
---
M pywikibot/page/_wikibase.py
M tests/wikibase_tests.py
2 files changed, 101 insertions(+), 4 deletions(-)
Approvals:
Xqt: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/page/_wikibase.py b/pywikibot/page/_wikibase.py
index 9bd9c59..ae4c2c5 100644
--- a/pywikibot/page/_wikibase.py
+++ b/pywikibot/page/_wikibase.py
@@ -2540,6 +2540,8 @@
"""Initializer."""
super().__init__(repo, id_)
self._on_lexeme = None
+ if self.id == '-1':
+ self._initialize_empty()
@classmethod
def fromJSON(cls, repo, data):
@@ -2548,7 +2550,27 @@
return new
def toJSON(self, diffto=None) -> dict:
+ """Serialize fields and explicit representation or gloss removals.
+
+ :param diffto: Original child data, or None for all current fields
+ """
+ fields = tuple(self.DATA_ATTRIBUTES)
+ if self.entity_type == 'form':
+ fields += ('grammaticalFeatures',)
+ if self.id != '-1' and any(key not in self.__dict__ for key in fields):
+ # Loading missing fields must not overwrite local assignments.
+ pending = {key: self.__dict__[key] for key in fields
+ if key in self.__dict__}
+ self.get()
+ for key, value in pending.items():
+ setattr(self, key, value)
data = super().toJSON(diffto)
+ if diffto:
+ for key in ('representations', 'glosses'):
+ for language in diffto.get(key, {}):
+ if language not in getattr(self, key, {}):
+ data.setdefault(key, {})[language] = {
+ 'language': language, 'remove': ''}
if self.id != '-1':
data['id'] = self.id
return data
@@ -2615,14 +2637,27 @@
'claims': ClaimCollection,
}
+ def __getattr__(self, name):
+ if name == 'grammaticalFeatures':
+ return self.get()[name]
+ return super().__getattr__(name)
+
+ def _initialize_empty(self) -> None:
+ super()._initialize_empty()
+ self.grammaticalFeatures = set()
+
def toJSON(self, diffto: dict | None = None) -> dict:
- """Create dict suitable for the MediaWiki API."""
+ """Serialize form changes, including an empty list to clear features.
+
+ :param diffto: Original form data, or None for all current fields
+ """
data = super().toJSON(diffto=diffto)
key = 'grammaticalFeatures'
- if getattr(self, key, None):
- # could also avoid if no change wrt. diffto
- data[key] = [value.getID() for value in self.grammaticalFeatures]
+ features = sorted(value.getID() for value in self.grammaticalFeatures)
+ if diffto is None or key not in diffto \
+ or set(features) != set(diffto[key]):
+ data[key] = features
return data
diff --git a/tests/wikibase_tests.py b/tests/wikibase_tests.py
index 2eb202e..a02051a 100755
--- a/tests/wikibase_tests.py
+++ b/tests/wikibase_tests.py
@@ -27,6 +27,7 @@
ItemPage,
LexemeForm,
LexemePage,
+ LexemeSense,
MediaInfo,
PropertyPage,
WikibasePage,
@@ -1307,6 +1308,67 @@
self.assertNotIn('_content', item.__dict__)
+class TestLexemeSubEntityJSON(WikidataTestCase):
+
+ """Test API serialization of forms and senses."""
+
+ dry = True
+
+ def test_term_removal(self) -> None:
+ """Use removal markers only for deleted representations or glosses."""
+ for cls, entity_id, key in (
+ (LexemeForm, 'L1-F1', 'representations'),
+ (LexemeSense, 'L1-S1', 'glosses'),
+ ):
+ with self.subTest(cls=cls):
+ baseline = {
+ 'id': entity_id, 'grammaticalFeatures': [],
+ key: {'en': {'language': 'en', 'value': 'colour'},
+ 'fr': {'language': 'fr', 'value': 'couleur'}},
+ 'claims': {},
+ }
+ entity = cls.fromJSON(self.get_repo(), baseline)
+ self.assertEqual(entity.toJSON(diffto=baseline),
+ {'id': entity_id})
+ del getattr(entity, key)['fr']
+ getattr(entity, key)['en'] = 'changed'
+ self.assertEqual(entity.toJSON(diffto=baseline), {
+ 'id': entity_id,
+ key: {'en': {'language': 'en', 'value': 'changed'},
+ 'fr': {'language': 'fr', 'remove': ''}},
+ })
+ self.assertIn('fr', baseline[key])
+
+ def test_features_and_new_form(self) -> None:
+ """Preserve new form fields and serialize feature clearing."""
+ form = LexemeForm(self.get_repo())
+ form.representations['en'] = 'colour'
+ expected = {
+ 'representations': {'en': {'language': 'en', 'value': 'colour'}},
+ 'grammaticalFeatures': [],
+ }
+ self.assertEqual(form.toJSON(), expected)
+ self.assertEqual(form.toJSON(), expected)
+ form.grammaticalFeatures.update(
+ ItemPage(self.get_repo(), qid) for qid in ('Q2', 'Q1'))
+ baseline = form.toJSON()
+ self.assertEqual(baseline['grammaticalFeatures'], ['Q1', 'Q2'])
+ baseline['grammaticalFeatures'].reverse()
+ self.assertEqual(form.toJSON(diffto=baseline), {})
+ form.grammaticalFeatures.clear()
+ self.assertEqual(form.toJSON(diffto=baseline),
+ {'grammaticalFeatures': []})
+ baseline['id'] = 'L1-F1'
+ for assign in (False, True):
+ saved = LexemeForm.fromJSON(self.get_repo(), baseline)
+ if assign:
+ saved.grammaticalFeatures = set()
+ else:
+ saved.grammaticalFeatures.clear()
+ self.assertEqual(saved.toJSON(diffto=baseline),
+ {'id': 'L1-F1', 'grammaticalFeatures': []})
+
+
class TestMediaInfoNormalizeData(TestCase):
"""Test cases for MediaInfo._normalizeData and MediaInfo.toJSON."""
--
To view, visit
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1358296?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: I53d4d6834fe91db5a89479e761c8da58a389e08a
Gerrit-Change-Number: 1358296
Gerrit-PatchSet: 5
Gerrit-Owner: Mahveotm <[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]