Skip to content

Commit

Permalink
Merge "[IMPR] Add a WaitingMixin to Requests and SparqlQuery class"
Browse files Browse the repository at this point in the history
  • Loading branch information
jenkins-bot authored and Gerrit Code Review committed Aug 24, 2023
2 parents 1957e0f + f605f64 commit fea6540
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 45 deletions.
52 changes: 51 additions & 1 deletion pywikibot/data/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,56 @@
"""Module providing several layers of data access to the wiki."""
#
# (C) Pywikibot team, 2007-2022
# (C) Pywikibot team, 2007-2023
#
# Distributed under the terms of the MIT license.
#
from typing import Optional

import pywikibot


class WaitingMixin:

"""A mixin to implement wait cycles.
.. versionadded:: 8.4
:ivar int max_retries: Maximum number of times to retry an API
request before quitting. Defaults to ``config.max_retries`` if
attribute is missing.
:ivar int retry_wait: Minimum time to wait before resubmitting a
failed API request. Defaults to ``config.retry_wait`` if
attribute is missing.
:ivar int current_retries: counter of retries made for the current
request. Starting with 1 if attribute is missing.
"""

def wait(self, delay: Optional[int] = None) -> None:
"""Determine how long to wait after a failed request.
:param delay: Minimum time in seconds to wait. Overwrites
``retry_wait`` variable if given. The delay doubles each
retry until ``retry_max`` seconds is reached.
"""
if not hasattr(self, 'max_retries'):
self.max_retries = pywikibot.config.max_retries

if not hasattr(self, 'retry_wait'):
self.retry_wait = pywikibot.config.retry_wait

if self.current_retries > self.max_retries:
raise pywikibot.exceptions.TimeoutError(
'Maximum retries attempted without success.')

if not hasattr(self, 'current_retries'):
self.current_retries = 1
else:
self.current_retries += 1

# double the next wait, but do not exceed config.retry_max seconds
delay = delay or self.retry_wait
delay *= 2 ** (self.current_retries - 1)
delay = min(delay, pywikibot.config.retry_max)

pywikibot.warning(f'Waiting {delay:.1f} seconds before retrying.')
pywikibot.sleep(delay)
30 changes: 7 additions & 23 deletions pywikibot/data/api/_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from pywikibot import config
from pywikibot.backports import Callable, Dict, Match, Tuple, removeprefix
from pywikibot.comms import http
from pywikibot.data import WaitingMixin
from pywikibot.exceptions import (
Client414Error,
Error,
Expand All @@ -32,7 +33,6 @@
NoUsernameError,
Server504Error,
SiteDefinitionError,
TimeoutError,
)
from pywikibot.login import LoginStatus
from pywikibot.textlib import removeDisabledParts, removeHTMLParts
Expand Down Expand Up @@ -72,7 +72,7 @@
r'Waiting for [\w.: ]+: (?P<lag>\d+(?:\.\d+)?) seconds? lagged')


class Request(MutableMapping):
class Request(MutableMapping, WaitingMixin):

"""A request to a Site's api.php interface.
Expand Down Expand Up @@ -126,6 +126,9 @@ class Request(MutableMapping):
True
>>> sorted(data['query'])
['namespaces', 'userinfo']
.. versionchanged:: 8.4
inherited from WaitingMixin.
"""

# To make sure the default value of 'parameters' can be identified.
Expand Down Expand Up @@ -194,14 +197,9 @@ def __init__(self, site=None,

self.throttle = throttle
self.use_get = use_get
if max_retries is None:
self.max_retries = pywikibot.config.max_retries
else:
if max_retries is not None:
self.max_retries = max_retries
self.current_retries = 0
if retry_wait is None:
self.retry_wait = pywikibot.config.retry_wait
else:
if retry_wait is not None:
self.retry_wait = retry_wait
self.json_warning = False
# The only problem with that system is that it won't detect when
Expand Down Expand Up @@ -1120,20 +1118,6 @@ def submit(self) -> dict:

raise MaxlagTimeoutError(msg)

def wait(self, delay=None):
"""Determine how long to wait after a failed request."""
self.current_retries += 1
if self.current_retries > self.max_retries:
raise TimeoutError('Maximum retries attempted without success.')

# double the next wait, but do not exceed config.retry_max seconds
delay = delay or self.retry_wait
delay *= 2 ** (self.current_retries - 1)
delay = min(delay, config.retry_max)

pywikibot.warning(f'Waiting {delay:.1f} seconds before retrying.')
pywikibot.sleep(delay)


class CachedRequest(Request):

Expand Down
32 changes: 11 additions & 21 deletions pywikibot/data/sparql.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@

from requests.exceptions import Timeout

from pywikibot import Site, config, sleep, warning
from pywikibot import Site
from pywikibot.backports import Dict, List, removeprefix
from pywikibot.comms import http
from pywikibot.exceptions import Error, TimeoutError
from pywikibot.data import WaitingMixin
from pywikibot.exceptions import Error


try:
Expand All @@ -25,11 +26,14 @@
'Accept': 'application/sparql-results+json'}


class SparqlQuery:
"""
SPARQL Query class.
class SparqlQuery(WaitingMixin):
"""SPARQL Query class.
This class allows to run SPARQL queries against any SPARQL endpoint.
.. versionchanged:: 8.4
inherited from :class:`data.WaitingMixin` which provides a
:meth:`data.WaitingMixin.wait` method.
"""

def __init__(self,
Expand Down Expand Up @@ -78,13 +82,9 @@ def __init__(self,

self.last_response = None

if max_retries is None:
self.max_retries = config.max_retries
else:
if max_retries is not None:
self.max_retries = max_retries
if retry_wait is None:
self.retry_wait = config.retry_wait
else:
if retry_wait is not None:
self.retry_wait = retry_wait

def get_last_response(self):
Expand Down Expand Up @@ -158,16 +158,6 @@ def query(self, query: str, headers: Optional[Dict[str, str]] = None):

return None

def wait(self):
"""Determine how long to wait after a failed request."""
self.max_retries -= 1
if self.max_retries < 0:
raise TimeoutError('Maximum retries attempted without success.')
warning(f'Waiting {self.retry_wait} seconds before retrying.')
sleep(self.retry_wait)
# double the next wait, but do not exceed config.retry_max seconds
self.retry_wait = min(config.retry_max, self.retry_wait * 2)

def ask(self, query: str,
headers: Optional[Dict[str, str]] = None) -> bool:
"""
Expand Down

0 comments on commit fea6540

Please sign in to comment.