示例#1
0
    def _handle_edit_errors(self, error, params, tries):
        """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).
        """
        if error.code in [
                "noedit", "cantcreate", "protectedtitle", "noimageredirect"
        ]:
            raise exceptions.PermissionsError(error.info)

        elif error.code in [
                "noedit-anon", "cantcreate-anon", "noimageredirect-anon"
        ]:
            if not all(self.site._login_info):
                # Insufficient login info:
                raise exceptions.PermissionsError(error.info)
            if tries == 0:
                # We have login info; try to login:
                self.site._login(self.site._login_info)
                self._token = None  # Need a new token; old one is invalid now
                return self._edit(params=params, tries=1)
            else:
                # 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."
                raise exceptions.LoginError(e)

        elif error.code in ["editconflict", "pagedeleted", "articleexists"]:
            # These attributes are now invalidated:
            self._content = None
            self._basetimestamp = None
            self._exists = self.PAGE_UNKNOWN
            raise exceptions.EditConflictError(error.info)

        elif error.code in ["emptypage", "emptynewsection"]:
            raise exceptions.NoContentError(error.info)

        elif error.code == "contenttoobig":
            raise exceptions.ContentTooBigError(error.info)

        elif error.code == "spamdetected":
            raise exceptions.SpamDetectedError(error.info)

        elif error.code == "filtered":
            raise exceptions.FilteredError(error.info)

        raise exceptions.EditError(": ".join((error.code, error.info)))
示例#2
0
    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):
        """Edit the page!

        If *params* is given, we'll use it as our API query parameters.
        Otherwise, we'll build params using the given kwargs via
        _build_edit_params().

        We'll then try to do the API query, and catch any errors the API raises
        in _handle_edit_errors(). We'll then throw these back as subclasses of
        EditError.
        """
        # Try to get our edit token, and die if we can't:
        if not self._token:
            self._load_attributes()
        if not self._token:
            e = "You don't have permission to edit this page."
            raise exceptions.PermissionsError(e)

        # Weed out invalid pages before we get too far:
        self._assert_validity()

        # Build our API query string:
        if not params:
            params = self._build_edit_params(text, summary, minor, bot, force,
                                             section, captcha_id, captcha_word)
        else:  # Make sure we have the right token:
            params["token"] = self._token

        # Try the API query, catching most errors with our handler:
        try:
            result = 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, tries)

        # If everything was successful, reset invalidated attributes:
        if result["edit"]["result"] == "Success":
            self._content = None
            self._basetimestamp = None
            self._exists = self.PAGE_UNKNOWN
            return

        # If we're here, then the edit failed. If it's because of AssertEdit,
        # handle that. Otherwise, die - something odd is going on:
        try:
            assertion = result["edit"]["assert"]
        except KeyError:
            raise exceptions.EditError(result["edit"])
        self._handle_assert_edit(assertion, params, tries)
示例#3
0
    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 the token is
        invalid, we'll try to get a new one).
        """
        perms = ["noedit", "noedit-anon", "cantcreate", "cantcreate-anon",
                 "protectedtitle", "noimageredirect", "noimageredirect-anon",
                 "blocked"]
        if error.code in perms:
            raise exceptions.PermissionsError(error.info)
        elif error.code in ["editconflict", "pagedeleted", "articleexists"]:
            # These attributes are now invalidated:
            self._content = None
            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(force=True)
            try:
                return self.site.api_query(**params)
            except exceptions.APIError as err:
                if not hasattr(err, "code"):
                    raise  # We can only handle errors with a code attribute
                return self._handle_edit_errors(err, params, retry=False)
        elif error.code in ["emptypage", "emptynewsection"]:
            raise exceptions.NoContentError(error.info)
        elif error.code == "contenttoobig":
            raise exceptions.ContentTooBigError(error.info)
        elif error.code == "spamdetected":
            raise exceptions.SpamDetectedError(error.info)
        elif error.code == "filtered":
            raise exceptions.FilteredError(error.info)
        raise exceptions.EditError(": ".join((error.code, error.info)))
示例#4
0
    def _handle_api_result(self, response, params, tries, wait, ae_retry):
        """Given an API query response, attempt to return useful data."""
        try:
            res = response.json()
        except ValueError:
            e = "API query failed: JSON could not be decoded."
            raise exceptions.APIError(e)

        if "warnings" in res:
            for name, value in res["warnings"].items():
                try:
                    warning = value["warnings"]
                except KeyError:
                    try:
                        warning = value["*"]
                    except KeyError:
                        warning = value
                self._logger.warning("API warning: %s: %s", name, warning)

        if self._should_save_cookiejar():
            self._save_cookiejar()

        try:
            code = res["error"]["code"]
            info = res["error"]["info"]
        except (TypeError, KeyError):  # If there's no error code/info, return
            if "query" in res and "tokens" in res["query"]:
                for name, token in res["query"]["tokens"].iteritems():
                    self._tokens[name.split("token")[0]] = token
            return res

        if code == "maxlag":  # We've been throttled by the server
            if tries >= self._max_retries:
                e = "Maximum number of retries reached ({0})."
                raise exceptions.APIError(e.format(self._max_retries))
            tries += 1
            msg = 'Server says "{0}"; retrying in {1} seconds ({2}/{3})'
            self._logger.info(msg.format(info, wait, tries, self._max_retries))
            sleep(wait)
            return self._api_query(params, tries, wait * 2, ae_retry=ae_retry)
        elif code in ["assertuserfailed", "assertbotfailed"]:  # AssertEdit
            if ae_retry and all(self._login_info) and not self._oauth:
                # 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"])
                return self._api_query(params, tries, wait, ae_retry=False)
            if not all(self._login_info) and not self._oauth:
                e = "Assertion failed, and no login info was provided."
            elif code == "assertbotfailed":
                e = "Bot assertion failed: we don't have a bot flag!"
            else:
                e = "User assertion failed due to an unknown issue. Cookie or OAuth problem?"
            raise exceptions.PermissionsError("AssertEdit: " + e)
        else:  # Some unknown error occurred
            e = 'API query failed: got error "{0}"; server says: "{1}".'
            error = exceptions.APIError(e.format(code, info))
            error.code, error.info = code, info
            raise error
示例#5
0
    def _handle_assert_edit(self, assertion, params, tries):
        """If we can't edit due to a failed AssertEdit assertion, handle that.

        If the assertion was 'user' and we have valid login information, try to
        log in. Otherwise, raise PermissionsError with details.
        """
        if assertion == "user":
            if not all(self.site._login_info):
                # Insufficient login info:
                e = "AssertEdit: user assertion failed, and no login info was provided."
                raise exceptions.PermissionsError(e)
            if tries == 0:
                # We have login info; try to login:
                self.site._login(self.site._login_info)
                self._token = None  # Need a new token; old one is invalid now
                return self._edit(params=params, tries=1)
            else:
                # 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."
                raise exceptions.LoginError(e)

        elif assertion == "bot":
            if not all(self.site._login_info):
                # Insufficient login info:
                e = "AssertEdit: bot assertion failed, and no login info was provided."
                raise exceptions.PermissionsError(e)
            if tries == 0:
                # Try to log in if we got logged out:
                self.site._login(self.site._login_info)
                self._token = None  # Need a new token; old one is invalid now
                return self._edit(params=params, tries=1)
            else:
                # We already tried to log in, so we don't have a bot flag:
                e = "AssertEdit: bot assertion failed: we don't have a bot flag!"
                raise exceptions.PermissionsError(e)

        # Unknown assertion, maybe "true", "false", or "exists":
        e = "AssertEdit: assertion '{0}' failed.".format(assertion)
        raise exceptions.PermissionsError(e)