jenkins-bot has submitted this change and it was merged. ( https://gerrit.wikimedia.org/r/347171 )
Change subject: [IMPR] Make a decorator for asynchronous methods
......................................................................
[IMPR] Make a decorator for asynchronous methods
Change-Id: Ib7a696aa45d395009bb096e30fb1900841a278d5
---
M pywikibot/page.py
1 file changed, 64 insertions(+), 41 deletions(-)
Approvals:
jenkins-bot: Verified
Xqt: Looks good to me, approved
diff --git a/pywikibot/page.py b/pywikibot/page.py
index 00c60c3..0b1d8b6 100644
--- a/pywikibot/page.py
+++ b/pywikibot/page.py
@@ -71,6 +71,7 @@
PYTHON_VERSION,
MediaWikiVersion, UnicodeMixin, ComparableMixin, DotReadableDict,
deprecated, deprecate_arg, deprecated_args, issue_deprecation_warning,
+ add_full_name, manage_wrapping,
ModuleDeprecationWrapper as _ModuleDeprecationWrapper,
first_upper, redirect_func, remove_last_args, _NotImplementedWarning,
OrderedDict, Counter,
@@ -100,6 +101,46 @@
)
logger = logging.getLogger("pywiki.wiki.page")
+
+
+@add_full_name
+def allow_asynchronous(func):
+ """
+ Decorator to make it possible to run a BasePage method asynchronously.
+
+ This is done when the method is called with kwarg asynchronous=True.
+ Optionally, you can also provide kwarg callback, which, if provided, is
+ a callable that gets the page as the first and a possible exception that
+ occurred during saving in the second thread or None as the second argument.
+ """
+ def handle(func, self, do_async=False, callback=None, *args, **kwargs):
+ err = None
+ try:
+ func(self, *args, **kwargs)
+ # TODO: other "expected" error types to catch?
+ except pywikibot.Error as edit_err:
+ err = edit_err # edit_err will be deleted in the end of the scope
+ link = self.title(asLink=True)
+ pywikibot.log('Error saving page %s (%s)\n' % (link, err),
+ exc_info=True)
+ if not callback and not do_async:
+ if isinstance(err, pywikibot.PageSaveRelatedError):
+ raise err
+ raise pywikibot.OtherPageSaveError(self, err)
+ if callback:
+ callback(self, err)
+
+ def wrapper(self, *args, **kwargs):
+ do_async = kwargs.pop('asynchronous', False)
+ if do_async:
+ pywikibot.async_request(handle, func, self, do_async=True,
+ *args, **kwargs)
+ else:
+ handle(func, self, *args, **kwargs)
+
+ manage_wrapping(wrapper, func)
+
+ return wrapper
# Note: Link objects (defined later on) represent a wiki-page's title, while
@@ -1197,7 +1238,7 @@
@param quiet: enable/disable successful save operation message;
defaults to False.
In asynchronous mode, if True, it is up to the calling bot to
- manage the ouput e.g. via callback.
+ manage the output e.g. via callback.
@type quiet: bool
"""
if not summary:
@@ -1209,47 +1250,26 @@
if not force and not self.botMayEdit():
raise pywikibot.OtherPageSaveError(
self, "Editing restricted by {{bots}} template")
- if asynchronous:
- pywikibot.async_request(self._save, summary=summary, watch=watch,
- minor=minor, botflag=botflag,
- asynchronous=asynchronous,
- callback=callback,
- cc=apply_cosmetic_changes,
- quiet=quiet, **kwargs)
- else:
- self._save(summary=summary, watch=watch, minor=minor,
- botflag=botflag, asynchronous=asynchronous,
- callback=callback, cc=apply_cosmetic_changes,
- quiet=quiet, **kwargs)
+ self._save(summary=summary, watch=watch, minor=minor, botflag=botflag,
+ asynchronous=asynchronous, callback=callback,
+ cc=apply_cosmetic_changes, quiet=quiet, **kwargs)
+ @allow_asynchronous
def _save(self, summary=None, watch=None, minor=True, botflag=None,
- asynchronous=False, callback=None, cc=None, quiet=False,
- **kwargs):
+ cc=None, quiet=False, **kwargs):
"""Helper function for save()."""
- err = None
link = self.title(asLink=True)
if cc or cc is None and config.cosmetic_changes:
summary = self._cosmetic_changes_hook(summary) or summary
- try:
- done = self.site.editpage(self, summary=summary, minor=minor,
- watch=watch, bot=botflag, **kwargs)
- if not done:
- if not quiet:
- pywikibot.warning(u"Page %s not saved" % link)
- raise pywikibot.PageNotSaved(self)
+
+ done = self.site.editpage(self, summary=summary, minor=minor,
+ watch=watch, bot=botflag, **kwargs)
+ if not done:
if not quiet:
- pywikibot.output(u"Page %s saved" % link)
- # TODO: other "expected" error types to catch?
- except pywikibot.Error as edit_err:
- err = edit_err # edit_err will be deleted in the end of the scope
- pywikibot.log(u"Error saving page %s (%s)\n" % (link, err),
- exc_info=True)
- if not callback and not asynchronous:
- if isinstance(err, pywikibot.PageSaveRelatedError):
- raise err
- raise pywikibot.OtherPageSaveError(self, err)
- if callback:
- callback(self, err)
+ pywikibot.warning('Page %s not saved' % link)
+ raise pywikibot.PageNotSaved(self)
+ if not quiet:
+ pywikibot.output('Page %s saved' % link)
def _cosmetic_changes_hook(self, comment):
if self.isTalkPage() or \
@@ -3849,7 +3869,8 @@
return site.dbName()
return site
- def editEntity(self, data=None, asynchronous=False, **kwargs):
+ @allow_asynchronous
+ def editEntity(self, data=None, **kwargs):
"""
Edit an entity using Wikibase wbeditentity API.
@@ -3861,14 +3882,16 @@
@param data: Data to be saved
@type data: dict, or None to save the current content of the entity.
- @param asynchronous: if True, launch a separate thread to edit
+ @keyword asynchronous: if True, launch a separate thread to edit
asynchronously
@type asynchronous: bool
+ @keyword callback: a callable object that will be called after the entity
+ has been updated. It must take two arguments: (1) a WikibasePage
+ object, and (2) an exception instance, which will be None if the
+ page was saved successfully. This is intended for use by bots that
+ need to keep track of which saves were successful.
+ @type callback: callable
"""
- if asynchronous:
- pywikibot.async_request(self.editEntity, data, **kwargs)
- return
-
if hasattr(self, '_revid'):
baserevid = self.latest_revision_id
else:
--
To view, visit https://gerrit.wikimedia.org/r/347171
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Ib7a696aa45d395009bb096e30fb1900841a278d5
Gerrit-PatchSet: 6
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Matěj Suchánek <matejsuchanek97(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: Matěj Suchánek <matejsuchanek97(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/272316 )
Change subject: [IMPR] Provide options by a separate handler class
......................................................................
[IMPR] Provide options by a separate handler class
- provide an option handler class
- derive BaseBot from OptionHandler class
There are som generator classes who could derive from OptionHandler:
- redirect.RedirectGenerator
- pagefromfile.PageFromFileReader
Will be done in a separate patch set.
- rewrite help message for redirect.py
Change-Id: Ib281147d38f3d6eaf34f1a1ad0c7c920513f0d79
---
M pywikibot/bot.py
1 file changed, 48 insertions(+), 27 deletions(-)
Approvals:
Dalba: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/bot.py b/pywikibot/bot.py
index 4785117..4cef28a 100644
--- a/pywikibot/bot.py
+++ b/pywikibot/bot.py
@@ -1131,31 +1131,16 @@
i18n.input('pywikibot-enter-finished-browser')
-class BaseBot(object):
+class OptionHandler(object):
- """
- Generic Bot to be subclassed.
+ """Class to get and set options."""
- This class provides a run() method for basic processing of a
- generator one page at a time.
-
- If the subclass places a page generator in self.generator,
- Bot will process each page in the generator, invoking the method treat()
- which must then be implemented by subclasses.
-
- If the subclass does not set a generator, or does not override
- treat() or run(), NotImplementedError is raised.
- """
-
- # Bot configuration.
+ # Handler configuration.
# Only the keys of the dict can be passed as init options
# The values are the default values
- # Extend this in subclasses!
- availableOptions = {
- 'always': False, # By default ask for confirmation when putting a page
- }
+ # Overwrite this in subclasses!
- _current_page = None
+ availableOptions = {}
def __init__(self, **kwargs):
"""
@@ -1164,13 +1149,7 @@
@param kwargs: bot options
@type kwargs: dict
"""
- if 'generator' in kwargs:
- self.generator = kwargs.pop('generator')
-
self.setOptions(**kwargs)
-
- self._treat_counter = 0
- self._save_counter = 0
def setOptions(self, **kwargs):
"""
@@ -1196,13 +1175,55 @@
"""
Get the current value of an option.
- @param option: key defined in Bot.availableOptions
+ @param option: key defined in OptionHandler.availableOptions
"""
try:
return self.options.get(option, self.availableOptions[option])
except KeyError:
raise pywikibot.Error(u'%s is not a valid bot option.' % option)
+
+class BaseBot(OptionHandler):
+
+ """
+ Generic Bot to be subclassed.
+
+ This class provides a run() method for basic processing of a
+ generator one page at a time.
+
+ If the subclass places a page generator in self.generator,
+ Bot will process each page in the generator, invoking the method treat()
+ which must then be implemented by subclasses.
+
+ If the subclass does not set a generator, or does not override
+ treat() or run(), NotImplementedError is raised.
+ """
+
+ # Handler configuration.
+ # The values are the default values
+ # Extend this in subclasses!
+
+ availableOptions = {
+ 'always': False, # By default ask for confirmation when putting a page
+ }
+
+ _current_page = None
+
+ def __init__(self, **kwargs):
+ """
+ Only accept options defined in availableOptions.
+
+ @param kwargs: bot options
+ @type kwargs: dict
+ """
+ if 'generator' in kwargs:
+ self.generator = kwargs.pop('generator')
+
+ super(BaseBot, self).__init__(**kwargs)
+
+ self._treat_counter = 0
+ self._save_counter = 0
+
@property
def current_page(self):
"""Return the current working page as a property."""
--
To view, visit https://gerrit.wikimedia.org/r/272316
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Ib281147d38f3d6eaf34f1a1ad0c7c920513f0d79
Gerrit-PatchSet: 15
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: Dalba <dalba.wiki(a)gmail.com>
Gerrit-Reviewer: DrTrigon <dr.trigon(a)surfeu.ch>
Gerrit-Reviewer: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: Ladsgroup <Ladsgroup(a)gmail.com>
Gerrit-Reviewer: Magul <tomasz.magulski(a)gmail.com>
Gerrit-Reviewer: Matěj Suchánek <matejsuchanek97(a)gmail.com>
Gerrit-Reviewer: Mpaa <mpaa.wiki(a)gmail.com>
Gerrit-Reviewer: Ricordisamoa <ricordisamoa(a)openmailbox.org>
Gerrit-Reviewer: Russell Blau <russblau(a)imapmail.org>
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. ( https://gerrit.wikimedia.org/r/348444 )
Change subject: config2: use repr() of exceptions to prevent UnicodeDecodeErrors
......................................................................
config2: use repr() of exceptions to prevent UnicodeDecodeErrors
When no program is configured to open .py files, the
_win32_extension_command function will throw an OSError. On non-English
Windows versions, this error often contains non-ascii characters. On
Python 2, this error will be a str in the Windows' multi byte encoding.
Trying to decode this error with a different encoding will cause an
UnicodeDecodeError, which then blocks Pywikibot from being used.
Instead of trying to guess the correct encoding to use, use the repr()
of the string. This is guaranteed to be ascii, and can therefore always
be embedded in a unicode string safely.
Bug: T120222
Change-Id: I47e2756259ecbc1279e1124c076622177b3fc1d6
---
M pywikibot/config2.py
1 file changed, 1 insertion(+), 1 deletion(-)
Approvals:
Merlijn van Deen: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/config2.py b/pywikibot/config2.py
index 90877ba..e0bac1e 100644
--- a/pywikibot/config2.py
+++ b/pywikibot/config2.py
@@ -942,7 +942,7 @@
return cmd[:-1].strip()
except WindowsError as e:
# Catch any key lookup errors
- output('Unable to detect program for file extension "{0}": {1}'.format(
+ output('Unable to detect program for file extension "{0}": {1!r}'.format(
extension, e))
--
To view, visit https://gerrit.wikimedia.org/r/348444
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I47e2756259ecbc1279e1124c076622177b3fc1d6
Gerrit-PatchSet: 2
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Gerrit Patch Uploader <gerritpatchuploader(a)gmail.com>
Gerrit-Reviewer: Framawiki <framawiki(a)tools.wmflabs.org>
Gerrit-Reviewer: Gerrit Patch Uploader <gerritpatchuploader(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. ( https://gerrit.wikimedia.org/r/347138 )
Change subject: pywikibot/__init__.py: Do not use `deprecate_arg`
......................................................................
pywikibot/__init__.py: Do not use `deprecate_arg`
`deprecate_arg` is deprecated itself and redirected to
`pywikibot.tools.deprecate_arg` which is imported as `__deprecate_arg`.
Rename `__deprecate_arg` to `_deprecate_arg`. Python mangles names that start
with two leading underscores inside classes to make them private. This prevented
the usage of `__deprecate_arg` in Coordinate class.
Bug: T162509
Change-Id: Icf557d5f337c302b328ad784317cdb2934bcf458
---
M pywikibot/__init__.py
1 file changed, 3 insertions(+), 3 deletions(-)
Approvals:
jenkins-bot: Verified
Xqt: Looks good to me, approved
diff --git a/pywikibot/__init__.py b/pywikibot/__init__.py
index 45b08f5..59016c3 100644
--- a/pywikibot/__init__.py
+++ b/pywikibot/__init__.py
@@ -69,7 +69,7 @@
from pywikibot.tools import (
# __ to avoid conflict with ModuleDeprecationWrapper._deprecated
deprecated as __deprecated,
- deprecate_arg as __deprecate_arg,
+ deprecate_arg as _deprecate_arg,
normalize_username,
MediaWikiVersion,
redirect_func,
@@ -130,7 +130,7 @@
deprecated = redirect_func(__deprecated)
-deprecate_arg = redirect_func(__deprecate_arg)
+deprecate_arg = redirect_func(_deprecate_arg)
class Timestamp(datetime.datetime):
@@ -235,7 +235,7 @@
_items = ('lat', 'lon', 'globe')
- @deprecate_arg('entity', 'globe_item')
+ @_deprecate_arg('entity', 'globe_item')
def __init__(self, lat, lon, alt=None, precision=None, globe='earth',
typ='', name='', dim=None, site=None, globe_item=None):
"""
--
To view, visit https://gerrit.wikimedia.org/r/347138
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Icf557d5f337c302b328ad784317cdb2934bcf458
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: Lokal Profil <lokal.profil(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/289227 )
Change subject: [IMPR] Show a warning when a LogEntry type is not known
......................................................................
[IMPR] Show a warning when a LogEntry type is not known
- a warning could ensure to update the bot for unknown types
- put the return statement outside the try statement to ensure
that only the missing 'type' key is fetched by this exception
Bug: T135505
Change-Id: I096cf636c6da1d65fe578e053594a5203d4dceb8
---
M pywikibot/logentries.py
1 file changed, 4 insertions(+), 2 deletions(-)
Approvals:
Mpaa: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/logentries.py b/pywikibot/logentries.py
index 7665806..310d826 100644
--- a/pywikibot/logentries.py
+++ b/pywikibot/logentries.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""Objects representing Mediawiki log entries."""
#
-# (C) Pywikibot team, 2007-2016
+# (C) Pywikibot team, 2007-2017
#
# Distributed under the terms of the MIT license.
#
@@ -431,6 +431,8 @@
try:
return cls.logtypes[logtype]
except KeyError:
+ pywikibot.warning(
+ 'Log entry key {0} is not known.'.format(logtype))
return LogEntry
def _createFromData(self, logdata):
@@ -443,8 +445,8 @@
"""
try:
logtype = logdata['type']
- return LogEntryFactory._getEntryClass(logtype)(logdata, self._site)
except KeyError:
pywikibot.debug('API log entry received:\n{0}'.format(logdata),
_logger)
raise Error("Log entry has no 'type' key")
+ return LogEntryFactory._getEntryClass(logtype)(logdata, self._site)
--
To view, visit https://gerrit.wikimedia.org/r/289227
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I096cf636c6da1d65fe578e053594a5203d4dceb8
Gerrit-PatchSet: 3
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: Magul <tomasz.magulski(a)gmail.com>
Gerrit-Reviewer: Mpaa <mpaa.wiki(a)gmail.com>
Gerrit-Reviewer: jenkins-bot <>