John Vandenberg has uploaded a new change for review.
https://gerrit.wikimedia.org/r/237977
Change subject: Expand login and rights exception tree
......................................................................
Expand login and rights exception tree
Allow APIError, with extra information, to be used as subclasses
for core exceptions like NoUsername, so that uncaught errors
include API supplied information like `servedby`.
Bug: T109173
Change-Id: I7f1119f2ccea48221ca400958683fd5de13ee34d
---
M pywikibot/data/api.py
M pywikibot/exceptions.py
M pywikibot/login.py
M pywikibot/site.py
M scripts/category.py
M scripts/interwiki.py
M scripts/redirect.py
M tests/exceptions_tests.py
8 files changed, 394 insertions(+), 81 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/pywikibot/core
refs/changes/77/237977/1
diff --git a/pywikibot/data/api.py b/pywikibot/data/api.py
index e574227..a8cc216 100644
--- a/pywikibot/data/api.py
+++ b/pywikibot/data/api.py
@@ -28,12 +28,21 @@
from warnings import warn
import pywikibot
+
from pywikibot import config, login
-from pywikibot.tools import MediaWikiVersion, deprecated, itergroup, ip, PY2
-from pywikibot.exceptions import (
- Server504Error, Server414Error, FatalServerError, NoUsername, Error
-)
+
from pywikibot.comms import http
+from pywikibot.exceptions import (
+ Server504Error, Server414Error, FatalServerError,
+ SiteRelatedError,
+ LoginError,
+ OAuthAuthenticationError,
+ Error,
+)
+from pywikibot.tools import (
+ MediaWikiVersion, itergroup, ip, PY2,
+ deprecated, issue_deprecation_warning,
+)
if not PY2:
# Subclassing necessary to fix a possible bug of the email package
@@ -87,23 +96,31 @@
lagpattern = re.compile(r"Waiting for [\d.]+: (?P<lag>\d+) seconds? lagged")
-class APIError(Error):
+class APIError(SiteRelatedError):
"""The wiki site returned an error message."""
- def __init__(self, code, info, **kwargs):
+ def __init__(self, code, info, site=None, **kwargs):
"""Save error dict returned by MW API."""
self.code = code
self.info = info
self.other = kwargs
- self.unicode = unicode(self.__str__())
+ self.unicode = unicode(self)
+ message = self.unicode
+
+ if site:
+ super(APIError, self).__init__(site, message)
+ else:
+ issue_deprecation_warning(
+ 'APIError without a site', instead=None, depth=2)
+ super(APIError, self).__init__(message=message)
def __repr__(self):
"""Return internal representation."""
return '{name}("{code}", "{info}", {other})'.format(
name=self.__class__.__name__, **self.__dict__)
- def __str__(self):
+ def __unicode__(self):
"""Return a string representation."""
if self.other:
return '{0}: {1} [{2}]'.format(
@@ -120,7 +137,7 @@
"""Upload failed with a warning message (passed as the argument)."""
- def __init__(self, code, message, file_key=None, offset=0):
+ def __init__(self, code, message, file_key=None, offset=0, site=None):
"""
Create a new UploadWarning instance.
@@ -131,7 +148,7 @@
there is no offset.
@type offset: int or bool
"""
- super(UploadWarning, self).__init__(code, message)
+ super(UploadWarning, self).__init__(code, message, site)
self.file_key = file_key
self.offset = offset
@@ -150,6 +167,16 @@
self.mediawiki_exception_class_name = mediawiki_exception_class_name
code = 'internal_api_error_' + mediawiki_exception_class_name
super(APIMWException, self).__init__(code, info, **kwargs)
+
+
+class APILoginError(APIError, LoginError):
+
+ """Login error during an API request."""
+
+
+class APIOAuthInvalidAuthorisation(APIError, OAuthAuthenticationError):
+
+ """OAuth failure during an API request."""
class ParamInfo(Container):
@@ -2150,10 +2177,12 @@
self.site.user(),
', '.join('{0}: {1}'.format(*e)
for e in user_tokens.items())))
+
if 'mwoauth-invalid-authorization' in code:
- raise NoUsername('Failed OAuth authentication for %s: %s'
- % (self.site, info))
- # raise error
+ raise APIOAuthInvalidAuthorisation(site=self.site, **error)
+ elif code == 'readapidenied':
+ raise APILoginError(site=self.site, **error)
+
try:
# Due to bug T66958, Page's repr may return non ASCII bytes
# Get as bytes in PY2 and decode with the console encoding as
diff --git a/pywikibot/exceptions.py b/pywikibot/exceptions.py
index a8a8beb..221f191 100644
--- a/pywikibot/exceptions.py
+++ b/pywikibot/exceptions.py
@@ -3,10 +3,7 @@
Exception and warning classes used throughout the framework.
Error: Base class, all exceptions should the subclass of this class.
- - NoUsername: Username is not in user-config.py, or it is invalid.
- - UserBlocked: Username or IP has been blocked
- AutoblockUser: requested action on a virtual autoblock user not valid
- - UserRightsError: insufficient rights for requested action
- BadTitle: Server responded with BadTitle
- InvalidTitle: Invalid page title
- CaptchaError: Captcha is asked and config.solve_captcha == False
@@ -14,6 +11,13 @@
- PageNotFound: Page not found (deprecated)
- i18n.TranslationError: i18n/l10n message not available
- UnknownExtension: Extension is not defined for this site
+
+---NotLoggedIn: A login was not possible
+ - NoUsername: Username is not in user-config.py, or it is invalid.
+ - LoginFailed: Login was not successful
+ - OAuthLoginFailure: OAuth login failure
+ - login.OAuthImpossible: OAuth dependencies failed
+ - data.api.OAuthInvalidAuthorisation: OAuth authorisation failed
SiteDefinitionError: Site loading problem
- UnknownSite: Site does not exist in Family
@@ -66,7 +70,7 @@
- FamilyMaintenanceWarning: missing information in family definition
"""
#
-# (C) Pywikibot team, 2008
+# (C) Pywikibot team, 2008-2015
#
# Distributed under the terms of the MIT license.
#
@@ -76,9 +80,14 @@
import sys
-from pywikibot.tools import UnicodeMixin, _NotImplementedWarning
+from pywikibot.tools import (
+ PY2,
+ UnicodeMixin,
+ _NotImplementedWarning,
+ issue_deprecation_warning,
+)
-if sys.version_info[0] > 2:
+if not PY2:
unicode = str
@@ -111,13 +120,57 @@
def __init__(self, arg):
"""Constructor."""
self.unicode = arg
+ super(Error, self).__init__(arg)
def __unicode__(self):
"""Return a unicode string representation."""
return self.unicode
-class PageRelatedError(Error):
+class SiteRelatedError(Error):
+
+ """
+ Abstract Exception, used when the exception concerns a particular Site.
+
+ This class should be used when the Exception concerns a particular
+ Site, and when a generic message can be written once for all.
+ """
+
+ def __init__(self, site=None, message=None):
+ """
+ Constructor.
+
+ @param page: Site that caused the exception
+ @type page: Site object
+ """
+ if message:
+ try:
+ self.message = message
+ except AttributeError:
+ pass
+
+ if not site:
+ issue_deprecation_warning(
+ 'SiteRelatedError(site=None)', instead=None, depth=2)
+ self.site = self.family = self.site_code = 'unknown'
+ else:
+ self.site = site
+ self.family = self.site.family
+ self.site_code = self.site.code
+
+ if hasattr(self, 'message'):
+ if '%(' in self.message and ')s' in self.message:
+ super(SiteRelatedError, self).__init__(
+ self.message % self.__dict__)
+ elif '%s' in self.message:
+ super(SiteRelatedError, self).__init__(self.message % site)
+ else:
+ super(SiteRelatedError, self).__init__(self.message)
+ else:
+ super(SiteRelatedError, self).__init__()
+
+
+class PageRelatedError(SiteRelatedError):
"""
Abstract Exception, used when the exception concerns a particular Page.
@@ -146,12 +199,11 @@
self.page = page
self.title = page.title(asLink=True)
- self.site = page.site
- if '%(' in self.message and ')s' in self.message:
- super(PageRelatedError, self).__init__(self.message %
self.__dict__)
- else:
- super(PageRelatedError, self).__init__(self.message % page)
+ if '%s' in self.message:
+ self.message = self.message % page
+
+ super(PageRelatedError, self).__init__(page.site)
def getPage(self):
"""Return the page related to the exception."""
@@ -195,11 +247,68 @@
return unicode(self.reason)
-class NoUsername(Error):
+class UserRightsError(Error):
+
+ """Insufficient user rights to perform an action."""
+
+ pass
+
+
+class LoginError(Error):
+
+ """Unable to login."""
+
+
+class NoUsername(LoginError):
+
+ """Backwards compatible exception."""
+
+
+class LoginNotPossible(NoUsername, LoginError):
+
+ """Login is not possible."""
+
+
+class UsernameNotSpecified(LoginNotPossible, SiteRelatedError):
"""Username is not in user-config.py."""
- pass
+ message = """
+ERROR: Username for %(site)s is undefined.
+If you have an account for that site, please add a line to user-config.py:
+
+usernames['%(family)s']['%(site_code)s'] = 'myUsername'"""
+
+
+class _SysopnameNotSpecified(UsernameNotSpecified):
+
+ """Sysopname is not in user-config.py."""
+
+ message = """
+ERROR: Sysop username for %(site)s is undefined.
+If you have a sysop account for that site, please add a line to user-config.py:
+
+sysopnames['%(family)s']['%(site_code)s'] = 'myUsername'"""
+
+
+class InvalidUsername(LoginNotPossible):
+
+ """Username is not recognised."""
+
+
+class LoginFailed(NoUsername, LoginError):
+
+ """Login attempt failed."""
+
+
+class RightsElevationFailed(LoginFailed, UserRightsError):
+
+ """Attempt to increase user rights failed."""
+
+
+class OAuthAuthenticationError(LoginError):
+
+ """OAuth authentication error."""
class NoPage(PageRelatedError): # noqa
@@ -449,14 +558,14 @@
pass
-class UserBlocked(Error): # noqa
+class UserBlocked(UserRightsError): # noqa
"""Your username or IP has been blocked"""
pass
-class CaptchaError(Error):
+class CaptchaError(LoginError):
"""Captcha is asked and config.solve_captcha == False."""
@@ -471,13 +580,6 @@
an action is requested on a virtual autoblock user that's not available
for him (i.e. roughly everything except unblock).
"""
-
- pass
-
-
-class UserRightsError(Error):
-
- """Insufficient user rights to perform an action."""
pass
diff --git a/pywikibot/login.py b/pywikibot/login.py
index ac37803..8e86cfd 100644
--- a/pywikibot/login.py
+++ b/pywikibot/login.py
@@ -27,10 +27,16 @@
from pywikibot import config
from pywikibot.tools import deprecated_args, normalize_username
-from pywikibot.exceptions import NoUsername
+from pywikibot.exceptions import (
+ LoginError,
+ RightsElevationFailed,
+ UsernameNotSpecified,
+ _SysopnameNotSpecified,
+ InvalidUsername,
+)
-class OAuthImpossible(ImportError):
+class OAuthImpossible(ImportError, LoginError):
"""OAuth authentication is not possible on your system."""
@@ -79,7 +85,7 @@
The sysop username is loaded from config.sysopnames.
@type sysop: bool
- @raises NoUsername: No username is configured for the requested site.
+ @raises UsernameNotSpecified: No username for the requested site.
"""
if site is not None:
self.site = site
@@ -93,26 +99,15 @@
self.username = family_sysopnames.get(self.site.code, None)
self.username = self.username or family_sysopnames['*']
except KeyError:
- raise NoUsername(u"""\
-ERROR: Sysop username for %(fam_name)s:%(wiki_code)s is undefined.
-If you have a sysop account for that site, please add a line to user-config.py:
-
-sysopnames['%(fam_name)s']['%(wiki_code)s'] = 'myUsername'"""
- % {'fam_name': self.site.family.name,
- 'wiki_code': self.site.code})
+ raise UsernameNotSpecified(self.site)
else:
try:
family_usernames = config.usernames[self.site.family.name]
self.username = family_usernames.get(self.site.code, None)
self.username = self.username or family_usernames['*']
except:
- raise NoUsername(u"""\
-ERROR: Username for %(fam_name)s:%(wiki_code)s is undefined.
-If you have an account for that site, please add a line to user-config.py:
+ raise _SysopnameNotSpecified(self.site)
-usernames['%(fam_name)s']['%(wiki_code)s'] = 'myUsername'"""
- % {'fam_name': self.site.family.name,
- 'wiki_code': self.site.code})
self.password = password
if getattr(config, 'password_file', ''):
self.readPassword()
@@ -121,12 +116,12 @@
"""
Check that the username exists on the site.
- @raises NoUsername: Username doesnt exist in user list.
+ @raises InvalidUsername: Username doesnt exist in user list.
"""
try:
data = self.site.allusers(start=self.username, total=1)
user = next(iter(data))
- except pywikibot.data.api.APIError as e:
+ except pywikibot.data.api.APILoginError as e:
if e.code == 'readapidenied':
pywikibot.warning('Could not check user %s exists on %s'
% (self.username, self.site))
@@ -136,8 +131,8 @@
if user['name'] != self.username:
# Report the same error as server error code NotExists
- raise NoUsername('Username \'%s\' is invalid on %s'
- % (self.username, self.site))
+ raise InvalidUsername('Username \'%s\' is invalid on %s'
+ % (self.username, self.site))
def botAllowed(self):
"""
@@ -257,7 +252,7 @@
@param retry: infinitely retry if the API returns an unknown error
@type retry: bool
- @raises NoUsername: Username is not recognised by the site.
+ @raises : Username is not recognised by the site.
"""
if not self.password:
# First check that the username exists,
@@ -278,10 +273,10 @@
except pywikibot.data.api.APIError as e:
pywikibot.error(u"Login failed (%s)." % e.code)
if e.code == 'NotExists':
- raise NoUsername(u"Username '%s' does not exist on %s"
+ raise InvalidUsername(u"Username '%s' does not exist on %s"
% (self.username, self.site))
elif e.code == 'Illegal':
- raise NoUsername(u"Username '%s' is invalid on %s"
+ raise InvalidUsername(u"Username '%s' is invalid on %s"
% (self.username, self.site))
# TODO: investigate other unhandled API codes (bug 73539)
if retry:
@@ -332,7 +327,7 @@
The sysop username is loaded from config.sysopnames.
@type sysop: bool
- @raises NoUsername: No username is configured for the requested site.
+ @raises : No username is configured for the requested site.
@raise OAuthImpossible: mwoauth isn't installed
"""
if isinstance(mwoauth, ImportError):
diff --git a/pywikibot/site.py b/pywikibot/site.py
index cbb092b..6dfa337 100644
--- a/pywikibot/site.py
+++ b/pywikibot/site.py
@@ -60,7 +60,8 @@
UnknownSite,
UnknownExtension,
FamilyMaintenanceWarning,
- NoUsername,
+ LoginError,
+ RightsElevationFailed,
SpamfilterError,
NoCreateError,
UserBlocked,
@@ -1972,15 +1973,15 @@
pass
if self.is_oauth_token_available():
if sysop:
- raise NoUsername('No sysop is permitted with OAuth')
+ raise RightsElevationFailed('No sysop is permitted with OAuth')
elif self.userinfo['name'] != self._username[sysop]:
- raise NoUsername('Logged in on %(site)s via OAuth as
%(wrong)s, '
+ raise LoginError('Logged in on %(site)s via OAuth as
%(wrong)s, '
'but expect as %(right)s'
% {'site': self,
'wrong': self.userinfo['name'],
'right': self._username[sysop]})
else:
- raise NoUsername('Logging in on %s via OAuth failed' % self)
+ raise LoginFailure('Logging in on %s via OAuth failed' % self)
loginMan = api.LoginManager(site=self, sysop=sysop,
user=self._username[sysop])
if loginMan.login(retry=True):
@@ -2879,7 +2880,7 @@
sysop_protected = "edit" in rest and rest['edit'][0] == 'sysop'
try:
api.LoginManager(site=self, sysop=sysop_protected)
- except NoUsername:
+ except LoginError:
return False
return True
@@ -4461,7 +4462,7 @@
if "deletedhistory" not in self.userinfo['rights']:
try:
self.login(True)
- except NoUsername:
+ except RightsElevationFailed:
pass
if "deletedhistory" not in self.userinfo['rights']:
raise Error(
@@ -4472,7 +4473,7 @@
if "undelete" not in self.userinfo['rights']:
try:
self.login(True)
- except NoUsername:
+ except RightsElevationFailed:
pass
if "undelete" not in self.userinfo['rights']:
raise Error(
diff --git a/scripts/category.py b/scripts/category.py
index 2bd8973..948b6fa 100755
--- a/scripts/category.py
+++ b/scripts/category.py
@@ -131,6 +131,7 @@
from pywikibot.bot import (
MultipleSitesBot, IntegerOption, StandardOption, ContextOption,
)
+from pywikibot.exceptions import UsernameNotSpecified
from pywikibot.tools import (
deprecated_args, deprecated, ModuleDeprecationWrapper
)
@@ -475,9 +476,9 @@
repo = self.site.data_repository()
if self.wikibase and repo.username() is None:
# The bot can't move categories nor update the Wikibase repo
- raise pywikibot.NoUsername(u"The 'wikibase' option is turned
on"
- u" and %s has no registered
username."
- % repo)
+ raise UsernameNotSpecified(
+ "The 'wikibase' option is turned on "
+ 'and %s has no registered username.', site=repo)
template_vars = {'oldcat': self.oldcat.title(withNamespace=False)}
if self.newcat:
diff --git a/scripts/interwiki.py b/scripts/interwiki.py
index beaab4c..7f0cfdf 100755
--- a/scripts/interwiki.py
+++ b/scripts/interwiki.py
@@ -360,6 +360,10 @@
from pywikibot import config, i18n, pagegenerators, textlib, interwiki_graph,
titletranslate
from pywikibot.bot import ListOption, StandardOption
+from pywikibot.exceptions import (
+ LoginError,
+ UserRightsError,
+)
from pywikibot.tools import first_upper
if sys.version_info[0] > 2:
@@ -1706,7 +1710,7 @@
updatedSites.append(site)
except SaveError:
notUpdatedSites.append(site)
- except pywikibot.NoUsername:
+ except (LoginError, UserRightsError):
pass
except GiveUpOnPage:
break
diff --git a/scripts/redirect.py b/scripts/redirect.py
index 70ae9c7..4f76db2 100755
--- a/scripts/redirect.py
+++ b/scripts/redirect.py
@@ -83,8 +83,11 @@
import sys
import datetime
+
import pywikibot
+
from pywikibot import i18n, xmlreader, Bot
+from pywikibot.exceptions import UserRightsError
if sys.version_info[0] > 2:
basestring = (str, )
@@ -461,10 +464,10 @@
% (targetPage, movedTarget, redir_page)):
try:
redir_page.save(reason)
- except pywikibot.NoUsername:
- pywikibot.output(u"Page [[%s]] not saved; "
- u"sysop privileges required."
- % redir_page.title())
+ except UserRightsError as e:
+ pywikibot.output(
+ 'Page [[%s]] not saved due to permissions'
+ % redir_page.title())
except pywikibot.LockedPage:
pywikibot.output(u'%s is locked.'
% redir_page.title())
diff --git a/tests/exceptions_tests.py b/tests/exceptions_tests.py
index 252877b..ec9a239 100644
--- a/tests/exceptions_tests.py
+++ b/tests/exceptions_tests.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""Tests for exceptions."""
#
-# (C) Pywikibot team, 2014
+# (C) Pywikibot team, 2014-2015
#
# Distributed under the terms of the MIT license.
#
@@ -11,21 +11,180 @@
import pywikibot
-from tests.aspects import unittest, DeprecationTestCase
+from pywikibot.data.api import APIError
+from pywikibot.exceptions import (
+ Error,
+ SiteRelatedError,
+ PageRelatedError,
+ UsernameNotSpecified,
+ _SysopnameNotSpecified,
+)
+from pywikibot.tools import PY2
+
+from tests.aspects import (
+ unittest,
+ TestCase,
+ DefaultSiteTestCase,
+ DeprecationTestCase,
+)
+
+if not PY2:
+ unicode = str
-class TestDeprecatedExceptions(DeprecationTestCase):
+class TestError(TestCase):
+
+ """Test base Error class."""
+
+ net = False
+
+ def test_base(self):
+ """Test Error('foo')."""
+ exception = Error('foo')
+ self.assertEqual(str(exception), 'foo')
+ self.assertEqual(unicode(exception), 'foo')
+ self.assertEqual(exception.args, ('foo', ))
+ self.assertEqual(exception.message, 'foo')
+
+
+class TestSiteRelatedError(DefaultSiteTestCase):
+
+ """Test base Error class."""
+
+ dry = True
+
+ def test_site_related_error_base(self):
+ exception = SiteRelatedError(self.site, 'foo')
+ self.assertEqual(str(exception), 'foo')
+ self.assertEqual(unicode(exception), 'foo')
+
+ self.assertEqual(exception.args, ('foo', ))
+ self.assertEqual(exception.message, 'foo')
+
+ self.assertEqual(exception.site, self.site)
+ self.assertEqual(exception.family, self.site.family.name)
+ self.assertEqual(exception.site_code, self.site.code)
+
+ def test_site_related_error_percent(self):
+ exception = SiteRelatedError(self.site, 'foo %s bar')
+ expect = 'foo %s bar' % self.site
+ self.assertEqual(str(exception), expect)
+ self.assertEqual(unicode(exception), expect)
+
+ self.assertEqual(exception.args, (expect, ))
+ self.assertEqual(exception.message, 'foo %s bar')
+
+ self.assertEqual(exception.site, self.site)
+ self.assertEqual(exception.family, self.site.family.name)
+ self.assertEqual(exception.site_code, self.site.code)
+
+ def test_site_related_error_percent_dict(self):
+ exception = SiteRelatedError(self.site, 'foo %(site)s bar')
+ expect = 'foo %s bar' % self.site
+ self.assertEqual(str(exception), expect)
+ self.assertEqual(unicode(exception), expect)
+
+ self.assertEqual(exception.args, (expect, ))
+ self.assertEqual(exception.message, 'foo %(site)s bar')
+
+ self.assertEqual(exception.site, self.site)
+ self.assertEqual(exception.family, self.site.family.name)
+ self.assertEqual(exception.site_code, self.site.code)
+
+
+class TestPageRelatedError(DefaultSiteTestCase):
+
+ """Test base Error class."""
+
+ dry = True
+
+ def test_page_related_error_base(self):
+ mainpage = self.get_mainpage()
+ exception = PageRelatedError(mainpage, 'foo')
+
+ self.assertEqual(str(exception), 'foo')
+ self.assertEqual(unicode(exception), 'foo')
+
+ self.assertEqual(exception.args, ('foo', ))
+ self.assertEqual(exception.message, 'foo')
+
+ self.assertEqual(exception.site, self.site)
+
+ def test_page_related_error_percent(self):
+ mainpage = self.get_mainpage()
+ exception = PageRelatedError(mainpage, 'foo %s bar')
+ expect = 'foo %s bar' % mainpage
+
+ self.assertEqual(str(exception), expect)
+ self.assertEqual(unicode(exception), expect)
+
+ self.assertEqual(exception.args, (expect, ))
+ self.assertEqual(exception.message, expect)
+
+ self.assertEqual(exception.site, self.site)
+
+ def test_page_related_error_percent_dict_page(self):
+ mainpage = self.get_mainpage()
+ exception = PageRelatedError(mainpage, 'foo %(site)s %(page)s bar')
+ expect = 'foo %s %s bar' % (self.site, mainpage)
+
+ self.assertEqual(str(exception), expect)
+ self.assertEqual(unicode(exception), expect)
+
+ self.assertEqual(exception.args, (expect, ))
+ self.assertEqual(exception.message, 'foo %(site)s %(page)s bar')
+
+ self.assertEqual(exception.site, self.site)
+
+ def test_page_related_error_percent_dict_title(self):
+ mainpage = self.get_mainpage()
+ exception = PageRelatedError(mainpage, 'foo %(site)s %(title)s bar')
+ expect = 'foo %s %s bar' % (self.site, mainpage.title(asLink=True))
+
+ self.assertEqual(str(exception), expect)
+ self.assertEqual(unicode(exception), expect)
+
+ self.assertEqual(exception.args, (expect, ))
+ self.assertEqual(exception.message, 'foo %(site)s %(title)s bar')
+
+ self.assertEqual(exception.site, self.site)
+
+
+class TestUsernameExceptions(DefaultSiteTestCase):
+
+ """Test cases for username exceptions."""
+
+ dry = True
+
+ def test_username_not_specified(self):
+ """Test message of UsernameNotSpecified."""
+ exception = UsernameNotSpecified(self.site)
+ self.assertIn(
+ "usernames['{0}']['{1}'] = 'myUsername'".format(
+ self.site.family.name, self.site.code),
+ str(exception))
+
+ def test_sysopname_not_specified(self):
+ """Test message of _SysopnameNotSpecified."""
+ exception = _SysopnameNotSpecified(self.site)
+ self.assertIn(
+ "sysopnames['{0}']['{1}'] = 'myUsername'".format(
+ self.site.family.name, self.site.code),
+ str(exception))
+
+
+class TestDeprecatedExceptions(DeprecationTestCase, DefaultSiteTestCase):
"""Test usage of deprecation in library code."""
- net = False
+ dry = False
def test_UploadWarning(self):
"""Test exceptions.UploadWarning is deprecated only."""
# Accessing from the main package should work fine.
cls = pywikibot.UploadWarning
self.assertNoDeprecation()
- e = cls('foo', 'bar')
+ e = cls('foo', 'bar', site=self.site)
self.assertIsInstance(e, pywikibot.Error)
self.assertNoDeprecation()
@@ -37,7 +196,7 @@
self.assertOneDeprecationParts('pywikibot.exceptions.UploadWarning',
'pywikibot.data.api.UploadWarning')
- e = cls('foo', 'bar')
+ e = cls('foo', 'bar', site=self.site)
self.assertIsInstance(e, pywikibot.Error)
self.assertNoDeprecation()
@@ -65,6 +224,25 @@
'pywikibot.exceptions.DeprecatedPageNotFoundError')
+class TestAPIError(DefaultSiteTestCase):
+
+ """Test base APIError class."""
+
+ dry = True
+
+ def test_base(self):
+ exception = APIError('foo-code', 'foo-message', self.site)
+ self.assertEqual(str(exception), 'foo-code: foo-message')
+ self.assertEqual(unicode(exception), 'foo-code: foo-message')
+
+ self.assertEqual(exception.args, ('foo-code: foo-message', ))
+ self.assertEqual(exception.message, 'foo-code: foo-message')
+
+ self.assertEqual(exception.site, self.site)
+ self.assertEqual(exception.family, self.site.family.name)
+ self.assertEqual(exception.site_code, self.site.code)
+
+
if __name__ == '__main__':
try:
unittest.main()
--
To view, visit https://gerrit.wikimedia.org/r/237977
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I7f1119f2ccea48221ca400958683fd5de13ee34d
Gerrit-PatchSet: 1
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: John Vandenberg <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits