def move(self, page: Page, new_title, reason='', move_talk=True, no_redirect=False, move_subpages=False, ignore_warnings=False): try: page.site = self.client page.move(new_title, reason=reason, move_talk=move_talk, no_redirect=no_redirect, move_subpages=move_subpages, ignore_warnings=ignore_warnings) except APIError as e: if e.code == 'badtoken': self._retry_login_action(self._retry_move, 'move', page=page, new_title=new_title, reason=reason, move_talk=move_talk, no_redirect=no_redirect, move_subpages=move_subpages, ignore_warnings=ignore_warnings) else: raise e
def test_proection_levels(self, mock_site): # If page is protected, check that protection is parsed correctly title = "Some page" mock_site.api.return_value = { "query": { "pages": { "728": { "protection": [ {"expiry": "infinity", "level": "autoconfirmed", "type": "edit"}, {"expiry": "infinity", "level": "sysop", "type": "move"}, ] } } } } mock_site.rights = ["read", "edit", "move"] page = Page(mock_site, title) assert page.protection == {"edit": ("autoconfirmed", "infinity"), "move": ("sysop", "infinity")} assert page.can("read") is True assert page.can("edit") is False # User does not have 'autoconfirmed' right assert page.can("move") is False # User does not have 'sysop' right mock_site.rights = ["read", "edit", "move", "autoconfirmed"] assert page.can("edit") is True # User has 'autoconfirmed' right assert page.can("move") is False # User doesn't have 'sysop' right mock_site.rights = ["read", "edit", "move", "autoconfirmed", "editprotected"] assert page.can("edit") is True # User has 'autoconfirmed' right assert page.can("move") is True # User has 'sysop' right
def test_captcha(self, mock_site): # Check that Captcha results in EditError mock_site.blocked = False mock_site.rights = ['read', 'edit'] title = 'Norge' mock_site.get.return_value = { 'query': { 'pages': { '728': { 'protection': [] } } } } page = Page(mock_site, title) mock_site.post.return_value = { 'edit': { 'result': 'Failure', 'captcha': { 'type': 'math', 'mime': 'text/tex', 'id': '509895952', 'question': '36 + 4 = ' } } } # For now, mwclient will just raise an EditError. # <https://github.com/mwclient/mwclient/issues/33> with pytest.raises(mwclient.errors.EditError): page.save('Some text')
def process(self, article): page = Page(self.client, article) wikitext = page.text() matches = re.search( r"\[\[((?:[^\]|$]+)(?=.png|.jpg|.jpeg|.gif|.svg).png|.jpg|.jpeg|.gif|svg)(?:|[^\]$]+)\]\]", wikitext) if matches: for match in matches.groups(): self.file.write(match + "\n")
def process(self, page_name): page = Page(self.client, page_name) wikitext = page.text() if BEGINNING: wikitext = TEMPLATE + "\n" + wikitext else: wikitext = wikitext + "\n" + TEMPLATE page.edit(wikitext, summary=self.lang.t("scripts.addtemplate.summary").format( template=TEMPLATE))
def setUp(self): title = 'Some page' self.page_text = 'Hello world' MockSite = mock.patch('mwclient.client.Site').start() self.site = MockSite() self.site.api.return_value = {'query': {'pages': {'1': {'title': title}}}} self.site.rights = ['read'] self.page = Page(self.site, title) self.site.api.return_value = {'query': {'pages': {'2': { 'ns': 0, 'pageid': 2, 'revisions': [{'*': 'Hello world', 'timestamp': '2014-08-29T22:25:15Z'}], 'title': title }}}}
def save_changes(self): if self.edited: self.logger.info( self.lang.t("interwiki.saving").format(name=self.page.name)) self.page.edit(self.wikitext, summary=self.generate_summary()) self.created = [] self.deleted = [] self.edited = False self.page = Page(self.page.site, self.page.name) self.wikitext = self.page.text() self.interwiki_links = dict() for lang, link in self.page.langlinks(): if lang not in self.interwiki_links: self.interwiki_links[lang] = [link] else: self.interwiki_links[lang].append(link)
def test_redirect(self, mock_site): # Check that page.redirect is set correctly title = 'Some redirect page' mock_site.get.return_value = { "query": { "pages": { "796917": { "contentmodel": "wikitext", "counter": "", "lastrevid": 9342494, "length": 70, "ns": 0, "pageid": 796917, "pagelanguage": "nb", "protection": [], "redirect": "", "title": title, "touched": "2014-08-29T22:25:15Z" } } } } page = Page(mock_site, title) assert page.exists is True assert page.redirect is True
def get_prplatform(page: Page): for template in mwparserfromhell.parse(page.text()).filter_templates(): if not template.name.matches('Infobox Team'): continue if not template.has('prplatform'): return None return template.get('prplatform').value.strip()
def print_article(self, article: Page): print(self.lang.t("scripts.recategorize.articleinfo.name") + article.name) print("-".join(map(lambda _: "", range(80)))) print(self.preview(article)) print("-".join(map(lambda _: "", range(80)))) print(self.lang.t("scripts.recategorize.articleinfo.current_categories") + ",".join(list(map(lambda x: "".join(x.name.split(":")[1:]), article.categories()))))
def test_pageprops(self, mock_site): # Check that variouse page props are read correctly from API response title = 'Some page' mock_site.get.return_value = { 'query': { 'pages': { '728': { 'contentmodel': 'wikitext', 'counter': '', 'lastrevid': 13355471, 'length': 58487, 'ns': 0, 'pageid': 728, 'pagelanguage': 'nb', 'protection': [], 'title': title, 'touched': '2014-09-14T21:11:52Z' } } } } page = Page(mock_site, title) assert page.exists is True assert page.redirect is False assert page.revision == 13355471 assert page.length == 58487 assert page.namespace == 0 assert page.name == title assert page.page_title == title
def test_existing_page(self, mock_site): # Check that API response results in page.exists being set to True title = 'Norge' mock_site.get.return_value = {'query': {'pages': {'728': {}}}} page = Page(mock_site, title) assert page.exists is True
def preview(self, article: Page): text = article.text() lines = [] for line in text.splitlines(): curline = line while len(curline) > 80: lines.append(curline[:80]) curline = curline[80:] lines.append(curline) return os.linesep.join(lines[:15])
def do_recategorize(self, article: Page, additions, removals, replaces): if self.query == "": return print("Guardando...") wikitext = article.text() wikitext = wikitext.replace("[[Category:", "[[" + self.category_name) for removal in removals: wikitext = re.sub(r'\[\[' + removal + r'(?:|.*)*\]\]', '', wikitext) for addition in additions: wikitext = wikitext + "\n[[{}]]".format(addition) for replace in replaces: search, rep = replace.split("%") wikitext = re.sub(r'\[\[' + self.category_name + search + r'(?:|.*)*\]\]', '[[' + self.category_name + rep + ']]', wikitext) plainadditions = ",".join(map(lambda x: "+" + ",".join(x.split(':')[1:]), additions)) plainremovals = ",".join(map(lambda x: "-" + ",".join(x.split(':')[1:]), removals)) plainreplaces = ",".join(map(lambda x: "=>".join(x.split('%')), replaces)) query = plainadditions + "; " + plainremovals + "; " + plainreplaces article.edit(wikitext, summary=self.lang.t("scripts.recategorize.reason") + query, minor=True, bot=True)
def get_recategorization(self, article: Page, query: str): actions = query.split(",") addition_candidates = list(map(lambda x: self.category_name + x[1:], filter(lambda x: x.strip()[0] == '+', actions))) removal_candidates = list(map(lambda x: x[1:].lower().strip(), filter(lambda x: x.strip()[0] == '-', actions))) replaces = list(map(lambda x: x[1:].strip(), filter(lambda x: x.strip()[0] == '%', actions))) categories = list(map(lambda x: x.name, article.categories())) additions = filter(lambda x: x.lower().strip() not in map(lambda y: y.lower().strip(), categories), addition_candidates) removals = filter(lambda x: ("".join(x.split(':')[1:])).lower().strip() in removal_candidates, categories) return list(additions), list(removals), replaces
def test_nonexisting_page(self, mock_site): # Check that API response results in page.exists being set to False title = 'Some nonexisting page' mock_site.api.return_value = { 'query': {'pages': {'-1': {'missing': ''}}} } page = Page(mock_site, title) assert page.exists is False
def test_captcha(self, mock_site): # Check that Captcha results in EditError mock_site.blocked = False mock_site.rights = ["read", "edit"] title = "Norge" mock_site.api.return_value = {"query": {"pages": {"728": {"protection": []}}}} page = Page(mock_site, title) mock_site.api.return_value = { "edit": { "result": "Failure", "captcha": {"type": "math", "mime": "text/tex", "id": "509895952", "question": "36 + 4 = "}, } } # For now, mwclient will just raise an EditError. # <https://github.com/mwclient/mwclient/issues/33> with pytest.raises(mwclient.errors.EditError): page.save("Some text")
def test_protection_levels(self, mock_site): # If page is protected, check that protection is parsed correctly title = 'Some page' mock_site.api.return_value = { 'query': { 'pages': { '728': { 'protection': [ { 'expiry': 'infinity', 'level': 'autoconfirmed', 'type': 'edit' }, { 'expiry': 'infinity', 'level': 'sysop', 'type': 'move' } ] } } } } mock_site.rights = ['read', 'edit', 'move'] page = Page(mock_site, title) assert page.protection == {'edit': ('autoconfirmed', 'infinity'), 'move': ('sysop', 'infinity')} assert page.can('read') is True assert page.can('edit') is False # User does not have 'autoconfirmed' right assert page.can('move') is False # User does not have 'sysop' right mock_site.rights = ['read', 'edit', 'move', 'autoconfirmed'] assert page.can('edit') is True # User has 'autoconfirmed' right assert page.can('move') is False # User doesn't have 'sysop' right mock_site.rights = ['read', 'edit', 'move', 'autoconfirmed', 'editprotected'] assert page.can('edit') is True # User has 'autoconfirmed' right assert page.can('move') is True # User has 'sysop' right
def test_api_call_on_page_init(self, mock_site): # Check that site.api() is called once on Page init title = 'Some page' mock_site.api.return_value = { 'query': {'pages': {'1': {}}} } page = Page(mock_site, title) # test that Page called site.api with the right parameters mock_site.api.assert_called_once_with('query', inprop='protection', titles=title, prop='info')
def save(self, page: Page, text, summary=u'', minor=False, bot=True, section=None, **kwargs): """ Performs a page edit, retrying the login once if the edit fails due to the user being logged out This function hopefully makes it easy to workaround the lag and frequent login timeouts experienced on the Fandom UCP platform compared to Gamepedia Hydra. :param page: mwclient Page object :param text: as in mwclient.Page.edit :param summary: as in mwclient.Page.edit :param minor: as in mwclient.Page.edit :param bot: as in mwclient.Page.edit :param section: as in mwclient.Page.edit :param kwargs: as in mwclient.Page.edit :return: nothing """ try: page.site = self.client page.edit(text, summary=summary, minor=minor, bot=bot, section=section, **kwargs) except self.write_errors: self._retry_login_action(self._retry_save, 'edit', page=page, text=text, summary=summary, minor=minor, bot=bot, section=section, **kwargs)
def process(self, article): page = Page(self.client, article) if not page.redirect: return self.logger.info(self.lang.t("scripts.cleanupdoubleredirects.processing").format(name=article)) redirection_chain = [] while page.redirect: if page.name in redirection_chain: self.logger.warning(self.lang.t("cleanupdoubleredirects.circular_reference").format(name=article)) return redirection_chain.append(page.name) page = Page(self.client, page.redirects_to()) if len(redirection_chain) > 1: for redirect in redirection_chain[:-1]: self.logger.info(self.lang.t("resolving").format(redirect=redirect, target=page.name)) redir = Page(self.client, redirect) redir.edit("#REDIRECT [[" + page.name + "]]", self.lang.t("scripts.cleanupdoubleredirects.reason") .format(target=page.name))
def remap_interwikis(self, name): if not self.interwikis: raise ValueError("call bootstrap first!") page = Page(self.client, name) if page.redirect: return self.remap_interwikis(page.resolve_redirect().name) self.logger.info(self.lang.t("interwiki.remapping").format(name=name)) task = InterwikiTask(page, self.client, self.interwikis, self.logger, self.lang) task.clean_broken_interwikis() missing = list(task.missing_interwikis()) for language in missing: self.logger.debug( self.lang.t("interwiki.locating").format(lang=language)) for strategy in self.strategies: self.logger.debug( self.lang.t("interwiki.attempting_strategy").format( strategy=strategy.get_name())) resultado = strategy.run(task, language) if resultado is not None: break task.save_changes()
def delete(self, page: Page, reason='', watch=False, unwatch=False, oldimage=False): try: page.site = self.client page.delete(reason=reason, watch=watch, unwatch=unwatch, oldimage=oldimage) except APIError as e: if e.code == 'badtoken': self._retry_login_action(self._retry_delete, 'delete', page=page, reason=reason, watch=watch, unwatch=unwatch, oldimage=oldimage) else: raise e
def test_captcha(self, mock_site): # Check that Captcha results in EditError mock_site.blocked = False mock_site.rights = ['read', 'edit'] title = 'Norge' mock_site.api.return_value = { 'query': {'pages': {'728': {'protection': []}}} } page = Page(mock_site, title) mock_site.api.return_value = { 'edit': {'result': 'Failure', 'captcha': { 'type': 'math', 'mime': 'text/tex', 'id': '509895952', 'question': '36 + 4 = ' }} } # For now, mwclient will just raise an EditError. # <https://github.com/mwclient/mwclient/issues/33> with pytest.raises(mwclient.errors.EditError): page.save('Some text')
def __init__(self, page: Page, client: Site, interwikis: List[Interwiki], logger: Logger, lang: Lang): self.page = page self.wikitext = self.page.text() self.client = client self.interwiki_links = dict() for language, link in page.langlinks(): if language not in self.interwiki_links: self.interwiki_links[language] = [link] else: self.interwiki_links[language].append(link) self.interwikis = interwikis self.logger = logger self.edited = False self.created = [] self.deleted = [] self.lang = lang
def test_invalid_title(self, mock_site): # Check that API page.exists is False for invalid title title = '[Test]' mock_site.get.return_value = { "query": { "pages": { "-1": { "title": "[Test]", "invalidreason": "The requested page title contains invalid characters: \"[\".", "invalid": "" } } } } with pytest.raises(InvalidPageTitle): page = Page(mock_site, title)
def test_protection_levels(self, mock_site): # If page is protected, check that protection is parsed correctly title = 'Some page' mock_site.get.return_value = { 'query': { 'pages': { '728': { 'protection': [{ 'expiry': 'infinity', 'level': 'autoconfirmed', 'type': 'edit' }, { 'expiry': 'infinity', 'level': 'sysop', 'type': 'move' }] } } } } mock_site.rights = ['read', 'edit', 'move'] page = Page(mock_site, title) assert page.protection == { 'edit': ('autoconfirmed', 'infinity'), 'move': ('sysop', 'infinity') } assert page.can('read') is True assert page.can( 'edit') is False # User does not have 'autoconfirmed' right assert page.can('move') is False # User does not have 'sysop' right mock_site.rights = ['read', 'edit', 'move', 'autoconfirmed'] assert page.can('edit') is True # User has 'autoconfirmed' right assert page.can('move') is False # User doesn't have 'sysop' right mock_site.rights = [ 'read', 'edit', 'move', 'autoconfirmed', 'editprotected' ] assert page.can('edit') is True # User has 'autoconfirmed' right assert page.can('move') is True # User has 'sysop' right
def setUp(self): title = "Some page" self.page_text = "Hello world" MockSite = mock.patch("mwclient.client.Site").start() self.site = MockSite() self.site.api.return_value = {"query": {"pages": {"1": {"title": title}}}} self.site.rights = ["read"] self.page = Page(self.site, title) self.site.api.return_value = { "query": { "pages": { "2": { "ns": 0, "pageid": 2, "revisions": [{"*": "Hello world", "timestamp": "2014-08-29T22:25:15Z"}], "title": title, } } } }
def touch(self, page: Page): try: page.site = self.client page.touch() except self.write_errors: self._retry_login_action(self._retry_touch, 'touch', page=page)
class TestPageApiArgs(unittest.TestCase): def setUp(self): title = 'Some page' self.page_text = 'Hello world' MockSite = mock.patch('mwclient.client.Site').start() self.site = MockSite() self.site.api.return_value = {'query': {'pages': {'1': {'title': title}}}} self.site.rights = ['read'] self.page = Page(self.site, title) self.site.api.return_value = {'query': {'pages': {'2': { 'ns': 0, 'pageid': 2, 'revisions': [{'*': 'Hello world', 'timestamp': '2014-08-29T22:25:15Z'}], 'title': title }}}} def get_last_api_call_args(self): args, kwargs = self.site.api.call_args action = args[0] args = args[1:] kwargs.update(args) return kwargs def tearDown(self): mock.patch.stopall() def test_get_page_text(self): # Check that page.text() works, and that a correct API call is made text = self.page.text() args = self.get_last_api_call_args() assert text == self.page_text assert args == { 'prop': 'revisions', 'rvdir': 'older', 'titles': self.page.page_title, 'rvprop': 'content|timestamp', 'rvlimit': '1' } def test_get_page_text_cached(self): # Check page.text() caching self.page.revisions = mock.Mock(return_value=iter([])) self.page.text() self.page.text() # When cache is hit, revisions is not, so call_count should be 1 assert self.page.revisions.call_count == 1 self.page.text(cache=False) # With cache explicitly disabled, we should hit revisions assert self.page.revisions.call_count == 2 def test_get_section_text(self): # Check that the 'rvsection' parameter is sent to the API text = self.page.text(section=0) args = self.get_last_api_call_args() assert args['rvsection'] == '0' def test_get_text_expanded(self): # Check that the 'rvexpandtemplates' parameter is sent to the API text = self.page.text(expandtemplates=True) args = self.get_last_api_call_args() assert args['rvexpandtemplates'] == '1' def test_get_text_expanded_deprecated(self): # Check that the 'rvexpandtemplates' parameter is sent to the API text = self.page.get_expanded() args = self.get_last_api_call_args() assert args['rvexpandtemplates'] == '1'
class TestPageApiArgs(unittest.TestCase): def setUp(self): title = 'Some page' self.page_text = 'Hello world' MockSite = mock.patch('mwclient.client.Site').start() self.site = MockSite() self.site.get.return_value = { 'query': { 'pages': { '1': { 'title': title } } } } self.site.rights = ['read'] self.site.api_limit = 500 self.site.version = (1, 32, 0) self.page = Page(self.site, title) self.site.get.return_value = { 'query': { 'pages': { '2': { 'ns': 0, 'pageid': 2, 'revisions': [{ '*': 'Hello world', 'timestamp': '2014-08-29T22:25:15Z' }], 'title': title } } } } def get_last_api_call_args(self, http_method='POST'): if http_method == 'GET': args, kwargs = self.site.get.call_args else: args, kwargs = self.site.post.call_args action = args[0] args = args[1:] kwargs.update(args) return kwargs def tearDown(self): mock.patch.stopall() def test_get_page_text(self): # Check that page.text() works, and that a correct API call is made text = self.page.text() args = self.get_last_api_call_args(http_method='GET') assert text == self.page_text assert args == { 'prop': 'revisions', 'rvdir': 'older', 'titles': self.page.page_title, 'rvprop': 'content|timestamp', 'rvlimit': '1', 'rvslots': 'main', } def test_get_page_text_cached(self): # Check page.text() caching self.page.revisions = mock.Mock(return_value=iter([])) self.page.text() self.page.text() # When cache is hit, revisions is not, so call_count should be 1 assert self.page.revisions.call_count == 1 self.page.text(cache=False) # With cache explicitly disabled, we should hit revisions assert self.page.revisions.call_count == 2 def test_get_section_text(self): # Check that the 'rvsection' parameter is sent to the API text = self.page.text(section=0) args = self.get_last_api_call_args(http_method='GET') assert args['rvsection'] == '0' def test_get_text_expanded(self): # Check that the 'rvexpandtemplates' parameter is sent to the API text = self.page.text(expandtemplates=True) args = self.get_last_api_call_args(http_method='GET') assert self.site.expandtemplates.call_count == 1 assert args.get('rvexpandtemplates') is None def test_assertuser_true(self): # Check that assert=user is sent when force_login=True self.site.blocked = False self.site.rights = ['read', 'edit'] self.site.logged_in = True self.site.force_login = True self.site.api.return_value = {'edit': {'result': 'Ok'}} self.page.save('Some text') args = self.get_last_api_call_args() assert args['assert'] == 'user' def test_assertuser_false(self): # Check that assert=user is not sent when force_login=False self.site.blocked = False self.site.rights = ['read', 'edit'] self.site.logged_in = False self.site.force_login = False self.site.api.return_value = {'edit': {'result': 'Ok'}} self.page.save('Some text') args = self.get_last_api_call_args() assert 'assert' not in args def test_handle_edit_error_assertuserfailed(self): # Check that AssertUserFailedError is triggered api_error = APIError( 'assertuserfailed', 'Assertion that the user is logged in failed', 'See https://en.wikipedia.org/w/api.php for API usage') with pytest.raises(AssertUserFailedError): self.page.handle_edit_error(api_error, 'n/a') def test_handle_edit_error_protected(self): # Check that ProtectedPageError is triggered api_error = APIError( 'protectedpage', 'The "editprotected" right is required to edit this page', 'See https://en.wikipedia.org/w/api.php for API usage') with pytest.raises(ProtectedPageError) as pp_error: self.page.handle_edit_error(api_error, 'n/a') assert pp_error.value.code == 'protectedpage' assert str( pp_error.value ) == 'The "editprotected" right is required to edit this page' def test_get_page_categories(self): # Check that page.categories() works, and that a correct API call is made self.site.get.return_value = { "batchcomplete": "", "query": { "pages": { "1009371": { "pageid": 1009371, "ns": 14, "title": "Category:1879 births", }, "1005547": { "pageid": 1005547, "ns": 14, "title": "Category:1955 deaths", } } } } cats = list(self.page.categories()) args = self.get_last_api_call_args(http_method='GET') assert { 'generator': 'categories', 'titles': self.page.page_title, 'iiprop': 'timestamp|user|comment|url|size|sha1|metadata|archivename', 'inprop': 'protection', 'prop': 'info|imageinfo', 'gcllimit': repr(self.page.site.api_limit), } == args assert set([c.name for c in cats]) == set([ 'Category:1879 births', 'Category:1955 deaths', ])
class TestPageApiArgs(unittest.TestCase): def setUp(self): title = 'Some page' self.page_text = 'Hello world' MockSite = mock.patch('mwclient.client.Site').start() self.site = MockSite() self.site.get.return_value = {'query': {'pages': {'1': {'title': title}}}} self.site.rights = ['read'] self.page = Page(self.site, title) self.site.get.return_value = {'query': {'pages': {'2': { 'ns': 0, 'pageid': 2, 'revisions': [{'*': 'Hello world', 'timestamp': '2014-08-29T22:25:15Z'}], 'title': title }}}} def get_last_api_call_args(self, http_method='POST'): if http_method == 'GET': args, kwargs = self.site.get.call_args else: args, kwargs = self.site.post.call_args action = args[0] args = args[1:] kwargs.update(args) return kwargs def tearDown(self): mock.patch.stopall() def test_get_page_text(self): # Check that page.text() works, and that a correct API call is made text = self.page.text() args = self.get_last_api_call_args(http_method='GET') assert text == self.page_text assert args == { 'prop': 'revisions', 'rvdir': 'older', 'titles': self.page.page_title, 'rvprop': 'content|timestamp', 'rvlimit': '1' } def test_get_page_text_cached(self): # Check page.text() caching self.page.revisions = mock.Mock(return_value=iter([])) self.page.text() self.page.text() # When cache is hit, revisions is not, so call_count should be 1 assert self.page.revisions.call_count == 1 self.page.text(cache=False) # With cache explicitly disabled, we should hit revisions assert self.page.revisions.call_count == 2 def test_get_section_text(self): # Check that the 'rvsection' parameter is sent to the API text = self.page.text(section=0) args = self.get_last_api_call_args(http_method='GET') assert args['rvsection'] == '0' def test_get_text_expanded(self): # Check that the 'rvexpandtemplates' parameter is sent to the API text = self.page.text(expandtemplates=True) args = self.get_last_api_call_args(http_method='GET') assert args['rvexpandtemplates'] == '1' def test_assertuser_true(self): # Check that assert=user is sent when force_login=True self.site.blocked = False self.site.rights = ['read', 'edit'] self.site.logged_in = True self.site.force_login = True self.site.api.return_value = { 'edit': {'result': 'Ok'} } self.page.save('Some text') args = self.get_last_api_call_args() assert args['assert'] == 'user' def test_assertuser_false(self): # Check that assert=user is not sent when force_login=False self.site.blocked = False self.site.rights = ['read', 'edit'] self.site.logged_in = False self.site.force_login = False self.site.api.return_value = { 'edit': {'result': 'Ok'} } self.page.save('Some text') args = self.get_last_api_call_args() assert 'assert' not in args def test_handle_edit_error_assertuserfailed(self): # Check that AssertUserFailedError is triggered api_error = APIError('assertuserfailed', 'Assertion that the user is logged in failed', 'See https://en.wikipedia.org/w/api.php for API usage') with pytest.raises(AssertUserFailedError): self.page.handle_edit_error(api_error, 'n/a') def test_handle_edit_error_protected(self): # Check that ProtectedPageError is triggered api_error = APIError('protectedpage', 'The "editprotected" right is required to edit this page', 'See https://en.wikipedia.org/w/api.php for API usage') with pytest.raises(ProtectedPageError) as pp_error: self.page.handle_edit_error(api_error, 'n/a') assert pp_error.value.code == 'protectedpage' assert str(pp_error.value) == 'The "editprotected" right is required to edit this page'
class TestPageApiArgs(unittest.TestCase): def setUp(self): title = "Some page" self.page_text = "Hello world" MockSite = mock.patch("mwclient.client.Site").start() self.site = MockSite() self.site.api.return_value = {"query": {"pages": {"1": {"title": title}}}} self.site.rights = ["read"] self.page = Page(self.site, title) self.site.api.return_value = { "query": { "pages": { "2": { "ns": 0, "pageid": 2, "revisions": [{"*": "Hello world", "timestamp": "2014-08-29T22:25:15Z"}], "title": title, } } } } def get_last_api_call_args(self): args, kwargs = self.site.api.call_args action = args[0] args = args[1:] kwargs.update(args) return kwargs def tearDown(self): mock.patch.stopall() def test_get_page_text(self): # Check that page.text() works, and that a correct API call is made text = self.page.text() args = self.get_last_api_call_args() assert text == self.page_text assert args == { "prop": "revisions", "rvdir": "older", "titles": self.page.page_title, "rvprop": "content|timestamp", "rvlimit": "1", } def test_get_page_text_cached(self): # Check page.text() caching self.page.revisions = mock.Mock(return_value=iter([])) self.page.text() self.page.text() # When cache is hit, revisions is not, so call_count should be 1 assert self.page.revisions.call_count == 1 self.page.text(cache=False) # With cache explicitly disabled, we should hit revisions assert self.page.revisions.call_count == 2 def test_get_section_text(self): # Check that the 'rvsection' parameter is sent to the API text = self.page.text(section=0) args = self.get_last_api_call_args() assert args["rvsection"] == "0" def test_get_text_expanded(self): # Check that the 'rvexpandtemplates' parameter is sent to the API text = self.page.text(expandtemplates=True) args = self.get_last_api_call_args() assert args["rvexpandtemplates"] == "1" def test_get_text_expanded_deprecated(self): # Check that the 'rvexpandtemplates' parameter is sent to the API text = self.page.get_expanded() args = self.get_last_api_call_args() assert args["rvexpandtemplates"] == "1"
def purge(self, page: Page): try: page.site = self.client page.purge() except self.write_errors: self._retry_login_action(self._retry_purge, 'purge', page=page)
class TestPageApiArgs(unittest.TestCase): def setUp(self): title = 'Some page' self.page_text = 'Hello world' MockSite = mock.patch('mwclient.client.Site').start() self.site = MockSite() self.site.get.return_value = { 'query': { 'pages': { '1': { 'title': title } } } } self.site.rights = ['read'] self.page = Page(self.site, title) self.site.get.return_value = { 'query': { 'pages': { '2': { 'ns': 0, 'pageid': 2, 'revisions': [{ '*': 'Hello world', 'timestamp': '2014-08-29T22:25:15Z' }], 'title': title } } } } def get_last_api_call_args(self, http_method='POST'): if http_method == 'GET': args, kwargs = self.site.get.call_args else: args, kwargs = self.site.post.call_args action = args[0] args = args[1:] kwargs.update(args) return kwargs def tearDown(self): mock.patch.stopall() def test_get_page_text(self): # Check that page.text() works, and that a correct API call is made text = self.page.text() args = self.get_last_api_call_args(http_method='GET') assert text == self.page_text assert args == { 'prop': 'revisions', 'rvdir': 'older', 'titles': self.page.page_title, 'rvprop': 'content|timestamp', 'rvlimit': '1' } def test_get_page_text_cached(self): # Check page.text() caching self.page.revisions = mock.Mock(return_value=iter([])) self.page.text() self.page.text() # When cache is hit, revisions is not, so call_count should be 1 assert self.page.revisions.call_count == 1 self.page.text(cache=False) # With cache explicitly disabled, we should hit revisions assert self.page.revisions.call_count == 2 def test_get_section_text(self): # Check that the 'rvsection' parameter is sent to the API text = self.page.text(section=0) args = self.get_last_api_call_args(http_method='GET') assert args['rvsection'] == '0' def test_get_text_expanded(self): # Check that the 'rvexpandtemplates' parameter is sent to the API text = self.page.text(expandtemplates=True) args = self.get_last_api_call_args(http_method='GET') assert args['rvexpandtemplates'] == '1' def test_assertuser_true(self): # Check that assert=user is sent when force_login=True self.site.blocked = False self.site.rights = ['read', 'edit'] self.site.logged_in = True self.site.force_login = True self.site.api.return_value = {'edit': {'result': 'Ok'}} self.page.save('Some text') args = self.get_last_api_call_args() assert args['assert'] == 'user' def test_assertuser_false(self): # Check that assert=user is not sent when force_login=False self.site.blocked = False self.site.rights = ['read', 'edit'] self.site.logged_in = False self.site.force_login = False self.site.api.return_value = {'edit': {'result': 'Ok'}} self.page.save('Some text') args = self.get_last_api_call_args() assert 'assert' not in args def test_handle_edit_error_assertuserfailed(self): # Check that AssertUserFailedError is triggered api_error = APIError( 'assertuserfailed', 'Assertion that the user is logged in failed', 'See https://en.wikipedia.org/w/api.php for API usage') with pytest.raises(AssertUserFailedError): self.page.handle_edit_error(api_error, 'n/a') def test_handle_edit_error_protected(self): # Check that ProtectedPageError is triggered api_error = APIError( 'protectedpage', 'The "editprotected" right is required to edit this page', 'See https://en.wikipedia.org/w/api.php for API usage') with pytest.raises(ProtectedPageError) as pp_error: self.page.handle_edit_error(api_error, 'n/a') assert pp_error.value.code == 'protectedpage' assert str( pp_error.value ) == 'The "editprotected" right is required to edit this page'
class TestPageApiArgs(unittest.TestCase): def setUp(self): title = 'Some page' self.page_text = 'Hello world' MockSite = mock.patch('mwclient.client.Site').start() self.site = MockSite() self.site.api.return_value = { 'query': { 'pages': { '1': { 'title': title } } } } self.site.rights = ['read'] self.page = Page(self.site, title) self.site.api.return_value = { 'query': { 'pages': { '2': { 'ns': 0, 'pageid': 2, 'revisions': [{ '*': 'Hello world', 'timestamp': '2014-08-29T22:25:15Z' }], 'title': title } } } } def get_last_api_call_args(self): args, kwargs = self.site.api.call_args action = args[0] args = args[1:] kwargs.update(args) return kwargs def tearDown(self): mock.patch.stopall() def test_get_page_text(self): # Check that page.text() works, and that a correct API call is made text = self.page.text() args = self.get_last_api_call_args() assert text == self.page_text assert args == { 'prop': 'revisions', 'rvdir': 'older', 'titles': self.page.page_title, 'rvprop': 'content|timestamp', 'rvlimit': '1' } def test_get_page_text_cached(self): # Check page.text() caching self.page.revisions = mock.Mock(return_value=iter([])) self.page.text() self.page.text() # When cache is hit, revisions is not, so call_count should be 1 assert self.page.revisions.call_count == 1 self.page.text(cache=False) # With cache explicitly disabled, we should hit revisions assert self.page.revisions.call_count == 2 def test_get_section_text(self): # Check that the 'rvsection' parameter is sent to the API text = self.page.text(section=0) args = self.get_last_api_call_args() assert args['rvsection'] == '0' def test_get_text_expanded(self): # Check that the 'rvexpandtemplates' parameter is sent to the API text = self.page.text(expandtemplates=True) args = self.get_last_api_call_args() assert args['rvexpandtemplates'] == '1' def test_get_text_expanded_deprecated(self): # Check that the 'rvexpandtemplates' parameter is sent to the API text = self.page.get_expanded() args = self.get_last_api_call_args() assert args['rvexpandtemplates'] == '1'
class InterwikiTask(object): def __init__(self, page: Page, client: Site, interwikis: List[Interwiki], logger: Logger, lang: Lang): self.page = page self.wikitext = self.page.text() self.client = client self.interwiki_links = dict() for language, link in page.langlinks(): if language not in self.interwiki_links: self.interwiki_links[language] = [link] else: self.interwiki_links[language].append(link) self.interwikis = interwikis self.logger = logger self.edited = False self.created = [] self.deleted = [] self.lang = lang def interwiki_exists(self, idioma): return idioma in self.interwiki_links def __fetch_interwiki(self, idioma): for iw in self.interwikis: if iw.language == idioma: return iw return None def get_interwiki_wikicode(self, idioma, articulo): interwiki = self.__fetch_interwiki(idioma) if interwiki is not None and interwiki.fake: interwiki_wikicode = "{{" + idioma + "|" + articulo + "}}" else: interwiki_wikicode = "[[" + idioma + ":" + articulo + "]]" return interwiki_wikicode def remove_interwiki(self, language, reason, article): self.logger.info( self.lang.t("interwiki.removing").format(reason=reason, lang=language, src=self.page.name, dest=article)) self.edited = True self.wikitext = self.wikitext.replace( self.get_interwiki_wikicode(language, article), "") self.deleted.append(language) if language in self.interwiki_links: del self.interwiki_links[language] def create_interwiki(self, language, article): self.logger.info( self.lang.t("interwiki.creating").format(lang=language, src=self.page.name, dest=article)) self.edited = True self.wikitext = self.wikitext + "\n" + self.get_interwiki_wikicode( language, article) self.created.append(language) def locate_article_on_interwiki(self, language, article, existed_earlier=False): interwiki = self.__fetch_interwiki(language) if interwiki is not None and interwiki.skip: return None if interwiki is None: self.logger.debug( self.lang.t("interwiki.dbg_obsolete").format( lang=language, src=self.page.name, dest=article)) self.remove_interwiki(language, self.lang.t("interwiki.obsolete"), article) return None url = "http://" + interwiki.api + "/wiki/" + quote(article) try: response = requests.get(url) except: # some requests error , don't change stuff return article if existed_earlier else None if response.status_code == 404: self.logger.debug( self.lang.t("interwiki.dbg_not_found").format( lang=language, src=self.page.name, dest=article)) if existed_earlier: self.remove_interwiki(language, self.lang.t("interwiki.not_found"), article) return None elif response.status_code == 200: self.logger.debug( self.lang.t("interwiki.dbg_found").format(lang=language, src=self.page.name, dest=article)) if not existed_earlier: self.create_interwiki(language, article) return article else: # some other error , don't change stuff return article if existed_earlier else None def missing_interwikis(self): missing = [] for interwiki in self.interwikis: found = False if not interwiki.fake: if interwiki.language in self.interwiki_links: found = True else: if re.search(r"{{" + interwiki.language + r"\|[^}]+(?:}){2}\n", self.wikitext) is not None: found = True if not found: missing.append(interwiki.language) return missing def clean_broken_interwikis(self): self.logger.debug( self.lang.t("interwiki.cleanup").format(name=self.page.name)) for lang, articles in list(self.interwiki_links.items()): for article in articles: self.locate_article_on_interwiki(lang, article, existed_earlier=True) def generate_summary(self): tasks_done = [] if len(self.created) > 0: tasks_done.append( self.lang.t("interwiki.reason_added") + ", ".join(self.created)) if len(self.deleted) > 0: tasks_done.append( self.lang.t("interwiki.reason_removed") + ", ".join(self.deleted)) return self.lang.t("interwiki.reason_header") + "; ".join(tasks_done) def save_changes(self): if self.edited: self.logger.info( self.lang.t("interwiki.saving").format(name=self.page.name)) self.page.edit(self.wikitext, summary=self.generate_summary()) self.created = [] self.deleted = [] self.edited = False self.page = Page(self.page.site, self.page.name) self.wikitext = self.page.text() self.interwiki_links = dict() for lang, link in self.page.langlinks(): if lang not in self.interwiki_links: self.interwiki_links[lang] = [link] else: self.interwiki_links[lang].append(link)
def find_article_in_wikipedia(self, nombre) -> Optional[Page]: wp_article = Page(self.wp_client, nombre) if wp_article.redirect: wp_article = wp_article.resolve_redirect() return wp_article if wp_article.exists else None