jenkins-bot has submitted this change and it was merged. ( https://gerrit.wikimedia.org/r/517031 )
Change subject: Handle closed_wikis as read-only ......................................................................
Handle closed_wikis as read-only
+ complete closed_wikis network + fix wikimania subdomain site
Detached from I68b5754d54f8
Bug: T74674 Change-Id: I78c538834555660becb2b304460c66d127bbbb9e --- M pywikibot/families/wikimania_family.py M pywikibot/family.py M pywikibot/site.py M tests/dry_site_tests.py M tests/family_tests.py M tests/site_tests.py 6 files changed, 94 insertions(+), 10 deletions(-)
Approvals: Xqt: Looks good to me, approved jenkins-bot: Verified
diff --git a/pywikibot/families/wikimania_family.py b/pywikibot/families/wikimania_family.py index 53d4dac..881e57e 100644 --- a/pywikibot/families/wikimania_family.py +++ b/pywikibot/families/wikimania_family.py @@ -8,10 +8,11 @@ from __future__ import absolute_import, division, unicode_literals
from pywikibot import family +from pywikibot.tools import classproperty
# The Wikimania family -class Family(family.WikimediaOrgFamily): +class Family(family.SubdomainFamily, family.WikimediaFamily):
"""Family class for Wikimania wikis."""
@@ -19,7 +20,22 @@
closed_wikis = [ '2005', '2006', '2007', '2008', '2009', '2010', '2011', '2012', '2013', - '2014', '2015', '2016', '2017', '2018', + '2014', '2015', '2016', '2017', '2018' ]
+ codes = ['wikimania'] + + code_aliases = {'2019': 'wikimania'} + interwiki_forward = 'wikipedia' + + @classproperty + def langs(cls): + """Property listing family languages.""" + codes = cls.codes + cls.closed_wikis + + # shortcut this classproperty + cls.langs = {code: '{}{}.{}'.format(cls.name, code, cls.domain) + for code in codes if code != 'wikimania'} + cls.langs['wikimania'] = 'wikimania.{}'.format(cls.domain) + return cls.langs diff --git a/pywikibot/family.py b/pywikibot/family.py index fb9671a..7d7dd69 100644 --- a/pywikibot/family.py +++ b/pywikibot/family.py @@ -1258,7 +1258,10 @@ if code in self.codes: pywikibot.warn('Interwiki removal %s is in %s codes' % (code, self)) - return 'RemovedSite' + if code in self.closed_wikis: + return 'ClosedSite' + if code in self.removed_wikis: + return 'RemovedSite'
return config.site_interface
diff --git a/pywikibot/site.py b/pywikibot/site.py index de25366..d9b1cc3 100644 --- a/pywikibot/site.py +++ b/pywikibot/site.py @@ -66,6 +66,7 @@ UnknownExtension, UnknownSite, UserBlocked, + UserRightsError, ) from pywikibot.throttle import Throttle from pywikibot.tools import ( @@ -1307,10 +1308,11 @@ """ def decorator(fn): def callee(self, *args, **kwargs): - if self.obsolete: - raise UnknownSite('Language %s in family %s is obsolete' - % (self.code, self.family.name)) grp = kwargs.pop('as_group', group) + if self.obsolete and not self.has_group('steward'): + raise UserRightsError('Site {} has been closed. Only steward ' + 'can perform requested action.' + .format(self.sitename)) if grp == 'user': self.login(False) elif grp == 'sysop': @@ -7456,6 +7458,63 @@ class_name='APISite', since='20141218')
+class ClosedSite(APISite): + """Site closed to read-only mode.""" + + def __init__(self, code, fam, user=None, sysop=None): + """Initializer.""" + super(ClosedSite, self).__init__(code, fam, user, sysop) + + def _closed_error(self, notice=''): + """An error instead of pointless API call.""" + pywikibot.error('Site {} has been closed. {}'.format(self.sitename, + notice)) + + def page_restrictions(self, page): + """Return a dictionary reflecting page protections.""" + if not self.page_exists(page): + raise NoPage(page) + if not hasattr(page, '_protection'): + page._protection = {'edit': ('steward', 'infinity'), + 'move': ('steward', 'infinity'), + 'delete': ('steward', 'infinity'), + 'upload': ('steward', 'infinity'), + 'create': ('steward', 'infinity')} + return page._protection + + def page_can_be_edited(self, page): + """Determine if the page can be edited.""" + rest = self.page_restrictions(page) + sysop_protected = 'edit' in rest and rest['edit'][0] == 'steward' + try: + api.LoginManager(site=self, sysop=sysop_protected) + except NoUsername: + return False + return True + + def recentchanges(self, **kwargs): + """An error instead of pointless API call.""" + self._closed_error('No recent changes can be returned.') + + def is_uploaddisabled(self): + """Return True if upload is disabled on site.""" + if not hasattr(self, '_uploaddisabled'): + self._uploaddisabled = True + return self._uploaddisabled + + def newpages(self, **kwargs): + """An error instead of pointless API call.""" + self._closed_error('No new pages can be returned.') + + def newfiles(self, **kwargs): + """An error instead of pointless API call.""" + self._closed_error('No new files can be returned.') + + def newimages(self, *args, **kwargs): + """An error instead of pointless API call.""" + self._closed_error('No new images can be returned.') + + class DataSite(APISite):
"""Wikibase data capable site.""" diff --git a/tests/dry_site_tests.py b/tests/dry_site_tests.py index b49aadc..95d0ffc 100644 --- a/tests/dry_site_tests.py +++ b/tests/dry_site_tests.py @@ -11,7 +11,7 @@ from pywikibot.tools import deprecated from pywikibot.site import must_be, need_version from pywikibot.comms.http import user_agent -from pywikibot.exceptions import UnknownSite +from pywikibot.exceptions import UserRightsError
from tests.aspects import ( unittest, @@ -137,6 +137,7 @@ self.code = 'test' self.family = lambda: None self.family.name = 'test' + self.sitename = self.family.name + ':' + self.code self._logged_in_as = None self.obsolete = False super(TestMustBe, self).setUp() @@ -146,6 +147,10 @@ """Fake the log in and just store who logged in.""" self._logged_in_as = 'sysop' if sysop else 'user'
+ def has_group(self, group): + """Fake the groups user belongs to.""" + return False + def testMockInTest(self): """Test that setUp and login work.""" self.assertIsNone(self._logged_in_as) @@ -198,7 +203,7 @@ self.obsolete = True args = (1, 2, 'a', 'b') kwargs = {'i': 'j', 'k': 'l'} - self.assertRaises(UnknownSite, self.call_this_user_req_function, + self.assertRaises(UserRightsError, self.call_this_user_req_function, args, kwargs)
diff --git a/tests/family_tests.py b/tests/family_tests.py index 43db8ed..8361090 100644 --- a/tests/family_tests.py +++ b/tests/family_tests.py @@ -53,7 +53,7 @@ self.assertIsInstance(f.languages_by_size, list) self.assertGreaterEqual(set(f.langs), set(f.languages_by_size)) if len(f.langs) > 2 and f.name not in { - 'wikimediachapter', 'vikidia', 'wikidata', + 'wikimediachapter', 'vikidia', 'wikidata', 'wikimania' }: self.assertNotEqual(f.languages_by_size, []) if isinstance(f, SingleSiteFamily): diff --git a/tests/site_tests.py b/tests/site_tests.py index 63d5cc2..6c177b5 100644 --- a/tests/site_tests.py +++ b/tests/site_tests.py @@ -3390,10 +3390,11 @@ self.assertEqual(site.code, 'mh') self.assertIsInstance(site.obsolete, bool) self.assertTrue(site.obsolete) - self.assertRaises(KeyError, site.hostname) + self.assertIsNotNone(site.hostname) r = http.fetch(uri='http://mh.wikipedia.org/w/api.php', default_error_handling=False) self.assertEqual(r.status, 200) + self.assertEqual(site.siteinfo['lang'], 'mh')
def test_removed_site(self): """Test Wikimedia offline site."""
pywikibot-commits@lists.wikimedia.org