jenkins-bot has submitted this change and it was merged.
Change subject: improvements to unlink.py
......................................................................
improvements to unlink.py
- UnlinkBot extends pywikibot.Bot
with built-in 'always' option
- BREAKING: require the page name to be wrapped in quotes
- use core-like page.save
- remove obsolete script name from showHelp
- make localized strings depend on the actual working site
Change-Id: I24757c086d1eda88d71148a547aebaa8aeca56ec
---
M scripts/unlink.py
1 file changed, 37 insertions(+), 32 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/unlink.py b/scripts/unlink.py
index f481f24..b6b5d09 100755
--- a/scripts/unlink.py
+++ b/scripts/unlink.py
@@ -1,23 +1,22 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
-
"""
This bot unlinks a page on every page that links to it.
This script understands this command-line argument:
- -namespace:n - Number of namespace to process. The parameter can be used
+ -namespace:n Number of namespace to process. The parameter can be used
multiple times. It works in combination with all other
parameters, except for the -start parameter. If you e.g.
want to iterate over all user pages starting at User:M, use
-start:User:M.
-All other parameters will be regarded as part of the title of the page that
-should be unlinked.
+Any other parameter will be regarded as the title of the page
+that should be unlinked.
Example:
-python unlink.py Foo bar -namespace:0 -namespace:6
+python unlink.py "Foo bar" -namespace:0 -namespace:6
Removes links to the page [[Foo bar]] in articles and image descriptions.
"""
@@ -31,20 +30,27 @@
import re
import pywikibot
-from pywikibot import pagegenerators
from pywikibot.editor import TextEditor
-from pywikibot import i18n
+from pywikibot import pagegenerators, i18n, Bot
-class UnlinkBot:
+class UnlinkBot(Bot):
- def __init__(self, pageToUnlink, namespaces, always):
+ def __init__(self, pageToUnlink, **kwargs):
+ self.availableOptions.update({
+ 'namespaces': [],
+ # Which namespaces should be processed?
+ # default to [] which means all namespaces will be processed
+ })
+
+ super(UnlinkBot, self).__init__(**kwargs)
self.pageToUnlink = pageToUnlink
+ linktrail = self.pageToUnlink.site.linktrail()
+
gen = pagegenerators.ReferringPageGenerator(pageToUnlink)
- if namespaces != []:
- gen = pagegenerators.NamespaceFilterPageGenerator(gen, namespaces)
+ if self.getOption('namespaces') != []:
+ gen = pagegenerators.NamespaceFilterPageGenerator(gen, self.getOption('namespaces'))
self.generator = pagegenerators.PreloadingGenerator(gen)
- linktrail = pywikibot.getSite().linktrail()
# The regular expression which finds links. Results consist of four
# groups:
#
@@ -62,9 +68,8 @@
# note that the definition of 'letter' varies from language to language.
self.linkR = re.compile(r'\[\[(?P<title>[^\]\|#]*)(?P<section>#[^\]\|]*)?(\|(?P<label>[^\]]*))?\]\](?P<linktrail>%s)'
% linktrail)
- self.always = always
self.done = False
- self.comment = i18n.twtranslate(pywikibot.getSite(), 'unlink-unlinking',
+ self.comment = i18n.twtranslate(self.pageToUnlink.site, 'unlink-unlinking',
self.pageToUnlink.title())
def handleNextLink(self, text, match, context=100):
@@ -91,7 +96,7 @@
else:
# at the beginning of the link, start red color.
# at the end of the link, reset the color to default
- if self.always:
+ if self.getOption('always'):
choice = 'a'
else:
pywikibot.output(
@@ -121,7 +126,7 @@
return self.handleNextLink(text, match,
context=context + 100)
elif choice == 'a':
- self.always = True
+ self.options['always'] = True
elif choice == 'q':
self.done = True
return text, False
@@ -152,7 +157,8 @@
pywikibot.output(u'No changes necessary.')
else:
pywikibot.showDiff(oldText, text)
- page.put(text, self.comment)
+ page.text = text
+ page.save(self.comment)
except pywikibot.NoPage:
pywikibot.output(u"Page %s does not exist?!"
% page.title(asLink=True))
@@ -170,31 +176,30 @@
def main():
- # This temporary array is used to read the page title if one single
- # page that should be unlinked.
- pageTitle = []
- # Which namespaces should be processed?
- # default to [] which means all namespaces will be processed
- namespaces = []
- always = False
+ # This temporary string is used to read the title
+ # of the page that should be unlinked.
+ page_title = None
+ options = {}
for arg in pywikibot.handleArgs():
if arg.startswith('-namespace:'):
+ if 'namespaces' not in options:
+ options['namespaces'] = []
try:
- namespaces.append(int(arg[11:]))
+ options['namespaces'].append(int(arg[11:]))
except ValueError:
- namespaces.append(arg[11:])
+ options['namespaces'].append(arg[11:])
elif arg == '-always':
- always = True
+ options['always'] = True
else:
- pageTitle.append(arg)
+ page_title = arg
- if pageTitle:
- page = pywikibot.Page(pywikibot.getSite(), ' '.join(pageTitle))
- bot = UnlinkBot(page, namespaces, always)
+ if page_title:
+ page = pywikibot.Page(pywikibot.Site(), page_title)
+ bot = UnlinkBot(page, **options)
bot.run()
else:
- pywikibot.showHelp('unlink')
+ pywikibot.showHelp()
if __name__ == "__main__":
main()
--
To view, visit https://gerrit.wikimedia.org/r/137346
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I24757c086d1eda88d71148a547aebaa8aeca56ec
Gerrit-PatchSet: 2
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Ricordisamoa <ricordisamoa(a)openmailbox.org>
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: Xqt <info(a)gno.de>
Gerrit-Reviewer: jenkins-bot <>
jenkins-bot has submitted this change and it was merged.
Change subject: (bug 66687) ExpatError obtaining version from SVN
......................................................................
(bug 66687) ExpatError obtaining version from SVN
Wrap expat parse and DOM access in exception block which raises
ParseError, so that the calling code can fallback to other version
determination routines.
Change-Id: I600ce4671861089c9c2d3ad628603fc7a3ac6d9e
---
M pywikibot/version.py
1 file changed, 5 insertions(+), 2 deletions(-)
Approvals:
Ladsgroup: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/version.py b/pywikibot/version.py
index 257dcac..c4c8f54 100644
--- a/pywikibot/version.py
+++ b/pywikibot/version.py
@@ -142,8 +142,11 @@
"<propfind xmlns=\"DAV:\"><allprop/></propfind>",
{'Label': rev, 'User-Agent': 'SVN/1.7.5-pywikibot1'})
resp = conn.getresponse()
- dom = xml.dom.minidom.parse(resp)
- hsh = dom.getElementsByTagName("C:git-commit")[0].firstChild.nodeValue
+ try:
+ dom = xml.dom.minidom.parse(resp)
+ hsh = dom.getElementsByTagName("C:git-commit")[0].firstChild.nodeValue
+ except Exception:
+ raise ParseError
rev = 's%s' % rev
if (not date or not tag or not rev) and not path:
raise ParseError
--
To view, visit https://gerrit.wikimedia.org/r/140069
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I600ce4671861089c9c2d3ad628603fc7a3ac6d9e
Gerrit-PatchSet: 1
Gerrit-Project: pywikibot/compat
Gerrit-Branch: master
Gerrit-Owner: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: Ladsgroup <ladsgroup(a)gmail.com>
Gerrit-Reviewer: jenkins-bot <>
jenkins-bot has submitted this change and it was merged.
Change subject: Support protecting action=create/upload
......................................................................
Support protecting action=create/upload
This patch makes it possible to protect creating a page or uploading a file.
For APISite.protect(), the prototype of the function is completely changed.
However, the function is used internally, so there should be no problem.
For Page.protect(), although it might be considered a breaking change since
parameter "create" and "upload" are inserted between the old parameters,
it is unlikely anyone will be affected negatively because most people would call
this function by specifying keyword argument. This change therefore should not
be problematic.
Besides this, I correct/elaborate docstring. I also change my name in CREDITS
to my preferable name.
Change-Id: I595228ea9a61f7e55dc14f8c3682494c8938cb1e
---
M CREDITS
M pywikibot/page.py
M pywikibot/site.py
3 files changed, 40 insertions(+), 22 deletions(-)
Approvals:
John Vandenberg: Looks good to me, approved
jenkins-bot: Verified
diff --git a/CREDITS b/CREDITS
index d99b3fe..a87d3e5 100644
--- a/CREDITS
+++ b/CREDITS
@@ -53,7 +53,6 @@
Mpaa
Nicolas Dumazet
notconfusing
-Nullzer0
Philip Tzou
Platonides
Purodha B Blissenbach
@@ -67,6 +66,7 @@
Shinjiman
Shi Zhao
Siebrand Mazeland
+Sorawee Porncharoenwase
Steve Sanbeg
Sumana Harihareswara
Thomas R. Koll
diff --git a/pywikibot/page.py b/pywikibot/page.py
index b19c09a..33bfb51 100644
--- a/pywikibot/page.py
+++ b/pywikibot/page.py
@@ -1457,21 +1457,27 @@
return self.site.undelete(self, comment)
@deprecate_arg("throttle", None)
- def protect(self, edit='sysop', move='sysop', unprotect=False,
- reason=None, prompt=True, expiry=None):
+ def protect(self, edit='sysop', move='sysop', create=None, upload=None,
+ unprotect=False, reason=None, prompt=True, expiry=None):
"""(Un)protect a wiki page. Requires administrator status.
Valid protection levels (in MediaWiki 1.12) are '' (equivalent to
- 'none'), 'autoconfirmed', and 'sysop'.
+ 'none'), 'autoconfirmed', and 'sysop'. If None is given, however,
+ that protection will be skipped.
@param edit: Level of edit protection
@param move: Level of move protection
- @param unprotect: If true, unprotect the page (equivalent to setting
- all protection levels to '')
+ @param create: Level of create protection
+ @param upload: Level of upload protection
+ @param unprotect: If true, unprotect page editing and moving
+ (equivalent to set both edit and move to '')
@param reason: Edit summary.
@param prompt: If true, ask user for confirmation.
- @param expiry: When the block should expire
-
+ @param expiry: When the block should expire. This expiry will be applied
+ to all protections. If None, 'infinite', 'indefinite', 'never', or ''
+ is given, there is no expiry.
+ @type expiry: pywikibot.Timestamp, string in GNU timestamp format
+ (including ISO 8601).
"""
if reason is None:
if unprotect:
@@ -1483,6 +1489,9 @@
reason = pywikibot.input(u'Please enter a reason for the action:')
if unprotect:
edit = move = ""
+ # Apply to only edit and move for backward compatibility.
+ # To unprotect article creation, for example,
+ # create must be set to '' and the rest must be None
answer = 'y'
if prompt and not hasattr(self.site, '_noProtectPrompt'):
answer = pywikibot.inputChoice(
@@ -1495,7 +1504,13 @@
answer = 'y'
self.site._noProtectPrompt = True
if answer in ['y', 'Y']:
- return self.site.protect(self, edit, move, reason, expiry)
+ protections = {
+ 'edit': edit,
+ 'move': move,
+ 'create': create,
+ 'upload': upload,
+ }
+ return self.site.protect(self, protections, reason, expiry)
def change_category(self, oldCat, newCat, comment=None, sortKey=None,
inPlace=True):
diff --git a/pywikibot/site.py b/pywikibot/site.py
index 7e621a3..c171f7a 100644
--- a/pywikibot/site.py
+++ b/pywikibot/site.py
@@ -3078,26 +3078,29 @@
}
@must_be(group='sysop')
- def protect(self, page, edit, move, summary, expiry=None):
+ def protect(self, page, protections, summary, expiry=None):
"""(Un)protect a wiki page. Requires administrator status.
- Valid protection levels (in MediaWiki 1.12) are '' (equivalent to
- 'none'), 'autoconfirmed', and 'sysop'.
-
- @param edit: Level of edit protection
- @param move: Level of move protection
- @param unprotect: If true, unprotect the page (equivalent to setting
- all protection levels to '')
- @param reason: Edit summary.
- @param prompt: If true, ask user for confirmation.
- @param expiry: When the block should expire
-
+ @param protections: A dict mapping type of protection to protection
+ level of that type. Valid types of protection are 'edit', 'move',
+ 'create', and 'upload'. Valid protection levels (in MediaWiki 1.12)
+ are '' (equivalent to 'none'), 'autoconfirmed', and 'sysop'.
+ If None is given, however, that protection will be skipped.
+ @param summary: Edit summary.
+ @param expiry: When the block should expire. This expiry will be applied
+ to all protections. If None, 'infinite', 'indefinite', 'never', or ''
+ is given, there is no expiry.
+ @type expiry: pywikibot.Timestamp, string in GNU timestamp format
+ (including ISO 8601).
"""
token = self.token(page, "protect")
self.lock_page(page)
+
+ protectList = [type + '=' + level for type, level in protections.items()
+ if level is not None]
req = api.Request(site=self, action="protect", token=token,
title=page.title(withSection=False),
- protections="edit=" + edit + "|" + "move=" + move,
+ protections=protectList,
reason=summary)
if isinstance(expiry, pywikibot.Timestamp):
expiry = expiry.toISOformat()
--
To view, visit https://gerrit.wikimedia.org/r/139792
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I595228ea9a61f7e55dc14f8c3682494c8938cb1e
Gerrit-PatchSet: 5
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Nullzero <nullzero.free(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: Nullzero <nullzero.free(a)gmail.com>
Gerrit-Reviewer: Xqt <info(a)gno.de>
Gerrit-Reviewer: jenkins-bot <>
jenkins-bot has submitted this change and it was merged.
Change subject: Bug 64833-Add page.userNameHuman as lastNonBotUser
......................................................................
Bug 64833-Add page.userNameHuman as lastNonBotUser
Site.isBot(): return True is username is a bot user on Site.
Site.botusers(): returns an iterator to a dict containing users info.
Page.lastNonBotUser(): returns the last human editor on the page.
Change-Id: I42c8e249072e7d2940f690a0c0ecb8ba2578677a
---
M pywikibot/page.py
M pywikibot/site.py
2 files changed, 50 insertions(+), 0 deletions(-)
Approvals:
John Vandenberg: Looks good to me, but someone else must approve
Merlijn van Deen: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/page.py b/pywikibot/page.py
index 141224c..2de06c2 100644
--- a/pywikibot/page.py
+++ b/pywikibot/page.py
@@ -449,6 +449,30 @@
self.site.loadrevisions(self)
return self._revisions[rev].anon
+ def lastNonBotUser(self):
+ """Return name or IP address of last human/non-bot user to edit page.
+
+ Returns the most recent human editor out of the last revisions
+ If it was not able to retrieve a human user returns None.
+ If the edit was done by a bot which is no longer flagged as 'bot',
+ i.e. which is not returned by Site.botusers(), it will be returned
+ as a non-bot edit.
+
+ """
+
+ if hasattr(self, '_lastNonBotUser'):
+ return self._lastNonBotUser
+
+ self._lastNonBotUser = None
+ for vh in self.getVersionHistory():
+ (revid, timestmp, username, comment) = vh[:4]
+
+ if username and (not self.site.isBot(username)):
+ self._lastNonBotUser = username
+ break
+
+ return self._lastNonBotUser
+
def editTime(self):
"""Return timestamp of last revision to page.
diff --git a/pywikibot/site.py b/pywikibot/site.py
index ef20492..e6d6f7f 100644
--- a/pywikibot/site.py
+++ b/pywikibot/site.py
@@ -2058,6 +2058,32 @@
limit = number
return self.allcategories(total=limit)
+ def isBot(self, username):
+ """Return True is username is a bot user. """
+
+ return username in [userdata['name'] for userdata in self.botusers()]
+
+ def botusers(self, step=None, total=None):
+ """Iterate bot users.
+
+ Iterated values are dicts containing 'name', 'userid', 'editcount',
+ 'registration', and 'groups' keys. 'groups' will be present only if
+ the user is a member of at least 1 group, and will be a list of
+ unicodes; all the other values are unicodes and should always be
+ present.
+
+ """
+
+ if not hasattr(self, "_bots"):
+ self._bots = {}
+
+ if not self._bots:
+ for item in self.allusers(group='bot', step=step, total=total):
+ self._bots.setdefault(item['name'], item)
+
+ for value in self._bots.values():
+ yield value
+
def allusers(self, start="!", prefix="", group=None, step=None,
total=None):
"""Iterate registered users, ordered by username.
--
To view, visit https://gerrit.wikimedia.org/r/132320
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I42c8e249072e7d2940f690a0c0ecb8ba2578677a
Gerrit-PatchSet: 6
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: Merlijn van Deen <valhallasw(a)arctus.nl>
Gerrit-Reviewer: Mpaa <mpaa.wiki(a)gmail.com>
Gerrit-Reviewer: Xqt <info(a)gno.de>
Gerrit-Reviewer: jenkins-bot <>