1 # python-simplemediawiki - Extremely low-level wrapper to the MediaWiki API
2 # Copyright (C) 2010 Red Hat, Inc.
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)
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
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/>.
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.
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.
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': {...}}}
36 from iso8601 import iso8601
38 from StringIO import StringIO
45 Class to represent a MediaWiki installation with an enabled API.
47 api_url: URL to api.php (usually similar to http://example.com/w/api.php)
51 _psuedo_namespaces = None
52 _mediawiki_version = None
54 def __init__(self, api_url, cookie_file=None, user_agent=DEFAULT_UA):
55 self._api_url = api_url
57 self._cj = cookielib.MozillaCookieJar(cookie_file)
64 self._cj = cookielib.CookieJar()
65 self._opener = urllib2.build_opener(
66 urllib2.HTTPCookieProcessor(self._cj)
68 self._opener.addheaders = [('User-agent', user_agent)]
70 def _fetch_http(self, url, params):
71 request = urllib2.Request(url, urllib.urlencode(params))
72 request.add_header('Accept-encoding', 'gzip')
73 response = self._opener.open(request)
74 if isinstance(self._cj, cookielib.MozillaCookieJar):
76 if response.headers.get('Content-Encoding') == 'gzip':
77 compressed = StringIO(response.read())
78 gzipper = gzip.GzipFile(fileobj=compressed)
81 data = response.read()
84 def call(self, params):
86 Make a call to the wiki. Returns a dictionary that represents the JSON
89 params['format'] = 'json'
90 return json.loads(self._fetch_http(self._api_url, params))
92 def normalize_api_url(self):
94 This function checks the given URL for a correct API endpoint and
95 returns that URL, while also helpfully setting this object's API URL to
96 it. If it can't magically conjure an API endpoint, it returns False.
98 data, data_json = self._normalize_api_url_tester(self._api_url)
102 # if there's an index.php in the URL, we might find the API
103 if 'index.php' in self._api_url:
104 test_api_url = self._api_url.split('index.php')[0] + 'api.php'
106 test_data, test_data_json = \
107 self._normalize_api_url_tester(test_api_url)
108 print (test_data, test_data_json)
110 self._api_url = test_api_url
114 def _normalize_api_url_tester(self, api_url):
115 data = self._fetch_http(api_url, {'action': 'query',
120 data_json = json.loads(data)
121 # may as well set the version
123 version_string = data_json['query']['general']['generator']
124 self._mediawiki_version = version_string.split(' ', 1)[1]
127 return (data, data_json)
131 def login(self, user, passwd, token=None):
133 Convenience function for logging into the wiki. It should never be
134 necessary to provide a token argument; it is part of the login process
135 since MediaWiki 1.15.3 (see MediaWiki bug 23076).
137 data = {'action': 'login',
139 'lgpassword': passwd}
141 data['lgtoken'] = token
142 result = self.call(data)
143 if result['login']['result'] == 'Success':
144 self._high_limits = None
146 elif result['login']['result'] == 'NeedToken' and not token:
147 return self.login(user, passwd, result['login']['token'])
153 Conveinence function for logging out of the wiki.
155 data = {'action': 'logout'}
157 self._high_limits = None
160 def limits(self, low, high):
162 Convenience function for determining appropriate limits in the API. If
163 the logged in user has the "apihighlimits" right, it will return the
164 high argument; otherwise it will return the low argument.
166 if self._high_limits == None:
167 result = self.call({'action': 'query',
170 self._high_limits = 'apihighlimits' in \
171 result['query']['userinfo']['rights']
172 if self._high_limits:
177 def namespaces(self, psuedo=True):
179 Fetches a list of namespaces for this wiki.
181 if self._namespaces == None:
182 result = self.call({'action': 'query',
184 'siprop': 'namespaces'})
185 self._namespaces = {}
186 self._psuedo_namespaces = {}
187 for nsid in result['query']['namespaces']:
189 self._namespaces[int(nsid)] = \
190 result['query']['namespaces'][nsid]['*']
192 self._psuedo_namespaces[int(nsid)] = \
193 result['query']['namespaces'][nsid]['*']
196 retval.update(self._namespaces)
197 retval.update(self._psuedo_namespaces)
200 return self._namespaces
203 def parse_date(date):
205 Converts dates provided by the MediaWiki API into datetime.datetime
208 return iso8601.parse_date(date)
211 __author__ = 'Ian Weller <ian@ianweller.org>'
212 __version__ = '1.0.1'
213 DEFAULT_UA = 'python-simplemediawiki/%s ' + \
214 '+https://github.com/ianweller/python-simplemediawiki' \