From 03f1ddc6657e93ed466858761c0ae62c6c64c072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Michael=20O=2E=20Hegg=C3=B8?= <danmichaelo@gmail.com> Date: Sun, 16 Jun 2013 23:09:01 +0200 Subject: [PATCH] PEP8: Fix W291 trailing whitespace --- mwclient/__init__.py | 4 +- mwclient/client.py | 203 ++++++++++++++++++------------------ mwclient/compatibility.py | 26 +++-- mwclient/errors.py | 7 +- mwclient/ex.py | 32 +++--- mwclient/http.py | 73 +++++++------ mwclient/listing.py | 61 ++++++----- mwclient/page.py | 131 ++++++++++++----------- mwclient/page_nowriteapi.py | 45 ++++---- mwclient/upload.py | 42 ++++---- 10 files changed, 307 insertions(+), 317 deletions(-) diff --git a/mwclient/__init__.py b/mwclient/__init__.py index afc17bf..0289838 100644 --- a/mwclient/__init__.py +++ b/mwclient/__init__.py @@ -9,10 +9,10 @@ copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND diff --git a/mwclient/client.py b/mwclient/client.py index 7df3f70..c1906af 100644 --- a/mwclient/client.py +++ b/mwclient/client.py @@ -29,7 +29,7 @@ def parse_timestamp(t): if t == '0000-00-00T00:00:00Z': return (0, 0, 0, 0, 0, 0, 0, 0) return time.strptime(t, '%Y-%m-%dT%H:%M:%SZ') - + class WaitToken(object): def __init__(self): self.id = '%x' % random.randint(0, sys.maxint) @@ -38,7 +38,7 @@ class WaitToken(object): class Site(object): api_limit = 500 - def __init__(self, host, path='/w/', ext='.php', pool=None, retry_timeout=30, + def __init__(self, host, path='/w/', ext='.php', pool=None, retry_timeout=30, max_retries=25, wait_callback=lambda *x: None, clients_useragent=None, max_lag=3, compress=True, force_login=True, do_init=True): # Setup member variables @@ -47,16 +47,16 @@ class Site(object): self.ext = ext self.credentials = None self.compress = compress - + self.retry_timeout = retry_timeout self.max_retries = max_retries self.wait_callback = wait_callback self.max_lag = str(max_lag) self.force_login = force_login - + # The token string => token object mapping self.wait_tokens = weakref.WeakKeyDictionary() - + # Site properties self.blocked = False # Whether current user is blocked self.hasmsg = False # Whether current user has new messages @@ -64,47 +64,47 @@ class Site(object): self.rights = [] # Rights current user has self.tokens = {} # Edit tokens of the current user self.version = None - + self.namespaces = self.default_namespaces self.writeapi = False - + # Setup connection if pool is None: self.connection = http.HTTPPool(clients_useragent) else: self.connection = pool - + # Page generators self.pages = listing.PageList(self) self.categories = listing.PageList(self, namespace=14) self.images = listing.PageList(self, namespace=6) - + # Compat page generators self.Pages = self.pages self.Categories = self.categories self.Images = self.images - + # Initialization status self.initialized = False - + if do_init: try: self.site_init() except errors.APIError, e: # Private wiki, do init after login - if e[0] not in (u'unknown_action', u'readapidenied'): + if e[0] not in (u'unknown_action', u'readapidenied'): raise - - + + def site_init(self): - meta = self.api('query', meta='siteinfo|userinfo', + meta = self.api('query', meta='siteinfo|userinfo', siprop='general|namespaces', uiprop='groups|rights') - + # Extract site info self.site = meta['query']['general'] self.namespaces = dict(((i['id'], i.get('*', '')) for i in meta['query']['namespaces'].itervalues())) self.writeapi = 'writeapi' in self.site - + # Determine version if self.site['generator'].startswith('MediaWiki '): version = self.site['generator'][10:].split('.') @@ -119,30 +119,30 @@ class Site(object): else: return (int(s[:i]), ) self.version = sum((split_num(s) for s in version), ()) - + if len(self.version) < 2: raise errors.MediaWikiVersionError('Unknown MediaWiki %s' % '.'.join(version)) else: raise errors.MediaWikiVersionError('Unknown generator %s' % self.site['generator']) # Require 1.11 until some compatibility issues are fixed self.require(1, 11) - - # User info + + # User info userinfo = compatibility.userinfo(meta, self.require(1, 12, raise_error=False)) self.username = userinfo['name'] self.groups = userinfo.get('groups', []) self.rights = userinfo.get('rights', []) self.initialized = True - - - default_namespaces = {0: u'', 1: u'Talk', 2: u'User', 3: u'User talk', 4: u'Project', 5: u'Project talk', - 6: u'Image', 7: u'Image talk', 8: u'MediaWiki', 9: u'MediaWiki talk', 10: u'Template', 11: u'Template talk', + + + default_namespaces = {0: u'', 1: u'Talk', 2: u'User', 3: u'User talk', 4: u'Project', 5: u'Project talk', + 6: u'Image', 7: u'Image talk', 8: u'MediaWiki', 9: u'MediaWiki talk', 10: u'Template', 11: u'Template talk', 12: u'Help', 13: u'Help talk', 14: u'Category', 15: u'Category talk', -1: u'Special', -2: u'Media'} - + def __repr__(self): return "<Site object '%s%s'>" % (self.host, self.path) - - + + def api(self, action, *args, **kwargs): """ An API call. Handles errors and returns dict object. """ kwargs.update(args) @@ -155,7 +155,7 @@ class Site(object): kwargs['uiprop'] += '|blockinfo|hasmsg' else: kwargs['uiprop'] = 'blockinfo|hasmsg' - + token = self.wait_token() while True: info = self.raw_api(action, **kwargs) @@ -163,12 +163,12 @@ class Site(object): res = self.handle_api_result(info, token=token) if res: return info - - + + def handle_api_result(self, info, kwargs=None, token=None): if token is None: token = self.wait_token() - + try: userinfo = compatibility.userinfo(info, self.require(1, 12, raise_error=None)) except KeyError: @@ -189,7 +189,7 @@ class Site(object): raise errors.APIError(info['error']['code'], info['error']['info'], kwargs) return True - + @staticmethod def _to_str(data): if type(data) is unicode: @@ -200,10 +200,10 @@ class Site(object): kwargs.update(args) qs = urllib.urlencode([(k, Site._to_str(v)) for k, v in kwargs.iteritems() if k != 'wpEditToken']) - if 'wpEditToken' in kwargs: + if 'wpEditToken' in kwargs: qs += '&wpEditToken=' + urllib.quote(Site._to_str(kwargs['wpEditToken'])) return qs - + def raw_call(self, script, data): url = self.path + script + self.ext headers = {} @@ -211,18 +211,18 @@ class Site(object): headers['Content-Type'] = 'application/x-www-form-urlencoded' if self.compress and gzip: headers['Accept-Encoding'] = 'gzip' - + token = self.wait_token((script, data)) while True: try: - stream = self.connection.post(self.host, + stream = self.connection.post(self.host, url, data=data, headers=headers) if stream.getheader('Content-Encoding') == 'gzip': # BAD. seekable_stream = StringIO(stream.read()) stream = gzip.GzipFile(fileobj=seekable_stream) return stream - + except errors.HTTPStatusError, e: if e[0] == 503 and e[1].getheader('X-Database-Lag'): self.wait(token, int(e[1].getheader('Retry-After'))) @@ -236,7 +236,7 @@ class Site(object): self.wait(token) except ValueError: self.wait(token) - + def raw_api(self, action, *args, **kwargs): """Sends a call to the API.""" kwargs['action'] = action @@ -249,14 +249,14 @@ class Site(object): if json_data.startswith('MediaWiki API is not enabled for this site.'): raise errors.APIDisabledError raise - + def raw_index(self, action, *args, **kwargs): """Sends a call to index.php rather than the API.""" kwargs['action'] = action kwargs['maxlag'] = self.max_lag data = self._query_string(*args, **kwargs) - return self.raw_call('index', data).read().decode('utf-8', 'ignore') - + return self.raw_call('index', data).read().decode('utf-8', 'ignore') + def wait_token(self, args=None): token = WaitToken() self.wait_tokens[token] = (0, args) @@ -268,7 +268,7 @@ class Site(object): if retry > self.max_retries and self.max_retries != -1: raise errors.MaximumRetriesExceeded(self, token, args) self.wait_callback(self, token, retry, args) - + timeout = self.retry_timeout * retry if timeout < min_wait: timeout = min_wait time.sleep(timeout) @@ -276,20 +276,20 @@ class Site(object): def require(self, major, minor, revision=None, raise_error=True): if self.version is None: - if raise_error is None: return + if raise_error is None: return raise RuntimeError('Site %s has not yet been initialized' % repr(self)) - + if revision is None: if self.version[:2] >= (major, minor): return True elif raise_error: - raise errors.MediaWikiVersionError('Requires version %s.%s, current version is %s.%s' + raise errors.MediaWikiVersionError('Requires version %s.%s, current version is %s.%s' % ((major, minor) + self.version[:2])) else: return False else: raise NotImplementedError - + # Actions def email(self, user, text, subject, cc=False): @@ -314,14 +314,14 @@ class Site(object): def login(self, username=None, password=None, cookies=None, domain=None): """Login to the wiki.""" if self.initialized: self.require(1, 10) - - if username and password: + + if username and password: self.credentials = (username, password, domain) if cookies: if self.host not in self.conn.cookies: self.conn.cookies[self.host] = http.CookieJar() self.conn.cookies[self.host].update(cookies) - + if self.credentials: wait_token = self.wait_token() kwargs = { @@ -340,8 +340,8 @@ class Site(object): self.wait(wait_token, login['login'].get('wait', 5)) else: raise errors.LoginError(self, login['login']) - - if self.initialized: + + if self.initialized: info = self.api('query', meta='userinfo', uiprop='groups|rights') userinfo = compatibility.userinfo(info, self.require(1, 12, raise_error=False)) self.username = userinfo['name'] @@ -356,25 +356,25 @@ class Site(object): url=None, session_key=None, comment=None): """Upload a file to the wiki.""" if self.version[:2] < (1, 16): - return compatibility.old_upload(self, file=file, filename=filename, - description=description, ignore=ignore, + return compatibility.old_upload(self, file=file, filename=filename, + description=description, ignore=ignore, file_size=file_size) - + image = self.Images[filename] if not image.can('upload'): raise errors.InsufficientPermission(filename) - - + + predata = {} - + if comment is None: predata['comment'] = description else: predata['comment'] = comment predata['text'] = description - if ignore: + if ignore: predata['ignorewarnings'] = 'true' predata['token'] = image.get_token('edit') predata['action'] = 'upload' @@ -383,11 +383,11 @@ class Site(object): if url: predata['url'] = url if session_key: - predata['session_key'] = session_key - + predata['session_key'] = session_key + if file is None: postdata = self._query_string(predata) - else: + else: if type(file) is str: file_size = len(file) file = StringIO(file) @@ -395,9 +395,9 @@ class Site(object): file.seek(0, 2) file_size = file.tell() file.seek(0, 0) - + postdata = upload.UploadFile('file', filename, file_size, file, predata) - + wait_token = self.wait_token() while True: try: @@ -417,7 +417,7 @@ class Site(object): except errors.HTTPError: self.wait(wait_token) file.seek(0, 0) - + def parse(self, text=None, title=None, page=None): kwargs = {} if text is not None: kwargs['text'] = text @@ -425,23 +425,23 @@ class Site(object): if page is not None: kwargs['page'] = page result = self.api('parse', **kwargs) return result['parse'] - + # def block: requires 1.12 # def unblock: requires 1.12 # def patrol: requires 1.14 # def import: requires 1.15 - + # Lists def allpages(self, start=None, prefix=None, namespace='0', filterredir='all', minsize=None, maxsize=None, prtype=None, prlevel=None, limit=None, dir='ascending', filterlanglinks='all', generator=True): """Retrieve all pages on the wiki as a generator.""" self.require(1, 9) - + pfx = listing.List.get_prefix('ap', generator) kwargs = dict(listing.List.generate_kwargs(pfx, ('from', start), prefix=prefix, minsize=minsize, maxsize=maxsize, prtype=prtype, prlevel=prlevel, - namespace=namespace, filterredir=filterredir, dir=dir, + namespace=namespace, filterredir=filterredir, dir=dir, filterlanglinks=filterlanglinks)) return listing.List.get_list(generator)(self, 'allpages', 'ap', limit=limit, return_values='title', **kwargs) # def allimages(self): requires 1.12 @@ -451,7 +451,7 @@ class Site(object): namespace='0', limit=None, generator=True): """Retrieve a list of all links on the wiki as a generator.""" self.require(1, 11) - + pfx = listing.List.get_prefix('al', generator) kwargs = dict(listing.List.generate_kwargs(pfx, ('from', start), prefix=prefix, prop=prop, namespace=namespace)) @@ -461,20 +461,20 @@ class Site(object): def allcategories(self, start=None, prefix=None, dir='ascending', limit=None, generator=True): """Retrieve all categories on the wiki as a generator.""" self.require(1, 12) - + pfx = listing.List.get_prefix('ac', generator) kwargs = dict(listing.List.generate_kwargs(pfx, ('from', start), prefix=prefix, dir=dir)) return listing.List.get_list(generator)(self, 'allcategories', 'ac', limit=limit, **kwargs) - + def allusers(self, start=None, prefix=None, group=None, prop=None, limit=None): """Retrieve all users on the wiki as a generator.""" self.require(1, 11) - + kwargs = dict(listing.List.generate_kwargs('au', ('from', start), prefix=prefix, group=group, prop=prop)) return listing.List(self, 'allusers', 'au', limit=limit, **kwargs) - def blocks(self, start=None, end=None, dir='older', ids=None, users=None, limit=None, + def blocks(self, start=None, end=None, dir='older', ids=None, users=None, limit=None, prop='id|user|by|timestamp|expiry|reason|flags'): """Retrieve blocks as a generator. @@ -487,20 +487,20 @@ class Site(object): - allowusertalk: key is present (empty string) if the user is allowed to edit their user talk page - by: the administrator who blocked the user - nocreate: key is present (empty string) if the user's ability to create accounts has been disabled. - + """ self.require(1, 12) # TODO: Fix. Fix what? - kwargs = dict(listing.List.generate_kwargs('bk', start=start, end=end, dir=dir, + kwargs = dict(listing.List.generate_kwargs('bk', start=start, end=end, dir=dir, users=users, prop=prop)) return listing.List(self, 'blocks', 'bk', limit=limit, **kwargs) - def deletedrevisions(self, start=None, end=None, dir='older', namespace=None, + def deletedrevisions(self, start=None, end=None, dir='older', namespace=None, limit=None, prop='user|comment'): # TODO: Fix self.require(1, 12) - + kwargs = dict(listing.List.generate_kwargs('dr', start=start, end=end, dir=dir, namespace=namespace, prop=prop)) return listing.List(self, 'deletedrevs', 'dr', limit=limit, **kwargs) @@ -521,18 +521,18 @@ class Site(object): - ns: namespace of the wiki page - pageid: the ID of the wiki page - title: the page title. - + """ self.require(1, 11) - - kwargs = dict(listing.List.generate_kwargs('eu', query=query, prop=prop, + + kwargs = dict(listing.List.generate_kwargs('eu', query=query, prop=prop, protocol=protocol, namespace=namespace)) - return listing.List(self, 'exturlusage', 'eu', limit=limit, **kwargs) + return listing.List(self, 'exturlusage', 'eu', limit=limit, **kwargs) - def logevents(self, type=None, prop=None, start=None, end=None, + def logevents(self, type=None, prop=None, start=None, end=None, dir='older', user=None, title=None, limit=None, action=None): self.require(1, 10) - + kwargs = dict(listing.List.generate_kwargs('le', prop=prop, type=type, start=start, end=end, dir=dir, user=user, title=title, action=action)) return listing.List(self, 'logevents', 'le', limit=limit, **kwargs) @@ -540,67 +540,66 @@ class Site(object): # def protectedtitles requires 1.15 def random(self, namespace, limit=20): """Retrieves a generator of random page from a particular namespace. - + limit specifies the number of random articles retrieved. namespace is a namespace identifier integer. - + Generator contains dictionary with namespace, page ID and title. - + """ self.require(1, 12) - + kwargs = dict(listing.List.generate_kwargs('rn', namespace=namespace)) return listing.List(self, 'random', 'rn', limit=limit, **kwargs) - def recentchanges(self, start=None, end=None, dir='older', namespace=None, + def recentchanges(self, start=None, end=None, dir='older', namespace=None, prop=None, show=None, limit=None, type=None): self.require(1, 9) - + kwargs = dict(listing.List.generate_kwargs('rc', start=start, end=end, dir=dir, namespace=namespace, prop=prop, show=show, type=type)) return listing.List(self, 'recentchanges', 'rc', limit=limit, **kwargs) def search(self, search, namespace='0', what='title', redirects=False, limit=None): self.require(1, 11) - + kwargs = dict(listing.List.generate_kwargs('sr', search=search, namespace=namespace, what=what)) if redirects: kwargs['srredirects'] = '1' return listing.List(self, 'search', 'sr', limit=limit, **kwargs) - def usercontributions(self, user, start=None, end=None, dir='older', namespace=None, + def usercontributions(self, user, start=None, end=None, dir='older', namespace=None, prop=None, show=None, limit=None): self.require(1, 9) - - kwargs = dict(listing.List.generate_kwargs('uc', user=user, start=start, end=end, + + kwargs = dict(listing.List.generate_kwargs('uc', user=user, start=start, end=end, dir=dir, namespace=namespace, prop=prop, show=show)) return listing.List(self, 'usercontribs', 'uc', limit=limit, **kwargs) def users(self, users, prop='blockinfo|groups|editcount'): self.require(1, 12) - + return listing.List(self, 'users', 'us', ususers='|'.join(users), usprop=prop) - + def watchlist(self, allrev=False, start=None, end=None, namespace=None, dir='older', prop=None, show=None, limit=None): self.require(1, 9) - - kwargs = dict(listing.List.generate_kwargs('wl', start=start, end=end, + + kwargs = dict(listing.List.generate_kwargs('wl', start=start, end=end, namespace=namespace, dir=dir, prop=prop, show=show)) if allrev: kwargs['wlallrev'] = '1' return listing.List(self, 'watchlist', 'wl', limit=limit, **kwargs) - + def expandtemplates(self, text, title=None, generatexml=False): """Takes wikitext (text) and expands templates.""" self.require(1, 11) - + kwargs = {} if title is None: kwargs['title'] = title if generatexml: kwargs['generatexml'] = '1' - + result = self.api('expandtemplates', text=text, **kwargs) - + if generatexml: return result['expandtemplates']['*'], result['parsetree']['*'] else: return result['expandtemplates']['*'] - diff --git a/mwclient/compatibility.py b/mwclient/compatibility.py index 65b5b3e..f2d8794 100644 --- a/mwclient/compatibility.py +++ b/mwclient/compatibility.py @@ -1,19 +1,19 @@ import upload, errors def title(prefix, new_format): - if new_format: + if new_format: return prefix + 'title' else: return 'titles' - + def userinfo(data, new_format=None): if new_format is None: # Unknown version; trying to guess - if 'userinfo' in data: + if 'userinfo' in data: return data['userinfo'] elif 'userinfo' in data.get('query', ()): return data['query']['userinfo'] - else: + else: return {} elif new_format: return data['query']['userinfo'] @@ -27,13 +27,13 @@ def iiprop(version): return 'timestamp|user|comment|url|size|sha1|metadata' else: return 'timestamp|user|comment|url|size|sha1' - + def cmtitle(page, new_format, prefix=''): if new_format: return prefix + 'title', page.name else: return prefix + 'category', page.strip_namespace(page.name) - + def protectright(version): if version[:2] >= (1, 13): return 'editprotected' @@ -41,13 +41,13 @@ def protectright(version): return 'protect' from cStringIO import StringIO -def old_upload(self, file, filename, description, license='', ignore=False, file_size=None): +def old_upload(self, file, filename, description, license='', ignore=False, file_size=None): image = self.Images[filename] if not image.can('upload'): raise errors.InsufficientPermission(filename) if image.exists and not ignore: raise errors.FileExists(filename) - + if type(file) is str: file_size = len(file) file = StringIO(file) @@ -55,7 +55,7 @@ def old_upload(self, file, filename, description, license='', ignore=False, file file.seek(0, 2) file_size = file.tell() file.seek(0, 0) - + predata = {} # Do this thing later so that an incomplete upload won't work # predata['wpDestFile'] = filename @@ -66,14 +66,14 @@ def old_upload(self, file, filename, description, license='', ignore=False, file predata['wpSourceType'] = 'file' predata['wpDestFile'] = filename predata['wpEditToken'] = image.get_token('edit') - + postdata = upload.UploadFile('wpUploadFile', filename, file_size, file, predata) - + wait_token = self.wait_token() while True: try: self.connection.post(self.host, - self.path + 'index.php?title=Special:Upload&maxlag=' + self.path + 'index.php?title=Special:Upload&maxlag=' + self.max_lag, data=postdata).read() except errors.HTTPStatusError, e: if e[0] == 503 and e[1].getheader('X-Database-Lag'): @@ -87,5 +87,3 @@ def old_upload(self, file, filename, description, license='', ignore=False, file else: return file.seek(0, 0) - - \ No newline at end of file diff --git a/mwclient/errors.py b/mwclient/errors.py index 59e1b44..0a17132 100644 --- a/mwclient/errors.py +++ b/mwclient/errors.py @@ -16,13 +16,13 @@ class HTTPRedirectError(HTTPError): class MaximumRetriesExceeded(MwClientError): pass - + class APIError(MwClientError): def __init__(self, code, info, kwargs): self.code = code self.info = info MwClientError.__init__(self, code, info, kwargs) - + class InsufficientPermission(MwClientError): pass class UserBlocked(InsufficientPermission): @@ -34,7 +34,7 @@ class ProtectedPageError(EditError, InsufficientPermission): pass class FileExists(EditError): pass - + class LoginError(MwClientError): pass @@ -43,4 +43,3 @@ class EmailError(MwClientError): pass class NoSpecifiedEmail(EmailError): pass - diff --git a/mwclient/ex.py b/mwclient/ex.py index 730fd05..2e0f239 100644 --- a/mwclient/ex.py +++ b/mwclient/ex.py @@ -6,12 +6,12 @@ def read_config(config_files, **predata): cfg.update(_read_config_file( config_file, predata)) return cfg - + def _read_config_file(_config_file, predata): _file = open(_config_file) exec _file in globals(), predata _file.close() - + for _k, _v in predata.iteritems(): if not _k.startswith('_'): yield _k, _v @@ -32,39 +32,39 @@ class SiteList(object): class ConfiguredSite(client.Site): def __init__(self, *config_files, **kwargs): self.config = read_config(config_files, sites=SiteList()) - + if 'name' in kwargs: self.config.update(self.config['sites'][kwargs['name']]) - + do_login = 'username' in self.config and 'password' in self.config - + client.Site.__init__(self, host=self.config['host'], - path=self.config['path'], ext=self.config.get('ext', '.php'), + path=self.config['path'], ext=self.config.get('ext', '.php'), do_init=not do_login, - retry_timeout =self.config.get('retry_timeout', 30), + retry_timeout=self.config.get('retry_timeout', 30), max_retries=self.config.get('max_retries', -1)) - - + + if do_login: self.login(self.config['username'], self.config['password']) - + class ConfiguredPool(list): def __init__(self, *config_files): self.config = read_config(config_files, sites=SiteList()) self.pool = http.HTTPPool() - + config = dict([(k, v) for k, v in self.config.iteritems() if k != 'sites']) - + for site in self.config['sites']: cfg = config.copy() cfg.update(site) site.update(cfg) - + do_login = 'username' in site and 'password' in site - - self.append(client.Site(host=site['host'], + + self.append(client.Site(host=site['host'], path=site['path'], ext=site.get('ext', '.php'), pool=self.pool, do_init=not do_login, retry_timeout=site.get('retry_timeout', 30), @@ -72,5 +72,3 @@ class ConfiguredPool(list): if do_login: self[-1].login(site['username'], site['password']) self[-1].config = site - - diff --git a/mwclient/http.py b/mwclient/http.py index daafa70..7fb3d54 100644 --- a/mwclient/http.py +++ b/mwclient/http.py @@ -22,27 +22,27 @@ class CookieJar(dict): if not cookie: return value, attrs = cookie.split(': ', 1)[1].split(';', 1) i = value.strip().split('=') - if len(i) == 1 and i[0] in self: + if len(i) == 1 and i[0] in self: del self[i[0]] else: self[i[0]] = i[1] - + def get_cookie_header(self): return '; '.join(('%s=%s' % i for i in self.iteritems())) def __iter__(self): for k, v in self.iteritems(): yield Cookie(k, v) - + class Cookie(object): def __init__(self, name, value): self.name = name self.value = value - + class HTTPPersistentConnection(object): http_class = httplib.HTTPConnection scheme_name = 'http' useragent = None - + def __init__(self, host, pool=None, clients_useragent=None): self._conn = self.http_class(host) self._conn.connect() @@ -55,35 +55,35 @@ class HTTPPersistentConnection(object): clients_useragent = clients_useragent or "" if clients_useragent != "": clients_useragent += " " self.useragent = clients_useragent + 'MwClient/' + __ver__ - + def request(self, method, host, path, headers, data, - raise_on_not_ok=True, auto_redirect=True): - + raise_on_not_ok=True, auto_redirect=True): + # Strip scheme if type(host) is tuple: host = host[1] - + # Dirty hack... if (time.time() - self.last_request) > 60: self._conn.close() - self._conn.connect() - + self._conn.connect() + _headers = headers headers = {} - + headers['Connection'] = 'Keep-Alive' headers['User-Agent'] = self.useragent headers['Host'] = host - if host in self.cookies: + if host in self.cookies: headers['Cookie'] = self.cookies[host].get_cookie_header() if issubclass(data.__class__, upload.Upload): headers['Content-Type'] = data.content_type headers['Content-Length'] = str(data.length) elif data: headers['Content-Length'] = str(len(data)) - + if _headers: headers.update(_headers) - + try: self._conn.request(method, path, headers=headers) if issubclass(data.__class__, upload.Upload): @@ -91,7 +91,7 @@ class HTTPPersistentConnection(object): self._conn.send(s) elif data: self._conn.send(data) - + self.last_request = time.time() try: res = self._conn.getresponse() @@ -105,13 +105,13 @@ class HTTPPersistentConnection(object): raise errors.HTTPError, e #except Exception, e: # raise errors.HTTPError, e - + if not host in self.cookies: self.cookies[host] = CookieJar() self.cookies[host].extract_cookies(res) - + if res.status >= 300 and res.status <= 399 and auto_redirect: res.read() - + location = urlparse.urlparse(res.getheader('Location')) if res.status in (302, 303): if 'Content-Type' in headers: @@ -123,14 +123,14 @@ class HTTPPersistentConnection(object): old_path = path path = location[2] if location[4]: path = path + '?' + location[4] - + if location[0].lower() != self.scheme_name: raise errors.HTTPRedirectError, ('Only HTTP connections are supported', res.getheader('Location')) - + if self.pool is None: - if location[1] != host: - raise errors.HTTPRedirectError, ('Redirecting to different hosts not supported', + if location[1] != host: + raise errors.HTTPRedirectError, ('Redirecting to different hosts not supported', res.getheader('Location')) return self.request(method, host, path, headers, data) @@ -138,28 +138,28 @@ class HTTPPersistentConnection(object): if host == location[1] and path == old_path: conn = self.__class__(location[1], self.pool) self.pool.append(([location[1]], conn)) - return self.pool.request(method, location[1], path, + return self.pool.request(method, location[1], path, headers, data, raise_on_not_ok, auto_redirect) - + if res.status != 200 and raise_on_not_ok: try: raise errors.HTTPStatusError, (res.status, res) finally: res.close() - + return res - + def get(self, host, path, headers=None): return self.request('GET', host, path, headers, None) def post(self, host, path, headers=None, data=None): return self.request('POST', host, path, headers, data) def head(self, host, path, headers=None, auto_redirect=False): - res = self.request('HEAD', host, path, headers, + res = self.request('HEAD', host, path, headers, data=None, raise_on_not_ok=False, auto_redirect=auto_redirect) res.read() return res.status, res.getheaders() - + def close(self): self._conn.close() def fileno(self): @@ -170,7 +170,7 @@ class HTTPConnection(HTTPPersistentConnection): raise_on_not_ok=True, auto_redirect=True): if not headers: headers = {} headers['Connection'] = 'Close' - res = HTTPPersistentConnection.request(self, method, host, path, headers, data, + res = HTTPPersistentConnection.request(self, method, host, path, headers, data, raise_on_not_ok, auto_redirect) return res @@ -178,7 +178,7 @@ class HTTPSPersistentConnection(HTTPPersistentConnection): http_class = httplib.HTTPSConnection scheme_name = 'https' - + class HTTPPool(list): def __init__(self, clients_useragent=None): list.__init__(self) @@ -188,10 +188,10 @@ class HTTPPool(list): def find_connection(self, host, scheme='http'): if type(host) is tuple: scheme, host = host - + for hosts, conn in self: if (scheme, host) in hosts: return conn - + redirected_host = None for hosts, conn in self: status, headers = conn.head(host, '/') @@ -215,13 +215,13 @@ class HTTPPool(list): self.append(([(scheme, host)], conn)) return conn def get(self, host, path, headers=None): - return self.find_connection(host).get(host, + return self.find_connection(host).get(host, path, headers) def post(self, host, path, headers=None, data=None): - return self.find_connection(host).post(host, + return self.find_connection(host).post(host, path, headers, data) def head(self, host, path, headers=None, auto_redirect=False): - return self.find_connection(host).head(host, + return self.find_connection(host).head(host, path, headers, auto_redirect) def request(self, method, host, path, headers, data, raise_on_not_ok, auto_redirect): @@ -230,4 +230,3 @@ class HTTPPool(list): def close(self): for hosts, conn in self: conn.close() - diff --git a/mwclient/listing.py b/mwclient/listing.py index 4ae49c4..152c89d 100644 --- a/mwclient/listing.py +++ b/mwclient/listing.py @@ -8,25 +8,25 @@ class List(object): self.list_name = list_name self.generator = 'list' self.prefix = prefix - + kwargs.update(args) self.args = kwargs - + if limit is None: limit = site.api_limit self.args[self.prefix + 'limit'] = str(limit) - + self.count = 0 self.max_items = max_items - + self._iter = iter(xrange(0)) - + self.last = False self.result_member = list_name self.return_values = return_values - + def __iter__(self): return self - + def next(self, full=False): if self.max_items is not None: if self.count >= self.max_items: @@ -37,26 +37,26 @@ class List(object): if 'timestamp' in item: item['timestamp'] = client.parse_timestamp(item['timestamp']) if full: return item - + if type(self.return_values) is tuple: return tuple((item[i] for i in self.return_values)) elif self.return_values is None: return item else: return item[self.return_values] - + except StopIteration: if self.last: raise StopIteration self.load_chunk() return List.next(self, full=full) - + def load_chunk(self): data = self.site.api('query', (self.generator, self.list_name), *[(str(k), v) for k, v in self.args.iteritems()]) if not data: # Non existent page raise StopIteration self.set_iter(data) - + if self.list_name in data.get('query-continue', ()): self.args.update(data['query-continue'][self.list_name]) else: @@ -69,12 +69,12 @@ class List(object): self._iter = iter(data['query'][self.result_member]) else: self._iter = data['query'][self.result_member].itervalues() - - + + def __repr__(self): return "<List object '%s' for %s>" % (self.list_name, self.site) - @staticmethod + @staticmethod def generate_kwargs(_prefix, *args, **kwargs): kwargs.update(args) for key, value in kwargs.iteritems(): @@ -99,18 +99,18 @@ class List(object): class GeneratorList(List): def __init__(self, site, list_name, prefix, *args, **kwargs): List.__init__(self, site, list_name, prefix, *args, **kwargs) - + self.args['g' + self.prefix + 'limit'] = self.args[self.prefix + 'limit'] del self.args[self.prefix + 'limit'] self.generator = 'generator' - + self.args['prop'] = 'info|imageinfo' self.args['inprop'] = 'protection' - + self.result_member = 'pages' - + self.page_class = page.Page - + def next(self): info = List.next(self, full=True) if info['ns'] == 14: @@ -118,14 +118,14 @@ class GeneratorList(List): if info['ns'] == 6: return page.Image(self.site, u'', info) return page.Page(self.site, u'', info) - + def load_chunk(self): - # Put this here so that the constructor does not fail + # Put this here so that the constructor does not fail # on uninitialized sites self.args['iiprop'] = compatibility.iiprop(self.site.version) return List.load_chunk(self) - - + + class Category(page.Page, GeneratorList): def __init__(self, site, name, info=None, namespace=None): page.Page.__init__(self, site, name, info) @@ -138,22 +138,22 @@ class Category(page.Page, GeneratorList): def __repr__(self): return "<Category object '%s' for %s>" % (self.name.encode('utf-8'), self.site) - def members(self, prop='ids|title', namespace=None, sort='sortkey', + def members(self, prop='ids|title', namespace=None, sort='sortkey', dir='asc', start=None, end=None, generator=True): prefix = self.get_prefix('cm', generator) kwargs = dict(self.generate_kwargs(prefix, prop=prop, namespace=namespace, sort=sort, dir=dir, start=start, end=end, *(compatibility.cmtitle( self, self.site.require(1, 12, raise_error=False)), ))) return self.get_list(generator)(self.site, 'categorymembers', 'cm', **kwargs) - + class PageList(GeneratorList): def __init__(self, site, prefix=None, start=None, namespace=0, redirects='all'): self.namespace = namespace - + kwargs = {} if prefix: kwargs['apprefix'] = prefix if start: kwargs['apfrom'] = start - + GeneratorList.__init__(self, site, 'allpages', 'ap', apnamespace=str(namespace), apfilterredir=redirects, **kwargs) @@ -176,7 +176,7 @@ class PageList(GeneratorList): elif namespace == 6: return page.Image(self.site, name, info) return page.Page(self.site, name, info) - + def guess_namespace(self, name): normal_name = page.Page.normalize_title(name) for ns in self.site.namespaces: @@ -188,7 +188,7 @@ class PageList(GeneratorList): return ns return 0 - + class PageProperty(List): def __init__(self, page, prop, prefix, *args, **kwargs): List.__init__(self, page.site, prop, prefix, titles=page.name, *args, **kwargs) @@ -202,7 +202,7 @@ class PageProperty(List): return raise StopIteration - + class PagePropertyGenerator(GeneratorList): def __init__(self, page, prop, prefix, *args, **kwargs): GeneratorList.__init__(self, page.site, prop, prefix, titles=page.name, *args, **kwargs) @@ -213,4 +213,3 @@ class RevisionsIterator(PageProperty): if 'rvstartid' in self.args and 'rvstart' in self.args: del self.args['rvstart'] return PageProperty.load_chunk(self) - diff --git a/mwclient/page.py b/mwclient/page.py index fee2542..7ec1773 100644 --- a/mwclient/page.py +++ b/mwclient/page.py @@ -3,7 +3,7 @@ import compatibility from page_nowriteapi import OldPage import urllib, urlparse, time - + class Page(object): def __init__(self, site, name, info=None, extra_properties={}): if type(name) is type(self): @@ -11,25 +11,25 @@ class Page(object): self.site = site self.name = name self.section = None - + if not info: if extra_properties: prop = 'info|' + '|'.join(extra_properties.iterkeys()) extra_props = [] - [extra_props.extend(extra_prop) for extra_prop in extra_properties.itervalues()] + [extra_props.extend(extra_prop) for extra_prop in extra_properties.itervalues()] else: prop = 'info' extra_props = () - + if type(name) is int: - info = self.site.api('query', prop=prop, pageids=name, + info = self.site.api('query', prop=prop, pageids=name, inprop='protection', *extra_props) else: - info = self.site.api('query', prop=prop, titles=name, + info = self.site.api('query', prop=prop, titles=name, inprop='protection', *extra_props) info = info['query']['pages'].itervalues().next() self._info = info - + self.namespace = info.get('ns', 0) self.name = info.get('title', u'') if self.namespace: @@ -43,7 +43,7 @@ class Page(object): self.length = info.get('length') self.protection = dict([(i['type'], (i['level'], i['expiry'])) for i in info.get('protection', ()) if i]) self.redirect = 'redirect' in info - + self.last_rev_time = None self.edit_time = None @@ -60,7 +60,7 @@ class Page(object): def resolve_redirect(self): """ Returns the redirect target page, or the current page if it's not a redirect page.""" - target_page = self.redirects_to() + target_page = self.redirects_to() if target_page == None: return self else: @@ -71,7 +71,7 @@ class Page(object): def __unicode__(self): return self.name - + @staticmethod def strip_namespace(title): if title[0] == ':': @@ -81,23 +81,23 @@ class Page(object): @staticmethod def normalize_title(title): # TODO: Make site dependent - title = title.strip() + title = title.strip() if title[0] == ':': title = title[1:] title = title[0].upper() + title[1:] title = title.replace(' ', '_') return title - + def can(self, action): level = self.protection.get(action, (action, ))[0] if level == 'sysop': level = compatibility.protectright(self.site.version) - + return level in self.site.rights - + def get_token(self, type, force=False): self.site.require(1, 11) - + if type not in self.site.tokens: self.site.tokens[type] = '0' if self.site.tokens.get(type, '0') == '0' or force: @@ -107,27 +107,27 @@ class Page(object): if i['title'] == self.name: self.site.tokens[type] = i['%stoken' % type] return self.site.tokens[type] - + def get_expanded(self): self.site.require(1, 12) - + revs = self.revisions(prop='content', limit=1, expandtemplates=True) try: return revs.next()['*'] except StopIteration: return u'' - + def edit(self, section=None, readonly=False): """Returns wikitext for a specified section or for the whole page. - + Retrieves the latest edit. - + """ if not self.can('read'): raise errors.InsufficientPermission(self) if not self.exists: return u'' - + revs = self.revisions(prop='content|timestamp', limit=1, section=section) try: rev = revs.next() @@ -140,7 +140,7 @@ class Page(object): self.edit_time = None self.edit_time = time.gmtime() return self.text - + def save(self, text=u'', summary=u'', minor=False, bot=True, section=None, **kwargs): """Save text of page.""" if not self.site.logged_in and self.site.force_login: @@ -150,13 +150,13 @@ class Page(object): raise errors.UserBlocked(self.site.blocked) if not self.can('edit'): raise errors.ProtectedPageError(self) - + if not text: text = self.text if not section: section = self.section - + if not self.site.writeapi: return OldPage.save(self, text=text, summary=summary, minor=False) - + data = {} if minor: data['minor'] = '1' if not minor: data['notminor'] = '1' @@ -164,16 +164,16 @@ class Page(object): if self.edit_time: data['starttimestamp'] = time.strftime('%Y%m%d%H%M%S', self.edit_time) if bot: data['bot'] = '1' if section: data['section'] = section - + data.update(kwargs) - + def do_edit(): - result = self.site.api('edit', title=self.name, text=text, - summary=summary, token=self.get_token('edit'), - **data) + result = self.site.api('edit', title=self.name, text=text, + summary=summary, token=self.get_token('edit'), + **data) if result['edit'].get('result').lower() == 'failure': raise errors.EditError(self, result['edit']) - return result + return result try: result = do_edit() except errors.APIError, e: @@ -190,89 +190,89 @@ class Page(object): if result['edit'] == 'Success': self.last_rev_time = client.parse_timestamp(result['newtimestamp']) return result['edit'] - + def handle_edit_error(self, e, summary): if e.code == 'editconflict': raise errors.EditError(self, summary, e.info) - elif e.code in ('protectedtitle', 'cantcreate', 'cantcreate-anon', 'noimageredirect-anon', + elif e.code in ('protectedtitle', 'cantcreate', 'cantcreate-anon', 'noimageredirect-anon', 'noimageredirect', 'noedit-anon', 'noedit'): raise errors.ProtectedPageError(self, e.code, e.info) else: - raise + raise def get_expanded(self): self.site.require(1, 12) - + revs = self.revisions(prop='content', limit=1, expandtemplates=True) try: return revs.next()['*'] except StopIteration: return u'' - + def move(self, new_title, reason='', move_talk=True, no_redirect=False): """Move (rename) page to new_title. If user account is an administrator, specify no_direct as True to not leave a redirect. - + If user does not have permission to move page, an InsufficientPermission exception is raised. - + """ if not self.can('move'): raise errors.InsufficientPermission(self) - + if not self.site.writeapi: - return OldPage.move(self, new_title=new_title, + return OldPage.move(self, new_title=new_title, reason=reason, move_talk=move_talk) - + data = {} if move_talk: data['movetalk'] = '1' if no_redirect: data['noredirect'] = '1' - result = self.site.api('move', ('from', self.name), to=new_title, + result = self.site.api('move', ('from', self.name), to=new_title, token=self.get_token('move'), reason=reason, **data) return result['move'] - - + + def delete(self, reason='', watch=False, unwatch=False, oldimage=False): """Delete page. - + If user does not have permission to delete page, an InsufficientPermission exception is raised. - + """ if not self.can('delete'): raise errors.InsufficientPermission(self) - + if not self.site.writeapi: return OldPage.delete(self, reason=reason) - + data = {} if watch: data['watch'] = '1' if unwatch: data['unwatch'] = '1' if oldimage: data['oldimage'] = oldimage - result = self.site.api('delete', title=self.name, - token=self.get_token('delete'), + result = self.site.api('delete', title=self.name, + token=self.get_token('delete'), reason=reason, **data) return result['delete'] - + def purge(self): """Purge server-side cache of page. This will re-render templates and other dynamic content. - + """ self.site.raw_index('purge', title=self.name) - + # def watch: requires 1.14 - + # Properties def backlinks(self, namespace=None, filterredir='all', redirect=False, limit=None, generator=True): self.site.require(1, 9) # Fix title for < 1.11 !! prefix = listing.List.get_prefix('bl', generator) - kwargs = dict(listing.List.generate_kwargs(prefix, + kwargs = dict(listing.List.generate_kwargs(prefix, namespace=namespace, filterredir=filterredir)) if redirect: kwargs['%sredirect' % prefix] = '1' kwargs[compatibility.title(prefix, self.site.require(1, 11, raise_error=False))] = self.name - + return listing.List.get_list(generator)(self.site, 'backlinks', 'bl', limit=limit, return_values='title', **kwargs) def categories(self, generator=True): @@ -291,7 +291,7 @@ class Page(object): namespace=namespace, filterredir=filterredir)) if redirect: kwargs['%sredirect' % prefix] = '1' kwargs[compatibility.title(prefix, self.site.require(1, 11, raise_error=False))] = self.name - + return listing.List.get_list(generator)(self.site, 'embeddedin', 'ei', limit=limit, return_values='title', **kwargs) def extlinks(self): @@ -318,8 +318,8 @@ class Page(object): else: return listing.PageProperty(self, 'links', 'pl', return_values='title', **kwargs) - def revisions(self, startid=None, endid=None, start=None, end=None, - dir='older', user=None, excludeuser=None, limit=50, + def revisions(self, startid=None, endid=None, start=None, end=None, + dir='older', user=None, excludeuser=None, limit=50, prop='ids|timestamp|flags|comment|user', expandtemplates=False, section=None): self.site.require(1, 8) kwargs = dict(listing.List.generate_kwargs('rv', startid=startid, endid=endid, @@ -328,7 +328,7 @@ class Page(object): kwargs['rvprop'] = prop if expandtemplates: kwargs['rvexpandtemplates'] = '1' if section: kwargs['rvsection'] = section - + return listing.RevisionsIterator(self, 'revisions', 'rv', limit=limit, **kwargs) def templates(self, namespace=None, generator=True): @@ -343,16 +343,16 @@ class Image(Page): def __init__(self, site, name, info=None): site.require(1, 11) Page.__init__(self, site, name, info, - extra_properties={'imageinfo': (('iiprop', + extra_properties={'imageinfo': (('iiprop', compatibility.iiprop(site.version)), )}) self.imagerepository = self._info.get('imagerepository', '') self.imageinfo = self._info.get('imageinfo', ({}, ))[0] def imagehistory(self): - return listing.PageProperty(self, 'imageinfo', 'ii', + return listing.PageProperty(self, 'imageinfo', 'ii', iiprop=compatibility.iiprop(self.site.version)) - def imageusage(self, namespace=None, filterredir='all', redirect=False, + def imageusage(self, namespace=None, filterredir='all', redirect=False, limit=None, generator=True): self.site.require(1, 11) # TODO: Fix for versions < 1.11 @@ -360,7 +360,7 @@ class Image(Page): kwargs = dict(listing.List.generate_kwargs(prefix, title=self.name, namespace=namespace, filterredir=filterredir)) if redirect: kwargs['%sredirect' % prefix] = '1' - return listing.List.get_list(generator)(self.site, 'imageusage', 'iu', + return listing.List.get_list(generator)(self.site, 'imageusage', 'iu', limit=limit, return_values='title', **kwargs) def duplicatefiles(self, limit=None): @@ -375,7 +375,6 @@ class Image(Page): url = urlparse.urlparse(url) # TODO: query string return self.site.connection.get(url[1], url[2]) - + def __repr__(self): return "<Image object '%s' for %s>" % (self.name.encode('utf-8'), self.site) - diff --git a/mwclient/page_nowriteapi.py b/mwclient/page_nowriteapi.py index 81e97ef..2373b46 100644 --- a/mwclient/page_nowriteapi.py +++ b/mwclient/page_nowriteapi.py @@ -1,6 +1,6 @@ import time from HTMLParser import HTMLParser -from htmlentitydefs import name2codepoint +from htmlentitydefs import name2codepoint import errors @@ -24,13 +24,13 @@ class OldPage(object): if minor: data['wpMinoredit'] = '1' data['title'] = self.name - + page_data = self.site.raw_index('submit', **data) - + page = EditPage('editform') page.feed(page_data) page.close() - + if page.data: if page.readonly: raise errors.ProtectedPageError(self) self.get_token('edit', True) @@ -45,16 +45,16 @@ class OldPage(object): 'wpEditToken': self.get_token('move')} if move_talk: postdata['wpMovetalk'] = '1' postdata['title'] = 'Special:Movepage' - + page_data = self.site.raw_index('submit', **data) - + page = EditPage('movepage') page.feed(page_data.decode('utf-8', 'ignore')) page.close() - + if 'wpEditToken' in page.data: raise errors.EditError(page.title, postdata) - + @staticmethod def delete(self, reason=''): postdata = {'wpReason': reason, @@ -62,54 +62,54 @@ class OldPage(object): 'mw-filedelete-submit': 'Delete', 'wpEditToken': self.get_token('delete'), 'title': self.name} - + page_data = self.site.raw_index('delete', **postdata) class EditPage(HTMLParser): def __init__(self, form): HTMLParser.__init__(self) - + self.form = form - + self.in_form = False self.in_text = False self.in_title = False - + self.data = {} self.textdata = [] self.title = u'' - + self.readonly = True - + def handle_starttag(self, tag, attrs): self.in_title = (tag == 'title') - + if (u'id', self.form) in attrs: attrs = dict(attrs) self.in_form = True self.action = attrs['action'] - + if tag == 'input' and self.in_form and (u'type', u'submit') \ not in attrs and (u'type', u'checkbox') not in attrs: attrs = dict(attrs) if u'name' in attrs: self.data[attrs[u'name']] = attrs.get(u'value', u'') - + if self.in_form and tag == 'textarea': self.in_text = True self.readonly = (u'readonly', u'readonly') in attrs - - + + def handle_endtag(self, tag): if self.in_title and tag == 'title': self.in_title = False if self.in_form and tag == 'form': self.in_form = False if self.in_text and tag == 'textarea': self.in_text = False - + def handle_data(self, data): if self.in_text: self.textdata.append(data) if self.in_title: self.title += data - + def handle_entityref(self, name): - if name in name2codepoint: + if name in name2codepoint: self.handle_data(unichr(name2codepoint[name])) else: self.handle_data(u'&%s;' % name) @@ -118,4 +118,3 @@ class EditPage(HTMLParser): self.handle_data(unichr(int(name))) except ValueError: self.handle_data(u'&#$s;' % name) - diff --git a/mwclient/upload.py b/mwclient/upload.py index a84d7e3..ec82edf 100644 --- a/mwclient/upload.py +++ b/mwclient/upload.py @@ -3,18 +3,18 @@ from cStringIO import StringIO class Upload(object): """ - Base class for upload objects. This class should always be subclassed + Base class for upload objects. This class should always be subclassed by upload classes and its constructor always be called. - - Upload classes are file like object/iterators that have additional + + Upload classes are file like object/iterators that have additional variables length and content_type. """ - + BLOCK_SIZE = 8192 def __init__(self, length, content_type): self.length = length self.content_type = content_type - + def __iter__(self): return self def next(self): @@ -22,7 +22,7 @@ class Upload(object): if data == '': raise StopIteration return data - + @staticmethod def encode(s): if type(s) is str: @@ -34,24 +34,24 @@ class Upload(object): class UploadRawData(Upload): """ - This upload class is simply a wrapper around StringIO + This upload class is simply a wrapper around StringIO """ def __init__(self, data, content_type='application/x-www-form-urlencoded'): self.fstr = StringIO(data) Upload.__init__(self, len(data), content_type) - def read(self, length = -1): + def read(self, length=-1): return self.fstr.read(length) - - + + class UploadDict(UploadRawData): """ - This class creates an x-www-form-urlencoded representation of a dict - and then passes it through its parent UploadRawData + This class creates an x-www-form-urlencoded representation of a dict + and then passes it through its parent UploadRawData """ def __init__(self, data): postdata = '&'.join('%s=%s' % (self.encode(i), self.encode(data[i])) for i in data) UploadRawData.__init__(self, postdata) - + class UploadFile(Upload): """ This class accepts a file with information and a postdata dictionary @@ -74,14 +74,14 @@ class UploadFile(Upload): self.file = file self.length_left = filelength self.str_data = None - + Upload.__init__(self, len(self.fileheader) + filelength + len(self.postdata) + len(self.footer) + 2, 'multipart/form-data; boundary=' + self.boundary) - + def read(self, length): if self.stage == self.STAGE_DONE: return '' - elif self.stage != self.STAGE_FILE: + elif self.stage != self.STAGE_FILE: if self.str_data is None: if self.stage == self.STAGE_FILEHEADER: self.str_data = StringIO(self.fileheader) @@ -99,24 +99,24 @@ class UploadFile(Upload): else: self.stage += 1 return '\r\n' - + if data == '': self.stage += 1 self.str_data = None return self.read(length) return data - + @staticmethod def generate_boundary(): return '----%s----' % ''.join((random.choice( - 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') + 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') for i in xrange(32))) - + def generate_multipart_from_dict(self, data): postdata = [] for i in data: - postdata.append('--' + self.boundary) + postdata.append('--' + self.boundary) postdata.append('Content-Disposition: form-data; name="%s"' % self.encode(i)) postdata.append('') postdata.append(self.encode(data[i])) -- GitLab