[Pywikipedia-l] SVN: [4914] branches/rewrite/pywikibot
russblau at svn.wikimedia.org
russblau at svn.wikimedia.org
Fri Jan 18 20:55:39 UTC 2008
Revision: 4914
Author: russblau
Date: 2008-01-18 20:55:39 +0000 (Fri, 18 Jan 2008)
Log Message:
-----------
First cut at a testable, documented api.py module. Only partially implemented.
Modified Paths:
--------------
branches/rewrite/pywikibot/data/__init__.py
branches/rewrite/pywikibot/data/api.py
branches/rewrite/pywikibot/data/http.py
branches/rewrite/pywikibot/tests/dummy.py
Added Paths:
-----------
branches/rewrite/pywikibot/__init__.py
branches/rewrite/pywikibot/tests/__init__.py
branches/rewrite/pywikibot/tests/api_tests.py
Added: branches/rewrite/pywikibot/__init__.py
===================================================================
Modified: branches/rewrite/pywikibot/data/__init__.py
===================================================================
--- branches/rewrite/pywikibot/data/__init__.py 2008-01-18 20:12:16 UTC (rev 4913)
+++ branches/rewrite/pywikibot/data/__init__.py 2008-01-18 20:55:39 UTC (rev 4914)
@@ -10,5 +10,4 @@
__version__ = '$Id: $'
#Import the classes exposed by this module:
-from http import HTTP
-from api import API
+# TODO
Modified: branches/rewrite/pywikibot/data/api.py
===================================================================
--- branches/rewrite/pywikibot/data/api.py 2008-01-18 20:12:16 UTC (rev 4913)
+++ branches/rewrite/pywikibot/data/api.py 2008-01-18 20:55:39 UTC (rev 4914)
@@ -10,47 +10,117 @@
__version__ = '$Id: $'
+from UserDict import DictMixin
import urllib
import http
import simplejson as json
import warnings
+
class APIError(Exception):
"""The wiki site returned an error message."""
- def __init__(self, errordict):
+ def __init__(self, code, info, **kwargs):
"""Save error dict returned by MW API."""
- self.errors = errordict
-
+ self.code = code
+ self.info = info
+ self.other = kwargs
+ def __repr__(self):
+ return 'APIError("%(code)s", "%(info)s", %(other)s)' % self.__dict__
def __str__(self):
- return "%(code)s: %(info)s" % self.errors
+ return "%(code)s: %(info)s" % self.__dict__
-class API:
+class Request(DictMixin):
+ """A request to a Site's api.php interface.
- def __init__(self, site):
- self.site = site
+ Attributes of this object get passed as commands to api.php, and can be
+ get or set using the dict interface. All attributes must be strings
+ (unicode). Attributes supplied without values are passed to the API as
+ keys.
+
+ @param site: The Site to which the request will be submitted. If not
+ supplied, uses the user's configured default Site.
+ @param format: (optional) Defaults to "json"
- def request(self, **params):
- if not params.has_key('format'): #Most probably, we want the JSON format
- params['format'] = 'json'
- return http.HTTP(None).POST('/w/api.php',params) #TODO: Use site's HTTP object instead
+ Example:
- def query(self, **params):
- if not params.has_key('action'):
- params['action'] = 'query'
- return self.request(**params)
+ >>> r = Request(site=mysite, action="query", meta="userinfo")
+ >>> # This is equivalent to
+ >>> # http://[path]/api.php?action=query&meta=userinfo&format=json
+ >>> # change a parameter
+ >>> r['meta'] = "userinfo|siteinfo"
+ >>> # add a new parameter
+ >>> r['siprop'] = "namespaces"
+ >>> r.params
+ {'action': 'query', 'meta': 'userinfo|siteinfo', 'siprop': 'namespaces',
+ 'format': 'json'}
+ >>> data = r.submit()
+ >>> type(data)
+ <type 'dict'>
+
+ """
+ def __init__(self, *args, **kwargs):
+ if "site" in kwargs:
+ self.site = kwargs["site"]
+ del kwargs["site"]
+ # else use defaultSite() ... when written
+ self.params = {}
+ if not "format" in kwargs:
+ self.params["format"] = "json"
+ self.update(*args, **kwargs)
- def query_response(self, **params):
- """Submit a query and parse the response, returning a dict or None."""
- if params.has_key('format') and params['format'] != 'json':
- raise TypeError("Query format '%s' cannot be parsed." % params['format'])
+ # implement dict interface
+ def __getitem__(self, key):
+ return self.params[key]
+
+ def __setitem__(self, key, value):
+ self.params[key] = value
+
+ def __delitem__(self, key):
+ del self.params[key]
+
+ def keys(self):
+ return self.params.keys()
+
+ def __contains__(self, key):
+ return self.params.__contains__(key)
+
+ def __iter__(self):
+ return self.params.__iter__()
+
+ def iteritems(self):
+ return self.params.iteritems()
+
+ def update(self, *args, **kwargs):
+ """Update the request parameters"""
+ self.params.update(kwargs)
+ for arg in args:
+ if arg not in self.params:
+ self.params[arg] = ""
+
+ def submit(self):
+ """Submit a query and parse the response.
+
+ @return: The data retrieved from api.php (a dict)
+
+ """
+ if self.params['format'] != 'json':
+ raise TypeError("Query format '%s' cannot be parsed."
+ % self.params['format'])
+ uri = self.site.script_path() + "api.php"
+ params = urllib.urlencode(self.params)
while True:
- httpcode, rawdata = self.query(**params)
- if httpcode != 200:
- raise APIError(
- {'code': httpcode,
- 'info': "HTTP error code received.",
- 'data': rawdata})
+ # TODO wait on errors
+ # TODO catch http errors
+ if self.params.get("action", "") in ("login",):
+ rawdata = http.request(self.site, uri, method="POST",
+ headers={'Content-Type':
+ 'application/x-www-form-urlencoded'},
+ body=params)
+ return rawdata
+ else:
+ uri = uri + "?" + params
+ rawdata = http.request(self.site, uri)
if rawdata.startswith(u"unknown_action"):
e = {'code': data[:14], 'info': data[16:]}
raise APIError(e)
@@ -63,27 +133,31 @@
warnings.warn(
"Non-JSON response received from server %s; the server may be down."
% self.site)
+ print rawdata
continue
+ if not result:
+ return {}
if type(result) is dict:
- if result.has_key("error"):
+ if "error" in result:
+ if "code" in result["error"]:
+ code = result["error"]["code"]
+ del result["error"]["code"]
+ else:
+ code = "Unknown"
+ if "info" in result["error"]:
+ info = result["error"]["info"]
+ del result["error"]["info"]
+ else:
+ info = None
# raise error
- raise APIError(result['error'])
- if result.has_key("query"):
- return result
- raise APIError(
- {'code': "Unknown API error",
- 'info': "Response received with no 'query' key.",
- 'data': result})
- if type(result) is list:
- if result == []:
- return None
- raise APIError(
- {'code': "Unknown API error",
- 'info': "Query returned a list instead of a dict.",
- 'data': result})
- raise APIError(
- {'code': "Unknown API error",
- 'info': "Unable to process query response of type %s."
- % type(result),
- 'data': result})
+ raise APIError(code, info, **result["error"])
+ return result
+ raise APIError("Unknown",
+ "Unable to process query response of type %s."
+ % type(result),
+ {'data': result})
+
+if __name__ == "__main__":
+ from pywikibot.tests.dummy import TestSite as Site
+ mysite = Site("en.wikipedia.org")
Modified: branches/rewrite/pywikibot/data/http.py
===================================================================
--- branches/rewrite/pywikibot/data/http.py 2008-01-18 20:12:16 UTC (rev 4913)
+++ branches/rewrite/pywikibot/data/http.py 2008-01-18 20:55:39 UTC (rev 4914)
@@ -1,4 +1,4 @@
-# -*- coding: utf-8 -*-
+# -*- coding: utf-8 -*-
"""
Basic HTTP access interface.
@@ -58,16 +58,17 @@
def request(site, uri, *args, **kwargs):
""" @param site The Site to connect to
- All other parameters are the same as `Http.request`, but the uri is relative
- Returns: The recieved data.
+ All other parameters are the same as L{httplib2.Http.request}, but
+ the uri is relative
+ @return The received data (a unicode string).
"""
- baseuri = site #.baseuri(), etc
+ baseuri = "%s://%s/" % (site.protocol(), site.hostname())
uri = urlparse.urljoin(baseuri, uri)
-
+
request = threadedhttp.HttpRequest(uri, *args, **kwargs)
http_queue.put(request)
request.lock.acquire()
#do some error correcting stuff
- return request.data[1]
\ No newline at end of file
+ return request.data[1]
Added: branches/rewrite/pywikibot/tests/__init__.py
===================================================================
Added: branches/rewrite/pywikibot/tests/api_tests.py
===================================================================
--- branches/rewrite/pywikibot/tests/api_tests.py (rev 0)
+++ branches/rewrite/pywikibot/tests/api_tests.py 2008-01-18 20:55:39 UTC (rev 4914)
@@ -0,0 +1,26 @@
+import unittest
+import pywikibot.data.api as api
+
+from pywikibot.tests.dummy import TestSite as Site
+mysite = Site('en.wikipedia.org')
+
+
+class TestApiFunctions(unittest.TestCase):
+
+ def testObjectCreation(self):
+ """Test that api.Request() creates an object with desired attributes"""
+ req = api.Request(mysite, "foo", bar="test")
+ self.assert_(req)
+ self.assertEqual(req.site, mysite)
+ self.assert_("foo" in req.params)
+ self.assertEqual(req["format"], "json")
+ self.assertEqual(req["bar"], "test")
+ # test item assignment
+ req["one"] = "1"
+ self.assertEqual(req.params['one'], "1")
+
+if __name__ == '__main__':
+ try:
+ unittest.main()
+ except SystemExit:
+ pass
Modified: branches/rewrite/pywikibot/tests/dummy.py
===================================================================
--- branches/rewrite/pywikibot/tests/dummy.py 2008-01-18 20:12:16 UTC (rev 4913)
+++ branches/rewrite/pywikibot/tests/dummy.py 2008-01-18 20:55:39 UTC (rev 4914)
@@ -11,13 +11,16 @@
class TestSite(object):
"""Mimic a Site object."""
- def __init__(self, hostname, protocol="http"):
+ def __init__(self, hostname, protocol="http", path="w/"):
self._hostname = hostname
self._protocol = protocol
+ self._path = path
def protocol(self):
return self._protocol
def hostname(self):
return self._hostname
+ def script_path(self):
+ return self._path
def cookies(self, sysop=False):
if hasattr(self, "_cookies"):
return self._cookies
More information about the Pywikipedia-l
mailing list