Mercurial > boomslang
diff pyweek-upload.py @ 0:f2c3b516741b simon
Theme is 'Caught'. Start with Skellington 1.9.
author | Simon Cross <hodgestar+bzr@gmail.com> |
---|---|
date | Sun, 22 Aug 2010 09:42:24 +0200 |
parents | |
children | 60c345d19daf |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pyweek-upload.py Sun Aug 22 09:42:24 2010 +0200 @@ -0,0 +1,198 @@ +''' +Upload script specifically engineered for the PyWeek challenge. + +Handles authentication and gives upload progress feedback. +''' +import sys, os, httplib, cStringIO, socket, time, getopt + +class Upload: + def __init__(self, filename): + self.filename = filename + +boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' +sep_boundary = '\n--' + boundary +end_boundary = sep_boundary + '--' + +def mimeEncode(data, sep_boundary=sep_boundary, end_boundary=end_boundary): + '''Take the mapping of data and construct the body of a + multipart/form-data message with it using the indicated boundaries. + ''' + ret = cStringIO.StringIO() + for key, value in data.items(): + # handle multiple entries for the same name + if type(value) != type([]): value = [value] + for value in value: + ret.write(sep_boundary) + if isinstance(value, Upload): + ret.write('\nContent-Disposition: form-data; name="%s"'%key) + filename = os.path.basename(value.filename) + ret.write('; filename="%s"\n\n'%filename) + value = open(os.path.join(value.filename), "rb").read() + else: + ret.write('\nContent-Disposition: form-data; name="%s"'%key) + ret.write("\n\n") + value = str(value) + ret.write(str(value)) + if value and value[-1] == '\r': + ret.write('\n') # write an extra newline + ret.write(end_boundary) + return ret.getvalue() + +class Progress: + def __init__(self, info, data): + self.info = info + self.tosend = len(data) + self.total = self.tosend/1024 + self.data = cStringIO.StringIO(data) + self.start = self.now = time.time() + self.sent = 0 + self.num = 0 + self.stepsize = self.total / 100 or 1 + self.steptimes = [] + self.display() + + def __iter__(self): return self + + def next(self): + self.num += 1 + if self.sent >= self.tosend: + print self.info, 'done', ' '*(75-len(self.info)-6) + sys.stdout.flush() + raise StopIteration + + chunk = self.data.read(1024) + self.sent += len(chunk) + #print (self.num, self.stepsize, self.total, self.sent, self.tosend) + + if self.num % self.stepsize: + return chunk + self.display() + return chunk + + def display(self): + # figure how long we've spent - guess how long to go + now = time.time() + steptime = now - self.now + self.steptimes.insert(0, steptime) + if len(self.steptimes) > 5: + self.steptimes.pop() + steptime = sum(self.steptimes) / len(self.steptimes) + self.now = now + eta = steptime * ((self.total - self.num)/self.stepsize) + + # tell it like it is (or might be) + if now - self.start > 3: + M = eta / 60 + H = M / 60 + M = M % 60 + S = eta % 60 + if self.total: + s = '%s %2d%% (ETA %02d:%02d:%02d)'%(self.info, + self.num * 100. / self.total, H, M, S) + else: + s = '%s 0%% (ETA %02d:%02d:%02d)'%(self.info, H, M, S) + elif self.total: + s = '%s %2d%%'%(self.info, self.num * 100. / self.total) + else: + s = '%s %d done'%(self.info, self.num) + sys.stdout.write(s + ' '*(75-len(s)) + '\r') + sys.stdout.flush() + +class progressHTTPConnection(httplib.HTTPConnection): + def progress_send(self, str): + """Send `str' to the server.""" + if self.sock is None: + self.connect() + + p = Progress('Uploading', str) + for chunk in p: + sent = 0 + while sent != len(chunk): + try: + sent += self.sock.send(chunk) + except socket.error, v: + if v[0] == 32: # Broken pipe + self.close() + raise + p.display() + +class progressHTTP(httplib.HTTP): + _connection_class = progressHTTPConnection + def _setup(self, conn): + httplib.HTTP._setup(self, conn) + self.progress_send = self._conn.progress_send + +def http_request(data, server, port, url): + h = progressHTTP(server, port) + + data = mimeEncode(data) + h.putrequest('POST', url) + h.putheader('Content-type', 'multipart/form-data; boundary=%s'%boundary) + h.putheader('Content-length', str(len(data))) + h.putheader('Host', server) + h.endheaders() + + h.progress_send(data) + + errcode, errmsg, headers = h.getreply() + + f = h.getfile() + response = f.read().strip() + f.close() + + print '%s %s'%(errcode, errmsg) + if response: print response + +def usage(): + print '''This program is to be used to upload files to the PyWeek system. +You may use it to upload screenshots or code submissions. + +REQUIRED ARGUMENTS: + -u username + -p password + -d description of file + -c file to upload + -e entry short name + +OPTIONAL ARGUMENTS: + -s file is a screenshot + -f file is FINAL submission + -h override default host name (www.pyweek.org) + -P override default host port (80) + +In order to qualify for judging at the end of the challenge, you MUST +upload your source and check the "Final Submission" checkbox. +''' + + +if __name__ == '__main__': + try: + optlist, args = getopt.getopt(sys.argv[1:], 'e:u:p:sfd:h:P:c:') + except getopt.GetoptError, message: + print message + usage() + sys.exit(1) + host = 'www.pyweek.org' + port = 80 + data = dict(version=2) + optional = {} + url = None + for opt, arg in optlist: + if opt == '-u': data['user'] = arg + elif opt == '-p': data['password'] = arg + elif opt == '-s': optional['is_screenshot'] = 'yes' + elif opt == '-f': optional['is_final'] = 'yes' + elif opt == '-d': data['description'] = arg + elif opt == '-c': data['content_file'] = Upload(arg) + elif opt == '-e': url = '/e/%s/oup/'%arg + elif opt == '-h': host = arg + elif opt == '-P': port = int(arg) + + if len(data) < 4 or url is None: + print 'Required argument missing' + usage() + sys.exit(1) + + data.update(optional) + http_request(data, host, port, url) +