jenkins-bot has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/core/+/738509 )
Change subject: [IMPR] Provide is_locked method ......................................................................
[IMPR] Provide is_locked method
- add APISite.get_globaluserinfo() method to retrieve global user info for a given user or user ID. This enables to get global info like global groups for any user. - add globaluserinfo deleter to force reloading - add APISite.is_locked() method - rename User.isBlocked() to User.is_blocked() and deprecate the old variant to be unified wit APISite.is_blocked() - add force parameter to APISite.is_blocked like we have in User method - add User.is_locked() method - add is_locked to user_tests - use renamed is_blocked in welcome.py script and also check for global lock
Bug: T163629 Bug: T249392 Change-Id: I735b3b46fea82354f9beddbdfe7df0ee9670eba9 --- M pywikibot/page/__init__.py M pywikibot/site/_apisite.py M scripts/welcome.py M tests/user_tests.py 4 files changed, 124 insertions(+), 30 deletions(-)
Approvals: Xqt: Looks good to me, approved jenkins-bot: Verified
diff --git a/pywikibot/page/__init__.py b/pywikibot/page/__init__.py index 4b78130..0552e50 100644 --- a/pywikibot/page/__init__.py +++ b/pywikibot/page/__init__.py @@ -2871,14 +2871,36 @@ """ return self.getprops(force).get('editcount', 0)
- def isBlocked(self, force: bool = False) -> bool: - """ - Determine whether the user is currently blocked. + def is_blocked(self, force: bool = False) -> bool: + """Determine whether the user is currently blocked. + + .. versionchanged:: 7.0 + renamed from :meth:`isBlocked` method
:param force: if True, forces reloading the data from API """ return 'blockedby' in self.getprops(force)
+ @deprecated('is_blocked', since='7.0.0') + def isBlocked(self, force: bool = False) -> bool: + """Determine whether the user is currently blocked. + + .. deprecated:: 7.0 + use :meth:`is_blocked` instead + + :param force: if True, forces reloading the data from API + """ + return self.is_blocked(force) + + def is_locked(self, force: bool = False) -> bool: + """Determine whether the user is currently locked globally. + + .. versionadded:: 7.0 + + :param force: if True, forces reloading the data from API + """ + return self.site.is_locked(self.username, force) + def isEmailable(self, force: bool = False) -> bool: """ Determine whether emails may be send to this user through MediaWiki. diff --git a/pywikibot/site/_apisite.py b/pywikibot/site/_apisite.py index 77658bc..74b45cd 100644 --- a/pywikibot/site/_apisite.py +++ b/pywikibot/site/_apisite.py @@ -14,12 +14,12 @@ from collections.abc import Iterable from contextlib import suppress from textwrap import fill -from typing import Optional, Union +from typing import Any, Optional, Union from warnings import warn
import pywikibot import pywikibot.family -from pywikibot.backports import List +from pywikibot.backports import Dict, List from pywikibot.comms.http import get_authentication from pywikibot.data import api from pywikibot.exceptions import ( @@ -109,11 +109,12 @@ def __init__(self, code, fam=None, user=None): """Initializer.""" super().__init__(code, fam, user) - self._msgcache = {} - self._loginstatus = _LoginStatus.NOT_ATTEMPTED - self._siteinfo = Siteinfo(self) - self._paraminfo = api.ParamInfo(self) + self._globaluserinfo = {} self._interwikimap = _InterwikiMap(self) + self._loginstatus = _LoginStatus.NOT_ATTEMPTED + self._msgcache = {} + self._paraminfo = api.ParamInfo(self) + self._siteinfo = Siteinfo(self) self.tokens = TokenWallet(self)
def __getstate__(self): @@ -450,7 +451,9 @@ To force retrieving userinfo ignoring cache, just delete this property.
- self._userinfo will be a dict with the following keys and values: + .. seealso:: https://www.mediawiki.org/wiki/API:Userinfo + + :return: A dict with the following keys and values:
- id: user id (numeric str) - name: username (if user is logged in) @@ -460,7 +463,6 @@ - message: present if user has a new message on talk page - blockinfo: present if user is blocked (dict)
- https://www.mediawiki.org/wiki/API:Userinfo """ if not hasattr(self, '_userinfo'): uirequest = self._simple_request( @@ -481,15 +483,24 @@
@userinfo.deleter def userinfo(self): - """Delete cached userinfo.""" + """Delete cached userinfo. + + ..versionadded:: 5.5 + """ if hasattr(self, '_userinfo'): del self._userinfo
- @property - def globaluserinfo(self): + def get_globaluserinfo(self, + user: Union[str, int, None] = None, + force: bool = False) -> Dict[str, Any]: """Retrieve globaluserinfo from site and cache it.
- self._globaluserinfo will be a dict with the following keys and values: + .. versionadded:: 7.0 + + :param user: The user name or user ID whose global info is + retrieved. Defaults to the current user. + :param force: Whether the cache should be discarded. + :return: A dict with the following keys and values:
- id: user id (numeric str) - home: dbname of home wiki @@ -497,36 +508,87 @@ - groups: list of groups (could be empty) - rights: list of rights (could be empty) - editcount: global editcount + + :raises TypeError: Inappropriate argument type of 'user' """ - if not hasattr(self, '_globaluserinfo'): - uirequest = self._simple_request( + if user is None: + user = self.username + param = {} + elif isinstance(user, str): + param = {'guiuser': user} + elif isinstance(user, int): + param = {'guiid': user} + else: + raise TypeError("Inappropriate argument type of 'user' ({})" + .format(type(user).__name__)) + + if force or user not in self._globaluserinfo: + param.update( action='query', meta='globaluserinfo', - guiprop='groups|rights|editcount' + guiprop='groups|rights|editcount', ) + uirequest = self._simple_request(**param) uidata = uirequest.submit() assert 'query' in uidata, \ "API userinfo response lacks 'query' key" assert 'globaluserinfo' in uidata['query'], \ - "API userinfo response lacks 'userinfo' key" - self._globaluserinfo = uidata['query']['globaluserinfo'] - ts = self._globaluserinfo['registration'] - iso_ts = pywikibot.Timestamp.fromISOformat(ts) - self._globaluserinfo['registration'] = iso_ts - return self._globaluserinfo + "API userinfo response lacks 'globaluserinfo' key" + data = uidata['query']['globaluserinfo'] + ts = data['registration'] + data['registration'] = pywikibot.Timestamp.fromISOformat(ts) + self._globaluserinfo[user] = data + return self._globaluserinfo[user]
- def is_blocked(self): + @property + def globaluserinfo(self) -> Dict[str, Any]: + """Retrieve globaluserinfo of the current user from site. + + To get globaluserinfo for a given user or user ID use + :meth:`get_globaluserinfo` method instead + + .. versionadded:: 3.0 """ - Return True when logged in user is blocked. + return self.get_globaluserinfo() + + @globaluserinfo.deleter + def globaluserinfo(self): + """Delete cached globaluserinfo of current user. + + ..versionadded:: 7.0 + """ + with suppress(KeyError): + del self._globaluserinfo[self.username] + + def is_blocked(self, force: bool = False) -> bool: + """Return True when logged in user is blocked.
To check whether a user can perform an action, the method has_right should be used. https://www.mediawiki.org/wiki/API:Userinfo
- :rtype: bool + .. versionadded:: 7.0 + The *force* parameter + + :param force: Whether the cache should be discarded. """ + if force: + del self.userinfo return 'blockinfo' in self.userinfo
+ def is_locked(self, + user: Union[str, int, None] = None, + force: bool = False) -> bool: + """Return True when given user is locked globally. + + .. versionadded:: 7.0 + + :param user: The user name or user ID. Defaults to the current + user. + :param force: Whether the cache should be discarded. + """ + return 'locked' in self.get_globaluserinfo(user, force) + def get_searched_namespaces(self, force=False): """ Retrieve the default searched namespaces for the user. diff --git a/scripts/welcome.py b/scripts/welcome.py index 6009db2..518cd19 100755 --- a/scripts/welcome.py +++ b/scripts/welcome.py @@ -768,8 +768,12 @@ return self._randomSignature
def skip_page(self, user) -> bool: - """Check whether the user is to be skipped.""" - if user.isBlocked(): + """Check whether the user is to be skipped. + + .. versionchanged:: 7.0 + also skip if user is locked globally + """ + if user.is_blocked() or user.is_locked(): self.show_status(Msg.SKIP) pywikibot.output('{} has been blocked!'.format(user.username))
diff --git a/tests/user_tests.py b/tests/user_tests.py index a8368d1..7485349 100644 --- a/tests/user_tests.py +++ b/tests/user_tests.py @@ -56,7 +56,7 @@ self.assertFalse(user.isAnonymous()) self.assertIsInstance(user.registration(), pywikibot.Timestamp) self.assertGreater(user.editCount(), 0) - self.assertFalse(user.isBlocked()) + self.assertFalse(user.is_blocked()) # self.assertTrue(user.isEmailable()) self.assertEqual(user.gender(), 'unknown') self.assertIn('userid', user.getprops()) @@ -81,6 +81,7 @@ for contrib in contribs)) self.assertIn('user', user.groups()) self.assertIn('edit', user.rights()) + self.assertFalse(user.is_locked())
def test_registered_user_without_timestamp(self): """Test registered user when registration timestamp is None.""" @@ -155,6 +156,11 @@ 'This is an autoblock ID'): user.getUserTalkPage()
+ def test_locked_user(self): + """Test global lock.""" + user = User(self.site, 'TonjaHeritage2') + self.assertTrue(user.is_locked()) +
class TestUserMethods(DefaultSiteTestCase):
pywikibot-commits@lists.wikimedia.org