@@ -37,18 +37,19 @@ __email__ = "ben.kurtovic@verizon.net" | |||||
__release__ = False | __release__ = False | ||||
if not __release__: | if not __release__: | ||||
def _add_git_commit_id_to_version_string(version): | |||||
def _get_git_commit_id(): | |||||
"""Return the ID of the git HEAD commit.""" | |||||
from git import Repo | from git import Repo | ||||
from os.path import split, dirname | from os.path import split, dirname | ||||
path = split(dirname(__file__))[0] | path = split(dirname(__file__))[0] | ||||
commit_id = Repo(path).head.object.hexsha | commit_id = Repo(path).head.object.hexsha | ||||
return version + ".git+" + commit_id[:8] | |||||
return commit_id[:8] | |||||
try: | try: | ||||
__version__ = _add_git_commit_id_to_version_string(__version__) | |||||
__version__ += ".git+" + _get_git_commit_id() | |||||
except Exception: | except Exception: | ||||
pass | pass | ||||
finally: | finally: | ||||
del _add_git_commit_id_to_version_string | |||||
del _get_git_commit_id | |||||
from earwigbot import bot | from earwigbot import bot | ||||
from earwigbot import commands | from earwigbot import commands | ||||
@@ -23,7 +23,6 @@ | |||||
from urllib import quote_plus | from urllib import quote_plus | ||||
from earwigbot import exceptions | from earwigbot import exceptions | ||||
from earwigbot import wiki | |||||
from earwigbot.commands import BaseCommand | from earwigbot.commands import BaseCommand | ||||
class Command(BaseCommand): | class Command(BaseCommand): | ||||
@@ -47,7 +46,7 @@ class Command(BaseCommand): | |||||
try: | try: | ||||
count = user.editcount | count = user.editcount | ||||
except earwigbot.UserNotFoundError: | |||||
except exceptions.UserNotFoundError: | |||||
msg = "the user \x0302{0}\x0301 does not exist." | msg = "the user \x0302{0}\x0301 does not exist." | ||||
self.reply(data, msg.format(name)) | self.reply(data, msg.format(name)) | ||||
return | return | ||||
@@ -23,7 +23,6 @@ | |||||
import time | import time | ||||
from earwigbot import exceptions | from earwigbot import exceptions | ||||
from earwigbot import wiki | |||||
from earwigbot.commands import BaseCommand | from earwigbot.commands import BaseCommand | ||||
class Command(BaseCommand): | class Command(BaseCommand): | ||||
@@ -21,7 +21,6 @@ | |||||
# SOFTWARE. | # SOFTWARE. | ||||
from earwigbot import exceptions | from earwigbot import exceptions | ||||
from earwigbot import wiki | |||||
from earwigbot.commands import BaseCommand | from earwigbot.commands import BaseCommand | ||||
class Command(BaseCommand): | class Command(BaseCommand): | ||||
@@ -79,7 +79,7 @@ class _ResourceManager(object): | |||||
""" | """ | ||||
f, path, desc = imp.find_module(name, [path]) | f, path, desc = imp.find_module(name, [path]) | ||||
try: | try: | ||||
module = imp.load_module(name, f, path, desc) | |||||
module = imp.load_module(name, f, path, desc) | |||||
except Exception: | except Exception: | ||||
e = "Couldn't load module {0} (from {1})" | e = "Couldn't load module {0} (from {1})" | ||||
self.logger.exception(e.format(name, path)) | self.logger.exception(e.format(name, path)) | ||||
@@ -107,9 +107,9 @@ class BaseTask(object): | |||||
Otherwise, we'll use our default site. | Otherwise, we'll use our default site. | ||||
""" | """ | ||||
if not site: | if not site: | ||||
try: | |||||
site = self.site | |||||
except AttributeError: | |||||
if hasattr(self, "site"): | |||||
site = getattr(self, "site") | |||||
else: | |||||
site = self.bot.wiki.get_site() | site = self.bot.wiki.get_site() | ||||
try: | try: | ||||
@@ -91,7 +91,7 @@ class Task(BaseTask): | |||||
def update(self, num_days): | def update(self, num_days): | ||||
self.logger.info("Updating past {0} days".format(num_days)) | self.logger.info("Updating past {0} days".format(num_days)) | ||||
generator = self.backwards_cat_iterator() | generator = self.backwards_cat_iterator() | ||||
for d in xrange(num_days): | |||||
for i in xrange(num_days): | |||||
category = generator.next() | category = generator.next() | ||||
date = category.title.split("/")[-1] | date = category.title.split("/")[-1] | ||||
self.update_date(date, category) | self.update_date(date, category) | ||||
@@ -102,7 +102,7 @@ class Task(BaseTask): | |||||
self.logger.info("Generating chart for past {0} days".format(num_days)) | self.logger.info("Generating chart for past {0} days".format(num_days)) | ||||
data = OrderedDict() | data = OrderedDict() | ||||
generator = self.backwards_cat_iterator() | generator = self.backwards_cat_iterator() | ||||
for d in xrange(num_days): | |||||
for i in xrange(num_days): | |||||
category = generator.next() | category = generator.next() | ||||
date = category.title.split("/")[-1] | date = category.title.split("/")[-1] | ||||
data[date] = self.get_date_counts(date) | data[date] = self.get_date_counts(date) | ||||
@@ -110,7 +110,7 @@ class Page(CopyrightMixin): | |||||
if prefix != title: # ignore a page that's titled "Category" or "User" | if prefix != title: # ignore a page that's titled "Category" or "User" | ||||
try: | try: | ||||
self._namespace = self._site.namespace_name_to_id(prefix) | self._namespace = self._site.namespace_name_to_id(prefix) | ||||
except NamespaceNotFoundError: | |||||
except exceptions.NamespaceNotFoundError: | |||||
self._namespace = 0 | self._namespace = 0 | ||||
else: | else: | ||||
self._namespace = 0 | self._namespace = 0 | ||||
@@ -191,10 +191,10 @@ class Page(CopyrightMixin): | |||||
Assuming the API is sound, this should not raise any exceptions. | Assuming the API is sound, this should not raise any exceptions. | ||||
""" | """ | ||||
if not result: | if not result: | ||||
params = {"action": "query", "rvprop": "user", "intoken": "edit", | |||||
"prop": "info|revisions", "rvlimit": 1, "rvdir": "newer", | |||||
"titles": self._title, "inprop": "protection|url"} | |||||
result = self._site._api_query(params) | |||||
query = self._site.api_query | |||||
result = query(action="query", rvprop="user", intoken="edit", | |||||
prop="info|revisions", rvlimit=1, rvdir="newer", | |||||
titles=self._title, inprop="protection|url") | |||||
res = result["query"]["pages"].values()[0] | res = result["query"]["pages"].values()[0] | ||||
@@ -255,9 +255,9 @@ class Page(CopyrightMixin): | |||||
want to force content reloading. | want to force content reloading. | ||||
""" | """ | ||||
if not result: | if not result: | ||||
params = {"action": "query", "prop": "revisions", "rvlimit": 1, | |||||
"rvprop": "content|timestamp", "titles": self._title} | |||||
result = self._site._api_query(params) | |||||
query = self._site.api_query | |||||
result = query(action="query", prop="revisions", rvlimit=1, | |||||
rvprop="content|timestamp", titles=self._title) | |||||
res = result["query"]["pages"].values()[0] | res = result["query"]["pages"].values()[0] | ||||
try: | try: | ||||
@@ -302,8 +302,8 @@ class Page(CopyrightMixin): | |||||
# Try the API query, catching most errors with our handler: | # Try the API query, catching most errors with our handler: | ||||
try: | try: | ||||
result = self._site._api_query(params) | |||||
except SiteAPIError as error: | |||||
result = self._site.api_query(**params) | |||||
except exceptions.SiteAPIError 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, tries) | ||||
@@ -609,10 +609,10 @@ class Page(CopyrightMixin): | |||||
if self._exists == 0: | if self._exists == 0: | ||||
# Kill two birds with one stone by doing an API query for both our | # Kill two birds with one stone by doing an API query for both our | ||||
# attributes and our page content: | # attributes and our page content: | ||||
params = {"action": "query", "rvlimit": 1, "titles": self._title, | |||||
"prop": "info|revisions", "inprop": "protection|url", | |||||
"intoken": "edit", "rvprop": "content|timestamp"} | |||||
result = self._site._api_query(params) | |||||
query = self._site.api_query | |||||
result = query(action="query", rvlimit=1, titles=self._title, | |||||
prop="info|revisions", inprop="protection|url", | |||||
intoken="edit", rvprop="content|timestamp") | |||||
self._load_attributes(result=result) | self._load_attributes(result=result) | ||||
self._assert_existence() | self._assert_existence() | ||||
self._load_content(result=result) | self._load_content(result=result) | ||||
@@ -122,6 +122,7 @@ class Site(object): | |||||
self._wait_between_queries = wait_between_queries | self._wait_between_queries = wait_between_queries | ||||
self._max_retries = 5 | self._max_retries = 5 | ||||
self._last_query_time = 0 | self._last_query_time = 0 | ||||
self._api_lock = Lock() | |||||
# Attributes used for SQL queries: | # Attributes used for SQL queries: | ||||
self._sql_data = sql | self._sql_data = sql | ||||
@@ -278,7 +279,7 @@ class Site(object): | |||||
return self._api_query(params, tries=tries, wait=wait*3) | return self._api_query(params, tries=tries, wait=wait*3) | ||||
else: # Some unknown error occurred | 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 = earwigbot.SiteAPIError(e.format(code, info)) | |||||
error = exceptions.SiteAPIError(e.format(code, info)) | |||||
error.code, error.info = code, info | error.code, error.info = code, info | ||||
raise error | raise error | ||||
@@ -297,17 +298,16 @@ class Site(object): | |||||
attrs = [self._name, self._project, self._lang, self._base_url, | attrs = [self._name, self._project, self._lang, self._base_url, | ||||
self._article_path, self._script_path] | self._article_path, self._script_path] | ||||
params = {"action": "query", "meta": "siteinfo"} | |||||
params = {"action": "query", "meta": "siteinfo", "siprop": "general"} | |||||
if not self._namespaces or force: | if not self._namespaces or force: | ||||
params["siprop"] = "general|namespaces|namespacealiases" | |||||
result = self._api_query(params) | |||||
params["siprop"] += "|namespaces|namespacealiases" | |||||
result = self.api_query(**params) | |||||
self._load_namespaces(result) | self._load_namespaces(result) | ||||
elif all(attrs): # Everything is already specified and we're not told | elif all(attrs): # Everything is already specified and we're not told | ||||
return # to force a reload, so do nothing | return # to force a reload, so do nothing | ||||
else: # We're only loading attributes other than _namespaces | else: # We're only loading attributes other than _namespaces | ||||
params["siprop"] = "general" | |||||
result = self._api_query(params) | |||||
result = self.api_query(**params) | |||||
res = result["query"]["general"] | res = result["query"]["general"] | ||||
self._name = res["wikiid"] | self._name = res["wikiid"] | ||||
@@ -402,8 +402,7 @@ class Site(object): | |||||
username argument) when cookie lookup fails, probably indicating that | username argument) when cookie lookup fails, probably indicating that | ||||
we are logged out. | we are logged out. | ||||
""" | """ | ||||
params = {"action": "query", "meta": "userinfo"} | |||||
result = self._api_query(params) | |||||
result = self.api_query(action="query", meta="userinfo") | |||||
return result["query"]["userinfo"]["name"] | return result["query"]["userinfo"]["name"] | ||||
def _get_username(self): | def _get_username(self): | ||||
@@ -456,12 +455,14 @@ class Site(object): | |||||
loop if MediaWiki isn't acting right. | loop if MediaWiki isn't acting right. | ||||
""" | """ | ||||
name, password = login | name, password = login | ||||
params = {"action": "login", "lgname": name, "lgpassword": password} | |||||
if token: | if token: | ||||
params["lgtoken"] = token | |||||
result = self._api_query(params) | |||||
res = result["login"]["result"] | |||||
result = self.api_query(action="login", lgname=name, | |||||
lgpassword=password, lgtoken=token) | |||||
else: | |||||
result = self.api_query(action="login", lgname=name, | |||||
lgpassword=password) | |||||
res = result["login"]["result"] | |||||
if res == "Success": | if res == "Success": | ||||
self._save_cookiejar() | self._save_cookiejar() | ||||
elif res == "NeedToken" and attempt == 0: | elif res == "NeedToken" and attempt == 0: | ||||
@@ -487,8 +488,7 @@ class Site(object): | |||||
cookiejar (which probably contains now-invalidated cookies) and try to | cookiejar (which probably contains now-invalidated cookies) and try to | ||||
save it, if it supports that sort of thing. | save it, if it supports that sort of thing. | ||||
""" | """ | ||||
params = {"action": "logout"} | |||||
self._api_query(params) | |||||
self.api_query(action="logout") | |||||
self._cookiejar.clear() | self._cookiejar.clear() | ||||
self._save_cookiejar() | self._save_cookiejar() | ||||
@@ -574,7 +574,8 @@ class Site(object): | |||||
There is helpful MediaWiki API documentation at `MediaWiki.org | There is helpful MediaWiki API documentation at `MediaWiki.org | ||||
<http://www.mediawiki.org/wiki/API>`_. | <http://www.mediawiki.org/wiki/API>`_. | ||||
""" | """ | ||||
return self._api_query(kwargs) | |||||
with self._api_lock: | |||||
return self._api_query(kwargs) | |||||
def sql_query(self, query, params=(), plain_query=False, dict_cursor=False, | def sql_query(self, query, params=(), plain_query=False, dict_cursor=False, | ||||
cursor_class=None, show_table=False): | cursor_class=None, show_table=False): | ||||
@@ -105,9 +105,9 @@ class User(object): | |||||
Normally, this is called by _get_attribute() when a requested attribute | Normally, this is called by _get_attribute() when a requested attribute | ||||
is not defined. This defines it. | is not defined. This defines it. | ||||
""" | """ | ||||
params = {"action": "query", "list": "users", "ususers": self._name, | |||||
"usprop": "blockinfo|groups|rights|editcount|registration|emailable|gender"} | |||||
result = self._site._api_query(params) | |||||
props = "blockinfo|groups|rights|editcount|registration|emailable|gender" | |||||
result = self._site.api_query(action="query", list="users", | |||||
ususers=self._name, usprop=props) | |||||
res = result["query"]["users"][0] | res = result["query"]["users"][0] | ||||
# normalize our username in case it was entered oddly | # normalize our username in case it was entered oddly | ||||