Browse Source

Merge branch 'feature/exceptions' into develop

tags/v0.1^2
Ben Kurtovic 12 years ago
parent
commit
6efebbfa43
15 changed files with 314 additions and 200 deletions
  1. +8
    -7
      docs/api/earwigbot.rst
  2. +0
    -7
      docs/api/earwigbot.wiki.rst
  3. +9
    -1
      earwigbot/__init__.py
  4. +2
    -2
      earwigbot/commands/threads.py
  5. +244
    -0
      earwigbot/exceptions.py
  6. +4
    -9
      earwigbot/irc/connection.py
  7. +4
    -8
      earwigbot/irc/data.py
  8. +0
    -1
      earwigbot/wiki/__init__.py
  9. +2
    -1
      earwigbot/wiki/constants.py
  10. +1
    -1
      earwigbot/wiki/copyright.py
  11. +0
    -123
      earwigbot/wiki/exceptions.py
  12. +21
    -21
      earwigbot/wiki/page.py
  13. +14
    -14
      earwigbot/wiki/site.py
  14. +1
    -1
      earwigbot/wiki/sitesdb.py
  15. +4
    -4
      earwigbot/wiki/user.py

+ 8
- 7
docs/api/earwigbot.rst View File

@@ -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
---------------------- ----------------------




+ 0
- 7
docs/api/earwigbot.wiki.rst View File

@@ -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
------------------ ------------------




+ 9
- 1
earwigbot/__init__.py View File

@@ -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

+ 2
- 2
earwigbot/commands/threads.py View File

@@ -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


+ 244
- 0
earwigbot/exceptions.py View File

@@ -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>`.
"""

+ 4
- 9
earwigbot/irc/connection.py View File

@@ -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




+ 4
- 8
earwigbot/irc/data.py View File

@@ -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)

+ 0
- 1
earwigbot/wiki/__init__.py View File

@@ -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 *


+ 2
- 1
earwigbot/wiki/constants.py View File

@@ -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:


+ 1
- 1
earwigbot/wiki/copyright.py View File

@@ -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):


+ 0
- 123
earwigbot/wiki/exceptions.py View File

@@ -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."""

+ 21
- 21
earwigbot/wiki/page.py View File

@@ -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.


+ 14
- 14
earwigbot/wiki/site.py View File

@@ -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)




+ 1
- 1
earwigbot/wiki/sitesdb.py View File

@@ -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"]


+ 4
- 4
earwigbot/wiki/user.py View File

@@ -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)

Loading…
Cancel
Save