|
@@ -190,24 +190,6 @@ class Site(object): |
|
|
There's helpful MediaWiki API documentation at |
|
|
There's helpful MediaWiki API documentation at |
|
|
<http://www.mediawiki.org/wiki/API>. |
|
|
<http://www.mediawiki.org/wiki/API>. |
|
|
""" |
|
|
""" |
|
|
if not self._base_url or self._script_path is None: |
|
|
|
|
|
e = "Tried to do an API query, but no API URL is known." |
|
|
|
|
|
raise SiteAPIError(e) |
|
|
|
|
|
|
|
|
|
|
|
base_url = self._base_url |
|
|
|
|
|
if base_url.startswith("//"): # Protocol-relative URLs from 1.18 |
|
|
|
|
|
if self._use_https: |
|
|
|
|
|
base_url = "https:" + base_url |
|
|
|
|
|
else: |
|
|
|
|
|
base_url = "http:" + base_url |
|
|
|
|
|
url = ''.join((base_url, self._script_path, "/api.php")) |
|
|
|
|
|
|
|
|
|
|
|
params["format"] = "json" # This is the only format we understand |
|
|
|
|
|
if self._assert_edit: # If requested, ensure that we're logged in |
|
|
|
|
|
params["assert"] = self._assert_edit |
|
|
|
|
|
if self._maxlag: # If requested, don't overload the servers |
|
|
|
|
|
params["maxlag"] = self._maxlag |
|
|
|
|
|
|
|
|
|
|
|
since_last_query = time() - self._last_query_time # Throttling support |
|
|
since_last_query = time() - self._last_query_time # Throttling support |
|
|
if since_last_query < self._wait_between_queries: |
|
|
if since_last_query < self._wait_between_queries: |
|
|
wait_time = self._wait_between_queries - since_last_query |
|
|
wait_time = self._wait_between_queries - since_last_query |
|
@@ -215,7 +197,7 @@ class Site(object): |
|
|
sleep(wait_time) |
|
|
sleep(wait_time) |
|
|
self._last_query_time = time() |
|
|
self._last_query_time = time() |
|
|
|
|
|
|
|
|
data = urlencode(params) |
|
|
|
|
|
|
|
|
url, data = self._build_api_query(params) |
|
|
logger.debug("{0} -> {1}".format(url, data)) |
|
|
logger.debug("{0} -> {1}".format(url, data)) |
|
|
|
|
|
|
|
|
try: |
|
|
try: |
|
@@ -236,8 +218,35 @@ class Site(object): |
|
|
gzipper = GzipFile(fileobj=stream) |
|
|
gzipper = GzipFile(fileobj=stream) |
|
|
result = gzipper.read() |
|
|
result = gzipper.read() |
|
|
|
|
|
|
|
|
|
|
|
return self._handle_api_query_result(result, params, tries, wait) |
|
|
|
|
|
|
|
|
|
|
|
def _build_api_query(self, params): |
|
|
|
|
|
"""Given API query params, return the URL to query and POST data.""" |
|
|
|
|
|
if not self._base_url or self._script_path is None: |
|
|
|
|
|
e = "Tried to do an API query, but no API URL is known." |
|
|
|
|
|
raise SiteAPIError(e) |
|
|
|
|
|
|
|
|
|
|
|
base_url = self._base_url |
|
|
|
|
|
if base_url.startswith("//"): # Protocol-relative URLs from 1.18 |
|
|
|
|
|
if self._use_https: |
|
|
|
|
|
base_url = "https:" + base_url |
|
|
|
|
|
else: |
|
|
|
|
|
base_url = "http:" + base_url |
|
|
|
|
|
url = ''.join((base_url, self._script_path, "/api.php")) |
|
|
|
|
|
|
|
|
|
|
|
params["format"] = "json" # This is the only format we understand |
|
|
|
|
|
if self._assert_edit: # If requested, ensure that we're logged in |
|
|
|
|
|
params["assert"] = self._assert_edit |
|
|
|
|
|
if self._maxlag: # If requested, don't overload the servers |
|
|
|
|
|
params["maxlag"] = self._maxlag |
|
|
|
|
|
|
|
|
|
|
|
data = urlencode(params) |
|
|
|
|
|
return url, data |
|
|
|
|
|
|
|
|
|
|
|
def _handle_api_query_result(self, result, params, tries, wait): |
|
|
|
|
|
"""Given the result of an API query, attempt to return useful data.""" |
|
|
try: |
|
|
try: |
|
|
res = loads(result) # 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 SiteAPIError(e) |
|
@@ -245,10 +254,10 @@ class Site(object): |
|
|
try: |
|
|
try: |
|
|
code = res["error"]["code"] |
|
|
code = res["error"]["code"] |
|
|
info = res["error"]["info"] |
|
|
info = res["error"]["info"] |
|
|
except (TypeError, KeyError): |
|
|
|
|
|
return res |
|
|
|
|
|
|
|
|
except (TypeError, KeyError): # Having these keys indicates a problem |
|
|
|
|
|
return res # All is well; return the decoded JSON |
|
|
|
|
|
|
|
|
if code == "maxlag": |
|
|
|
|
|
|
|
|
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 SiteAPIError(e.format(self._max_retries)) |
|
@@ -257,7 +266,7 @@ class Site(object): |
|
|
logger.info(msg.format(info, wait, tries, self._max_retries)) |
|
|
logger.info(msg.format(info, wait, tries, self._max_retries)) |
|
|
sleep(wait) |
|
|
sleep(wait) |
|
|
return self._api_query(params, tries=tries, wait=wait*3) |
|
|
return self._api_query(params, tries=tries, wait=wait*3) |
|
|
else: |
|
|
|
|
|
|
|
|
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 = SiteAPIError(e.format(code, info)) |
|
|
error.code, error.info = code, info |
|
|
error.code, error.info = code, info |
|
|