Переглянути джерело

Cleanup; fix site locking mechanism; badtoken handling.

tags/v0.2
Ben Kurtovic 11 роки тому
джерело
коміт
4fff908912
2 змінених файлів з 20 додано та 20 видалено
  1. +13
    -6
      earwigbot/wiki/page.py
  2. +7
    -14
      earwigbot/wiki/site.py

+ 13
- 6
earwigbot/wiki/page.py Переглянути файл

@@ -280,8 +280,7 @@ class Page(CopyvioMixIn):
self._assert_existence() self._assert_existence()


def _edit(self, params=None, text=None, summary=None, minor=None, bot=None, def _edit(self, params=None, text=None, summary=None, minor=None, bot=None,
force=None, section=None, captcha_id=None, captcha_word=None,
tries=0):
force=None, section=None, captcha_id=None, captcha_word=None):
"""Edit the page! """Edit the page!


If *params* is given, we'll use it as our API query parameters. If *params* is given, we'll use it as our API query parameters.
@@ -316,7 +315,7 @@ class Page(CopyvioMixIn):
except exceptions.APIError as error: except exceptions.APIError as error:
if not hasattr(error, "code"): if not hasattr(error, "code"):
raise # We can only handle errors with a code attribute raise # We can only handle errors with a code attribute
result = self._handle_edit_errors(error, params, tries)
result = self._handle_edit_errors(error, params)


# If everything was successful, reset invalidated attributes: # If everything was successful, reset invalidated attributes:
if result["edit"]["result"] == "Success": if result["edit"]["result"] == "Success":
@@ -360,12 +359,12 @@ class Page(CopyvioMixIn):


return params return params


def _handle_edit_errors(self, error, params, tries):
def _handle_edit_errors(self, error, params, retry=True):
"""If our edit fails due to some error, try to handle it. """If our edit fails due to some error, try to handle it.


We'll either raise an appropriate exception (for example, if the page We'll either raise an appropriate exception (for example, if the page
is protected), or we'll try to fix it (for example, if we can't edit
due to being logged out, we'll try to log in).
is protected), or we'll try to fix it (for example, if the token is
invalid, we'll try to get a new one).
""" """
perms = ["noedit", "noedit-anon", "cantcreate", "cantcreate-anon", perms = ["noedit", "noedit-anon", "cantcreate", "cantcreate-anon",
"protectedtitle", "noimageredirect", "noimageredirect-anon", "protectedtitle", "noimageredirect", "noimageredirect-anon",
@@ -378,6 +377,14 @@ class Page(CopyvioMixIn):
self._basetimestamp = None self._basetimestamp = None
self._exists = self.PAGE_UNKNOWN self._exists = self.PAGE_UNKNOWN
raise exceptions.EditConflictError(error.info) raise exceptions.EditConflictError(error.info)
elif error.code == "badtoken" and retry:
params["token"] = self.site.get_token("edit")
try:
return self.site.api_query(**params)
except exceptions.APIError as error:
if not hasattr(error, "code"):
raise # We can only handle errors with a code attribute
result = self._handle_edit_errors(error, params, retry=False)
elif error.code in ["emptypage", "emptynewsection"]: elif error.code in ["emptypage", "emptynewsection"]:
raise exceptions.NoContentError(error.info) raise exceptions.NoContentError(error.info)
elif error.code == "contenttoobig": elif error.code == "contenttoobig":


+ 7
- 14
earwigbot/wiki/site.py Переглянути файл

@@ -26,7 +26,7 @@ from json import loads
from logging import getLogger, NullHandler from logging import getLogger, NullHandler
from os.path import expanduser from os.path import expanduser
from StringIO import StringIO from StringIO import StringIO
from threading import Lock
from threading import RLock
from time import sleep, time from time import sleep, time
from urllib import quote_plus, unquote_plus from urllib import quote_plus, unquote_plus
from urllib2 import build_opener, HTTPCookieProcessor, URLError from urllib2 import build_opener, HTTPCookieProcessor, URLError
@@ -124,7 +124,7 @@ class Site(object):
self._wait_between_queries = wait_between_queries self._wait_between_queries = wait_between_queries
self._max_retries = 6 self._max_retries = 6
self._last_query_time = 0 self._last_query_time = 0
self._api_lock = Lock()
self._api_lock = RLock()
self._api_info_cache = {"maxlag": 0, "lastcheck": 0} self._api_info_cache = {"maxlag": 0, "lastcheck": 0}


# Attributes used for SQL queries: # Attributes used for SQL queries:
@@ -133,7 +133,7 @@ class Site(object):
else: else:
self._sql_data = {} self._sql_data = {}
self._sql_conn = None self._sql_conn = None
self._sql_lock = Lock()
self._sql_lock = RLock()
self._sql_info_cache = {"replag": 0, "lastcheck": 0, "usable": None} self._sql_info_cache = {"replag": 0, "lastcheck": 0, "usable": None}


# Attribute used in copyright violation checks (see CopyrightMixIn): # Attribute used in copyright violation checks (see CopyrightMixIn):
@@ -298,7 +298,7 @@ class Site(object):
# Try to log in if we got logged out: # Try to log in if we got logged out:
self._login(self._login_info) self._login(self._login_info)
if "token" in params: # Fetch a new one; this is invalid now if "token" in params: # Fetch a new one; this is invalid now
params["token"] = self.get_token(params["action"], False)
params["token"] = self.get_token(params["action"])
return self._api_query(params, tries, wait, ae_retry=False) return self._api_query(params, tries, wait, ae_retry=False)
if not all(self._login_info): if not all(self._login_info):
e = "Assertion failed, and no login info was provided." e = "Assertion failed, and no login info was provided."
@@ -762,27 +762,20 @@ class Site(object):
result = list(self.sql_query(query)) result = list(self.sql_query(query))
return int(result[0][0]) return int(result[0][0])


def get_token(self, action, lock=True):
def get_token(self, action):
"""Return a token for a data-modifying API action. """Return a token for a data-modifying API action.


*action* must be one of the types listed on *action* must be one of the types listed on
<https://www.mediawiki.org/wiki/API:Tokens>. If it's given as a union <https://www.mediawiki.org/wiki/API:Tokens>. If it's given as a union
of types separated by |, then the function will return a dictionary of types separated by |, then the function will return a dictionary
of tokens instead of a single one. *lock* is for internal usage;
setting it to ``False`` prevents the query from acquiring the internal
API access lock.
of tokens instead of a single one.


Raises :py:exc:`~earwigbot.exceptions.PermissionsError` if we don't Raises :py:exc:`~earwigbot.exceptions.PermissionsError` if we don't
have permissions for the requested action(s), or they are invalid. have permissions for the requested action(s), or they are invalid.
Raises :py:exc:`~earwigbot.exceptions.APIError` if there was some other Raises :py:exc:`~earwigbot.exceptions.APIError` if there was some other
API issue. API issue.
""" """
if lock:
res = self.api_query(action="tokens", type=action)
else:
params = {"action": "tokens", "type": action}
res = self._api_query(params, ae_retry=False)

res = self.api_query(action="tokens", type=action)
if "warnings" in res and "tokens" in res["warnings"]: if "warnings" in res and "tokens" in res["warnings"]:
raise exceptions.PermissionsError(res["warnings"]["tokens"]["*"]) raise exceptions.PermissionsError(res["warnings"]["tokens"]["*"])
if "|" in action: if "|" in action:


Завантаження…
Відмінити
Зберегти