| @@ -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 | ||||