jenkins-bot has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/core/+/832739 )
Change subject: [IMPR] raise TypeError in AliasesDict.normalizeData if data value is not a list ......................................................................
[IMPR] raise TypeError in AliasesDict.normalizeData if data value is not a list
Also update wikibase documentation
Bug: T318034 Change-Id: Iec54152b97efe2987f67c8274ba3f8fb7ebffc53 --- M pywikibot/page/_collections.py M pywikibot/page/_wikibase.py 2 files changed, 116 insertions(+), 55 deletions(-)
Approvals: Xqt: Looks good to me, approved jenkins-bot: Verified
diff --git a/pywikibot/page/_collections.py b/pywikibot/page/_collections.py index cbf9c6c..cd8af8e 100644 --- a/pywikibot/page/_collections.py +++ b/pywikibot/page/_collections.py @@ -149,8 +149,12 @@ def normalizeData(cls, data: dict) -> dict: """Helper function to expand data into the Wikibase API structure.
+ .. versionchanged:: 7.7 + raises TypeError *data* value is not a list. + :param data: Data to normalize :return: The dict with normalized data + :raises TypeError: data values must be a list """ norm_data = {} for key, values in data.items(): @@ -162,6 +166,11 @@ else: strings.append(value) norm_data[key] = strings + else: + raise TypeError( + "Unsupported value type {!r} for '{}'; list expected." + .format(type(values).__name__, values)) + return norm_data
def toJSON(self, diffto: Optional[dict] = None) -> dict: diff --git a/pywikibot/page/_wikibase.py b/pywikibot/page/_wikibase.py index 4193764..a01ac8e 100644 --- a/pywikibot/page/_wikibase.py +++ b/pywikibot/page/_wikibase.py @@ -18,10 +18,10 @@ from collections import OrderedDict, defaultdict from contextlib import suppress from itertools import chain -from typing import Any, Optional +from typing import Any, Optional, Union
import pywikibot -from pywikibot.backports import Dict +from pywikibot.backports import Dict, List from pywikibot.exceptions import ( APIError, EntityTypeUnknownError, @@ -63,6 +63,12 @@ )
+ALIASES_TYPE = Dict[Union[str, pywikibot.APISite], List[str]] +LANGUAGE_TYPE = Dict[Union[str, pywikibot.APISite], str] +SITELINK_TYPE = Union['pywikibot.page.BasePage', 'pywikibot.page.BaseLink', + Dict[str, str]] + + class WikibaseEntity:
""" @@ -272,12 +278,22 @@ data[key] = value return data
- def editEntity(self, data=None, **kwargs) -> None: - """ - Edit an entity using Wikibase wbeditentity API. + def editEntity( + self, + data: Union[LANGUAGE_TYPE, ALIASES_TYPE, SITELINK_TYPE, None] = None, + **kwargs + ) -> None: + """Edit an entity using Wikibase ``wbeditentity`` API. + + This function is wrapped around by: + - :meth:`editLabels` + - :meth:`editDescriptions` + - :meth:`editAliases` + - :meth:`ItemPage.setSitelinks` + + .. seealso:: :meth:`WikibasePage.editEntity`
:param data: Data to be saved - :type data: dict, or None to save the current content of the entity. """ if data is None: data = self.toJSON(diffto=getattr(self, '_content', None)) @@ -589,62 +605,96 @@ self.clear_cache()
@allow_asynchronous - def editEntity(self, data=None, **kwargs) -> None: - """ - Edit an entity using Wikibase wbeditentity API. + def editEntity( + self, + data: Union[LANGUAGE_TYPE, ALIASES_TYPE, SITELINK_TYPE, None] = None, + **kwargs: Any + ) -> None: + """Edit an entity using Wikibase ``wbeditentity`` API.
This function is wrapped around by: - - editLabels - - editDescriptions - - editAliases - - ItemPage.setSitelinks + - :meth:`editLabels` + - :meth:`editDescriptions` + - :meth:`editAliases` + - :meth:`ItemPage.setSitelinks` + + It supports *asynchronous* and *callback* keyword arguments. The + callback function is intended for use by bots that need to keep + track of which saves were successful. The minimal callback + function signature is:: + + def my_callback(page: WikibasePage, err: Optional[Exception]) -> Any: + + The arguments are: + + ``page`` + a :class:`WikibasePage` object + + ``err`` + an Exception instance, which will be None if the page was + saved successfully + + .. seealso:: :meth:`WikibaseEntity.editEntity`
:param data: Data to be saved - :type data: dict, or None to save the current content of the entity. - :keyword asynchronous: if True, launch a separate thread to edit - asynchronously - :type asynchronous: bool - :keyword callback: a callable object that will be called after the - entity has been updated. It must take two arguments: (1) a - WikibasePage object, and (2) an exception instance, which will be - None if the page was saved successfully. This is intended for use - by bots that need to keep track of which saves were successful. - :type callback: callable + :keyword bool asynchronous: if True, launch a separate thread to + edit asynchronously + :keyword Callable[[WikibasePage, Optional[Exception]], Any] callback: + a callable object that will be called after the entity has + been updated. It must take two arguments, see above. """ - # kept for the decorator + # kept for the decorator which provides the keyword arguments super().editEntity(data, **kwargs)
- def editLabels(self, labels, **kwargs) -> None: - """ - Edit entity labels. + def editLabels(self, labels: LANGUAGE_TYPE, **kwargs) -> None: + """Edit entity labels.
- Labels should be a dict, with the key - as a language or a site object. The - value should be the string to set it to. - You can set it to '' to remove the label. + *labels* should be a dict, with the key as a language or a site + object. The value should be the string to set it to. You can set + it to ``''`` to remove the label. + + Refer :meth:`editEntity` for *asynchronous* and *callback* usage. + + Usage: + + >>> repo = pywikibot.Site('wikidata:test') + >>> item = pywikibot.ItemPage(repo, 'Q68') + >>> item.editLabels({'en': 'Test123'}) # doctest: +SKIP """ data = {'labels': labels} self.editEntity(data, **kwargs)
- def editDescriptions(self, descriptions, **kwargs) -> None: - """ - Edit entity descriptions. + def editDescriptions(self, descriptions: LANGUAGE_TYPE, **kwargs) -> None: + """Edit entity descriptions.
- Descriptions should be a dict, with the key - as a language or a site object. The - value should be the string to set it to. - You can set it to '' to remove the description. + *descriptions* should be a dict, with the key as a language or a + site object. The value should be the string to set it to. You + can set it to ``''`` to remove the description. + + Refer :meth:`editEntity` for *asynchronous* and *callback* usage. + + Usage: + + >>> repo = pywikibot.Site('wikidata:test') + >>> item = pywikibot.ItemPage(repo, 'Q68') + >>> item.editDescriptions({'en': 'Pywikibot test'}) # doctest: +SKIP """ data = {'descriptions': descriptions} self.editEntity(data, **kwargs)
- def editAliases(self, aliases, **kwargs) -> None: - """ - Edit entity aliases. + def editAliases(self, aliases: ALIASES_TYPE, **kwargs) -> None: + """Edit entity aliases.
- Aliases should be a dict, with the key - as a language or a site object. The - value should be a list of strings. + *aliases* should be a dict, with the key as a language or a site + object. The value should be a list of strings. + + Refer :meth:`editEntity` for *asynchronous* and *callback* usage. + + Usage: + + >>> repo = pywikibot.Site('wikidata:test') + >>> item = pywikibot.ItemPage(repo, 'Q68') + >>> item.editAliases({'en': ['pwb test item']}) # doctest: +SKIP """ data = {'aliases': aliases} self.editEntity(data, **kwargs) @@ -1004,12 +1054,13 @@
return self.sitelinks[site].canonical_title()
- def setSitelink(self, sitelink, **kwargs) -> None: - """ - Set sitelinks. Calls setSitelinks(). + def setSitelink(self, sitelink: SITELINK_TYPE, **kwargs) -> None: + """Set sitelinks. Calls :meth:`setSitelinks`.
- A sitelink can be a Page object, a BaseLink object - or a {'site':dbname,'title':title} dictionary. + A *sitelink* can be a Page object, a BaseLink object or a + ``{'site': dbname, 'title': title}`` dictionary. + + Refer :meth:`editEntity` for *asynchronous* and *callback* usage. """ self.setSitelinks([sitelink], **kwargs)
@@ -1034,13 +1085,14 @@ data.append({'site': site, 'title': ''}) self.setSitelinks(data, **kwargs)
- def setSitelinks(self, sitelinks, **kwargs) -> None: - """ - Set sitelinks. + def setSitelinks(self, sitelinks: List[SITELINK_TYPE], **kwargs) -> None: + """Set sitelinks.
- Sitelinks should be a list. Each item in the - list can either be a Page object, a BaseLink object, or a dict - with a value for 'site' and 'title'. + *sitelinks* should be a list. Each item in the list can either + be a Page object, a BaseLink object, or a dict with key for + 'site' and a value for 'title'. + + Refer :meth:`editEntity` for *asynchronous* and *callback* usage. """ data = {'sitelinks': sitelinks} self.editEntity(data, **kwargs)