jenkins-bot has submitted this change and it was merged.
Change subject: Allow APISite randompages to yield indefinitely
......................................................................
Allow APISite randompages to yield indefinitely
randompages currently supplies a default limit of 10,
and will stop after the first batch if limit is None.
Change-Id: I67066d587f70978f33bb48d341f3e90b4f17bdd3
---
M pywikibot/data/api.py
M tests/site_tests.py
2 files changed, 23 insertions(+), 3 deletions(-)
Approvals:
XZise: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/data/api.py b/pywikibot/data/api.py
index 0400f5d..c0b6e2a 100644
--- a/pywikibot/data/api.py
+++ b/pywikibot/data/api.py
@@ -1519,7 +1519,7 @@
# self.resultkey not in data in last request.submit()
# only "(query-)continue" was retrieved.
previous_result_had_data = False
- if self.modules[0] == "random" and self.limit:
+ if self.modules[0] == "random":
# "random" module does not return "(query-)continue"
# now we loop for a new random query
del self.data # a new request is needed
diff --git a/tests/site_tests.py b/tests/site_tests.py
index e738859..ae2c17f 100644
--- a/tests/site_tests.py
+++ b/tests/site_tests.py
@@ -1166,17 +1166,37 @@
"""Test random methods of a site."""
- def testRandompages(self):
- """Test the site.randompages() method."""
+ def test_unlimited_small_step(self):
+ """Test site.randompages() without limit."""
+ mysite = self.get_site()
+ pages = []
+ for rndpage in mysite.randompages(step=5, total=None):
+ self.assertIsInstance(rndpage, pywikibot.Page)
+ self.assertNotIn(rndpage, pages)
+ pages.append(rndpage)
+ if len(pages) == 11:
+ break
+ self.assertEqual(len(pages), 11)
+
+ def test_limit_10(self):
+ """Test site.randompages() with limit."""
mysite = self.get_site()
rn = list(mysite.randompages(total=10))
self.assertLessEqual(len(rn), 10)
self.assertTrue(all(isinstance(a_page, pywikibot.Page)
for a_page in rn))
self.assertFalse(all(a_page.isRedirectPage() for a_page in rn))
+
+ def test_redirects(self):
+ """Test site.randompages() with redirects."""
+ mysite = self.get_site()
for rndpage in mysite.randompages(total=5, redirects=True):
self.assertIsInstance(rndpage, pywikibot.Page)
self.assertTrue(rndpage.isRedirectPage())
+
+ def test_namespaces(self):
+ """Test site.randompages() with namespaces."""
+ mysite = self.get_site()
for rndpage in mysite.randompages(total=5, namespaces=[6, 7]):
self.assertIsInstance(rndpage, pywikibot.Page)
self.assertIn(rndpage.namespace(), [6, 7])
--
To view, visit https://gerrit.wikimedia.org/r/175993
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I67066d587f70978f33bb48d341f3e90b4f17bdd3
Gerrit-PatchSet: 1
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: Ladsgroup <ladsgroup(a)gmail.com>
Gerrit-Reviewer: Merlijn van Deen <valhallasw(a)arctus.nl>
Gerrit-Reviewer: XZise <CommodoreFabianus(a)gmx.de>
Gerrit-Reviewer: jenkins-bot <>
jenkins-bot has submitted this change and it was merged.
Change subject: [FIX] Sametitle: Don't discard invalid namespaces
......................................................................
[FIX] Sametitle: Don't discard invalid namespaces
If the namespace was invalid, sametitle would've still just compared the
text after the 'namespace' so a page title like 'Foo:Bar' would result
in 'namespace main' and 'name is Bar' although it should've been 'name
is Foo:Bar' because 'Foo' is not a valid namespace.
Also modified the tests so this will be more visible in the future.
Change-Id: Idd42d63125fa2470374c0392e26cceac9e49bb30
---
M pywikibot/site.py
M tests/site_tests.py
2 files changed, 24 insertions(+), 15 deletions(-)
Approvals:
John Vandenberg: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/site.py b/pywikibot/site.py
index 009adeb..d83a098 100644
--- a/pywikibot/site.py
+++ b/pywikibot/site.py
@@ -818,11 +818,13 @@
"""
def ns_split(title):
"""Separate the namespace from the name."""
- if ':' not in title:
- title = ':' + title
- ns, _, name = title.partition(':')
- ns = Namespace.lookup_name(ns, self.namespaces) or default_ns
- return ns, name
+ ns, delim, name = title.partition(':')
+ if delim:
+ ns = Namespace.lookup_name(ns, self.namespaces)
+ if not delim or not ns:
+ return default_ns, title
+ else:
+ return ns, name
if title1 == title2:
return True
diff --git a/tests/site_tests.py b/tests/site_tests.py
index e738859..82e11f5 100644
--- a/tests/site_tests.py
+++ b/tests/site_tests.py
@@ -1905,7 +1905,7 @@
self.assertEqual(item.id, 'Q5296')
-class TestSameTitleSite(TestCase):
+class TestSametitleSite(TestCase):
"""Test APISite.sametitle on sites with known behaviour."""
@@ -1924,27 +1924,34 @@
}
}
- def check(self, site, case_sensitive):
- self.assertEqual(site.sametitle('Foo', 'foo'), not case_sensitive)
- self.assertTrue(site.sametitle('File:Foo', 'Image:Foo'))
- self.assertTrue(site.sametitle(':Foo', 'Foo'))
- self.assertFalse(site.sametitle('User:Foo', 'Foo'))
-
def test_enwp(self):
- self.check(self.get_site('enwp'), False)
+ self.assertTrue(self.get_site('enwp').sametitle('Foo', 'foo'))
self.assertFalse(self.get_site('enwp').sametitle(
'Template:Test template', 'Template:Test Template'))
def test_dewp(self):
site = self.get_site('dewp')
- self.check(site, False)
+ self.assertTrue(site.sametitle('Foo', 'foo'))
self.assertTrue(site.sametitle('Benutzer:Foo', 'User:Foo'))
self.assertTrue(site.sametitle('Benutzerin:Foo', 'User:Foo'))
self.assertTrue(site.sametitle('Benutzerin:Foo', 'Benutzer:Foo'))
def test_enwt(self):
- self.check(self.get_site('enwt'), True)
+ self.assertFalse(self.get_site('enwt').sametitle('Foo', 'foo'))
+ def test_general(self, code):
+ site = self.get_site(code)
+ self.assertTrue(site.sametitle('File:Foo', 'Image:Foo'))
+ self.assertTrue(site.sametitle(':Foo', 'Foo'))
+ self.assertFalse(site.sametitle('User:Foo', 'Foo'))
+ self.assertFalse(site.sametitle('User:Foo', 'Project:Foo'))
+
+ self.assertTrue(site.sametitle('Namespace:', 'Namespace:'))
+
+ self.assertFalse(site.sametitle('Invalid:Foo', 'Foo'))
+ self.assertFalse(site.sametitle('Invalid1:Foo', 'Invalid2:Foo'))
+ self.assertFalse(site.sametitle('Invalid:Foo', ':Foo'))
+ self.assertFalse(site.sametitle('Invalid:Foo', 'Invalid:foo'))
if __name__ == '__main__':
try:
--
To view, visit https://gerrit.wikimedia.org/r/176002
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Idd42d63125fa2470374c0392e26cceac9e49bb30
Gerrit-PatchSet: 1
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: XZise <CommodoreFabianus(a)gmx.de>
Gerrit-Reviewer: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: Ladsgroup <ladsgroup(a)gmail.com>
Gerrit-Reviewer: Merlijn van Deen <valhallasw(a)arctus.nl>
Gerrit-Reviewer: jenkins-bot <>
jenkins-bot has submitted this change and it was merged.
Change subject: Introduce weblinkchecker-badurl_msg
......................................................................
Introduce weblinkchecker-badurl_msg
We need this message to tell the users
the URL could not be checked at all
due to its incorrect format - as opposed
to 404 or some other error condition
Change-Id: I33c04301685050bc3daced8d6e7599b21e33f7fe
---
M weblinkchecker.py
M weblinkchecker/en.json
M weblinkchecker/qqq.json
3 files changed, 4 insertions(+), 0 deletions(-)
Approvals:
Xqt: Looks good to me, approved
jenkins-bot: Verified
diff --git a/weblinkchecker.py b/weblinkchecker.py
index 6b02833..ee41813 100644
--- a/weblinkchecker.py
+++ b/weblinkchecker.py
@@ -2,12 +2,14 @@
msg = {
'en': {
'weblinkchecker-archive_msg': u'The web page has been saved by the Internet Archive. Please consider linking to an appropriate archived version: [%(URL)s].',
+ 'weblinkchecker-badurl_msg': u'The link provided does not appear to be a valid URL',
'weblinkchecker-caption': u'Dead link',
'weblinkchecker-summary': u'Bot: Reporting unavailable external link',
'weblinkchecker-report': u'During several automated bot runs the following external link was found to be unavailable. Please check if the link is in fact down and fix or remove it in that case!',
},
'qqq': {
'weblinkchecker-archive_msg': u'weblinkchecker report message for web archives',
+ 'weblinkchecker-badurl_msg': u'weblinkchecker report message if the link in the article is not a valid URL',
'weblinkchecker-caption': u'The weblinkchecker report\'s caption',
'weblinkchecker-summary': u'Edit summary for weblinkchecker report',
'weblinkchecker-report': u'The weblinkchecker report',
diff --git a/weblinkchecker/en.json b/weblinkchecker/en.json
index b2b9d36..a322664 100644
--- a/weblinkchecker/en.json
+++ b/weblinkchecker/en.json
@@ -1,5 +1,6 @@
{
"weblinkchecker-archive_msg": "The web page has been saved by the Internet Archive. Please consider linking to an appropriate archived version: [%(URL)s].",
+ "weblinkchecker-badurl": "The link provided does not seem to be a valid URL",
"weblinkchecker-caption": "Dead link",
"weblinkchecker-summary": "Bot: Reporting unavailable external link",
"weblinkchecker-report": "During several automated bot runs the following external link was found to be unavailable. Please check if the link is in fact down and fix or remove it in that case!"
diff --git a/weblinkchecker/qqq.json b/weblinkchecker/qqq.json
index 5df3d60..43d0eb9 100644
--- a/weblinkchecker/qqq.json
+++ b/weblinkchecker/qqq.json
@@ -1,6 +1,7 @@
{
"@metadata": [],
"weblinkchecker-archive_msg": "weblinkchecker report message for web archives",
+ "weblinkchecker-badurl_msg": "weblinkchecker report message if the link in the article is not a valid URL",
"weblinkchecker-caption": "The weblinkchecker report's caption",
"weblinkchecker-summary": "Edit summary for weblinkchecker report",
"weblinkchecker-report": "The weblinkchecker report"
--
To view, visit https://gerrit.wikimedia.org/r/175632
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I33c04301685050bc3daced8d6e7599b21e33f7fe
Gerrit-PatchSet: 4
Gerrit-Project: pywikibot/i18n
Gerrit-Branch: master
Gerrit-Owner: saper <saper(a)saper.info>
Gerrit-Reviewer: Merlijn van Deen <valhallasw(a)arctus.nl>
Gerrit-Reviewer: Siebrand <siebrand(a)kitano.nl>
Gerrit-Reviewer: Xqt <info(a)gno.de>
Gerrit-Reviewer: jenkins-bot <>
jenkins-bot has submitted this change and it was merged.
Change subject: Introduce InterwikiRedirectPage
......................................................................
Introduce InterwikiRedirectPage
Interwiki links in redirects will raise InterwikiRedirectPage instead
of CircularRedirect exception.
Interwiki redirects are considered an invalid page by Pywikibot due
to limited support for this kind of redirects on Mediawiki.
The API can't follow redirects if the redirect is an interwiki link.
Also fixed:
- uniform importing of exceptions in site.py
- pep257 in pywikibot.exceptions
Bug: 73184
Change-Id: Ia0d4dadf713fb97572c5d482485858331bda5ea8
---
M pywikibot/exceptions.py
M pywikibot/site.py
2 files changed, 87 insertions(+), 22 deletions(-)
Approvals:
John Vandenberg: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/exceptions.py b/pywikibot/exceptions.py
index 298ef6c..8300ecd 100644
--- a/pywikibot/exceptions.py
+++ b/pywikibot/exceptions.py
@@ -22,6 +22,7 @@
- IsRedirectPage: Page is a redirect page
- IsNotRedirectPage: Page is not a redirect page
- CircularRedirect: Page is a circular redirect
+ - InterwikiRedirectPage: Page is a redirect to another site.
- SectionError: The section specified by # does not exist
PageSaveRelatedError: page exceptions within the save operation on a Page
@@ -62,9 +63,11 @@
# NOTE: UnicodeMixin must be the first object Error class is derived from.
def __init__(self, arg):
+ """Constructor."""
self.unicode = arg
def __unicode__(self):
+ """Return a unicode string representation."""
return self.unicode
@@ -77,7 +80,7 @@
Page, and when a generic message can be written once for all.
"""
- # Preformated UNICODE message where the page title will be inserted
+ # Preformatted UNICODE message where the page title will be inserted
# Override this in subclasses.
# u"Oh noes! Page %s is too funky, we should not delete it ;("
message = None
@@ -105,6 +108,7 @@
super(PageRelatedError, self).__init__(self.message % page)
def getPage(self):
+ """Return the page related to the exception."""
return self.page
@@ -120,6 +124,7 @@
# which could be printed
@property
def args(self):
+ """Expose args."""
return unicode(self)
@@ -140,6 +145,7 @@
@property
def args(self):
+ """Expose args."""
return unicode(self.reason)
@@ -215,6 +221,30 @@
"""
message = u"Page %s is a circular redirect."
+
+
+class InterwikiRedirectPage(PageRelatedError):
+
+ """
+ Page is a redirect to another site.
+
+ This is considered invalid in Pywikibot. See Bug 73184.
+
+ """
+
+ message = (u"Page redirects to a page on another Site.\n"
+ u"Page: %(page)s\n"
+ u"Target page: %(target_page)s on %(target_site)s.")
+
+ def __init__(self, page, target_page):
+ """ Constructor.
+
+ @param target_page: Target page of the redirect.
+ @type reason: Page
+ """
+ self.target_page = target_page
+ self.target_site = target_page.site
+ super(InterwikiRedirectPage, self).__init__(page)
class InvalidTitle(Error): # noqa
@@ -313,6 +343,7 @@
message = "Edit to page %(title)s rejected by spam filter due to content:\n%(url)s"
def __init__(self, page, url):
+ """Constructor."""
self.url = url
super(SpamfilterError, self).__init__(page)
diff --git a/pywikibot/site.py b/pywikibot/site.py
index ba1b00c..8bcb658 100644
--- a/pywikibot/site.py
+++ b/pywikibot/site.py
@@ -42,6 +42,9 @@
PageCreatedConflict,
PageDeletedConflict,
ArticleExistsConflict,
+ IsNotRedirectPage,
+ CircularRedirect,
+ InterwikiRedirectPage,
LockedPage,
CascadeLockedPage,
LockedNoPage,
@@ -52,6 +55,7 @@
SpamfilterError,
NoCreateError,
UserBlocked,
+ EntityTypeUnknownException,
)
from pywikibot.echo import Notification
@@ -447,7 +451,7 @@
and oldcode == pywikibot.config.mylang:
pywikibot.config.mylang = self.__code
else:
- raise UnknownSite("Language %s does not exist in family %s"
+ raise UnknownSite("Language '%s' does not exist in family %s"
% (self.__code, self.__family.name))
self.nocapitalize = self.code in self.family.nocapitalize
@@ -2341,9 +2345,10 @@
def getredirtarget(self, page):
"""Return Page object for the redirect target of page."""
if not self.page_isredirect(page):
- raise pywikibot.IsNotRedirectPage(page)
+ raise IsNotRedirectPage(page)
if hasattr(page, '_redirtarget'):
return page._redirtarget
+
title = page.title(withSection=False)
query = api.Request(site=self, action="query", prop="info",
inprop="protection|talkid|subjectid",
@@ -2354,35 +2359,64 @@
raise RuntimeError(
"getredirtarget: No 'redirects' found for page %s."
% title.encode(self.encoding()))
+
redirmap = dict((item['from'],
{'title': item['to'],
'section': u'#' + item['tofragment']
if 'tofragment' in item and item['tofragment']
else ''})
for item in result['query']['redirects'])
- if 'normalized' in result['query']:
- for item in result['query']['normalized']:
- if item['from'] == title:
- title = item['to']
- break
+
+ # Normalize title
+ for item in result['query'].get('normalized', []):
+ if item['from'] == title:
+ title = item['to']
+ break
+
if title not in redirmap:
raise RuntimeError(
"getredirtarget: 'redirects' contains no key for page %s."
% title.encode(self.encoding()))
target_title = u'%(title)s%(section)s' % redirmap[title]
- if target_title == title or "pages" not in result['query']:
- # no "pages" element indicates a circular redirect
- raise pywikibot.CircularRedirect(page)
+
+ if self.sametitle(title, target_title):
+ raise CircularRedirect(page)
+
+ if "pages" not in result['query']:
+ # No "pages" element might indicate a circular redirect
+ # Check that a "to" link is also a "from" link in redirmap
+ for _from, _to in redirmap.items():
+ if _to['title'] in redirmap:
+ raise CircularRedirect(page)
+ else:
+ target = pywikibot.Page(source=page.site, title=target_title)
+
+ # Check if target is on another site.
+ if target.site != page.site:
+ raise InterwikiRedirectPage(page, target)
+ else:
+ # Redirect to Special: & Media: pages, which do not work
+ # like redirects, but are rendered like a redirect.
+ page._redirtarget = target
+ return page._redirtarget
+
pagedata = list(result['query']['pages'].values())[0]
- # there should be only one value in 'pages', and it is the target
+ # There should be only one value in 'pages' (the ultimate
+ # target, also in case of double redirects).
if self.sametitle(pagedata['title'], target_title):
+ # target_title is the ultimate target
target = pywikibot.Page(self, pagedata['title'], pagedata['ns'])
api.update_page(target, pagedata, ['info'])
page._redirtarget = target
else:
- # double redirect; target is an intermediate redirect
+ # Target is an intermediate redirect -> double redirect.
+ # Do not bypass double-redirects and return the ultimate target;
+ # it would be impossible to detect and fix double-redirects.
+ # This handles also redirects to sections, as sametitle()
+ # does not ignore sections.
target = pywikibot.Page(self, target_title)
page._redirtarget = target
+
return page._redirtarget
def preloadpages(self, pagelist, groupsize=50, templates=False,
@@ -3272,12 +3306,12 @@
if starttime and endtime:
if reverse:
if starttime > endtime:
- raise pywikibot.Error(
+ raise Error(
"blocks: "
"starttime must be before endtime with reverse=True")
else:
if endtime > starttime:
- raise pywikibot.Error(
+ raise Error(
"blocks: "
"endtime must be before starttime with reverse=False")
bkgen = self._generator(api.ListGenerator, type_arg="blocks",
@@ -4002,7 +4036,7 @@
"""
if len(page._revisions) < 2:
- raise pywikibot.Error(
+ raise Error(
u"Rollback of %s aborted; load revision history first."
% page.title(asLink=True))
last_rev = page._revisions[page.latestRevision()]
@@ -4012,7 +4046,7 @@
if rev.user != last_user:
break
else:
- raise pywikibot.Error(
+ raise Error(
u"Rollback of %s aborted; only one user in revision history."
% page.title(asLink=True))
token = self.tokens["rollback"]
@@ -4428,7 +4462,7 @@
# check for required user right
if "upload" not in self.userinfo["rights"]:
- raise pywikibot.Error(
+ raise Error(
"User '%s' does not have upload rights on site %s."
% (self.user(), self))
# check for required parameters
@@ -4516,7 +4550,7 @@
else:
# upload by URL
if "upload_by_url" not in self.userinfo["rights"]:
- raise pywikibot.Error(
+ raise Error(
"User '%s' is not authorized to upload by URL on site %s."
% (self.user(), self))
req = api.Request(site=self, action="upload", token=token,
@@ -4908,7 +4942,7 @@
if isinstance(self._item_namespace, Namespace):
return self._item_namespace
else:
- raise pywikibot.exceptions.EntityTypeUnknownException(
+ raise EntityTypeUnknownException(
'%r does not support entity type "item"'
% self)
@@ -4926,7 +4960,7 @@
if isinstance(self._property_namespace, Namespace):
return self._property_namespace
else:
- raise pywikibot.exceptions.EntityTypeUnknownException(
+ raise EntityTypeUnknownException(
'%r does not support entity type "property"'
% self)
@@ -5130,7 +5164,7 @@
raise NotImplementedError
if not claim.snak:
# We need to already have the snak value
- raise pywikibot.NoPage(claim)
+ raise NoPage(claim)
params = dict(action='wbsetclaimvalue',
claim=claim.snak,
snaktype=snaktype,
--
To view, visit https://gerrit.wikimedia.org/r/172176
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Ia0d4dadf713fb97572c5d482485858331bda5ea8
Gerrit-PatchSet: 7
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Mpaa <mpaa.wiki(a)gmail.com>
Gerrit-Reviewer: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: Ladsgroup <ladsgroup(a)gmail.com>
Gerrit-Reviewer: Merlijn van Deen <valhallasw(a)arctus.nl>
Gerrit-Reviewer: Mpaa <mpaa.wiki(a)gmail.com>
Gerrit-Reviewer: XZise <CommodoreFabianus(a)gmx.de>
Gerrit-Reviewer: Xqt <info(a)gno.de>
Gerrit-Reviewer: jenkins-bot <>
jenkins-bot has submitted this change and it was merged.
Change subject: [FIX] Replace: Update expected message
......................................................................
[FIX] Replace: Update expected message
Because babc7cef9b7805b0bb8c2b5abab4b715e7fc1e7a changed the returned
message the script_tests must now expect the new message.
Change-Id: I2b66019e8d817938df13b517eac8f44b1bba3cf8
---
M tests/script_tests.py
1 file changed, 1 insertion(+), 1 deletion(-)
Approvals:
John Vandenberg: Looks good to me, approved
jenkins-bot: Verified
diff --git a/tests/script_tests.py b/tests/script_tests.py
index e37cda9..8536715 100644
--- a/tests/script_tests.py
+++ b/tests/script_tests.py
@@ -129,7 +129,7 @@
'interwiki': 'does not exist. Skipping.', # 'Test page' does not exist
'login': 'Logged in on ',
'pagefromfile': 'Please enter the file name',
- 'replace': 'Press Enter to use this default message',
+ 'replace': 'Press Enter to use this automatic message',
'script_wui': 'Pre-loading all relevant page contents',
'shell': 'Welcome to the',
'spamremove': 'No spam site specified',
--
To view, visit https://gerrit.wikimedia.org/r/175649
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I2b66019e8d817938df13b517eac8f44b1bba3cf8
Gerrit-PatchSet: 1
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: XZise <CommodoreFabianus(a)gmx.de>
Gerrit-Reviewer: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: Ladsgroup <ladsgroup(a)gmail.com>
Gerrit-Reviewer: Merlijn van Deen <valhallasw(a)arctus.nl>
Gerrit-Reviewer: jenkins-bot <>
jenkins-bot has submitted this change and it was merged.
Change subject: Add whitespace to -help
......................................................................
Add whitespace to -help
Change-Id: I07901e73516fd8c98fc472b8d2ace0d4fe4e4303
---
M scripts/pagefromfile.py
1 file changed, 2 insertions(+), 0 deletions(-)
Approvals:
John Vandenberg: Looks good to me, but someone else must approve
Ladsgroup: Looks good to me, approved
jenkins-bot: Verified
diff --git a/scripts/pagefromfile.py b/scripts/pagefromfile.py
index cf067ba..9f28900 100644
--- a/scripts/pagefromfile.py
+++ b/scripts/pagefromfile.py
@@ -16,6 +16,7 @@
the -include option.
Specific arguments:
+
-start:xxx Specify the text that marks the beginning of a page
-end:xxx Specify the text that marks the end of a page
-file:xxx Give the filename we are getting our material from
@@ -40,6 +41,7 @@
-minor set minor edit flag on page edits
If the page to be uploaded already exists:
+
-safe do nothing (default)
-appendtop add the text to the top of it
-appendbottom add the text to the bottom of it
--
To view, visit https://gerrit.wikimedia.org/r/175482
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I07901e73516fd8c98fc472b8d2ace0d4fe4e4303
Gerrit-PatchSet: 2
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: Ladsgroup <ladsgroup(a)gmail.com>
Gerrit-Reviewer: Merlijn van Deen <valhallasw(a)arctus.nl>
Gerrit-Reviewer: jenkins-bot <>
jenkins-bot has submitted this change and it was merged.
Change subject: [FEAT] Replace: Support multiple replacements
......................................................................
[FEAT] Replace: Support multiple replacements
This supports multiple -fix and command line replacements at once. Each
fix set and also each fix in that set could define it's own edit summary
(the third value in the tuple).
If no user defined summary is given it will only use the summaries of
the applied replacements. For the replacements given via the command
line it will create one merged replacement message. The rest are added
sorted alphabetically after it.
Change-Id: Ic14a8236d143050b267bee9308006b4d14d8ca6d
---
M scripts/replace.py
1 file changed, 160 insertions(+), 103 deletions(-)
Approvals:
John Vandenberg: Looks good to me, approved
XZise: Looks good to me, but someone else must approve
jenkins-bot: Verified
diff --git a/scripts/replace.py b/scripts/replace.py
index cd9fcfb..014e1dd 100755
--- a/scripts/replace.py
+++ b/scripts/replace.py
@@ -81,6 +81,8 @@
-allowoverlap When occurences of the pattern overlap, replace all of them.
Be careful, this might lead to an infinite loop.
+-fullsummary Use one large summary for all command line replacements.
+
other: First argument is the old text, second argument is the new
text. If the -regex argument is given, the first argument
will be regarded as a regular expression, and the second
@@ -145,6 +147,54 @@
'¶ms;': pagegenerators.parameterHelp,
'&fixes-help;': fixes.help,
}
+
+
+def precompile_exceptions(exceptions, use_regex, flags):
+ if not exceptions:
+ return
+ for exceptionCategory in [
+ 'title', 'require-title', 'text-contains', 'inside']:
+ if exceptionCategory in exceptions:
+ patterns = exceptions[exceptionCategory]
+ if not use_regex:
+ patterns = [re.escape(pattern) for pattern in patterns]
+ patterns = [re.compile(pattern, flags) for pattern in patterns]
+ exceptions[exceptionCategory] = patterns
+
+
+class Replacement(object):
+
+ """The replacement instructions."""
+
+ def __init__(self, old, new, use_regex=None, exceptions=None,
+ case_insensitive=None, edit_summary=None,
+ default_summary=True):
+ self.old = old
+ self.old_regex = None
+ self.new = new
+ self.use_regex = use_regex
+ self.exceptions = exceptions
+ self.case_insensitive = case_insensitive
+ self.edit_summary = edit_summary
+ self.default_summary = default_summary
+
+ def compile(self, flags, use_regex):
+ # Set the regular aexpression flags
+ flags |= re.UNICODE
+
+ if self.case_insensitive is False:
+ flags &= ~re.IGNORECASE
+ elif self.case_insensitive:
+ flags |= re.IGNORECASE
+
+ if self.use_regex is not None:
+ use_regex = self.use_regex # this replacement overrides it
+ if not use_regex:
+ self.old_regex = re.escape(self.old)
+ else:
+ self.old_regex = self.old
+ self.old_regex = re.compile(self.old_regex, flags)
+ precompile_exceptions(self.exceptions, use_regex, flags)
class XmlDumpReplacePageGenerator(object):
@@ -328,30 +378,62 @@
return True
return False
- def doReplacements(self, original_text):
+ def apply_replacements(self, original_text, applied):
"""
Apply all replacements to the given text.
- @rtype: unicode
+ @rtype: unicode, set
"""
+ def get_exceptions(exceptions):
+ return exceptions.get('inside-tags', []) + exceptions.get('inside', [])
new_text = original_text
- exceptions = []
- if "inside-tags" in self.exceptions:
- exceptions += self.exceptions['inside-tags']
- if "inside" in self.exceptions:
- exceptions += self.exceptions['inside']
- for old, new in self.replacements:
+ exceptions = get_exceptions(self.exceptions)
+ for replacement in self.replacements:
if self.sleep is not None:
time.sleep(self.sleep)
- new_text = textlib.replaceExcept(new_text, old, new, exceptions,
- allowoverlap=self.allowoverlap,
- site=self.site)
+ old_text = new_text
+ new_text = textlib.replaceExcept(
+ new_text, replacement.old_regex, replacement.new,
+ exceptions + get_exceptions(replacement.exceptions or {}),
+ allowoverlap=self.allowoverlap, site=self.site)
+ if old_text != new_text:
+ applied.add(replacement)
+
return new_text
+
+ def doReplacements(self, original_text):
+ return self.apply_replacements(original_text, set())
def count_changes(self, page, err):
"""Count succesfully changed pages."""
if not isinstance(err, Exception):
self.changed_pages += 1
+
+ def generate_summary(self, applied_replacements):
+ """Generate a summary message for the replacements."""
+ # all replacements which are merged into the default message
+ default_summaries = set()
+ # all message parts
+ summary_messages = set()
+ for replacement in applied_replacements:
+ if replacement.edit_summary:
+ summary_messages.add(replacement.edit_summary)
+ elif replacement.default_summary:
+ default_summaries.add((replacement.old, replacement.new))
+ summary_messages = sorted(summary_messages)
+ if default_summaries:
+ if self.summary:
+ summary_messages.insert(0, self.summary)
+ else:
+ default_summary = ', '.join(
+ u'-{0} +{1}'.format(default_summary)
+ for default_summary in default_summaries)
+ summary_messages.insert(0, i18n.twtranslate(
+ pywikibot.Site(), 'replace-replacing',
+ {'description':
+ u' ({0})'.format(default_summary)}
+ ))
+ return u'; '.join(summary_messages)
def run(self):
"""Start the bot."""
@@ -373,6 +455,7 @@
except pywikibot.NoPage:
pywikibot.output(u'Page %s not found' % page.title(asLink=True))
continue
+ applied = set()
new_text = original_text
while True:
if self.isTextExcepted(new_text):
@@ -380,16 +463,16 @@
u'that is on the exceptions list.'
% page.title(asLink=True))
break
- new_text = self.doReplacements(new_text)
+ last_text = None
+ while new_text != last_text:
+ last_text = new_text
+ new_text = self.apply_replacements(last_text, applied)
+ if not self.recursive:
+ break
if new_text == original_text:
pywikibot.output(u'No changes were necessary in %s'
% page.title(asLink=True))
break
- if self.recursive:
- newest_text = self.doReplacements(new_text)
- while (newest_text != new_text):
- new_text = newest_text
- newest_text = self.doReplacements(new_text)
if hasattr(self, "addedCat"):
cats = page.categories(nofollow_redirects=True)
if self.addedCat not in cats:
@@ -433,12 +516,12 @@
if choice == 'a':
self.acceptall = True
if choice == 'y':
- page.put_async(new_text, self.summary, callback=self.count_changes)
+ page.put_async(new_text, self.generate_summary(applied), callback=self.count_changes)
# choice must be 'N'
break
if self.acceptall and new_text != original_text:
try:
- page.put(new_text, self.summary, callback=self.count_changes)
+ page.put(new_text, self.generate_summary(applied), callback=self.count_changes)
except pywikibot.EditConflict:
pywikibot.output(u'Skipping %s because of edit conflict'
% (page.title(),))
@@ -479,7 +562,6 @@
add_cat = None
gen = None
# summary message
- summary_commandline = False
edit_summary = u""
# Array which will collect commandline parameters.
# First element is original text, second element is replacement text.
@@ -499,7 +581,7 @@
# as regular expressions?
regex = False
# Predefined fixes from dictionary 'fixes' (see above).
- fix = None
+ fixes_set = []
# the dump's path, either absolute or relative, which will be used
# if -xml flag is present
xmlFilename = None
@@ -555,7 +637,7 @@
elif arg.startswith('-exceptinsidetag:'):
exceptions['inside-tags'].append(arg[17:])
elif arg.startswith('-fix:'):
- fix = arg[5:]
+ fixes_set += [arg[5:]]
elif arg.startswith('-sleep:'):
sleep = float(arg[7:])
elif arg == '-always':
@@ -572,7 +654,6 @@
add_cat = arg[8:]
elif arg.startswith('-summary:'):
edit_summary = arg[9:]
- summary_commandline = True
elif arg.startswith('-allowoverlap'):
allowoverlap = True
else:
@@ -582,83 +663,70 @@
if (len(commandline_replacements) % 2):
raise pywikibot.Error('require even number of replacements.')
- elif (len(commandline_replacements) == 2 and fix is None):
- replacements.append((commandline_replacements[0],
- commandline_replacements[1]))
- if not summary_commandline:
- edit_summary = i18n.twtranslate(
- site, 'replace-replacing',
- {'description': ' (-%s +%s)' % (commandline_replacements[0],
- commandline_replacements[1])}
- )
- elif (len(commandline_replacements) > 1):
- if (fix is None):
- for i in range(0, len(commandline_replacements), 2):
- replacements.append((commandline_replacements[i],
- commandline_replacements[i + 1]))
- if not summary_commandline:
- pairs = [(commandline_replacements[i],
- commandline_replacements[i + 1])
- for i in range(0, len(commandline_replacements), 2)]
- replacementsDescription = '(%s)' % ', '.join(
- [('-' + pair[0] + ' +' + pair[1]) for pair in pairs])
- edit_summary = i18n.twtranslate(site,
- 'replace-replacing',
- {'description':
- replacementsDescription})
+ if not commandline_replacements:
+ if fixes_set:
+ manual = pywikibot.input_yn('Replacements via -fix: set. Apply '
+ 'also manual replacements?', default=False)
else:
- raise pywikibot.Error(
- 'Specifying -fix with replacements is undefined')
- elif fix is None:
- old = pywikibot.input(u'Please enter the text that should be replaced:')
- new = pywikibot.input(u'Please enter the new text:')
- change = '(-' + old + ' +' + new
- replacements.append((old, new))
- while True:
- old = pywikibot.input(
- u'Please enter another text that should be replaced,' +
- u'\nor press Enter to start:')
- if old == '':
- change += ')'
- break
- new = i18n.input('pywikibot-enter-new-text')
- change += ' & -' + old + ' +' + new
- replacements.append((old, new))
- if not summary_commandline:
- default_summary_message = i18n.twtranslate(site,
- 'replace-replacing',
- {'description': change})
- pywikibot.output(u'The summary message will default to: %s'
- % default_summary_message)
- summary_message = pywikibot.input(
- u'Press Enter to use this default message, or enter a ' +
- u'description of the\nchanges your bot will make:')
- if summary_message == '':
- summary_message = default_summary_message
- edit_summary = summary_message
+ manual = True
+ if manual:
+ old = pywikibot.input(u'Please enter the text that should be replaced:')
+ while old:
+ new = pywikibot.input(u'Please enter the new text:')
+ commandline_replacements += [old, new]
+ old = pywikibot.input(
+ u'Please enter another text that should be replaced,' +
+ u'\nor press Enter to start:')
- else:
- # Perform one of the predefined actions.
+ single_summary = None
+ for i in range(0, len(commandline_replacements), 2):
+ replacement = Replacement(commandline_replacements[i],
+ commandline_replacements[i + 1])
+ if not single_summary:
+ single_summary = i18n.twtranslate(
+ site, 'replace-replacing',
+ {'description':
+ ' (-%s +%s)' % (replacement.old, replacement.new)}
+ )
+ replacements.append(replacement)
+
+ if not edit_summary:
+ if single_summary:
+ pywikibot.output(u'The summary message for the command line '
+ 'replacements will be something like: %s'
+ % single_summary)
+ if fixes_set:
+ pywikibot.output('If a summary is defined for the fix, this '
+ 'default summary won\'t be applied.')
+ edit_summary = pywikibot.input(
+ u'Press Enter to use this automatic message, or enter a ' +
+ u'description of the\nchanges your bot will make:')
+
+ # Perform one of the predefined actions.
+ for fix in fixes_set:
try:
fix = fixes.fixes[fix]
except KeyError:
pywikibot.output(u'Available predefined fixes are: %s'
% ', '.join(fixes.fixes.keys()))
return
- if "regex" in fix:
- regex = fix['regex']
if "msg" in fix:
if isinstance(fix['msg'], basestring):
- edit_summary = i18n.twtranslate(site,
- str(fix['msg']))
+ set_summary = i18n.twtranslate(site, str(fix['msg']))
else:
- edit_summary = i18n.translate(site,
- fix['msg'], fallback=True)
- if "exceptions" in fix:
- exceptions = fix['exceptions']
- if "nocase" in fix:
- caseInsensitive = fix['nocase']
- replacements = fix['replacements']
+ set_summary = i18n.translate(site, fix['msg'], fallback=True)
+ else:
+ set_summary = None
+ for replacement in fix['replacements']:
+ summary = set_summary if len(replacement) < 3 else replacement[2]
+ replacements.append(Replacement(
+ old=replacement[0],
+ new=replacement[1],
+ use_regex=fix.get('regex'),
+ edit_summary=summary,
+ exceptions=fix.get('exceptions'),
+ case_insensitive=fix.get('nocase')
+ ))
# Set the regular expression flags
flags = re.UNICODE
@@ -670,21 +738,10 @@
flags = flags | re.MULTILINE
# Pre-compile all regular expressions here to save time later
- for i in range(len(replacements)):
- old, new = replacements[i]
- if not regex:
- old = re.escape(old)
- oldR = re.compile(old, flags)
- replacements[i] = oldR, new
+ for replacement in replacements:
+ replacement.compile(regex, flags)
- for exceptionCategory in [
- 'title', 'require-title', 'text-contains', 'inside']:
- if exceptionCategory in exceptions:
- patterns = exceptions[exceptionCategory]
- if not regex:
- patterns = [re.escape(pattern) for pattern in patterns]
- patterns = [re.compile(pattern, flags) for pattern in patterns]
- exceptions[exceptionCategory] = patterns
+ precompile_exceptions(exceptions, regex, flags)
if xmlFilename:
try:
--
To view, visit https://gerrit.wikimedia.org/r/175228
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Ic14a8236d143050b267bee9308006b4d14d8ca6d
Gerrit-PatchSet: 10
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: XZise <CommodoreFabianus(a)gmx.de>
Gerrit-Reviewer: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: Ladsgroup <ladsgroup(a)gmail.com>
Gerrit-Reviewer: Martineznovo <martineznovo(a)gmail.com>
Gerrit-Reviewer: Merlijn van Deen <valhallasw(a)arctus.nl>
Gerrit-Reviewer: XZise <CommodoreFabianus(a)gmx.de>
Gerrit-Reviewer: jenkins-bot <>
jenkins-bot has submitted this change and it was merged.
Change subject: [FIX] Allow deprecation of trailing parameters
......................................................................
[FIX] Allow deprecation of trailing parameters
With 7aa43ba4a0c8e12bda4a62e32e062672ec8851fa the parameters of several
methods have been deprecated leaving just 'self'. Because only one or
two parameters were used before, the possibility is high that they were
used positionally (without the parameter name) and the @deprecated_args
syntax can't support that (reliably).
This adds a decorator to deprecate parameters which appeared at the end
of a method/function. The names of those parameters must be specified
and if that method/function is called with too many parameters those are
removed and a warning is issued.
Bug: T75489
Change-Id: I9035f3b6be86fe7b34e515e19affe8ef1a9a558e
---
M pywikibot/page.py
M pywikibot/site.py
M pywikibot/tools.py
M tests/deprecation_tests.py
4 files changed, 227 insertions(+), 11 deletions(-)
Approvals:
John Vandenberg: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/page.py b/pywikibot/page.py
index 2d71d8a..fb80790 100644
--- a/pywikibot/page.py
+++ b/pywikibot/page.py
@@ -49,7 +49,8 @@
SiteDefinitionError
)
from pywikibot.tools import (
- ComparableMixin, deprecated, deprecate_arg, deprecated_args
+ ComparableMixin, deprecated, deprecate_arg, deprecated_args,
+ remove_last_args
)
from pywikibot import textlib
@@ -241,7 +242,7 @@
title = title.replace(forbidden, '_')
return title
- @deprecated_args(decode=None, underscore=None)
+ @remove_last_args(('decode', 'underscore'))
def section(self):
"""Return the name of the section this Page refers to.
@@ -540,7 +541,7 @@
return self._lastNonBotUser
- @deprecated_args(datetime=None)
+ @remove_last_args(('datetime', ))
def editTime(self):
"""Return timestamp of last revision to page.
diff --git a/pywikibot/site.py b/pywikibot/site.py
index 2a55df6..8f984ca 100644
--- a/pywikibot/site.py
+++ b/pywikibot/site.py
@@ -29,7 +29,7 @@
import pywikibot.family
from pywikibot.tools import (
itergroup, deprecated, deprecate_arg, UnicodeMixin, ComparableMixin,
- redirect_func, add_decorated_full_name, deprecated_args,
+ redirect_func, add_decorated_full_name, deprecated_args, remove_last_args,
SelfCallDict, SelfCallString, signature,
)
from pywikibot.tools import MediaWikiVersion as LV
@@ -707,17 +707,17 @@
old_name='normalizeNamespace',
class_name='BaseSite')
- @deprecated_args(default=None)
+ @remove_last_args(('default', ))
def redirect(self):
"""Return list of localized redirect tags for the site."""
return [u"REDIRECT"]
- @deprecated_args(default=None)
+ @remove_last_args(('default', ))
def pagenamecodes(self):
"""Return list of localized PAGENAME tags for the site."""
return [u"PAGENAME"]
- @deprecated_args(default=None)
+ @remove_last_args(('default', ))
def pagename2codes(self):
"""Return list of localized PAGENAMEE tags for the site."""
return [u"PAGENAMEE"]
@@ -2015,7 +2015,7 @@
else:
return [word]
- @deprecated_args(default=None)
+ @remove_last_args(('default', ))
def redirect(self):
"""Return the localized #REDIRECT keyword."""
# return the magic word without the preceding '#' character
@@ -2038,12 +2038,12 @@
pattern = None
return BaseSite.redirectRegex(self, pattern)
- @deprecated_args(default=None)
+ @remove_last_args(('default', ))
def pagenamecodes(self):
"""Return list of localized PAGENAME tags for the site."""
return self.getmagicwords("pagename")
- @deprecated_args(default=None)
+ @remove_last_args(('default', ))
def pagename2codes(self):
"""Return list of localized PAGENAMEE tags for the site."""
return self.getmagicwords("pagenamee")
diff --git a/pywikibot/tools.py b/pywikibot/tools.py
index d34291d..7ff4008 100644
--- a/pywikibot/tools.py
+++ b/pywikibot/tools.py
@@ -705,6 +705,74 @@
return decorator
+def remove_last_args(arg_names):
+ """
+ Decorator to declare all args additionally provided deprecated.
+
+ All positional arguments appearing after the normal arguments are marked
+ deprecated. It marks also all keyword arguments present in arg_names as
+ deprecated. Any arguments (positional or keyword) which are not present in
+ arg_names are forwarded. For example a call with 3 parameters and the
+ original function requests one and arg_names contain one name will result
+ in an error, because the function got called with 2 parameters.
+
+ The decorated function may not use *args or **kwargs.
+
+ @param arg_names: The names of all arguments.
+ @type arg_names: iterable; for the most explanatory message it should
+ retain the given order (so not a set for example).
+ """
+ def decorator(obj):
+ """Outer wrapper.
+
+ The outer wrapper is used to create the decorating wrapper.
+
+ @param obj: function being wrapped
+ @type obj: object
+ """
+ def wrapper(*__args, **__kw):
+ """Replacement function.
+
+ @param __args: args passed to the decorated function
+ @type __args: list
+ @param __kwargs: kwargs passed to the decorated function
+ @type __kwargs: dict
+ @return: the value returned by the decorated function
+ @rtype: any
+ """
+ name = obj.__full_name__
+ args, varargs, kwargs, _ = inspect.getargspec(wrapper.__wrapped__)
+ if varargs is not None and kwargs is not None:
+ raise ValueError(u'{1} may not have * or ** args.'.format(
+ name))
+ deprecated = set(__kw) & set(arg_names)
+ if len(__args) > len(args):
+ deprecated.update(arg_names[:len(__args) - len(args)])
+ # remove at most |arg_names| entries from the back
+ new_args = tuple(__args[:max(len(args), len(__args) - len(arg_names))])
+ new_kwargs = dict((arg, val) for arg, val in __kw.items()
+ if arg not in arg_names)
+
+ if deprecated:
+ # sort them according to arg_names
+ deprecated = [arg for arg in arg_names if arg in deprecated]
+ warning(u"The trailing arguments ('{0}') of {1} are "
+ "deprecated. The value(s) provided for '{2}' have "
+ "been dropped.".format("', '".join(arg_names), name,
+ "', '".join(deprecated)))
+ return obj(*new_args, **new_kwargs)
+
+ wrapper.__doc__ = obj.__doc__
+ wrapper.__name__ = obj.__name__
+ wrapper.__module__ = obj.__module__
+ if not hasattr(obj, '__full_name__'):
+ add_decorated_full_name(obj)
+ wrapper.__full_name__ = obj.__full_name__
+ wrapper.__wrapped__ = getattr(obj, '__wrapped__', obj)
+ return wrapper
+ return decorator
+
+
def redirect_func(target, source_module=None, target_module=None,
old_name=None, class_name=None):
"""
diff --git a/tests/deprecation_tests.py b/tests/deprecation_tests.py
index 98f3ccc..a7a27bc 100644
--- a/tests/deprecation_tests.py
+++ b/tests/deprecation_tests.py
@@ -8,7 +8,7 @@
__version__ = '$Id$'
from pywikibot.tools import (
- deprecated, deprecate_arg, deprecated_args, add_full_name
+ deprecated, deprecate_arg, deprecated_args, add_full_name, remove_last_args
)
from tests.aspects import unittest, DeprecationTestCase
@@ -116,6 +116,16 @@
return foo
+@remove_last_args(['foo', 'bar'])
+def deprecated_all():
+ return None
+
+
+@remove_last_args(['bar'])
+def deprecated_all2(foo):
+ return foo
+
+
class DeprecatedMethodClass(object):
"""Class with methods deprecated."""
@@ -165,6 +175,14 @@
@deprecated()
def deprecated_instance_method_and_arg2(self, foo):
self.foo = foo
+ return foo
+
+ @remove_last_args(['foo', 'bar'])
+ def deprecated_all(self):
+ return None
+
+ @remove_last_args(['bar'])
+ def deprecated_all2(self, foo):
return foo
@@ -408,6 +426,135 @@
DeprecatorTestCase._reset_messages()
+ def test_function_remove_last_args(self):
+ """Test @remove_last_args on functions."""
+ rv = deprecated_all()
+ self.assertEqual(rv, None)
+ self.assertNoDeprecation()
+
+ rv = deprecated_all(foo=42)
+ self.assertEqual(rv, None)
+ self.assertDeprecation("The trailing arguments ('foo', 'bar') of " + __name__ + ".deprecated_all are deprecated. The value(s) provided for 'foo' have been dropped.")
+
+ self._reset_messages()
+
+ rv = deprecated_all(42)
+ self.assertEqual(rv, None)
+ self.assertDeprecation("The trailing arguments ('foo', 'bar') of " + __name__ + ".deprecated_all are deprecated. The value(s) provided for 'foo' have been dropped.")
+
+ self._reset_messages()
+
+ rv = deprecated_all(foo=42, bar=47)
+ self.assertEqual(rv, None)
+ self.assertDeprecation("The trailing arguments ('foo', 'bar') of " + __name__ + ".deprecated_all are deprecated. The value(s) provided for 'foo', 'bar' have been dropped.")
+
+ self._reset_messages()
+
+ rv = deprecated_all(42, 47)
+ self.assertEqual(rv, None)
+ self.assertDeprecation("The trailing arguments ('foo', 'bar') of " + __name__ + ".deprecated_all are deprecated. The value(s) provided for 'foo', 'bar' have been dropped.")
+
+ self._reset_messages()
+
+ rv = deprecated_all2(foo=42)
+ self.assertEqual(rv, 42)
+ self.assertNoDeprecation()
+
+ rv = deprecated_all2(42)
+ self.assertEqual(rv, 42)
+ self.assertNoDeprecation()
+
+ rv = deprecated_all2(42, bar=47)
+ self.assertEqual(rv, 42)
+ self.assertDeprecation("The trailing arguments ('bar') of " + __name__ + ".deprecated_all2 are deprecated. The value(s) provided for 'bar' have been dropped.")
+
+ self._reset_messages()
+
+ def test_method_remove_last_args(self):
+ """Test @remove_last_args on functions."""
+ f = DeprecatedMethodClass()
+
+ rv = f.deprecated_all()
+ self.assertEqual(rv, None)
+ self.assertNoDeprecation()
+
+ rv = f.deprecated_all(foo=42)
+ self.assertEqual(rv, None)
+ self.assertDeprecation("The trailing arguments ('foo', 'bar') of " + __name__ + ".DeprecatedMethodClass.deprecated_all are deprecated. The value(s) provided for 'foo' have been dropped.")
+
+ self._reset_messages()
+
+ rv = f.deprecated_all(42)
+ self.assertEqual(rv, None)
+ self.assertDeprecation("The trailing arguments ('foo', 'bar') of " + __name__ + ".DeprecatedMethodClass.deprecated_all are deprecated. The value(s) provided for 'foo' have been dropped.")
+
+ self._reset_messages()
+
+ rv = f.deprecated_all(foo=42, bar=47)
+ self.assertEqual(rv, None)
+ self.assertDeprecation("The trailing arguments ('foo', 'bar') of " + __name__ + ".DeprecatedMethodClass.deprecated_all are deprecated. The value(s) provided for 'foo', 'bar' have been dropped.")
+
+ self._reset_messages()
+
+ rv = f.deprecated_all(42, 47)
+ self.assertEqual(rv, None)
+ self.assertDeprecation("The trailing arguments ('foo', 'bar') of " + __name__ + ".DeprecatedMethodClass.deprecated_all are deprecated. The value(s) provided for 'foo', 'bar' have been dropped.")
+
+ self._reset_messages()
+
+ rv = f.deprecated_all2(foo=42)
+ self.assertEqual(rv, 42)
+ self.assertNoDeprecation()
+
+ rv = f.deprecated_all2(42)
+ self.assertEqual(rv, 42)
+ self.assertNoDeprecation()
+
+ rv = f.deprecated_all2(42, bar=47)
+ self.assertEqual(rv, 42)
+ self.assertDeprecation("The trailing arguments ('bar') of " + __name__ + ".DeprecatedMethodClass.deprecated_all2 are deprecated. The value(s) provided for 'bar' have been dropped.")
+
+ def test_remove_last_args_invalid(self):
+ self.assertRaisesRegex(
+ TypeError,
+ r"(deprecated_all2\(\) missing 1 required positional argument: 'foo'|" # Python 3
+ "deprecated_all2\(\) takes exactly 1 argument \(0 given\))", # Python 2
+ deprecated_all2)
+
+ self.assertRaisesRegex(
+ TypeError,
+ r"deprecated_all2\(\) got an unexpected keyword argument 'hello'",
+ deprecated_all2,
+ hello='world')
+
+ self.assertRaisesRegex(
+ TypeError,
+ r'deprecated_all2\(\) takes (exactly )?1 (positional )?argument'
+ ' (but 2 were given|\(2 given\))',
+ deprecated_all2,
+ 1, 2, 3)
+
+ f = DeprecatedMethodClass()
+
+ self.assertRaisesRegex(
+ TypeError,
+ r"(deprecated_all2\(\) missing 1 required positional argument: 'foo'|" # Python 3
+ "deprecated_all2\(\) takes exactly 2 arguments \(1 given\))", # Python 2
+ f.deprecated_all2)
+
+ self.assertRaisesRegex(
+ TypeError,
+ r"deprecated_all2\(\) got an unexpected keyword argument 'hello'",
+ f.deprecated_all2,
+ hello='world')
+
+ self.assertRaisesRegex(
+ TypeError,
+ r'deprecated_all2\(\) takes (exactly )?2 (positional )?arguments '
+ '(but 3 were given|\(3 given\))',
+ f.deprecated_all2,
+ 1, 2, 3)
+
def test_deprecated_instance_method_zero_arg(self):
"""Test @deprecate_arg with classes, without arguments."""
f = DeprecatedMethodClass()
--
To view, visit https://gerrit.wikimedia.org/r/174107
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I9035f3b6be86fe7b34e515e19affe8ef1a9a558e
Gerrit-PatchSet: 9
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: XZise <CommodoreFabianus(a)gmx.de>
Gerrit-Reviewer: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: Ladsgroup <ladsgroup(a)gmail.com>
Gerrit-Reviewer: Merlijn van Deen <valhallasw(a)arctus.nl>
Gerrit-Reviewer: XZise <CommodoreFabianus(a)gmx.de>
Gerrit-Reviewer: jenkins-bot <>