| @@ -9,14 +9,6 @@ earwigbot Package | |||||
| :undoc-members: | :undoc-members: | ||||
| :show-inheritance: | :show-inheritance: | ||||
| :mod:`blowfish` Module | |||||
| .. automodule:: earwigbot.blowfish | |||||
| :members: | |||||
| :undoc-members: | |||||
| :show-inheritance: | |||||
| :mod:`bot` Module | :mod:`bot` Module | ||||
| ----------------- | ----------------- | ||||
| @@ -33,6 +25,14 @@ earwigbot Package | |||||
| :undoc-members: | :undoc-members: | ||||
| :show-inheritance: | :show-inheritance: | ||||
| :mod:`exceptions` Module | |||||
| ------------------------ | |||||
| .. automodule:: earwigbot.exceptions | |||||
| :members: | |||||
| :undoc-members: | |||||
| :show-inheritance: | |||||
| :mod:`managers` Module | :mod:`managers` Module | ||||
| ---------------------- | ---------------------- | ||||
| @@ -33,14 +33,6 @@ wiki Package | |||||
| :undoc-members: | :undoc-members: | ||||
| :show-inheritance: | :show-inheritance: | ||||
| :mod:`exceptions` Module | |||||
| .. automodule:: earwigbot.wiki.exceptions | |||||
| :members: | |||||
| :undoc-members: | |||||
| :show-inheritance: | |||||
| :mod:`page` Module | :mod:`page` Module | ||||
| ------------------ | ------------------ | ||||
| @@ -49,4 +49,12 @@ if not __release__: | |||||
| finally: | finally: | ||||
| del _add_git_commit_id_to_version_string | del _add_git_commit_id_to_version_string | ||||
| from earwigbot import bot, commands, config, irc, managers, tasks, util, wiki | |||||
| from earwigbot import bot | |||||
| from earwigbot import commands | |||||
| from earwigbot import config | |||||
| from earwigbot import exceptions | |||||
| from earwigbot import irc | |||||
| from earwigbot import managers | |||||
| from earwigbot import tasks | |||||
| from earwigbot import util | |||||
| from earwigbot import wiki | |||||
| @@ -24,7 +24,7 @@ import threading | |||||
| import re | import re | ||||
| from earwigbot.commands import BaseCommand | from earwigbot.commands import BaseCommand | ||||
| from earwigbot.irc import KwargParseException | |||||
| from earwigbot.exceptions import KwargParseError | |||||
| class Command(BaseCommand): | class Command(BaseCommand): | ||||
| """Manage wiki tasks from IRC, and check on thread status.""" | """Manage wiki tasks from IRC, and check on thread status.""" | ||||
| @@ -135,7 +135,7 @@ class Command(BaseCommand): | |||||
| try: | try: | ||||
| data.parse_kwargs() | data.parse_kwargs() | ||||
| except KwargParseException, arg: | |||||
| except KwargParseError, arg: | |||||
| msg = "error parsing argument: \x0303{0}\x0301.".format(arg) | msg = "error parsing argument: \x0303{0}\x0301.".format(arg) | ||||
| self.reply(data, msg) | self.reply(data, msg) | ||||
| return | return | ||||
| @@ -0,0 +1,244 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009-2012 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
| # of this software and associated documentation files (the "Software"), to deal | |||||
| # in the Software without restriction, including without limitation the rights | |||||
| # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
| # copies of the Software, and to permit persons to whom the Software is | |||||
| # furnished to do so, subject to the following conditions: | |||||
| # | |||||
| # The above copyright notice and this permission notice shall be included in | |||||
| # all copies or substantial portions of the Software. | |||||
| # | |||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
| # SOFTWARE. | |||||
| """ | |||||
| EarwigBot Exceptions | |||||
| This module contains all exceptions used by EarwigBot:: | |||||
| EarwigBotError | |||||
| +-- IRCError | |||||
| | +-- BrokenSocketError | |||||
| | +-- KwargParseError | |||||
| +-- WikiToolsetError | |||||
| +-- SiteNotFoundError | |||||
| +-- SiteAPIError | |||||
| +-- LoginError | |||||
| +-- NamespaceNotFoundError | |||||
| +-- PageNotFoundError | |||||
| +-- InvalidPageError | |||||
| +-- RedirectError | |||||
| +-- UserNotFoundError | |||||
| +-- EditError | |||||
| | +-- PermissionsError | |||||
| | +-- EditConflictError | |||||
| | +-- NoContentError | |||||
| | +-- ContentTooBigError | |||||
| | +-- SpamDetectedError | |||||
| | +-- FilteredError | |||||
| +-- SQLError | |||||
| +-- CopyvioCheckError | |||||
| +-- UnknownSearchEngineError | |||||
| +-- UnsupportedSearchEngineError | |||||
| +-- SearchQueryError | |||||
| """ | |||||
| class EarwigBotError(Exception): | |||||
| """Base exception class for errors in EarwigBot.""" | |||||
| class IRCError(EarwigBotError): | |||||
| """Base exception class for errors in IRC-relation sections of the bot.""" | |||||
| class BrokenSocketError(IRCError): | |||||
| """A socket has broken, because it is not sending data. | |||||
| Raised by :py:meth:`IRCConnection._get | |||||
| <earwigbot.irc.connection.IRCConnection._get>`. | |||||
| """ | |||||
| class KwargParseError(IRCError): | |||||
| """Couldn't parse a certain keyword argument in an IRC message. | |||||
| This is usually caused by it being given incorrectly: e.g., no value (abc), | |||||
| just a value (=xyz), just an equal sign (=), instead of the correct form | |||||
| (abc=xyz). | |||||
| Raised by :py:meth:`Data.parse_kwargs | |||||
| <earwigbot.irc.data.Data.parse_kwargs>`. | |||||
| """ | |||||
| class WikiToolsetError(EarwigBotError): | |||||
| """Base exception class for errors in the Wiki Toolset.""" | |||||
| class SiteNotFoundError(WikiToolsetError): | |||||
| """A particular site could not be found in the sites database. | |||||
| Raised by :py:class:`~earwigbot.wiki.sitesdb.SitesDB`. | |||||
| """ | |||||
| class SiteAPIError(WikiToolsetError): | |||||
| """Couldn't connect to a site's API. | |||||
| Perhaps the server doesn't exist, our URL is wrong or incomplete, or | |||||
| there are temporary problems on their end. | |||||
| Raised by :py:meth:`Site.api_query <earwigbot.wiki.site.Site.api_query>`. | |||||
| """ | |||||
| class LoginError(WikiToolsetError): | |||||
| """An error occured while trying to login. | |||||
| Perhaps the username/password is incorrect. | |||||
| Raised by :py:meth:`Site._login <earwigbot.wiki.site.Site._login>`. | |||||
| """ | |||||
| class NamespaceNotFoundError(WikiToolsetError): | |||||
| """A requested namespace name or namespace ID does not exist. | |||||
| Raised by :py:meth:`Site.namespace_id_to_name | |||||
| <earwigbot.wiki.site.Site.namespace_id_to_name>` and | |||||
| :py:meth:`Site.namespace_name_to_id | |||||
| <earwigbot.wiki.site.Site.namespace_name_to_id>`. | |||||
| """ | |||||
| class PageNotFoundError(WikiToolsetError): | |||||
| """Attempted to get information about a page that does not exist. | |||||
| Raised by :py:class:`~earwigbot.wiki.page.Page`. | |||||
| """ | |||||
| class InvalidPageError(WikiToolsetError): | |||||
| """Attempted to get information about a page whose title is invalid. | |||||
| Raised by :py:class:`~earwigbot.wiki.page.Page`. | |||||
| """ | |||||
| class RedirectError(WikiToolsetError): | |||||
| """A redirect-only method was called on a malformed or non-redirect page. | |||||
| Raised by :py:meth:`Page.get_redirect_target | |||||
| <earwigbot.wiki.page.Page.get_redirect_target>`. | |||||
| """ | |||||
| class UserNotFoundError(WikiToolsetError): | |||||
| """Attempted to get certain information about a user that does not exist. | |||||
| Raised by :py:class:`~earwigbot.wiki.user.User`. | |||||
| """ | |||||
| class EditError(WikiToolsetError): | |||||
| """An error occured while editing. | |||||
| This is used as a base class for all editing errors; this one specifically | |||||
| is used only when a generic error occurs that we don't know about. | |||||
| Raised by :py:meth:`Page.edit <earwigbot.wiki.page.Page.edit>` and | |||||
| :py:meth:`Page.add_section <earwigbot.wiki.page.Page.add_section>`. | |||||
| """ | |||||
| class PermissionsError(EditError): | |||||
| """A permissions error ocurred while editing. | |||||
| We tried to do something we don't have permission to, like trying to delete | |||||
| a page as a non-admin, or trying to edit a page without login information | |||||
| and AssertEdit enabled. | |||||
| Raised by :py:meth:`Page.edit <earwigbot.wiki.page.Page.edit>` and | |||||
| :py:meth:`Page.add_section <earwigbot.wiki.page.Page.add_section>`. | |||||
| """ | |||||
| class EditConflictError(EditError): | |||||
| """We gotten an edit conflict or a (rarer) delete/recreate conflict. | |||||
| Raised by :py:meth:`Page.edit <earwigbot.wiki.page.Page.edit>` and | |||||
| :py:meth:`Page.add_section <earwigbot.wiki.page.Page.add_section>`. | |||||
| """ | |||||
| class NoContentError(EditError): | |||||
| """We tried to create a page or new section with no content. | |||||
| Raised by :py:meth:`Page.edit <earwigbot.wiki.page.Page.edit>` and | |||||
| :py:meth:`Page.add_section <earwigbot.wiki.page.Page.add_section>`. | |||||
| """ | |||||
| class ContentTooBigError(EditError): | |||||
| """The edit we tried to push exceeded the article size limit. | |||||
| Raised by :py:meth:`Page.edit <earwigbot.wiki.page.Page.edit>` and | |||||
| :py:meth:`Page.add_section <earwigbot.wiki.page.Page.add_section>`. | |||||
| """ | |||||
| class SpamDetectedError(EditError): | |||||
| """The spam filter refused our edit. | |||||
| Raised by :py:meth:`Page.edit <earwigbot.wiki.page.Page.edit>` and | |||||
| :py:meth:`Page.add_section <earwigbot.wiki.page.Page.add_section>`. | |||||
| """ | |||||
| class FilteredError(EditError): | |||||
| """The edit filter refused our edit. | |||||
| Raised by :py:meth:`Page.edit <earwigbot.wiki.page.Page.edit>` and | |||||
| :py:meth:`Page.add_section <earwigbot.wiki.page.Page.add_section>`. | |||||
| """ | |||||
| class SQLError(WikiToolsetError): | |||||
| """Some error involving SQL querying occurred. | |||||
| Raised by :py:meth:`Site.sql_query <earwigbot.wiki.site.Site.sql_query>`. | |||||
| """ | |||||
| class CopyvioCheckError(WikiToolsetError): | |||||
| """An error occured when checking a page for copyright violations. | |||||
| This is a base class for multiple exceptions; usually one of those will be | |||||
| raised instead of this. | |||||
| Raised by :py:meth:`Page.copyvio_check | |||||
| <earwigbot.wiki.copyvios.CopyvioMixin.copyvio_check>` and | |||||
| :py:meth:`Page.copyvio_compare | |||||
| <earwigbot.wiki.copyvios.CopyvioMixin.copyvio_compare>`. | |||||
| """ | |||||
| class UnknownSearchEngineError(CopyvioCheckError): | |||||
| """Attempted to do a copyvio check with an unknown search engine. | |||||
| Search engines are specified in :file:`config.yml` as | |||||
| :py:attr:`config.wiki["search"]["engine"]`. | |||||
| Raised by :py:meth:`Page.copyvio_check | |||||
| <earwigbot.wiki.copyvios.CopyvioMixin.copyvio_check>` and | |||||
| :py:meth:`Page.copyvio_compare | |||||
| <earwigbot.wiki.copyvios.CopyvioMixin.copyvio_compare>`. | |||||
| """ | |||||
| class UnsupportedSearchEngineError(CopyvioCheckError): | |||||
| """Attmpted to do a copyvio check using an unavailable engine. | |||||
| This might occur if, for example, an engine requires oauth2 but the package | |||||
| couldn't be imported. | |||||
| Raised by :py:meth:`Page.copyvio_check | |||||
| <earwigbot.wiki.copyvios.CopyvioMixin.copyvio_check>` and | |||||
| :py:meth:`Page.copyvio_compare | |||||
| <earwigbot.wiki.copyvios.CopyvioMixin.copyvio_compare>`. | |||||
| """ | |||||
| class SearchQueryError(CopyvioCheckError): | |||||
| """Some error ocurred while doing a search query. | |||||
| Raised by :py:meth:`Page.copyvio_check | |||||
| <earwigbot.wiki.copyvios.CopyvioMixin.copyvio_check>` and | |||||
| :py:meth:`Page.copyvio_compare | |||||
| <earwigbot.wiki.copyvios.CopyvioMixin.copyvio_compare>`. | |||||
| """ | |||||
| @@ -24,14 +24,9 @@ import socket | |||||
| from threading import Lock | from threading import Lock | ||||
| from time import sleep | from time import sleep | ||||
| __all__ = ["BrokenSocketException", "IRCConnection"] | |||||
| from earwigbot.exceptions import BrokenSocketError | |||||
| class BrokenSocketException(Exception): | |||||
| """A socket has broken, because it is not sending data. | |||||
| Raised by IRCConnection()._get(). | |||||
| """ | |||||
| pass | |||||
| __all__ = ["IRCConnection"] | |||||
| class IRCConnection(object): | class IRCConnection(object): | ||||
| """A class to interface with IRC.""" | """A class to interface with IRC.""" | ||||
| @@ -72,7 +67,7 @@ class IRCConnection(object): | |||||
| data = self._sock.recv(size) | data = self._sock.recv(size) | ||||
| if not data: | if not data: | ||||
| # Socket isn't giving us any data, so it is dead or broken: | # Socket isn't giving us any data, so it is dead or broken: | ||||
| raise BrokenSocketException() | |||||
| raise BrokenSocketError() | |||||
| return data | return data | ||||
| def _send(self, msg): | def _send(self, msg): | ||||
| @@ -137,7 +132,7 @@ class IRCConnection(object): | |||||
| while 1: | while 1: | ||||
| try: | try: | ||||
| read_buffer += self._get() | read_buffer += self._get() | ||||
| except BrokenSocketException: | |||||
| except BrokenSocketError: | |||||
| self._is_running = False | self._is_running = False | ||||
| break | break | ||||
| @@ -22,13 +22,9 @@ | |||||
| import re | import re | ||||
| __all__ = ["KwargParseException", "Data"] | |||||
| from earwigbot.exceptions import KwargParseError | |||||
| class KwargParseException(Exception): | |||||
| """Couldn't parse a certain keyword argument in self.args, probably because | |||||
| it was given incorrectly: e.g., no value (abc), just a value (=xyz), just | |||||
| an equal sign (=), instead of the correct (abc=xyz).""" | |||||
| pass | |||||
| __all__ = ["Data"] | |||||
| class Data(object): | class Data(object): | ||||
| """Store data from an individual line received on IRC.""" | """Store data from an individual line received on IRC.""" | ||||
| @@ -81,8 +77,8 @@ class Data(object): | |||||
| try: | try: | ||||
| key, value = re.findall("^(.*?)\=(.*?)$", arg)[0] | key, value = re.findall("^(.*?)\=(.*?)$", arg)[0] | ||||
| except IndexError: | except IndexError: | ||||
| raise KwargParseException(arg) | |||||
| raise KwargParseError(arg) | |||||
| if key and value: | if key and value: | ||||
| self.kwargs[key] = value | self.kwargs[key] = value | ||||
| else: | else: | ||||
| raise KwargParseException(arg) | |||||
| raise KwargParseError(arg) | |||||
| @@ -37,7 +37,6 @@ Category, and User) needs. | |||||
| from earwigbot.wiki.category import * | from earwigbot.wiki.category import * | ||||
| from earwigbot.wiki.constants import * | from earwigbot.wiki.constants import * | ||||
| from earwigbot.wiki.exceptions import * | |||||
| from earwigbot.wiki.page import * | from earwigbot.wiki.page import * | ||||
| from earwigbot.wiki.site import * | from earwigbot.wiki.site import * | ||||
| from earwigbot.wiki.sitesdb import * | from earwigbot.wiki.sitesdb import * | ||||
| @@ -35,7 +35,8 @@ earwigbot.wiki (e.g. `earwigbot.wiki.USER_AGENT`). | |||||
| # Default User Agent when making API queries: | # Default User Agent when making API queries: | ||||
| from earwigbot import __version__ as _v | from earwigbot import __version__ as _v | ||||
| from platform import python_version as _p | from platform import python_version as _p | ||||
| USER_AGENT = "EarwigBot/{0} (Python/{1}; https://github.com/earwig/earwigbot)".format(_v, _p()) | |||||
| USER_AGENT = "EarwigBot/{0} (Python/{1}; https://github.com/earwig/earwigbot)" | |||||
| USER_AGENT = USER_AGENT.format(_v, _p()) | |||||
| del _v, _p | del _v, _p | ||||
| # Default namespace IDs: | # Default namespace IDs: | ||||
| @@ -35,7 +35,7 @@ try: | |||||
| except ImportError: | except ImportError: | ||||
| oauth = None | oauth = None | ||||
| from earwigbot.wiki.exceptions import * | |||||
| from earwigbot.exceptions import * | |||||
| class _CopyvioCheckResult(object): | class _CopyvioCheckResult(object): | ||||
| def __init__(self, violation, confidence, url, queries, article, chains): | def __init__(self, violation, confidence, url, queries, article, chains): | ||||
| @@ -1,124 +0,0 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009-2012 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
| # of this software and associated documentation files (the "Software"), to deal | |||||
| # in the Software without restriction, including without limitation the rights | |||||
| # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
| # copies of the Software, and to permit persons to whom the Software is | |||||
| # furnished to do so, subject to the following conditions: | |||||
| # | |||||
| # The above copyright notice and this permission notice shall be included in | |||||
| # all copies or substantial portions of the Software. | |||||
| # | |||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
| # SOFTWARE. | |||||
| """ | |||||
| EarwigBot's Wiki Toolset: Exceptions | |||||
| This module contains all exceptions used by the wiki package. There are a lot: | |||||
| -- SiteNotFoundError | |||||
| -- SiteAPIError | |||||
| -- LoginError | |||||
| -- NamespaceNotFoundError | |||||
| -- PageNotFoundError | |||||
| -- InvalidPageError | |||||
| -- RedirectError | |||||
| -- UserNotFoundError | |||||
| -- EditError | |||||
| -- PermissionsError | |||||
| -- EditConflictError | |||||
| -- NoContentError | |||||
| -- ContentTooBigError | |||||
| -- SpamDetectedError | |||||
| -- FilteredError | |||||
| -- SQLError | |||||
| -- CopyvioCheckError | |||||
| -- UnknownSearchEngineError | |||||
| -- UnsupportedSearchEngineError | |||||
| -- SearchQueryError | |||||
| """ | |||||
| class WikiToolsetError(Exception): | |||||
| """Base exception class for errors in the Wiki Toolset.""" | |||||
| class SiteNotFoundError(WikiToolsetError): | |||||
| """A site matching the args given to get_site() could not be found in the | |||||
| config file.""" | |||||
| class SiteAPIError(WikiToolsetError): | |||||
| """We couldn't connect to a site's API, perhaps because the server doesn't | |||||
| exist, our URL is wrong or incomplete, or they're having temporary | |||||
| problems.""" | |||||
| class LoginError(WikiToolsetError): | |||||
| """An error occured while trying to login. Perhaps the username/password is | |||||
| incorrect.""" | |||||
| class NamespaceNotFoundError(WikiToolsetError): | |||||
| """A requested namespace name or namespace ID does not exist.""" | |||||
| class PageNotFoundError(WikiToolsetError): | |||||
| """Attempting to get certain information about a page that does not | |||||
| exist.""" | |||||
| class InvalidPageError(WikiToolsetError): | |||||
| """Attempting to get certain information about a page whose title is | |||||
| invalid.""" | |||||
| class RedirectError(WikiToolsetError): | |||||
| """Page's get_redirect_target() method failed because the page is either | |||||
| not a redirect, or it is malformed.""" | |||||
| class UserNotFoundError(WikiToolsetError): | |||||
| """Attempting to get certain information about a user that does not | |||||
| exist.""" | |||||
| class EditError(WikiToolsetError): | |||||
| """We got some error while editing. Sometimes, a subclass of this exception | |||||
| will be used, like PermissionsError or EditConflictError.""" | |||||
| class PermissionsError(EditError): | |||||
| """We tried to do something we don't have permission to, like a non-admin | |||||
| trying to delete a page, or trying to edit a page when no login information | |||||
| was provided.""" | |||||
| class EditConflictError(EditError): | |||||
| """We've gotten an edit conflict or a (rarer) delete/recreate conflict.""" | |||||
| class NoContentError(EditError): | |||||
| """We tried to create a page or new section with no content.""" | |||||
| class ContentTooBigError(EditError): | |||||
| """The edit we tried to push exceeded the article size limit.""" | |||||
| class SpamDetectedError(EditError): | |||||
| """The spam filter refused our edit.""" | |||||
| class FilteredError(EditError): | |||||
| """The edit filter refused our edit.""" | |||||
| class SQLError(WikiToolsetError): | |||||
| """Some error involving SQL querying occurred.""" | |||||
| class CopyvioCheckError(WikiToolsetError): | |||||
| """An error occured when checking a page for copyright violations.""" | |||||
| class UnknownSearchEngineError(CopyvioCheckError): | |||||
| """CopyrightMixin().copyvio_check() called with an unknown engine.""" | |||||
| class UnsupportedSearchEngineError(CopyvioCheckError): | |||||
| """The engine requested is not available, e.g., because a required package | |||||
| is missing.""" | |||||
| class SearchQueryError(CopyvioCheckError): | |||||
| """Some error ocurred while doing a search query.""" | |||||
| @@ -25,8 +25,8 @@ import re | |||||
| from time import gmtime, strftime | from time import gmtime, strftime | ||||
| from urllib import quote | from urllib import quote | ||||
| from earwigbot import exceptions | |||||
| from earwigbot.wiki.copyright import CopyrightMixin | from earwigbot.wiki.copyright import CopyrightMixin | ||||
| from earwigbot.wiki.exceptions import * | |||||
| __all__ = ["Page"] | __all__ = ["Page"] | ||||
| @@ -132,7 +132,7 @@ class Page(CopyrightMixin): | |||||
| """ | """ | ||||
| if self._exists == 1: | if self._exists == 1: | ||||
| e = "Page '{0}' is invalid.".format(self._title) | e = "Page '{0}' is invalid.".format(self._title) | ||||
| raise InvalidPageError(e) | |||||
| raise exceptions.InvalidPageError(e) | |||||
| def _force_existence(self): | def _force_existence(self): | ||||
| """Used to ensure that our page exists. | """Used to ensure that our page exists. | ||||
| @@ -144,7 +144,7 @@ class Page(CopyrightMixin): | |||||
| self._force_validity() | self._force_validity() | ||||
| if self._exists == 2: | if self._exists == 2: | ||||
| e = "Page '{0}' does not exist.".format(self._title) | e = "Page '{0}' does not exist.".format(self._title) | ||||
| raise PageNotFoundError(e) | |||||
| raise exceptions.PageNotFoundError(e) | |||||
| def _load_wrapper(self): | def _load_wrapper(self): | ||||
| """Calls _load_attributes() and follows redirects if we're supposed to. | """Calls _load_attributes() and follows redirects if we're supposed to. | ||||
| @@ -278,8 +278,8 @@ class Page(CopyrightMixin): | |||||
| self._load_attributes() | self._load_attributes() | ||||
| if not self._token: | if not self._token: | ||||
| e = "You don't have permission to edit this page." | e = "You don't have permission to edit this page." | ||||
| raise PermissionsError(e) | |||||
| raise exceptions.PermissionsError(e) | |||||
| # Weed out invalid pages before we get too far: | # Weed out invalid pages before we get too far: | ||||
| self._force_validity() | self._force_validity() | ||||
| @@ -310,7 +310,7 @@ class Page(CopyrightMixin): | |||||
| try: | try: | ||||
| assertion = result["edit"]["assert"] | assertion = result["edit"]["assert"] | ||||
| except KeyError: | except KeyError: | ||||
| raise EditError(result["edit"]) | |||||
| raise exceptions.EditError(result["edit"]) | |||||
| self._handle_assert_edit(assertion, params, tries) | self._handle_assert_edit(assertion, params, tries) | ||||
| def _build_edit_params(self, text, summary, minor, bot, force, section, | def _build_edit_params(self, text, summary, minor, bot, force, section, | ||||
| @@ -353,13 +353,13 @@ class Page(CopyrightMixin): | |||||
| """ | """ | ||||
| if error.code in ["noedit", "cantcreate", "protectedtitle", | if error.code in ["noedit", "cantcreate", "protectedtitle", | ||||
| "noimageredirect"]: | "noimageredirect"]: | ||||
| raise PermissionsError(error.info) | |||||
| raise exceptions.PermissionsError(error.info) | |||||
| elif error.code in ["noedit-anon", "cantcreate-anon", | elif error.code in ["noedit-anon", "cantcreate-anon", | ||||
| "noimageredirect-anon"]: | "noimageredirect-anon"]: | ||||
| if not all(self._site._login_info): | if not all(self._site._login_info): | ||||
| # Insufficient login info: | # Insufficient login info: | ||||
| raise PermissionsError(error.info) | |||||
| raise exceptions.PermissionsError(error.info) | |||||
| if tries == 0: | if tries == 0: | ||||
| # We have login info; try to login: | # We have login info; try to login: | ||||
| self._site._login(self._site._login_info) | self._site._login(self._site._login_info) | ||||
| @@ -368,28 +368,28 @@ class Page(CopyrightMixin): | |||||
| else: | else: | ||||
| # We already tried to log in and failed! | # We already tried to log in and failed! | ||||
| e = "Although we should be logged in, we are not. This may be a cookie problem or an odd bug." | e = "Although we should be logged in, we are not. This may be a cookie problem or an odd bug." | ||||
| raise LoginError(e) | |||||
| raise exceptions.LoginError(e) | |||||
| elif error.code in ["editconflict", "pagedeleted", "articleexists"]: | elif error.code in ["editconflict", "pagedeleted", "articleexists"]: | ||||
| # These attributes are now invalidated: | # These attributes are now invalidated: | ||||
| self._content = None | self._content = None | ||||
| self._basetimestamp = None | self._basetimestamp = None | ||||
| self._exists = 0 | self._exists = 0 | ||||
| raise EditConflictError(error.info) | |||||
| raise exceptions.EditConflictError(error.info) | |||||
| elif error.code in ["emptypage", "emptynewsection"]: | elif error.code in ["emptypage", "emptynewsection"]: | ||||
| raise NoContentError(error.info) | |||||
| raise exceptions.NoContentError(error.info) | |||||
| elif error.code == "contenttoobig": | elif error.code == "contenttoobig": | ||||
| raise ContentTooBigError(error.info) | |||||
| raise exceptions.ContentTooBigError(error.info) | |||||
| elif error.code == "spamdetected": | elif error.code == "spamdetected": | ||||
| raise SpamDetectedError(error.info) | |||||
| raise exceptions.SpamDetectedError(error.info) | |||||
| elif error.code == "filtered": | elif error.code == "filtered": | ||||
| raise FilteredError(error.info) | |||||
| raise exceptions.FilteredError(error.info) | |||||
| raise EditError(": ".join((error.code, error.info))) | |||||
| raise exceptions.EditError(": ".join((error.code, error.info))) | |||||
| def _handle_assert_edit(self, assertion, params, tries): | def _handle_assert_edit(self, assertion, params, tries): | ||||
| """If we can't edit due to a failed AssertEdit assertion, handle that. | """If we can't edit due to a failed AssertEdit assertion, handle that. | ||||
| @@ -401,7 +401,7 @@ class Page(CopyrightMixin): | |||||
| if not all(self._site._login_info): | if not all(self._site._login_info): | ||||
| # Insufficient login info: | # Insufficient login info: | ||||
| e = "AssertEdit: user assertion failed, and no login info was provided." | e = "AssertEdit: user assertion failed, and no login info was provided." | ||||
| raise PermissionsError(e) | |||||
| raise exceptions.PermissionsError(e) | |||||
| if tries == 0: | if tries == 0: | ||||
| # We have login info; try to login: | # We have login info; try to login: | ||||
| self._site._login(self._site._login_info) | self._site._login(self._site._login_info) | ||||
| @@ -410,15 +410,15 @@ class Page(CopyrightMixin): | |||||
| else: | else: | ||||
| # We already tried to log in and failed! | # We already tried to log in and failed! | ||||
| e = "Although we should be logged in, we are not. This may be a cookie problem or an odd bug." | e = "Although we should be logged in, we are not. This may be a cookie problem or an odd bug." | ||||
| raise LoginError(e) | |||||
| raise exceptions.LoginError(e) | |||||
| elif assertion == "bot": | elif assertion == "bot": | ||||
| e = "AssertEdit: bot assertion failed; we don't have a bot flag!" | e = "AssertEdit: bot assertion failed; we don't have a bot flag!" | ||||
| raise PermissionsError(e) | |||||
| raise exceptions.PermissionsError(e) | |||||
| # Unknown assertion, maybe "true", "false", or "exists": | # Unknown assertion, maybe "true", "false", or "exists": | ||||
| e = "AssertEdit: assertion '{0}' failed.".format(assertion) | e = "AssertEdit: assertion '{0}' failed.".format(assertion) | ||||
| raise PermissionsError(e) | |||||
| raise exceptions.PermissionsError(e) | |||||
| def title(self, force=False): | def title(self, force=False): | ||||
| """Returns the Page's title, or pagename. | """Returns the Page's title, or pagename. | ||||
| @@ -570,7 +570,7 @@ class Page(CopyrightMixin): | |||||
| if self._namespace < 0: | if self._namespace < 0: | ||||
| ns = self._site.namespace_id_to_name(self._namespace) | ns = self._site.namespace_id_to_name(self._namespace) | ||||
| e = "Pages in the {0} namespace can't have talk pages.".format(ns) | e = "Pages in the {0} namespace can't have talk pages.".format(ns) | ||||
| raise InvalidPageError(e) | |||||
| raise exceptions.InvalidPageError(e) | |||||
| if self._is_talkpage: | if self._is_talkpage: | ||||
| new_ns = self._namespace - 1 | new_ns = self._namespace - 1 | ||||
| @@ -650,7 +650,7 @@ class Page(CopyrightMixin): | |||||
| return re.findall(self.re_redirect, content, flags=re.I)[0] | return re.findall(self.re_redirect, content, flags=re.I)[0] | ||||
| except IndexError: | except IndexError: | ||||
| e = "The page does not appear to have a redirect target." | e = "The page does not appear to have a redirect target." | ||||
| raise RedirectError(e) | |||||
| raise exceptions.RedirectError(e) | |||||
| def edit(self, text, summary, minor=False, bot=True, force=False): | def edit(self, text, summary, minor=False, bot=True, force=False): | ||||
| """Replaces the page's content or creates a new page. | """Replaces the page's content or creates a new page. | ||||
| @@ -38,9 +38,9 @@ try: | |||||
| except ImportError: | except ImportError: | ||||
| oursql = None | oursql = None | ||||
| from earwigbot import exceptions | |||||
| from earwigbot.wiki import constants | |||||
| from earwigbot.wiki.category import Category | from earwigbot.wiki.category import Category | ||||
| from earwigbot.wiki.constants import * | |||||
| from earwigbot.wiki.exceptions import * | |||||
| from earwigbot.wiki.page import Page | from earwigbot.wiki.page import Page | ||||
| from earwigbot.wiki.user import User | from earwigbot.wiki.user import User | ||||
| @@ -128,7 +128,7 @@ class Site(object): | |||||
| else: | else: | ||||
| self._cookiejar = CookieJar() | self._cookiejar = CookieJar() | ||||
| if not user_agent: | if not user_agent: | ||||
| user_agent = USER_AGENT # Set default UA from wiki.constants | |||||
| user_agent = constants.USER_AGENT # Set default UA | |||||
| self._opener = build_opener(HTTPCookieProcessor(self._cookiejar)) | self._opener = build_opener(HTTPCookieProcessor(self._cookiejar)) | ||||
| self._opener.addheaders = [("User-Agent", user_agent), | self._opener.addheaders = [("User-Agent", user_agent), | ||||
| ("Accept-Encoding", "gzip")] | ("Accept-Encoding", "gzip")] | ||||
| @@ -232,7 +232,7 @@ class Site(object): | |||||
| e = e.format(error.code) | e = e.format(error.code) | ||||
| else: | else: | ||||
| e = "API query failed." | e = "API query failed." | ||||
| raise SiteAPIError(e) | |||||
| raise exceptions.SiteAPIError(e) | |||||
| result = response.read() | result = response.read() | ||||
| if response.headers.get("Content-Encoding") == "gzip": | if response.headers.get("Content-Encoding") == "gzip": | ||||
| @@ -246,7 +246,7 @@ class Site(object): | |||||
| """Given API query params, return the URL to query and POST data.""" | """Given API query params, return the URL to query and POST data.""" | ||||
| if not self._base_url or self._script_path is None: | if not self._base_url or self._script_path is None: | ||||
| e = "Tried to do an API query, but no API URL is known." | e = "Tried to do an API query, but no API URL is known." | ||||
| raise SiteAPIError(e) | |||||
| raise exceptions.SiteAPIError(e) | |||||
| base_url = self._base_url | base_url = self._base_url | ||||
| if base_url.startswith("//"): # Protocol-relative URLs from 1.18 | if base_url.startswith("//"): # Protocol-relative URLs from 1.18 | ||||
| @@ -271,7 +271,7 @@ class Site(object): | |||||
| res = loads(result) # Try to parse as a JSON object | res = loads(result) # Try to parse as a JSON object | ||||
| except ValueError: | except ValueError: | ||||
| e = "API query failed: JSON could not be decoded." | e = "API query failed: JSON could not be decoded." | ||||
| raise SiteAPIError(e) | |||||
| raise exceptions.SiteAPIError(e) | |||||
| try: | try: | ||||
| code = res["error"]["code"] | code = res["error"]["code"] | ||||
| @@ -282,7 +282,7 @@ class Site(object): | |||||
| if code == "maxlag": # We've been throttled by the server | if code == "maxlag": # We've been throttled by the server | ||||
| if tries >= self._max_retries: | if tries >= self._max_retries: | ||||
| e = "Maximum number of retries reached ({0})." | e = "Maximum number of retries reached ({0})." | ||||
| raise SiteAPIError(e.format(self._max_retries)) | |||||
| raise exceptions.SiteAPIError(e.format(self._max_retries)) | |||||
| tries += 1 | tries += 1 | ||||
| msg = 'Server says "{0}"; retrying in {1} seconds ({2}/{3})' | msg = 'Server says "{0}"; retrying in {1} seconds ({2}/{3})' | ||||
| self._logger.info(msg.format(info, wait, tries, self._max_retries)) | self._logger.info(msg.format(info, wait, tries, self._max_retries)) | ||||
| @@ -290,7 +290,7 @@ class Site(object): | |||||
| return self._api_query(params, tries=tries, wait=wait*3) | return self._api_query(params, tries=tries, wait=wait*3) | ||||
| else: # Some unknown error occurred | else: # Some unknown error occurred | ||||
| e = 'API query failed: got error "{0}"; server says: "{1}".' | e = 'API query failed: got error "{0}"; server says: "{1}".' | ||||
| error = SiteAPIError(e.format(code, info)) | |||||
| error = earwigbot.SiteAPIError(e.format(code, info)) | |||||
| error.code, error.info = code, info | error.code, error.info = code, info | ||||
| raise error | raise error | ||||
| @@ -491,7 +491,7 @@ class Site(object): | |||||
| e = "The given password is incorrect." | e = "The given password is incorrect." | ||||
| else: | else: | ||||
| e = "Couldn't login; server says '{0}'.".format(res) | e = "Couldn't login; server says '{0}'.".format(res) | ||||
| raise LoginError(e) | |||||
| raise exceptions.LoginError(e) | |||||
| def _logout(self): | def _logout(self): | ||||
| """Safely logout through the API. | """Safely logout through the API. | ||||
| @@ -518,7 +518,7 @@ class Site(object): | |||||
| """ | """ | ||||
| if not oursql: | if not oursql: | ||||
| e = "Module 'oursql' is required for SQL queries." | e = "Module 'oursql' is required for SQL queries." | ||||
| raise SQLError(e) | |||||
| raise exceptions.SQLError(e) | |||||
| args = self._sql_data | args = self._sql_data | ||||
| for key, value in kwargs.iteritems(): | for key, value in kwargs.iteritems(): | ||||
| @@ -638,7 +638,7 @@ class Site(object): | |||||
| return self._namespaces[ns_id][0] | return self._namespaces[ns_id][0] | ||||
| except KeyError: | except KeyError: | ||||
| e = "There is no namespace with id {0}.".format(ns_id) | e = "There is no namespace with id {0}.".format(ns_id) | ||||
| raise NamespaceNotFoundError(e) | |||||
| raise exceptions.NamespaceNotFoundError(e) | |||||
| def namespace_name_to_id(self, name): | def namespace_name_to_id(self, name): | ||||
| """Given a namespace name, returns the associated ID. | """Given a namespace name, returns the associated ID. | ||||
| @@ -655,7 +655,7 @@ class Site(object): | |||||
| return ns_id | return ns_id | ||||
| e = "There is no namespace with name '{0}'.".format(name) | e = "There is no namespace with name '{0}'.".format(name) | ||||
| raise NamespaceNotFoundError(e) | |||||
| raise exceptions.NamespaceNotFoundError(e) | |||||
| def get_page(self, title, follow_redirects=False): | def get_page(self, title, follow_redirects=False): | ||||
| """Returns a Page object for the given title (pagename). | """Returns a Page object for the given title (pagename). | ||||
| @@ -667,7 +667,7 @@ class Site(object): | |||||
| Note that this doesn't do any direct checks for existence or | Note that this doesn't do any direct checks for existence or | ||||
| redirect-following - Page's methods provide that. | redirect-following - Page's methods provide that. | ||||
| """ | """ | ||||
| prefixes = self.namespace_id_to_name(NS_CATEGORY, all=True) | |||||
| prefixes = self.namespace_id_to_name(constants.NS_CATEGORY, all=True) | |||||
| prefix = title.split(":", 1)[0] | prefix = title.split(":", 1)[0] | ||||
| if prefix != title: # Avoid a page that is simply "Category" | if prefix != title: # Avoid a page that is simply "Category" | ||||
| if prefix in prefixes: | if prefix in prefixes: | ||||
| @@ -680,7 +680,7 @@ class Site(object): | |||||
| `catname` should be given *without* a namespace prefix. This method is | `catname` should be given *without* a namespace prefix. This method is | ||||
| really just shorthand for get_page("Category:" + catname). | really just shorthand for get_page("Category:" + catname). | ||||
| """ | """ | ||||
| prefix = self.namespace_id_to_name(NS_CATEGORY) | |||||
| prefix = self.namespace_id_to_name(constants.NS_CATEGORY) | |||||
| pagename = ':'.join((prefix, catname)) | pagename = ':'.join((prefix, catname)) | ||||
| return Category(self, pagename, follow_redirects) | return Category(self, pagename, follow_redirects) | ||||
| @@ -28,7 +28,7 @@ import stat | |||||
| import sqlite3 as sqlite | import sqlite3 as sqlite | ||||
| from earwigbot import __version__ | from earwigbot import __version__ | ||||
| from earwigbot.wiki.exceptions import SiteNotFoundError | |||||
| from earwigbot.exceptions import SiteNotFoundError | |||||
| from earwigbot.wiki.site import Site | from earwigbot.wiki.site import Site | ||||
| __all__ = ["SitesDB"] | __all__ = ["SitesDB"] | ||||
| @@ -22,8 +22,8 @@ | |||||
| from time import gmtime, strptime | from time import gmtime, strptime | ||||
| from earwigbot.wiki.constants import * | |||||
| from earwigbot.wiki.exceptions import UserNotFoundError | |||||
| from earwigbot.exceptions import UserNotFoundError | |||||
| from earwigbot.wiki import constants | |||||
| from earwigbot.wiki.page import Page | from earwigbot.wiki.page import Page | ||||
| __all__ = ["User"] | __all__ = ["User"] | ||||
| @@ -252,7 +252,7 @@ class User(object): | |||||
| No checks are made to see if it exists or not. Proper site namespace | No checks are made to see if it exists or not. Proper site namespace | ||||
| conventions are followed. | conventions are followed. | ||||
| """ | """ | ||||
| prefix = self._site.namespace_id_to_name(NS_USER) | |||||
| prefix = self._site.namespace_id_to_name(constants.NS_USER) | |||||
| pagename = ':'.join((prefix, self._name)) | pagename = ':'.join((prefix, self._name)) | ||||
| return Page(self._site, pagename) | return Page(self._site, pagename) | ||||
| @@ -262,6 +262,6 @@ class User(object): | |||||
| No checks are made to see if it exists or not. Proper site namespace | No checks are made to see if it exists or not. Proper site namespace | ||||
| conventions are followed. | conventions are followed. | ||||
| """ | """ | ||||
| prefix = self._site.namespace_id_to_name(NS_USER_TALK) | |||||
| prefix = self._site.namespace_id_to_name(constants.NS_USER_TALK) | |||||
| pagename = ':'.join((prefix, self._name)) | pagename = ':'.join((prefix, self._name)) | ||||
| return Page(self._site, pagename) | return Page(self._site, pagename) | ||||