jenkins-bot has submitted this change. (
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/803388?usp=email )
Change subject: [feature] Enable all prop informations with usercontribs and
User.contribs
......................................................................
[feature] Enable all prop informations with usercontribs and User.contribs
see API:Usercontribs
- add a new 'prop' parameter to APISite.usercontribs() to enable all prop;
they can be given as iterable or as a string with '|' as delimiter.
This parameter is keyword only.
- add formatversion parameter to this method to enable the new format version
- enable 'size' prop by default in usercontribs
- rename 'top_only' parameter to 'top'
- drop deprecated uctoponly api parameter for 'top' and use ucshow
instead; 'top' is None by default which gives the same result as
uctoponly = False
- add a new Contribution mapping which uses the collection.DataRecord
- add a new method 'User.contribs' which iterates Contribution items
- add a new UnexpectedAPIDataError for API response validation
- use user.contribs in UserContributionsGenerator
- update documentation
Bug: T308961
Change-Id: Id80df6dd27eec79cb7dfdca326400659761995a8
---
M pywikibot/exceptions.py
M pywikibot/page/__init__.py
M pywikibot/page/_user.py
M pywikibot/pagegenerators/_generators.py
M pywikibot/site/_generators.py
5 files changed, 177 insertions(+), 38 deletions(-)
Approvals:
Xqt: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/exceptions.py b/pywikibot/exceptions.py
index 3ff630c..9fdb2b4 100644
--- a/pywikibot/exceptions.py
+++ b/pywikibot/exceptions.py
@@ -57,9 +57,10 @@
+-- ApiTimeoutError
| +-- MaxlagTimeoutError
+-- TranslationError
+ +-- UnexpectedAPIDataError (ValueError)
+ +-- UnknownExtensionError (NotImplementedError)
+-- UserRightsError
| +-- HiddenKeyError (KeyError)
- +-- UnknownExtensionError (NotImplementedError)
+-- VersionParseError
+-- WikiBaseError
+-- CoordinateGlobeUnknownError (NotImplementedError)
@@ -84,6 +85,7 @@
- SectionError: The section specified by # does not exist
- TranslationError: no language translation found, i18n/l10n message
not available
+ - UnexpectedAPIDataError: API data contains unexpected fields or values
- UnknownExtensionError: Extension is not defined for this site
- UserRightsError: insufficient rights for requested action
- VersionParseError: failed to parse version information
@@ -177,6 +179,9 @@
.. version-changed:: 8.1
``Server414Error`` class is deprecated; use :class:`Client414Error`
instead.
+
+.. version-changed:: 11.6
+ :exc:`UnexpectedAPIDataError` was added.
"""
from __future__ import annotations
@@ -450,6 +455,14 @@
"""Family is not registered."""
+class UnexpectedAPIDataError(Error, ValueError):
+
+ """Raised when API data contains unexpected fields or values.
+
+ .. version-added:: 11.6
+ """
+
+
class UnknownExtensionError(Error, NotImplementedError):
"""Extension is not defined."""
diff --git a/pywikibot/page/__init__.py b/pywikibot/page/__init__.py
index a20225a..5115ca0 100644
--- a/pywikibot/page/__init__.py
+++ b/pywikibot/page/__init__.py
@@ -14,7 +14,7 @@
from pywikibot.page._links import BaseLink, Link, SiteLink, html2unicode
from pywikibot.page._page import Page
from pywikibot.page._revision import Revision
-from pywikibot.page._user import User
+from pywikibot.page._user import Contribution, User
from pywikibot.page._wikibase import (
Claim,
ItemPage,
@@ -50,6 +50,7 @@
'FileInfo',
'WikibaseEntity',
'MediaInfo',
+ 'Contribution',
'Revision',
'html2unicode',
)
diff --git a/pywikibot/page/_user.py b/pywikibot/page/_user.py
index a2518a5..6f9d486 100644
--- a/pywikibot/page/_user.py
+++ b/pywikibot/page/_user.py
@@ -15,15 +15,37 @@
AutoblockUserError,
NoRenameTargetError,
NotEmailableError,
+ UnexpectedAPIDataError,
UserRightsError,
)
from pywikibot.page._links import Link
from pywikibot.page._page import Page
from pywikibot.page._revision import Revision
+from pywikibot.time import Timestamp
from pywikibot.tools import is_ip_address, is_ip_network
+from pywikibot.tools.collections import DataRecord
-__all__ = ('User', )
+__all__ = ('Contribution', 'User')
+
+
+class Contribution(DataRecord):
+
+ """A structure holding information about a user contribution.
+
+ In addition to the API result, it provides the ``site`` and ``page``
+ for the current Site and Page object of the contribution.
+
+ .. version-added:: 11.6
+ """
+
+ @staticmethod
+ def normalize(data: dict[str, Any]) -> None:
+ """Upcast dictionary values."""
+ if 'timestamp' in data:
+ data['timestamp'] = Timestamp.fromISOformat(data['timestamp'])
+ if 'title' in data:
+ data['page'] = Page(data['site'], data['title'], data['ns'])
class User(Page):
@@ -488,7 +510,8 @@
Each tuple is composed of a pywikibot.Page object, the revision
id, the edit timestamp and the comment. Pages returned are not
- guaranteed to be unique.
+ guaranteed to be unique. Use :meth:`contribs` if you need
+ additional revision information.
Example:
@@ -506,8 +529,18 @@
>>> contrib[3]
''
- .. seealso:: :meth:`Site.usercontribs()
- <pywikibot.site._generators.GeneratorsMixin.usercontribs>`
+ .. version-changed:: 3.0.20200609
+ The *showMinor* parameter was renamed to *minor*.
+ .. version-changed:: 11.6
+ The keyword *top_only* was renamed to *top*. This parameter
+ now accepts ``None`` to iterate both latest and non-latest
+ contributions. ``False`` now iterates only non-latest
+ contributions. Default is ``None``.
+ .. seealso::
+ - :meth:`contribs`
+ - :meth:`Site.usercontribs()
+ <pywikibot.site._generators.GeneratorsMixin.usercontribs>`
+ - :api:`Usercontribs`
:param total: Limit result to this number of pages
:keyword start: Iterate contributions starting at this Timestamp
@@ -517,19 +550,79 @@
:type namespaces: Iterable of str or Namespace key,
or a single instance of those types. May be a '|' separated
list of namespace identifiers.
- :keyword showMinor: If True, iterate only minor edits; if False and
+ :keyword minor: If True, iterate only minor edits; if False and
not None, iterate only non-minor edits (default: iterate both)
- :keyword top_only: If True, iterate only edits which are the latest
- revision (default: False)
+ :param top: if ``True``, iterate only edits which are the latest
+ revision; if ``False``, do not iterate last revision edits;
+ ``None`` to iterate both (default: ``None``)
:return: Tuple of pywikibot.Page, revid, pywikibot.Timestamp, comment
"""
+ prop = ('comment', 'ids', 'timestamp', 'title')
+ for c in self.contribs(total=total, prop=prop, **kwargs):
+ yield c.page, c.revid, c.timestamp, c.comment # type:
ignore[attr-defined] # noqa: E501
+
+ def contribs(self, **kwargs) -> Generator[Contribution]:
+ """Yield :class:`Contribution` items describing this user edits.
+
+ Refer :meth:`APISite.usercontribs()
+ <pywikibot.site._generators.GeneratorsMixin.usercontribs>`
+ method for for keyword parameters except of `user`and `userprefix`.
+
+ .. version-added:: 11.6
+
+ Usage:
+
+ >>> site = pywikibot.Site('wikipedia:test')
+ >>> user = pywikibot.User(site, 'Pywikibot-oauth')
+ >>> prop = ['title', 'tags', 'flags']
+ >>> uc = list(user.contribs(total=8, reverse=True, prop=prop))
+ >>> contrib = uc[-1]
+ >>> contrib.user == user
+ True
+ >>> str(contrib.site)
+ 'wikipedia:test'
+ >>> contrib.title
+ 'User:Pywikibot-oauth/edit test'
+ >>> contrib.page.title() == contrib.title
+ True
+ >>> contrib.top
+ False
+ >>> contrib.tags # attribute access
+ ['OAuth CID: 281']
+ >>> contrib['tags'] # key access
+ ['OAuth CID: 281']
+
+ .. seealso::
+ - :meth:`contributions`
+ - :meth:`Site.usercontribs()
+ <pywikibot.site._generators.GeneratorsMixin.usercontribs>`
+ - :api:`Usercontribs`
+
+ :keyword start: Iterate contributions starting at this Timestamp
+ :keyword end: Iterate contributions ending at this Timestamp
+ :keyword reverse: Iterate oldest contributions first (default:
+ newest)
+ :keyword namespaces: Only iterate pages in these namespaces
+ :keyword minor: If ``True``, iterate only minor edits; if ``False``
+ and not ``None``, iterate only non-minor edits (default:
+ iterate both)
+ :keyword total: Limit result to this number of pages
+ :keyword top: if ``True``, iterate only edits which are the latest
+ revision; if ``False``, do not iterate last revision edits;
+ ``None`` to iterate both (default: ``None``)
+ :keyword prop: Include additional pieces of information. Refer
+ :api:`Usercontribs` for the elements and the default setting.
+ :return: For each entry return a tuple of Page, Revision
+ """
for contrib in self.site.usercontribs(
- user=self.username, total=total, **kwargs):
- ts = pywikibot.Timestamp.fromISOformat(contrib['timestamp'])
- yield (Page(self.site, contrib['title'], contrib['ns']),
- contrib['revid'],
- ts,
- contrib.get('comment'))
+ user=self.username, formatversion=2, **kwargs):
+ if {'site', 'page'} & contrib.keys() or 'user' not in contrib:
+ raise UnexpectedAPIDataError(
+ "API response contains reserved keys 'site' or 'page' "
+ "or 'user' is missing"
+ )
+ contrib.pop('user')
+ yield Contribution(site=self.site, user=self, **contrib)
@property
def first_edit(
diff --git a/pywikibot/pagegenerators/_generators.py
b/pywikibot/pagegenerators/_generators.py
index a53d39c..456b262 100644
--- a/pywikibot/pagegenerators/_generators.py
+++ b/pywikibot/pagegenerators/_generators.py
@@ -546,8 +546,10 @@
pywikibot.warning(
f'User "{user.username}" does not exist on site "{site}".')
- gen = (contrib[0] for contrib in user.contributions(
- namespaces=namespaces, total=total))
+ gen = (
+ contrib.page for contrib in user.contribs(namespaces=namespaces,
+ total=total, prop='title')
+ )
if _filter_unique:
return _filter_unique(gen)
return gen
diff --git a/pywikibot/site/_generators.py b/pywikibot/site/_generators.py
index c4b4851..44e2b61 100644
--- a/pywikibot/site/_generators.py
+++ b/pywikibot/site/_generators.py
@@ -28,11 +28,18 @@
)
from pywikibot.site._decorators import need_right
from pywikibot.site._namespace import NamespaceArgType
-from pywikibot.tools import deprecate_arg, deprecated_signature, is_ip_address
+from pywikibot.tools import (
+ deprecate_arg,
+ deprecated_args,
+ deprecated_signature,
+ is_ip_address,
+)
from pywikibot.tools.itertools import filter_unique
if typing.TYPE_CHECKING:
+ from datetime import datetime
+
from pywikibot.data.api import ParamInfo, Request
from pywikibot.site._namespace import NamespacesDict, SingleNamespaceType
from pywikibot.site._tokenwallet import TokenWallet
@@ -1630,17 +1637,21 @@
total=total, g_content=content,
parameters=parameters)
+ @deprecated_args(top_only='top') # since 11.6.0
def usercontribs(
self,
user: str | None = None,
userprefix: str | None = None,
- start=None,
- end=None,
+ start: pywikibot.time.Timestamp | datetime | str | None = None,
+ end: pywikibot.time.Timestamp | datetime | str | None = None,
reverse: bool = False,
namespaces: NamespaceArgType = None,
minor: bool | None = None,
total: int | None = None,
- top_only: bool = False,
+ top: bool | None = None,
+ *,
+ prop: Iterable[str] | str | None = None,
+ formatversion: int = 1
) -> Iterable[dict[str, Any]]:
"""Iterate contributions by a particular user.
@@ -1650,20 +1661,36 @@
- :api:`Usercontribs`
- :meth:`pywikibot.User.contributions`
+ .. version-changed:: 3.0.20200609
+ The *showMinor* parameter was renamed to *minor*.
+ .. version-changed:: 11.6
+ The *prop* and *formatversion* parameter were added. The
+ *top_only* was renamed to *top*. This parameter now accepts
+ ``None`` to iterate both latest and non-latest contributions.
+ ``False`` now iterates only non-latest contributions. Default
+ is ``None``. The ``size`` property is included by default.
+
:param user: Iterate contributions by this user (name or IP)
- :param userprefix: Iterate contributions by all users whose names
- or IPs start with this substring
+ :param userprefix: Iterate contributions by all users whose
+ names or IPs start with this substring
:param start: Iterate contributions starting at this Timestamp
:param end: Iterate contributions ending at this Timestamp
- :param reverse: Iterate oldest contributions first (default: newest)
+ :param reverse: Iterate oldest contributions first (default:
+ newest)
:param namespaces: Only iterate pages in these namespaces
- :param minor: If True, iterate only minor edits; if False and
- not None, iterate only non-minor edits (default: iterate both)
+ :param minor: If ``True``, iterate only minor edits; if ``False``
+ and not ``None``, iterate only non-minor edits (default:
+ iterate both)
:param total: Limit result to this number of pages
- :param top_only: If True, iterate only edits which are the latest
- revision (default: False)
- :raises pywikibot.exceptions.Error: either user or userprefix must be
- non-empty
+ :param top: if ``True``, iterate only edits which are the latest
+ revision; if ``False``, do not iterate last revision edits;
+ ``None`` to iterate both (default: ``None``)
+ :param prop: Include additional pieces of information. Refer
+ :api:`Usercontribs` for the elements and the default setting.
+ :param formatversion: The API format version to use for the
+ response. (``1`` by deault)
+ :raises pywikibot.exceptions.Error: either user or userprefix
+ must be non-empty
:raises KeyError: A namespace identifier was not resolved
:raises TypeError: A namespace identifier has an inappropriate
type such as NoneType or bool
@@ -1675,22 +1702,25 @@
if start and end:
self.assert_valid_iter_params('usercontribs', start, end, reverse)
- ucgen = self._generator(api.ListGenerator, type_arg='usercontribs',
- ucprop='ids|title|timestamp|comment|flags',
- namespaces=namespaces,
- total=total, uctoponly=top_only)
+ ucgen = self._generator(
+ api.ListGenerator,
+ type_arg='usercontribs',
+ namespaces=namespaces,
+ total=total,
+ ucprop=prop,
+ ucstart=start,
+ ucend=end,
+ formatversion=formatversion
+ )
if user:
ucgen.request['ucuser'] = user
if userprefix:
ucgen.request['ucuserprefix'] = userprefix
- if start is not None:
- ucgen.request['ucstart'] = str(start)
- if end is not None:
- ucgen.request['ucend'] = str(end)
if reverse:
ucgen.request['ucdir'] = 'newer'
option_set = api.OptionSet(self, 'usercontribs', 'show')
option_set['minor'] = minor
+ option_set['top'] = top
ucgen.request['ucshow'] = option_set
return ucgen
--
To view, visit
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/803388?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: Id80df6dd27eec79cb7dfdca326400659761995a8
Gerrit-Change-Number: 803388
Gerrit-PatchSet: 19
Gerrit-Owner: Xqt <[email protected]>
Gerrit-Reviewer: Xqt <[email protected]>
Gerrit-Reviewer: jenkins-bot
Gerrit-CC: JJMC89 <[email protected]>
_______________________________________________
Pywikibot-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]