* Site's api_query() is much smarter. It uses a custom urllib2 URL opener with cookie support and catches URLErrors, raising its own brand new exception (SiteAPIError) when something is wrong.
* The opener now uses a custom User-Agent, which is a constant in wiki.tools.constants.
* Site instances automatically login via _login(), which accepts a username and password (provided via config by get_site()) and uses two api_query()s and stores the login data as cookies in self._cookiejar. Login data is not preserved between bot restarts yet. Login errors, e.g. a bad password or username, raise the new LoginError.
* Site's get_user()'s username argument is now optional. If left blank, will return the current logged-in user, provided by an API query.
* Misc cleanup throughout.
* Site's __init__() takes more args, all optional. As long as enough are
provided to do an API query, the missing ones will be filled in
automatically by _load_attributes(), which is called in __init__().
* User: _get_attribute_from_api() -> _get_attribute();
_load_attributes_from_api() -> _load_attributes.
* Sites in config.json are stored with different keys/values.
* Got rid of ConfigError from exceptions.py.
* Try to load config ourselves if it isn't already, via the new _load_config()
method of Site. It uses getpass if passwords are encrypted, as done by
earwigbot.py.
* Cleaned up UserNotFoundError in user.py and exceptions.py.
tools.get_site() returns an actual Site object, thanks to tools.functions._get_site_object_from_dict().
Site objects now have a working (but primitive) .api_query(), .get_page(), .get_category(), and .get_user().
Page objects now have a working .get(), for getting page content from the API.
Category is now a subclass of Page, and has its own .get_members(), which returns a list of titles.
Still need to implement proper namespace logic in pages.