diff --git a/earwigbot/wiki/page.py b/earwigbot/wiki/page.py index eacb221..175505a 100644 --- a/earwigbot/wiki/page.py +++ b/earwigbot/wiki/page.py @@ -280,8 +280,7 @@ class Page(CopyvioMixIn): self._assert_existence() 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! 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: if not hasattr(error, "code"): 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 result["edit"]["result"] == "Success": @@ -360,12 +359,12 @@ class Page(CopyvioMixIn): 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. 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", "protectedtitle", "noimageredirect", "noimageredirect-anon", @@ -378,6 +377,14 @@ class Page(CopyvioMixIn): self._basetimestamp = None self._exists = self.PAGE_UNKNOWN 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"]: raise exceptions.NoContentError(error.info) elif error.code == "contenttoobig": diff --git a/earwigbot/wiki/site.py b/earwigbot/wiki/site.py index 015ba7c..9874f1a 100644 --- a/earwigbot/wiki/site.py +++ b/earwigbot/wiki/site.py @@ -26,7 +26,7 @@ from json import loads from logging import getLogger, NullHandler from os.path import expanduser from StringIO import StringIO -from threading import Lock +from threading import RLock from time import sleep, time from urllib import quote_plus, unquote_plus from urllib2 import build_opener, HTTPCookieProcessor, URLError @@ -124,7 +124,7 @@ class Site(object): self._wait_between_queries = wait_between_queries self._max_retries = 6 self._last_query_time = 0 - self._api_lock = Lock() + self._api_lock = RLock() self._api_info_cache = {"maxlag": 0, "lastcheck": 0} # Attributes used for SQL queries: @@ -133,7 +133,7 @@ class Site(object): else: self._sql_data = {} self._sql_conn = None - self._sql_lock = Lock() + self._sql_lock = RLock() self._sql_info_cache = {"replag": 0, "lastcheck": 0, "usable": None} # Attribute used in copyright violation checks (see CopyrightMixIn): @@ -298,7 +298,7 @@ class Site(object): # Try to log in if we got logged out: self._login(self._login_info) 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) if not all(self._login_info): e = "Assertion failed, and no login info was provided." @@ -762,27 +762,20 @@ class Site(object): result = list(self.sql_query(query)) 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. *action* must be one of the types listed on . If it's given as a union 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 have permissions for the requested action(s), or they are invalid. Raises :py:exc:`~earwigbot.exceptions.APIError` if there was some other 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"]: raise exceptions.PermissionsError(res["warnings"]["tokens"]["*"]) if "|" in action: