jenkins-bot has submitted this change and it was merged. ( https://gerrit.wikimedia.org/r/543107 )
Change subject: [bugfix] Fix generate_family_file.py for private wikis ......................................................................
[bugfix] Fix generate_family_file.py for private wikis
In site_detect.py, MWSite._parse_post_117 attempts to create a temporary pywikibot.Family when generate_family_file.py is run on private wikis, but this fails as pywikibot.Family was made abstract in commit 5c5292e87cf318f67ad295239766cfc991319bcf. This commit fixes.
Bug: T235768 Change-Id: I0ce0f596fd50a61d373a1c0e7089ab480b2899c1 --- M pywikibot/site_detect.py M tests/site_detect_tests.py 2 files changed, 110 insertions(+), 8 deletions(-)
Approvals: Zhuyifei1999: Looks good to me, approved jenkins-bot: Verified
diff --git a/pywikibot/site_detect.py b/pywikibot/site_detect.py index 09b3be4..c85d984 100644 --- a/pywikibot/site_detect.py +++ b/pywikibot/site_detect.py @@ -15,7 +15,6 @@ import pywikibot
from pywikibot.comms.http import fetch -from pywikibot import config from pywikibot.exceptions import ServerError from pywikibot.tools import MediaWikiVersion, PY2
@@ -177,13 +176,11 @@ username = pywikibot.input( 'Private wiki detected. Login is required.\n' 'Please enter your username?') - config.usernames['temporary_family'] = {'temporary_code': username} # Setup a dummy family so that we can create a site object - fam = pywikibot.Family() - fam.name = 'temporary_family' - fam.scriptpath = lambda code: self.api[:-8] # without /api.php - fam.langs = {'temporary_code': self.server} - site = pywikibot.Site('temporary_code', fam) + fam = pywikibot.family.AutoFamily( + 'temporary_family', + self.api[:-8]) + site = pywikibot.Site(fam.code, fam, username) site.version = lambda: str(self.version) # Now the site object is able to login info = site.siteinfo diff --git a/tests/site_detect_tests.py b/tests/site_detect_tests.py index f8cd7a7..0b7048a 100644 --- a/tests/site_detect_tests.py +++ b/tests/site_detect_tests.py @@ -10,10 +10,18 @@
from requests.exceptions import ConnectionError, Timeout
+import pywikibot from pywikibot.exceptions import ServerError from pywikibot.site_detect import MWSite +from pywikibot.tools import PY2
-from tests.aspects import unittest, TestCase +from tests.aspects import unittest, TestCase, PatchingTestCase +from tests.utils import DrySite + +if not PY2: + from urllib.parse import urlparse +else: + from urlparse import urlparse
class SiteDetectionTestCase(TestCase): @@ -267,6 +275,103 @@ self.assertNoSite('http://musicbrainz.org/doc/$1')
+class PrivateWikiTestCase(PatchingTestCase): + + """Test generate_family_file works for private wikis.""" + + net = False + + SCHEME = 'https' + NETLOC = 'privatewiki.example.com' + WEBPATH = '/wiki/' + SCRIPTPATH = '/w' + APIPATH = SCRIPTPATH + '/api.php' + USERNAME = 'Private Wiki User' + VERSION = '1.33.0' + LANG = 'ike-cans' + + _server = SCHEME + '://' + NETLOC + _weburl = _server + WEBPATH + _apiurl = _server + APIPATH + _generator = 'MediaWiki ' + VERSION + + _responses = { + # site_detect.MWSite.__init__ first fetches whatever is at + # the user-supplied URL. We need to return enough data for + # site_detect.WikiHTMLPageParser to determine the server + # version and the API URL. + WEBPATH: ''.join(( + '<meta name="generator" content="', _generator, + '"/>\n<link rel="EditURI" type="application/rsd+xml" ' + 'href="', _apiurl, '?action=rsd"/>')), + APIPATH: '{"error":{"code":"readapidenied"}}', + } + + _siteinfo = { + 'generator': _generator, + 'server': _server, + 'scriptpath': SCRIPTPATH, + 'articlepath': WEBPATH.rstrip('/') + '/$1', + 'lang': LANG, + } + + @PatchingTestCase.patched(pywikibot.site_detect, 'fetch') + def fetch(self, url, *args, **kwargs): + """Patched version of pywikibot.site_detect.fetch.""" + parsed_url = urlparse(url) + self.assertEqual(parsed_url.scheme, self.SCHEME) + self.assertEqual(parsed_url.netloc, self.NETLOC) + self.assertIn(parsed_url.path, self._responses) + + return type(str('Response'), + (object,), + {'status': 200, + 'text': self._responses[parsed_url.path], + 'data': type(str('ResponseData'), + (object,), + {'url': url})}) + + @PatchingTestCase.patched(pywikibot, 'input') + def input(self, question, *args, **kwargs): + """Patched version of pywikibot.input.""" + self.assertTrue(question.endswith('username?')) + return self.USERNAME + + @PatchingTestCase.patched(pywikibot, 'Site') + def Site(self, code=None, fam=None, user=None, *args, **kwargs): + """Patched version of pywikibot.Site.""" + self.assertEqual(code, fam.code) + self.assertEqual(fam.domain, self.NETLOC) + self.assertEqual(user, self.USERNAME) + if not args and 'sysop' not in kwargs: + kwargs['sysop'] = None + site = DrySite(code, fam, user, *args, **kwargs) + site._siteinfo._cache.update( + (key, (value, True)) + for key, value in self._siteinfo.items()) + return site + + def test_T235768_failure(self): + """Test generate_family_file works for private wikis. + + generate_family_file.FamilyFileGenerator.run() does: + w = self.Wiki(self.base_url) + self.wikis[w.lang] = w + + where self.Wiki is pywikibot.site_detect.MWSite.__init__. + That calls MWSite._parse_post_117() which sets lang, but + that call's wrapped to log exceptions and then continue + past them. In T235768, the code that handles private + wikis raises an exception that's consumed in that way. + The value returned to FamilyFileGenerator.run() does not + have lang set, causing generate_family_file to bomb. + """ + site = MWSite(self._weburl) + self.assertIsInstance(site, MWSite) + self.assertTrue(hasattr(site, 'lang')) + self.assertEqual(site.lang, self.LANG) + + if __name__ == '__main__': # pragma: no cover try: unittest.main()
pywikibot-commits@lists.wikimedia.org