99284c74a5994c651e9a549fe8feea829db12890
[python-simplemediawiki.debian] / simplemediawiki.py
1 # python-simplemediawiki - Extremely low-level wrapper to the MediaWiki API
2 # Copyright (C) 2010 Red Hat, Inc.
3 #
4 # This library is free software; you can redistribute it and/or modify it under
5 # the terms of the GNU Lesser General Public License as published by the Free
6 # Software Foundation; either version 2.1 of the License, or (at your option)
7 # any later version.
8 #
9 # This library is distributed in the hope that it will be useful, but WITHOUT
10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
12 # details.
13 #
14 # You should have received a copy of the GNU General Public License along with
15 # this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 """
18 simplemediawiki is an extremely low-level wrapper to the MediaWiki API. It
19 automatically handles cookies and gzip compression so that you can make basic
20 calls to the API in the easiest way possible. It also provides a few functions
21 to make day-to-day API access easier.
22
23 To use this module, instantiate a MediaWiki object, passing it the URL of
24 api.php for the wiki you want to work with. Calls go through MediaWiki.call().
25 A generic login wrapper as well as functions to determine limits and get a list
26 of namespaces are provided for your convenience.
27
28 >>> from simplemediawiki import MediaWiki
29 >>> wiki = MediaWiki('http://en.wikipedia.org/w/api.php')
30 >>> wiki.call({'action': 'query', 'prop': 'revisions', 'titles': 'Main Page'})
31 {u'query': {u'pages': {...}}}
32 """
33
34 import cookielib
35 import gzip
36 from iso8601 import iso8601
37 import json
38 from StringIO import StringIO
39 import urllib
40 import urllib2
41
42
43 class MediaWiki():
44     """
45     Class to represent a MediaWiki installation with an enabled API.
46
47     api_url: URL to api.php (usually similar to http://example.com/w/api.php)
48     """
49     _high_limits = None
50     _namespaces = None
51     _psuedo_namespaces = None
52
53     def __init__(self, api_url, cookie_file=None):
54         self._api_url = api_url
55         if cookie_file:
56             self._cj = cookielib.MozillaCookieJar(cookie_file)
57             try:
58                 self._cj.load()
59             except IOError:
60                 self._cj.save()
61                 self._cj.load()
62         else:
63             self._cj = cookielib.CookieJar()
64         self._opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self._cj))
65
66     def call(self, params):
67         """
68         Make a call to the wiki. Returns a dictionary that represents the JSON
69         returned by the API.
70         """
71         params['format'] = 'json'
72         request = urllib2.Request(self._api_url, urllib.urlencode(params))
73         request.add_header('Accept-encoding', 'gzip')
74         response = self._opener.open(request)
75         if isinstance(self._cj, cookielib.MozillaCookieJar):
76             self._cj.save()
77         if response.headers.get('Content-Encoding') == 'gzip':
78             compressed = StringIO(response.read())
79             gzipper = gzip.GzipFile(fileobj=compressed)
80             data = gzipper.read()
81         else:
82             data = response.read()
83         return json.loads(data)
84
85     def login(self, user, passwd, token=None):
86         """
87         Convenience function for logging into the wiki. It should never be
88         necessary to provide a token argument; it is part of the login process
89         since MediaWiki 1.15.3 (see MediaWiki bug 23076).
90         """
91         data = {'action': 'login',
92                 'lgname': user,
93                 'lgpassword': passwd}
94         if token:
95             data['lgtoken'] = token
96         result = self.call(data)
97         if result['login']['result'] == 'Success':
98             return True
99         elif result['login']['result'] == 'NeedToken' and not token:
100             return self.login(user, passwd, result['login']['token'])
101         else:
102             return False
103
104     def limits(self, low, high):
105         """
106         Convenience function for determining appropriate limits in the API. If
107         the logged in user has the "apihighlimits" right, it will return the
108         high argument; otherwise it will return the low argument.
109         """
110         if self._high_limits == None:
111             result = self.call({'action': 'query',
112                                 'meta': 'userinfo',
113                                 'uiprop': 'rights'})
114             self._high_limits = 'apihighlimits' in \
115                     result['query']['userinfo']['rights']
116         if self._high_limits:
117             return high
118         else:
119             return low
120
121     def namespaces(self, psuedo=True):
122         """
123         Fetches a list of namespaces for this wiki.
124         """
125         if self._namespaces == None:
126             result = self.call({'action': 'query',
127                                 'meta': 'siteinfo',
128                                 'siprop': 'namespaces'})
129             self._namespaces = {}
130             self._psuedo_namespaces = {}
131             for nsid in result['query']['namespaces']:
132                 if int(nsid) >= 0:
133                     self._namespaces[int(nsid)] = \
134                             result['query']['namespaces'][nsid]['*']
135                 else:
136                     self._psuedo_namespaces[int(nsid)] = \
137                             result['query']['namespaces'][nsid]['*']
138         if psuedo:
139             retval = {}
140             retval.update(self._namespaces)
141             retval.update(self._psuedo_namespaces)
142             return retval
143         else:
144             return self._namespaces
145
146     @staticmethod
147     def parse_date(date):
148         """
149         Converts dates provided by the MediaWiki API into datetime.datetime
150         objects.
151         """
152         return iso8601.parse_date(date)
153
154
155 __author__ = 'Ian Weller <ian@ianweller.org>'
156 __version__ = '1.0'

Benjamin Mako Hill || Want to submit a patch?