jenkins-bot has submitted this change and it was merged. ( https://gerrit.wikimedia.org/r/370182 )
Change subject: [doc] Give a hint how to use that script
......................................................................
[doc] Give a hint how to use that script
Change-Id: I7034faea524962f78c378c9a573add90b8fd738a
---
M scripts/maintenance/wikimedia_sites.py
1 file changed, 7 insertions(+), 1 deletion(-)
Approvals:
Dalba: Looks good to me, approved
jenkins-bot: Verified
diff --git a/scripts/maintenance/wikimedia_sites.py b/scripts/maintenance/wikimedia_sites.py
index 89db954..8f87d60 100755
--- a/scripts/maintenance/wikimedia_sites.py
+++ b/scripts/maintenance/wikimedia_sites.py
@@ -1,6 +1,12 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
-"""Script that updates the language lists in Wikimedia family files."""
+"""Script that updates the language lists in Wikimedia family files.
+
+Usage:
+
+ python pwb.py wikimedia_sites [ {<family>} ]
+
+"""
#
# (C) xqt, 2009-2017
# (C) Pywikibot team, 2008-2017
--
To view, visit https://gerrit.wikimedia.org/r/370182
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I7034faea524962f78c378c9a573add90b8fd738a
Gerrit-PatchSet: 1
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: Dalba <dalba.wiki(a)gmail.com>
Gerrit-Reviewer: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: Magul <tomasz.magulski(a)gmail.com>
Gerrit-Reviewer: jenkins-bot <>
jenkins-bot has submitted this change and it was merged. ( https://gerrit.wikimedia.org/r/369669 )
Change subject: [PEP8] Keep line length beneath 80 chars
......................................................................
[PEP8] Keep line length beneath 80 chars
Change-Id: I2fc59f971ed4b2418a135ad79be6573472825644
---
M scripts/pagefromfile.py
M scripts/patrol.py
M scripts/redirect.py
M scripts/replace.py
M scripts/replicate_wiki.py
M scripts/table2wiki.py
M scripts/welcome.py
7 files changed, 51 insertions(+), 35 deletions(-)
Approvals:
Dalba: Looks good to me, approved
jenkins-bot: Verified
diff --git a/scripts/pagefromfile.py b/scripts/pagefromfile.py
index f4e9341..36fb613 100755
--- a/scripts/pagefromfile.py
+++ b/scripts/pagefromfile.py
@@ -165,15 +165,15 @@
else:
above, below = pagecontents, contents
comment = comment_bottom
- pywikibot.output('Page {0} already exists, appending on {1}!'.format(
- title, self.getOption('append')[0]))
+ pywikibot.output('Page {0} already exists, appending on {1}!'
+ .format(title, self.getOption('append')[0]))
contents = above + separator + below
elif self.getOption('force'):
pywikibot.output(u"Page %s already exists, ***overwriting!"
% title)
comment = comment_force
else:
- pywikibot.output(u"Page %s already exists, not adding!" % title)
+ pywikibot.output('Page %s already exists, not adding!' % title)
return
else:
if self.getOption('autosummary'):
diff --git a/scripts/patrol.py b/scripts/patrol.py
index 14c24a7..bc3bdc9 100755
--- a/scripts/patrol.py
+++ b/scripts/patrol.py
@@ -183,7 +183,7 @@
for item in pagelist:
if pywikibot.config.verbose_output:
- pywikibot.output(u'checking against whitelist item = %s' % item)
+ pywikibot.output('checking against whitelist item = %s' % item)
if isinstance(item, LinkedPagesRule):
if pywikibot.config.verbose_output:
@@ -252,13 +252,14 @@
if not user or current_user == user:
if self.is_wikisource_author_page(page):
if pywikibot.config.verbose_output:
- pywikibot.output(u'Whitelist author: %s' % page)
+ pywikibot.output('Whitelist author: %s' % page)
page = LinkedPagesRule(page)
else:
if pywikibot.config.verbose_output:
pywikibot.output(u'Whitelist page: %s' % page)
if pywikibot.config.verbose_output:
- pywikibot.output('Adding {0}:{1}'.format(current_user, page))
+ pywikibot.output('Adding {0}:{1}'
+ .format(current_user, page))
whitelist[current_user].append(page)
elif pywikibot.config.verbose_output:
pywikibot.output(u'Discarding whitelist page for '
@@ -340,7 +341,8 @@
if self.getOption('ask'):
choice = pywikibot.input_yn(
- u'Do you want to mark page as patrolled?', automatic_quit=False)
+ 'Do you want to mark page as patrolled?',
+ automatic_quit=False)
# Patrol the page
if choice:
diff --git a/scripts/redirect.py b/scripts/redirect.py
index bd8ddec..86855f0 100755
--- a/scripts/redirect.py
+++ b/scripts/redirect.py
@@ -168,7 +168,8 @@
not in self.namespaces:
continue
if alsoGetPageTitles:
- pageTitles.add(space_to_underscore(pywikibot.Link(entry.title, self.site)))
+ pageTitles.add(space_to_underscore(pywikibot.Link(entry.title,
+ self.site)))
m = redirR.match(entry.text)
if m:
@@ -186,8 +187,9 @@
else:
if target_link.site != self.site:
pywikibot.output(
- u'NOTE: Ignoring {0} which is a redirect to '
- u'another site {1}.'.format(entry.title, target_link.site))
+ 'NOTE: Ignoring {0} which is a redirect to '
+ 'another site {1}.'
+ .format(entry.title, target_link.site))
target_link = None
# if the redirect does not link to another wiki
if target_link and target_link.title:
@@ -497,7 +499,7 @@
except pywikibot.IsNotRedirectPage:
pywikibot.output(u'%s is not a redirect.' % redir_page.title())
except pywikibot.CircularRedirect:
- pywikibot.output(u'%s is a circular redirect.' % redir_page.title())
+ pywikibot.output('%s is a circular redirect.' % redir_page.title())
except pywikibot.NoPage:
pywikibot.output(u'%s doesn\'t exist.' % redir_page.title())
except pywikibot.InvalidTitle:
@@ -565,7 +567,8 @@
redir_page.title(asLink=True))):
self.delete_redirect(redir_page, 'redirect-remove-broken')
elif not (self.getOption('delete') or movedTarget):
- pywikibot.output(u'Cannot fix or delete the broken redirect')
+ pywikibot.output(
+ 'Cannot fix or delete the broken redirect')
except pywikibot.IsRedirectPage:
pywikibot.output(
"Redirect target {0} is also a redirect! {1}".format(
@@ -671,7 +674,8 @@
# Delete the two redirects
# TODO: Check whether pages aren't vandalized
# and (maybe) do not have a version history
- self.delete_redirect(targetPage, 'redirect-remove-loop')
+ self.delete_redirect(targetPage,
+ 'redirect-remove-loop')
self.delete_redirect(redir, 'redirect-remove-loop')
break
else: # redirect target found
@@ -693,7 +697,8 @@
pywikibot.output('Fixing double item redirect')
redir.set_redirect_target(targetPage)
break
- redir.set_redirect_target(targetPage, keep_section=True, save=False)
+ redir.set_redirect_target(targetPage, keep_section=True,
+ save=False)
summary = i18n.twtranslate(self.site, 'redirect-fix-double',
{'to': targetPage.title(asLink=True)}
)
diff --git a/scripts/replace.py b/scripts/replace.py
index 7e32b7d..c1e7bcb 100755
--- a/scripts/replace.py
+++ b/scripts/replace.py
@@ -630,15 +630,15 @@
page.title(), replacement.exceptions):
if replacement.container:
pywikibot.output(
- 'Skipping fix "{0}" on {1} because the title is on the '
- 'exceptions list.'.format(
+ 'Skipping fix "{0}" on {1} because the title is on '
+ 'the exceptions list.'.format(
replacement.container.name,
page.title(asLink=True)))
skipped_containers.add(replacement.container.name)
else:
pywikibot.output(
- 'Skipping unnamed replacement ({0}) on {1} because the '
- 'title is on the exceptions list.'.format(
+ 'Skipping unnamed replacement ({0}) on {1} because '
+ 'the title is on the exceptions list.'.format(
replacement.description, page.title(asLink=True)))
continue
old_text = new_text
@@ -670,7 +670,8 @@
self.changed_pages += 1
self._pending_processed_titles.put((page.title(asLink=True), True))
else: # unsuccessful pages
- self._pending_processed_titles.put((page.title(asLink=True), False))
+ self._pending_processed_titles.put((page.title(asLink=True),
+ False))
def _replace_async_callback(self, page, err):
"""Callback for asynchronous page edit."""
@@ -727,7 +728,7 @@
% page.title(asLink=True))
continue
except pywikibot.NoPage:
- pywikibot.output(u'Page %s not found' % page.title(asLink=True))
+ pywikibot.output('Page %s not found' % page.title(asLink=True))
continue
applied = set()
new_text = original_text
@@ -749,8 +750,8 @@
% page.title(asLink=True))
break
if hasattr(self, 'addedCat'):
- # Fetch only categories in wikitext, otherwise the others will
- # be explicitly added.
+ # Fetch only categories in wikitext, otherwise the others
+ # will be explicitly added.
cats = textlib.getCategoryLinks(new_text, site=page.site)
if self.addedCat not in cats:
cats.append(self.addedCat)
@@ -766,7 +767,8 @@
break
choice = pywikibot.input_choice(
u'Do you want to accept these changes?',
- [('Yes', 'y'), ('No', 'n'), ('Edit original', 'e'), ('edit Latest', 'l'),
+ [('Yes', 'y'), ('No', 'n'), ('Edit original', 'e'),
+ ('edit Latest', 'l'),
('open in Browser', 'b'), ('all', 'a')],
default='N')
if choice == 'e':
@@ -782,7 +784,8 @@
# if user didn't press Cancel
if as_edited and as_edited != new_text:
new_text = as_edited
- last_text = new_text # prevent changes from being applied again
+ # prevent changes from being applied again
+ last_text = new_text
continue
if choice == 'b':
pywikibot.bot.open_webbrowser(page)
@@ -801,7 +804,8 @@
page.text = new_text
page.save(summary=self.generate_summary(applied),
asynchronous=True,
- callback=self._replace_async_callback, quiet=True)
+ callback=self._replace_async_callback,
+ quiet=True)
while not self._pending_processed_titles.empty():
proc_title, res = self._pending_processed_titles.get()
pywikibot.output('Page %s%s saved'
@@ -1010,7 +1014,7 @@
commandline_replacements.extend(file_replacements)
if not(commandline_replacements or fixes_set) or manual_input:
- old = pywikibot.input(u'Please enter the text that should be replaced:')
+ old = pywikibot.input('Please enter the text that should be replaced:')
while old:
new = pywikibot.input(u'Please enter the new text:')
commandline_replacements += [old, new]
@@ -1107,8 +1111,8 @@
% single_summary)
if missing_fixes_summaries:
pywikibot.output('The summary will not be used when the fix has '
- 'one defined but the following fix(es) do(es) not '
- 'have a summary defined: '
+ 'one defined but the following fix(es) do(es) '
+ 'not have a summary defined: '
'{0}'.format(', '.join(missing_fixes_summaries)))
if edit_summary is not True:
edit_summary = pywikibot.input(
@@ -1170,8 +1174,8 @@
site.login()
bot.run()
- # Explicitly call pywikibot.stopme().
- # It will make sure the callback is triggered before replace.py is unloaded.
+ # Explicitly call pywikibot.stopme(). It will make sure the callback is
+ # triggered before replace.py is unloaded.
pywikibot.stopme()
pywikibot.output(u'\n%s pages changed.' % bot.changed_pages)
diff --git a/scripts/replicate_wiki.py b/scripts/replicate_wiki.py
index f2c0729..7009737 100755
--- a/scripts/replicate_wiki.py
+++ b/scripts/replicate_wiki.py
@@ -95,7 +95,8 @@
if options.namespace and 'help' in options.namespace:
for namespace in self.original.namespaces.values():
- pywikibot.output('%s %s' % (namespace.id, namespace.custom_name))
+ pywikibot.output(
+ '{0} {1}'.format(namespace.id, namespace.custom_name))
sys.exit()
self.sites = [pywikibot.Site(s, family) for s in sites]
@@ -174,12 +175,14 @@
else:
output += "All important pages are the same"
- output += "\n\n== Admins from original that are missing here ==\n\n"
+ output += (
+ '\n\n== Admins from original that are missing here ==\n\n')
if self.user_diff[site]:
output += "".join('* %s\n' % l.replace('_', ' ') for l in
self.user_diff[site])
else:
- output += "All users from original are also present on this wiki"
+ output += (
+ 'All users from original are also present on this wiki')
pywikibot.output(output)
sync_overview_page.text = output
diff --git a/scripts/table2wiki.py b/scripts/table2wiki.py
index b784842..10f133e 100644
--- a/scripts/table2wiki.py
+++ b/scripts/table2wiki.py
@@ -130,7 +130,8 @@
# Note that we added the ## characters in markActiveTables().
# <table> tag with attributes, with more text on the same line
newTable = re.sub(
- r'(?i)[\r\n]*?<##table## (?P<attr>[\w\W]*?)>(?P<more>[\w\W]*?)[\r\n ]*',
+ r'(?i)[\r\n]*?<##table## (?P<attr>[\w\W]*?)>'
+ r'(?P<more>[\w\W]*?)[\r\n ]*',
r'\r\n{| \g<attr>\r\n\g<more>', newTable)
# <table> tag without attributes, with more text on the same line
newTable = re.sub(r'(?i)[\r\n]*?<##table##>(?P<more>[\w\W]*?)[\r\n ]*',
diff --git a/scripts/welcome.py b/scripts/welcome.py
index 6496438..3d4d0d7 100755
--- a/scripts/welcome.py
+++ b/scripts/welcome.py
@@ -827,7 +827,8 @@
if locale.getlocale()[1]:
strfstr = time.strftime(
'%d %b %Y %H:%M:%S (UTC)', time.gmtime())
- if not isinstance(strfstr, UnicodeType): # py2-py3 compatibility
+ # py2-py3 compatibility
+ if not isinstance(strfstr, UnicodeType):
strfstr = strfstr.decode(locale.getlocale()[1])
else:
strfstr = time.strftime(
--
To view, visit https://gerrit.wikimedia.org/r/369669
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I2fc59f971ed4b2418a135ad79be6573472825644
Gerrit-PatchSet: 2
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: Dalba <dalba.wiki(a)gmail.com>
Gerrit-Reviewer: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: Magul <tomasz.magulski(a)gmail.com>
Gerrit-Reviewer: jenkins-bot <>
jenkins-bot has submitted this change and it was merged. ( https://gerrit.wikimedia.org/r/371771 )
Change subject: exceptions.py: Let Server504Error and Server414Error inherit from ServerError
......................................................................
exceptions.py: Let Server504Error and Server414Error inherit from ServerError
This is potentially a breaking change, but it makes sense and hopefully nobody
relies on catching a `ServerError` without catching `Server414Error` or
`Server504Error`.
Bug: T173289
Change-Id: I5f068886442d55aa0072815f4edbffbefa3577ae
---
M pywikibot/exceptions.py
1 file changed, 2 insertions(+), 2 deletions(-)
Approvals:
jenkins-bot: Verified
Xqt: Looks good to me, approved
diff --git a/pywikibot/exceptions.py b/pywikibot/exceptions.py
index d9dff19..2914eb7 100644
--- a/pywikibot/exceptions.py
+++ b/pywikibot/exceptions.py
@@ -464,14 +464,14 @@
pass
-class Server504Error(Error): # noqa
+class Server504Error(ServerError): # noqa
"""Server timed out with HTTP 504 code"""
pass
-class Server414Error(Error):
+class Server414Error(ServerError):
"""Server returned with HTTP 414 code."""
--
To view, visit https://gerrit.wikimedia.org/r/371771
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I5f068886442d55aa0072815f4edbffbefa3577ae
Gerrit-PatchSet: 1
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Dalba <dalba.wiki(a)gmail.com>
Gerrit-Reviewer: Dalba <dalba.wiki(a)gmail.com>
Gerrit-Reviewer: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: Xqt <info(a)gno.de>
Gerrit-Reviewer: jenkins-bot <>
jenkins-bot has submitted this change and it was merged. ( https://gerrit.wikimedia.org/r/364993 )
Change subject: Decommission rcstream
......................................................................
Decommission rcstream
rcstream is offline since 2017-07-10
- remove comms/rcstream.py
- change docs/api_ref/pywikibot.comms.rst which solves T168831
- change docs and README.rst
- add usage documentation to EventStreams
- update documentation in pagegenerators.py
- remove rcstreams_port and rcstreams_path methods from WikimediaFamily
and rename rcstreams_host to eventstreams_host
- remove dependency from setup.py
- skip doctest for eventstreams.py
- Add numpydoc and autosummary to enable section headers in docs
Bug: T170534
Bug: T168831
Change-Id: Ic5de5d07c5065c6c2759c7eef4fdb83ab10b8b6f
---
M docs/api_ref/pywikibot.comms.rst
M docs/conf.py
M docs/requirements-py3.txt
M pywikibot/README.rst
M pywikibot/comms/eventstreams.py
D pywikibot/comms/rcstream.py
M pywikibot/family.py
M pywikibot/pagegenerators.py
M setup.py
M tox.ini
10 files changed, 57 insertions(+), 262 deletions(-)
Approvals:
Krinkle: Looks good to me, but someone else must approve
Dalba: Looks good to me, approved
jenkins-bot: Verified
diff --git a/docs/api_ref/pywikibot.comms.rst b/docs/api_ref/pywikibot.comms.rst
index 2c25c5d..39ccc28 100644
--- a/docs/api_ref/pywikibot.comms.rst
+++ b/docs/api_ref/pywikibot.comms.rst
@@ -17,10 +17,10 @@
:undoc-members:
:show-inheritance:
-pywikibot.comms.rcstream module
+pywikibot.comms.eventstreams module
-------------------------------
-.. automodule:: pywikibot.comms.rcstream
+.. automodule:: pywikibot.comms.eventstreams
:members:
:undoc-members:
:show-inheritance:
diff --git a/docs/conf.py b/docs/conf.py
index 17cf9ac..04d20f7 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""Configuration file for Sphinx."""
#
-# (C) Pywikibot team, 2015-2016
+# (C) Pywikibot team, 2015-2017
#
# Distributed under the terms of the MIT license.
#
@@ -36,7 +36,9 @@
'sphinx_epytext',
'sphinx.ext.todo',
'sphinx.ext.coverage',
- 'sphinx.ext.viewcode']
+ 'sphinx.ext.viewcode',
+ 'sphinx.ext.autosummary',
+ 'numpydoc']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
diff --git a/docs/requirements-py3.txt b/docs/requirements-py3.txt
index c99a6f2..7403a65 100644
--- a/docs/requirements-py3.txt
+++ b/docs/requirements-py3.txt
@@ -4,4 +4,5 @@
sphinx==1.3.1
sphinx-epytext>=0.0.4
+numpydoc
diff --git a/pywikibot/README.rst b/pywikibot/README.rst
index 702f1a1..181ddd3 100644
--- a/pywikibot/README.rst
+++ b/pywikibot/README.rst
@@ -106,11 +106,9 @@
+---------------------------+-------------------------------------------------------+
| comms | Communication layer. |
+===========================+=======================================================+
- | eventstreams.py | rcstream client for server sent events |
+ | eventstreams.py | stream client for server sent events |
+---------------------------+-------------------------------------------------------+
| http.py | Basic HTTP access interface |
- +---------------------------+-------------------------------------------------------+
- | rcstream.py | SocketIO-based rcstream client (deprecated) |
+---------------------------+-------------------------------------------------------+
| threadedhttp.py | Httplib2 threaded cookie layer extending httplib2 |
+---------------------------+-------------------------------------------------------+
diff --git a/pywikibot/comms/eventstreams.py b/pywikibot/comms/eventstreams.py
index 2236481..5d4db87 100644
--- a/pywikibot/comms/eventstreams.py
+++ b/pywikibot/comms/eventstreams.py
@@ -38,6 +38,28 @@
It provides access to arbitrary streams of data including recent changes.
It replaces rcstream.py implementation.
+
+ Usage:
+
+ >>> stream = EventStreams(stream='recentchange')
+ >>> change = iter(stream).next()
+ >>> change
+ {'comment': '/* wbcreateclaim-create:1| */ [[Property:P31]]: [[Q4167836]]',
+ 'wiki': 'wikidatawiki', 'type': 'edit', 'server_name': 'www.wikidata.org',
+ 'server_script_path': '/w', 'namespace': 0, 'title': 'Q32857263',
+ 'bot': True, 'server_url': 'https://www.wikidata.org',
+ 'length': {'new': 1223, 'old': 793},
+ 'meta': {'domain': 'www.wikidata.org', 'partition': 0,
+ 'uri': 'https://www.wikidata.org/wiki/Q32857263',
+ 'offset': 288986585, 'topic': 'eqiad.mediawiki.recentchange',
+ 'request_id': '1305a006-8204-4f51-a27b-0f2df58289f4',
+ 'schema_uri': 'mediawiki/recentchange/1',
+ 'dt': '2017-07-13T10:55:31+00:00',
+ 'id': 'ca13742b-67b9-11e7-935d-141877614a33'},
+ 'user': 'XXN-bot', 'timestamp': 1499943331, 'patrolled': True,
+ 'id': 551158959, 'minor': False,
+ 'revision': {'new': 518751558, 'old': 517180066}}
+ >>> del stream
"""
def __init__(self, **kwargs):
@@ -80,7 +102,7 @@
raise NotImplementedError(
'No stream specified for class {0}'
.format(self.__class__.__name__))
- self._url = ('{0}{1}/{2}'.format(self._site.rcstream_host(),
+ self._url = ('{0}{1}/{2}'.format(self._site.eventstreams_host(),
self._site.eventstreams_path(),
self._stream))
return self._url
@@ -106,6 +128,7 @@
Filter types
============
+
There are 3 types of filter: 'all', 'any' and 'none'.
The filter type must be given with the keyword argument 'ftype'
(see below). If no 'ftype' keyword argument is given, 'all' is
@@ -120,6 +143,7 @@
Filter functions
================
+
Filter may be specified as external function methods given as
positional argument like::
@@ -134,6 +158,7 @@
Filter keys and values
======================
+
Another method to register a filter is to pass pairs of keys and values
as keyword arguments to this method. The key must be a key of the event
data dict and the value must be any value or an iterable of values the
@@ -247,20 +272,11 @@
@type total: int
@return: pywikibot.comms.eventstream.rc_listener configured for given site
+ @raises ImportError: sseclient installation is required
"""
if isinstance(EventSource, Exception):
- warning('sseclient is required for EventStreams;\n'
- 'install it with "pip install sseclient"\n')
- # fallback to old rcstream method
- # NOTE: this will be deprecated soon
- from pywikibot.comms.rcstream import rc_listener
- return rc_listener(
- wikihost=site.hostname(),
- rchost=site.rcstream_host(),
- rcport=site.rcstream_port(),
- rcpath=site.rcstream_path(),
- total=total,
- )
+ raise ImportError('sseclient is required for EventStreams;\n'
+ 'install it with "pip install sseclient"\n')
stream = EventStreams(stream='recentchange', site=site)
stream.set_maximum_items(total)
diff --git a/pywikibot/comms/rcstream.py b/pywikibot/comms/rcstream.py
deleted file mode 100644
index a235f90..0000000
--- a/pywikibot/comms/rcstream.py
+++ /dev/null
@@ -1,226 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-SocketIO-based rcstream client.
-
-This file is part of the Pywikibot framework.
-
-This module requires socketIO_client to be installed:
- pip install socketIO_client
-"""
-#
-# (C) 2014 Merlijn van Deen
-# (C) Pywikibot team, 2014-2017
-#
-# Distributed under the terms of the MIT license.
-#
-from __future__ import absolute_import, unicode_literals
-
-__version__ = '$Id$'
-#
-
-import sys
-import threading
-
-if sys.version_info[0] > 2:
- from queue import Queue, Empty
-else:
- from Queue import Queue, Empty
-
-try:
- import socketIO_client
-except ImportError as e:
- socketIO_client = e
-
-from pywikibot.bot import debug, warning
-from pywikibot.tools import deprecated
-
-_logger = 'pywikibot.rcstream'
-
-
-class RcListenerThread(threading.Thread):
-
- """
- Low-level RC Listener Thread, pushing RC stream events into a queue.
-
- @param wikihost: the hostname of the wiki we want to get changes for. This
- is passed to rcstream using a 'subscribe' command. Pass
- '*' to listen to all wikis for a given rc host.
- @param rchost: the recent changes stream host to connect to. For Wikimedia
- wikis, this is 'https://stream.wikimedia.org'
- @param rcport: the port to connect to (default: 80)
- @param rcpath: the sockets.io path. For Wikimedia wikis, this is '/rc'.
- (default: '/rc')
- @param total: the maximum number of entries to return. The underlying
- thread is shut down then this number is reached.
-
- This part of the rc listener runs in a Thread. It makes the actual
- socketIO/websockets connection to the rc stream server, subscribes
- to a single site and pushes those entries into a queue.
-
- Usage:
-
- >>> t = RcListenerThread('en.wikipedia.org', 'https://stream.wikimedia.org')
- >>> t.start()
- >>> change = t.queue.get()
- >>> change
- {'server_name': 'en.wikipedia.org', 'wiki': 'enwiki', 'minor': True,
- 'length': {'new': 2038, 'old': 2009}, 'timestamp': 1419964350,
- 'server_script_path': '/w', 'bot': False, 'user': 'Od Mishehu',
- 'comment': 'stub sorting', 'title': 'Bradwell Bay Wilderness',
- 'server_url': 'http://en.wikipedia.org', 'id': 703158386,
- 'revision': {'new': 640271171, 'old': 468264850},
- 'type': 'edit', 'namespace': 0}
- >>> t.stop() # optional, the thread will shut down on exiting python
- """
-
- def __init__(self, wikihost, rchost, rcport=80, rcpath='/rc', total=None):
- """Constructor for RcListenerThread."""
- super(RcListenerThread, self).__init__()
- self.rchost = rchost
- self.rcport = rcport
- self.rcpath = rcpath
- self.wikihost = wikihost
-
- self.daemon = True
- self.running = False
- self.queue = Queue()
-
- self.warn_queue_length = 100
-
- self.total = total
- self.count = 0
-
- debug('Opening connection to %r' % self, _logger)
- self.client = socketIO_client.SocketIO(rchost, rcport)
-
- thread = self
-
- class RCListener(socketIO_client.BaseNamespace):
- def on_change(self, change):
- debug('Received change %r' % change, _logger)
- if not thread.running:
- debug('Thread in shutdown mode; ignoring change.', _logger)
- return
-
- thread.count += 1
- thread.queue.put(change)
- if thread.queue.qsize() > thread.warn_queue_length:
- warning('%r queue length exceeded %i'
- % (thread,
- thread.warn_queue_length),
- _logger=_logger)
- thread.warn_queue_length = thread.warn_queue_length + 100
-
- if thread.total is not None and thread.count >= thread.total:
- thread.stop()
- return
-
- def on_connect(self):
- debug('Connected to %r; subscribing to %s'
- % (thread, thread.wikihost),
- _logger)
- self.emit('subscribe', thread.wikihost)
- debug('Subscribed to %s' % thread.wikihost, _logger)
-
- def on_reconnect(self):
- debug('Reconnected to %r' % (thread,), _logger)
- self.on_connect()
-
- class GlobalListener(socketIO_client.BaseNamespace):
- def on_heartbeat(self):
- self._transport.send_heartbeat()
-
- self.client.define(RCListener, rcpath)
- self.client.define(GlobalListener)
-
- def __repr__(self):
- """Return representation."""
- return "<rcstream for socketio://%s@%s:%s%s>" % (
- self.wikihost, self.rchost, self.rcport, self.rcpath
- )
-
- def run(self):
- """
- Threaded function.
-
- Runs inside the thread when started with .start().
- """
- self.running = True
- while self.running:
- self.client.wait(seconds=0.1)
- debug('Shut down event loop for %r' % self, _logger)
- self.client.disconnect()
- debug('Disconnected %r' % self, _logger)
- self.queue.put(None)
-
- def stop(self):
- """Stop the thread."""
- self.running = False
-
-
-def rc_listener(wikihost, rchost, rcport=80, rcpath='/rc', total=None):
- """Yield changes received from RCstream.
-
- @param wikihost: the hostname of the wiki we want to get changes for. This
- is passed to rcstream using a 'subscribe' command. Pass
- '*' to listen to all wikis for a given rc host.
- @param rchost: the recent changes stream host to connect to. For Wikimedia
- wikis, this is 'https://stream.wikimedia.org'
- @param rcport: the port to connect to (default: 80)
- @param rcpath: the sockets.io path. For Wikimedia wikis, this is '/rc'.
- (default: '/rc')
- @param total: the maximum number of entries to return. The underlying thread
- is shut down then this number is reached.
-
- @return: yield dict as formatted by MediaWiki's
- MachineReadableRCFeedFormatter, which consists of at least id
- (recent changes id), type ('edit', 'new', 'log' or 'external'),
- namespace, title, comment, timestamp, user and bot (bot flag for the
- change).
- @see: U{MachineReadableRCFeedFormatter<https://doc.wikimedia.org/
- mediawiki-core/master/php/classMachineReadableRCFeedFormatter.html>}
- @rtype: generator
- @raises ImportError
- """
- if isinstance(socketIO_client, Exception):
- raise ImportError('socketIO_client is required for the rc stream;\n'
- 'install it with pip install "socketIO_client==0.5.6"')
-
- rc_thread = RcListenerThread(
- wikihost=wikihost,
- rchost=rchost, rcport=rcport, rcpath=rcpath,
- total=total
- )
-
- debug('Starting rcstream thread %r' % rc_thread,
- _logger)
- rc_thread.start()
-
- while True:
- try:
- element = rc_thread.queue.get(timeout=0.1)
- except Empty:
- continue
- if element is None:
- return
- yield element
-
-
-@deprecated('eventstreams.site_rc_listener')
-def site_rc_listener(site, total=None):
- """Yield changes received from RCstream.
-
- @param site: the Pywikibot.Site object to yield live recent changes for
- @type site: Pywikibot.BaseSite
- @param total: the maximum number of changes to return
- @type total: int
-
- @return: pywikibot.comms.rcstream.rc_listener configured for the given site
- """
- return rc_listener(
- wikihost=site.hostname(),
- rchost=site.rcstream_host(),
- rcport=site.rcstream_port(),
- rcpath=site.rcstream_path(),
- total=total,
- )
diff --git a/pywikibot/family.py b/pywikibot/family.py
index 1ff3d7f..d5b8d6f 100644
--- a/pywikibot/family.py
+++ b/pywikibot/family.py
@@ -1144,12 +1144,19 @@
def rcstream_host(self, code):
"""Hostname for RCStream."""
- raise NotImplementedError(
- 'This family does support neither RCStream nor EventStreams')
+ raise NotImplementedError('This family does not support RCStream')
def rcstream_path(self, code):
"""Return path for RCStream."""
raise NotImplementedError("This family does not support RCStream")
+
+ def rcstream_port(self, code):
+ """Return port for RCStream."""
+ raise NotImplementedError('This family does not support RCStream')
+
+ def eventstreams_host(self, code):
+ """Hostname for EventStreams."""
+ raise NotImplementedError('This family does not support EventStreams')
def eventstreams_path(self, code):
"""Return path for EventStreams."""
@@ -1639,17 +1646,14 @@
"""Return 'https' as the protocol."""
return 'https'
+ @deprecated('eventstreams_host')
def rcstream_host(self, code):
- """Return 'https://stream.wikimedia.org' as the RCStream hostname."""
+ """DEPRECATED: use eventstreams_host instead."""
+ return self.eventstreams_host(code)
+
+ def eventstreams_host(self, code):
+ """Return 'https://stream.wikimedia.org' as the stream hostname."""
return 'https://stream.wikimedia.org'
-
- def rcstream_port(self, code):
- """Return 443 as the RCStream port number."""
- return 443
-
- def rcstream_path(self, code):
- """Return path for RCStream."""
- return '/rc'
def eventstreams_path(self, code):
"""Return path for EventStreams."""
diff --git a/pywikibot/pagegenerators.py b/pywikibot/pagegenerators.py
index 32c1c22..f125ab8 100644
--- a/pywikibot/pagegenerators.py
+++ b/pywikibot/pagegenerators.py
@@ -2403,10 +2403,12 @@
"""
Yield pages from a socket.io RC stream.
- Generates pages based on the socket.io recent changes stream.
+ Generates pages based on the EventStreams Server-Sent-Event (SSE) recent
+ changes stream.
The Page objects will have an extra property ._rcinfo containing the
literal rc data. This can be used to e.g. filter only new pages. See
- `pywikibot.comms.rcstream.rc_listener` for details on the .rcinfo format.
+ `pywikibot.comms.eventstreams.rc_listener` for details on the .rcinfo
+ format.
@param site: site to return recent changes for
@type site: pywikibot.BaseSite
diff --git a/setup.py b/setup.py
index cb74bca..1657ad5 100644
--- a/setup.py
+++ b/setup.py
@@ -61,8 +61,6 @@
'IRC': [irc_dep],
'mwparserfromhell': ['mwparserfromhell>=0.3.3'],
'Tkinter': ['Pillow<3.5.0' if PY26 else 'Pillow'],
- # 0.6.1 supports socket.io 1.0, but WMF is using 0.9 (T91393 and T85716)
- 'rcstream': ['socketIO-client<0.6.1'],
'security': ['requests[security]', 'pycparser!=2.14'],
'mwoauth': ['mwoauth>=0.2.4,!=0.3.1'],
'html': ['BeautifulSoup4'],
diff --git a/tox.ini b/tox.ini
index 724aa80..0b652a8 100644
--- a/tox.ini
+++ b/tox.ini
@@ -12,7 +12,7 @@
envlist = flake8,pyflakes-{py3,pypy}
[params]
-doctest_skip = --ignore-files=(gui\.py|botirc\.py|rcstream\.py)
+doctest_skip = --ignore-files=(gui\.py|botirc\.py|eventstreams\.py)
[testenv]
setenv =
--
To view, visit https://gerrit.wikimedia.org/r/364993
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Ic5de5d07c5065c6c2759c7eef4fdb83ab10b8b6f
Gerrit-PatchSet: 12
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: Dalba <dalba.wiki(a)gmail.com>
Gerrit-Reviewer: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: Krinkle <krinklemail(a)gmail.com>
Gerrit-Reviewer: Lokal Profil <lokal.profil(a)gmail.com>
Gerrit-Reviewer: Magul <tomasz.magulski(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 <>
jenkins-bot has submitted this change and it was merged. ( https://gerrit.wikimedia.org/r/371697 )
Change subject: Add retry logic to timeouted requests
......................................................................
Add retry logic to timeouted requests
Reuses the same basic logic as api.py. The retry logic is needed
for sparql queries which time out (http-wise) but actually terminate
successfully so are cached the next time you make the same query.
Change-Id: I2e4feff5338eef3c669ec4f0e5bef8412f12bbfb
---
M pywikibot/data/api.py
M pywikibot/data/sparql.py
M pywikibot/exceptions.py
3 files changed, 57 insertions(+), 18 deletions(-)
Approvals:
jenkins-bot: Verified
Xqt: Looks good to me, approved
diff --git a/pywikibot/data/api.py b/pywikibot/data/api.py
index 94be83c..6dc86d6 100644
--- a/pywikibot/data/api.py
+++ b/pywikibot/data/api.py
@@ -35,8 +35,7 @@
from pywikibot.comms import http
from pywikibot.exceptions import (
Server504Error, Server414Error, FatalServerError, NoUsername,
- Error,
- InvalidTitle
+ Error, TimeoutError, InvalidTitle
)
from pywikibot.tools import (
MediaWikiVersion, deprecated, itergroup, ip, PY2, getargspec,
@@ -1241,11 +1240,6 @@
def __len__(self):
"""Return the number of enabled and disabled options."""
return len(self._enabled) + len(self._disabled)
-
-
-class TimeoutError(Error):
-
- """API request failed with a timeout error."""
class EnableSSLSiteWrapper(object):
diff --git a/pywikibot/data/sparql.py b/pywikibot/data/sparql.py
index b577bb0..392f8a8 100644
--- a/pywikibot/data/sparql.py
+++ b/pywikibot/data/sparql.py
@@ -9,14 +9,18 @@
import json
import sys
+import time
if sys.version_info[0] > 2:
from urllib.parse import quote
else:
from urllib2 import quote
-from pywikibot import Site, Error
+from requests.exceptions import Timeout
+
+from pywikibot import config, warning, Site
from pywikibot.comms import http
from pywikibot.tools import UnicodeMixin, py2_encode_utf_8
+from pywikibot.exceptions import Error, TimeoutError
DEFAULT_HEADERS = {'cache-control': 'no-cache',
'Accept': 'application/sparql-results+json'}
@@ -29,7 +33,8 @@
This class allows to run SPARQL queries against any SPARQL endpoint.
"""
- def __init__(self, endpoint=None, entity_url=None, repo=None):
+ def __init__(self, endpoint=None, entity_url=None, repo=None,
+ max_retries=None, retry_wait=None):
"""
Create endpoint.
@@ -38,9 +43,16 @@
@param entity_url: URL prefix for any entities returned in a query.
@type entity_url: string
@param repo: The Wikibase site which we want to run queries on. If
- provided this overrides any value in endpoint and entity_url.
- Defaults to Wikidata.
+ provided this overrides any value in endpoint and entity_url.
+ Defaults to Wikidata.
@type repo: pywikibot.site.DataSite
+ @param max_retries: (optional) Maximum number of times to retry after
+ errors, defaults to config.max_retries.
+ @type max_retries: int
+ @param retry_wait: (optional) Minimum time in seconds to wait after an
+ error, defaults to config.retry_wait seconds (doubles each retry
+ until max of 120 seconds is reached).
+ @type retry_wait: float
"""
# default to Wikidata
if not repo and not endpoint:
@@ -67,6 +79,15 @@
self.entity_url = entity_url
self.last_response = None
+
+ if max_retries is None:
+ self.max_retries = config.max_retries
+ else:
+ self.max_retries = max_retries
+ if retry_wait is None:
+ self.retry_wait = config.retry_wait
+ else:
+ self.retry_wait = retry_wait
def get_last_response(self):
"""
@@ -120,13 +141,28 @@
@type query: string
"""
url = '%s?query=%s' % (self.endpoint, quote(query))
- self.last_response = http.fetch(url, headers=headers)
- if not self.last_response.content:
- return None
- try:
- return json.loads(self.last_response.content)
- except ValueError:
- return None
+ while True:
+ try:
+ self.last_response = http.fetch(url, headers=headers)
+ if not self.last_response.content:
+ return None
+ try:
+ return json.loads(self.last_response.content)
+ except ValueError:
+ return None
+ except Timeout:
+ self.wait()
+ continue
+
+ def wait(self):
+ """Determine how long to wait after a failed request."""
+ self.max_retries -= 1
+ if self.max_retries < 0:
+ raise TimeoutError('Maximum retries attempted without success.')
+ warning('Waiting {0} seconds before retrying.'.format(self.retry_wait))
+ time.sleep(self.retry_wait)
+ # double the next wait, but do not exceed 120 seconds
+ self.retry_wait = min(120, self.retry_wait * 2)
def ask(self, query, headers=DEFAULT_HEADERS):
"""
diff --git a/pywikibot/exceptions.py b/pywikibot/exceptions.py
index aa249c7..d9dff19 100644
--- a/pywikibot/exceptions.py
+++ b/pywikibot/exceptions.py
@@ -58,6 +58,8 @@
- CoordinateGlobeUnknownException: globe is not implemented yet.
- EntityTypeUnknownException: entity type is not available on the site.
+TimeoutError: request failed with a timeout
+
DeprecationWarning: old functionality replaced by new functionality
PendingDeprecationWarning: problematic code which has not yet been
@@ -549,6 +551,13 @@
pass
+class TimeoutError(Error):
+
+ """Request failed with a timeout error."""
+
+ pass
+
+
@__deprecated
class DeprecatedPageNotFoundError(Error):
--
To view, visit https://gerrit.wikimedia.org/r/371697
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I2e4feff5338eef3c669ec4f0e5bef8412f12bbfb
Gerrit-PatchSet: 2
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Lokal Profil <lokal.profil(a)gmail.com>
Gerrit-Reviewer: Dalba <dalba.wiki(a)gmail.com>
Gerrit-Reviewer: Jean-Frédéric <jeanfrederic.wiki(a)gmail.com>
Gerrit-Reviewer: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: Lokal Profil <lokal.profil(a)gmail.com>
Gerrit-Reviewer: Smalyshev <smalyshev(a)wikimedia.org>
Gerrit-Reviewer: Xqt <info(a)gno.de>
Gerrit-Reviewer: jenkins-bot <>
jenkins-bot has submitted this change and it was merged. ( https://gerrit.wikimedia.org/r/371719 )
Change subject: [bugfix] rename the mainpage image because "image" is already in use
......................................................................
[bugfix] rename the mainpage image because "image" is already in use
Bug: T172770
Change-Id: I9e5729c803ed6d6b763dc1fe25eba47945038b47
---
M tests/page_tests.py
1 file changed, 3 insertions(+), 4 deletions(-)
Approvals:
Dalba: Looks good to me, approved
jenkins-bot: Verified
diff --git a/tests/page_tests.py b/tests/page_tests.py
index 17c69ef..3c74326 100644
--- a/tests/page_tests.py
+++ b/tests/page_tests.py
@@ -563,10 +563,9 @@
if MediaWikiVersion(site.version()) < MediaWikiVersion('1.20'):
self.assertRaises(NotImplementedError, mainpage.page_image)
elif site.has_extension('PageImages'):
- image = mainpage.page_image()
- if image is not None:
- self.assertIsInstance(mainpage.page_image(),
- pywikibot.FilePage)
+ mainpage_image = mainpage.page_image()
+ if mainpage_image is not None:
+ self.assertIsInstance(mainpage_image, pywikibot.FilePage)
# for file pages, the API should return the file itself
self.assertEqual(image.page_image(), image)
else:
--
To view, visit https://gerrit.wikimedia.org/r/371719
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I9e5729c803ed6d6b763dc1fe25eba47945038b47
Gerrit-PatchSet: 1
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: Dalba <dalba.wiki(a)gmail.com>
Gerrit-Reviewer: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: Strainu <wiki(a)strainu.ro>
Gerrit-Reviewer: jenkins-bot <>
jenkins-bot has submitted this change and it was merged. ( https://gerrit.wikimedia.org/r/368192 )
Change subject: family.py: Get the stable MW version number from mediawiki.org
......................................................................
family.py: Get the stable MW version number from mediawiki.org
Use mediawiki.org's {{Template:MW stable release number}} to retrieve the
latest stable MediaWiki version number.
The template is used on https://www.mediawiki.org/wiki/Download and looks
to be reliable enough.
Change-Id: I158e4d01ca668d4569a65f54bee719dc2e05d7db
---
M pywikibot/family.py
1 file changed, 5 insertions(+), 3 deletions(-)
Approvals:
jenkins-bot: Verified
Xqt: Looks good to me, approved
diff --git a/pywikibot/family.py b/pywikibot/family.py
index 1ff3d7f..fd9ea4e 100644
--- a/pywikibot/family.py
+++ b/pywikibot/family.py
@@ -29,9 +29,7 @@
from warnings import warn
import pywikibot
-
from pywikibot import config
-
from pywikibot.exceptions import UnknownFamily, FamilyMaintenanceWarning
from pywikibot.tools import (
deprecated, deprecated_args, issue_deprecation_warning,
@@ -1268,7 +1266,11 @@
Use L{pywikibot.tools.MediaWikiVersion} to compare version strings.
"""
# Here we return the latest mw release for downloading
- return '1.28.1'
+ if not hasattr(self, '_version'):
+ self._version = \
+ pywikibot.Site('mediawiki', 'mediawiki').expand_text(
+ '{{MW stable release number}}')
+ return self._version
def force_version(self, code):
"""
--
To view, visit https://gerrit.wikimedia.org/r/368192
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I158e4d01ca668d4569a65f54bee719dc2e05d7db
Gerrit-PatchSet: 10
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Dalba <dalba.wiki(a)gmail.com>
Gerrit-Reviewer: Dalba <dalba.wiki(a)gmail.com>
Gerrit-Reviewer: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: Magul <tomasz.magulski(a)gmail.com>
Gerrit-Reviewer: Xqt <info(a)gno.de>
Gerrit-Reviewer: jenkins-bot <>
jenkins-bot has submitted this change and it was merged. ( https://gerrit.wikimedia.org/r/371704 )
Change subject: [doc] max_retries and retry_wait defaults is set in config2.py
......................................................................
[doc] max_retries and retry_wait defaults is set in config2.py
- defaults for max_retries and retry_wait are set in config file
(and config.max_retries is now 15 btw.)
- fix spelling mistake in doc string
- keep lines beneath 80 chars
Change-Id: I72b291b69bebd009aa5a822b06496461c3479ee8
---
M pywikibot/data/api.py
1 file changed, 19 insertions(+), 18 deletions(-)
Approvals:
Lokal Profil: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/data/api.py b/pywikibot/data/api.py
index f905bc6..3abfb7f 100644
--- a/pywikibot/data/api.py
+++ b/pywikibot/data/api.py
@@ -1339,16 +1339,16 @@
"""
Create a new Request instance with the given parameters.
- The parameters for the request can be defined via eiter the 'parameters'
- parameter or the keyword arguments. The keyword arguments were the
- previous implementation but could cause problems when there are
- arguments to the API named the same as normal arguments to this class.
- So the second parameter 'parameters' was added which just contains all
- parameters. When a Request instance is created it must use either one
- of them and not both at the same time. To have backwards compatibility
- it adds a parameter named 'parameters' to kwargs when both parameters
- are set as that indicates an old call and 'parameters' was originally
- supplied as a keyword parameter.
+ The parameters for the request can be defined via either the
+ 'parameters' parameter or the keyword arguments. The keyword arguments
+ were the previous implementation but could cause problems when there
+ are arguments to the API named the same as normal arguments to this
+ class. So the second parameter 'parameters' was added which just
+ contains all parameters. When a Request instance is created it must use
+ either one of them and not both at the same time. To have backwards
+ compatibility it adds a parameter named 'parameters' to kwargs when
+ both parameters are set as that indicates an old call and 'parameters'
+ was originally supplied as a keyword parameter.
If undefined keyword arguments were given AND the 'parameters'
parameter was supplied as a positional parameter it still assumes
@@ -1365,17 +1365,18 @@
@param site: The Site to which the request will be submitted. If not
supplied, uses the user's configured default Site.
@param mime: If true, send in "multipart/form-data" format (default
- False). Parameters which should only be transferred via mime mode
- can be defined via that parameter too (an empty dict acts like
- 'True' not like 'False'!).
+ False). Parameters which should only be transferred via mime
+ mode can be defined via that parameter too (an empty dict acts
+ like 'True' not like 'False'!).
@type mime: bool or dict
@param mime_params: DEPRECATED! A dictionary of parameter which should
- only be transferred via mime mode. If not None sets mime to True.
+ only be transferred via mime mode. If not None sets mime to
+ True.
@param max_retries: (optional) Maximum number of times to retry after
- errors, defaults to 25
- @param retry_wait: (optional) Minimum time to wait after an error,
- defaults to 5 seconds (doubles each retry until max of 120 is
- reached)
+ errors, defaults to config.max_retries.
+ @param retry_wait: (optional) Minimum time in seconds to wait after an
+ error, defaults to config.retry_wait seconds (doubles each retry
+ until max of 120 seconds is reached).
@param use_get: (optional) Use HTTP GET request if possible. If False
it uses a POST request. If None, it'll try to determine via
action=paraminfo if the action requires a POST.
--
To view, visit https://gerrit.wikimedia.org/r/371704
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I72b291b69bebd009aa5a822b06496461c3479ee8
Gerrit-PatchSet: 1
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: Lokal Profil <lokal.profil(a)gmail.com>
Gerrit-Reviewer: jenkins-bot <>
jenkins-bot has submitted this change and it was merged. ( https://gerrit.wikimedia.org/r/362324 )
Change subject: Add ability for Pywikibot to thank Flow posts
......................................................................
Add ability for Pywikibot to thank Flow posts
This change adds a thank method to Post, which will allow bots to
thank the creators of Flow posts. A property was added to Post
which returns a User object corresponding to the creator of that
post. Two tests, adapted from the revision thanking tests, have
been added and pass locally.
Bug: T135411
Change-Id: Ie629aaa46aa007a39e6d5dce6400504bf73e5980
---
M pywikibot/data/api.py
M pywikibot/flow.py
M pywikibot/site.py
A tests/flow_thanks_tests.py
4 files changed, 96 insertions(+), 2 deletions(-)
Approvals:
jenkins-bot: Verified
Xqt: Looks good to me, approved
diff --git a/pywikibot/data/api.py b/pywikibot/data/api.py
index 7f4c80b..9a19f8a 100644
--- a/pywikibot/data/api.py
+++ b/pywikibot/data/api.py
@@ -1442,7 +1442,7 @@
"wbcreateclaim", "wbremoveclaims", "wbsetclaimvalue",
"wbsetreference", "wbremovereferences", "wbsetclaim",
'wbcreateredirect',
- 'thank',
+ 'thank', 'flowthank'
)
# Client side verification that the request is being performed
# by a logged in user, and warn if it isn't a config username.
diff --git a/pywikibot/flow.py b/pywikibot/flow.py
index 5f8a006..e5920a3 100644
--- a/pywikibot/flow.py
+++ b/pywikibot/flow.py
@@ -12,7 +12,7 @@
import logging
from pywikibot.exceptions import NoPage, UnknownExtension, LockedPage
-from pywikibot.page import BasePage
+from pywikibot.page import BasePage, User
from pywikibot.tools import PY2
if not PY2:
@@ -428,6 +428,16 @@
self._load()
return self._current_revision['isModerated']
+ @property
+ def creator(self):
+ """The creator of this post."""
+ if not hasattr(self, '_current_revision'):
+ self._load()
+ if not hasattr(self, '_creator'):
+ self._creator = User(self.site,
+ self._current_revision['creator']['name'])
+ return self._creator
+
def get(self, format='wikitext', force=False, sysop=False):
"""Return the contents of the post in the given format.
@@ -535,3 +545,7 @@
"""
self.site.restore_post(self, reason)
self._load()
+
+ def thank(self):
+ """Thank the user who made this post."""
+ self.site.thank_post(self)
diff --git a/pywikibot/site.py b/pywikibot/site.py
index e2bc6cc..08b7b7a 100644
--- a/pywikibot/site.py
+++ b/pywikibot/site.py
@@ -6775,6 +6775,7 @@
comparison = data['compare']['*']
return comparison
+ # Thanks API calls
@need_extension('Thanks')
def thank_revision(self, revid, source=None):
"""Corresponding method to the 'action=thank' API action.
@@ -6794,6 +6795,25 @@
raise api.APIError('Thanking unsuccessful')
return data
+ @need_extension('Flow')
+ @need_extension('Thanks')
+ def thank_post(self, post):
+ """Corresponding method to the 'action=flowthank' API action.
+
+ @param post: The post to be thanked for.
+ @type post: Post
+ @raise APIError: On thanking oneself or other API errors.
+ @return: The API response.
+ """
+ post_id = post.uuid
+ token = self.tokens['csrf']
+ req = self._simple_request(action='flowthank',
+ postid=post_id, token=token)
+ data = req.submit()
+ if data['result']['success'] != 1:
+ raise api.APIError('Thanking unsuccessful')
+ return data
+
# Flow API calls
@need_extension('Flow')
def load_board(self, page):
diff --git a/tests/flow_thanks_tests.py b/tests/flow_thanks_tests.py
new file mode 100644
index 0000000..00710c9
--- /dev/null
+++ b/tests/flow_thanks_tests.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+"""Tests for thanks-related code."""
+#
+# (C) Pywikibot team, 2016-17
+#
+# Distributed under the terms of the MIT license.
+#
+from __future__ import absolute_import, unicode_literals
+
+from pywikibot.flow import Topic
+
+from tests.aspects import TestCase
+
+
+NO_THANKABLE_POSTS = 'There is no recent post which can be test thanked.'
+
+
+class TestThankFlowPost(TestCase):
+
+ """Test thanks for Flow posts."""
+
+ family = 'test'
+ code = 'test'
+
+ write = True
+
+ @classmethod
+ def setUpClass(cls):
+ """Set up class."""
+ super(TestThankFlowPost, cls).setUpClass()
+ cls._topic_title = 'Topic:Tvkityksg1ukyrrw'
+
+ def test_thank_post(self):
+ """Test thanks for Flow posts."""
+ found_log = False
+ site = self.get_site()
+ topic = Topic(site, self._topic_title)
+ for post in reversed(topic.replies()):
+ user = post.creator
+ if site.user() == user.username:
+ continue
+ if user.is_thankable:
+ break
+ else:
+ self.skipTest(NO_THANKABLE_POSTS)
+ before_time = site.getcurrenttimestamp()
+ post.thank()
+ log_entries = site.logevents(logtype='thanks', total=5, page=user,
+ start=before_time, reverse=True)
+ for __ in log_entries:
+ found_log = True
+ break
+ self.assertTrue(found_log)
+
+ def test_self_thank(self):
+ """Test that thanking one's own Flow post causes an error."""
+ site = self.get_site()
+ topic = Topic(site, self._topic_title)
+ my_reply = topic.reply('My attempt to thank myself.')
+ self.assertAPIError('invalidrecipient', None, my_reply.thank)
--
To view, visit https://gerrit.wikimedia.org/r/362324
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Ie629aaa46aa007a39e6d5dce6400504bf73e5980
Gerrit-PatchSet: 8
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Happy5214 <happy5214(a)gmail.com>
Gerrit-Reviewer: Framawiki <framawiki(a)tools.wmflabs.org>
Gerrit-Reviewer: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: Magul <tomasz.magulski(a)gmail.com>
Gerrit-Reviewer: Xqt <info(a)gno.de>
Gerrit-Reviewer: jenkins-bot <>
jenkins-bot has submitted this change and it was merged. ( https://gerrit.wikimedia.org/r/370663 )
Change subject: [bugfix] Maybe mainpage image does not exist
......................................................................
[bugfix] Maybe mainpage image does not exist
Ignore the test when the image is None
Change-Id: I9c1af39b1c1e82da58f78b1b4331411bc059a790
---
M tests/page_tests.py
1 file changed, 4 insertions(+), 1 deletion(-)
Approvals:
Strainu: Looks good to me, approved
jenkins-bot: Verified
diff --git a/tests/page_tests.py b/tests/page_tests.py
index b4d9a5e..17c69ef 100644
--- a/tests/page_tests.py
+++ b/tests/page_tests.py
@@ -563,7 +563,10 @@
if MediaWikiVersion(site.version()) < MediaWikiVersion('1.20'):
self.assertRaises(NotImplementedError, mainpage.page_image)
elif site.has_extension('PageImages'):
- self.assertIsInstance(mainpage.page_image(), pywikibot.FilePage)
+ image = mainpage.page_image()
+ if image is not None:
+ self.assertIsInstance(mainpage.page_image(),
+ pywikibot.FilePage)
# for file pages, the API should return the file itself
self.assertEqual(image.page_image(), image)
else:
--
To view, visit https://gerrit.wikimedia.org/r/370663
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I9c1af39b1c1e82da58f78b1b4331411bc059a790
Gerrit-PatchSet: 1
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: Dalba <dalba.wiki(a)gmail.com>
Gerrit-Reviewer: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: Strainu <wiki(a)strainu.ro>
Gerrit-Reviewer: jenkins-bot <>