summaryrefslogtreecommitdiff
path: root/distutils2/command/upload_docs.py
diff options
context:
space:
mode:
Diffstat (limited to 'distutils2/command/upload_docs.py')
-rw-r--r--distutils2/command/upload_docs.py156
1 files changed, 156 insertions, 0 deletions
diff --git a/distutils2/command/upload_docs.py b/distutils2/command/upload_docs.py
new file mode 100644
index 0000000..d53dd03
--- /dev/null
+++ b/distutils2/command/upload_docs.py
@@ -0,0 +1,156 @@
+import os
+import base64
+import httplib
+import socket
+import urlparse
+import zipfile
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
+
+from distutils2 import log
+from distutils2.command.upload import upload
+from distutils2.command.cmd import Command
+from distutils2.errors import DistutilsFileError
+from distutils2.util import read_pypirc, DEFAULT_REPOSITORY, DEFAULT_REALM
+
+def zip_dir(directory):
+ """Compresses recursively contents of directory into a StringIO object"""
+ destination = StringIO()
+ zip_file = zipfile.ZipFile(destination, "w")
+ for root, dirs, files in os.walk(directory):
+ for name in files:
+ full = os.path.join(root, name)
+ relative = root[len(directory):].lstrip(os.path.sep)
+ dest = os.path.join(relative, name)
+ zip_file.write(full, dest)
+ zip_file.close()
+ return destination
+
+# grabbed from
+# http://code.activestate.com/recipes/146306-http-client-to-post-using-multipartform-data/
+def encode_multipart(fields, files, boundary=None):
+ """
+ fields is a sequence of (name, value) elements for regular form fields.
+ files is a sequence of (name, filename, value) elements for data to be uploaded as files
+ Return (content_type, body) ready for httplib.HTTP instance
+ """
+ if boundary is None:
+ boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
+ l = []
+ for (key, value) in fields:
+ l.extend([
+ '--' + boundary,
+ 'Content-Disposition: form-data; name="%s"' % key,
+ '',
+ value])
+ for (key, filename, value) in files:
+ l.extend([
+ '--' + boundary,
+ 'Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename),
+ '',
+ value])
+ l.append('--' + boundary + '--')
+ l.append('')
+ body = '\r\n'.join(l)
+ content_type = 'multipart/form-data; boundary=%s' % boundary
+ return content_type, body
+
+class upload_docs(Command):
+
+ user_options = [
+ ('repository=', 'r',
+ "repository URL [default: %s]" % DEFAULT_REPOSITORY),
+ ('show-response', None,
+ "display full response text from server"),
+ ('upload-dir=', None,
+ "directory to upload"),
+ ]
+
+ def initialize_options(self):
+ self.repository = None
+ self.realm = None
+ self.show_response = 0
+ self.upload_dir = None
+ self.username = ''
+ self.password = ''
+
+ def finalize_options(self):
+ if self.repository is None:
+ self.repository = DEFAULT_REPOSITORY
+ if self.realm is None:
+ self.realm = DEFAULT_REALM
+ if self.upload_dir is None:
+ build = self.get_finalized_command('build')
+ self.upload_dir = os.path.join(build.build_base, "docs")
+ self.announce('Using upload directory %s' % self.upload_dir)
+ self.verify_upload_dir(self.upload_dir)
+ config = read_pypirc(self.repository, self.realm)
+ if config != {}:
+ self.username = config['username']
+ self.password = config['password']
+ self.repository = config['repository']
+ self.realm = config['realm']
+
+ def verify_upload_dir(self, upload_dir):
+ self.ensure_dirname('upload_dir')
+ index_location = os.path.join(upload_dir, "index.html")
+ if not os.path.exists(index_location):
+ mesg = "No 'index.html found in docs directory (%s)"
+ raise DistutilsFileError(mesg % upload_dir)
+
+ def run(self):
+ name = self.distribution.metadata['Name']
+ version = self.distribution.metadata['Version']
+ zip_file = zip_dir(self.upload_dir)
+
+ fields = [(':action', 'doc_upload'), ('name', name), ('version', version)]
+ files = [('content', name, zip_file.getvalue())]
+ content_type, body = encode_multipart(fields, files)
+
+ credentials = self.username + ':' + self.password
+ auth = "Basic " + base64.encodestring(credentials).strip()
+
+ self.announce("Submitting documentation to %s" % (self.repository),
+ log.INFO)
+
+ schema, netloc, url, params, query, fragments = \
+ urlparse.urlparse(self.repository)
+ if schema == "http":
+ conn = httplib.HTTPConnection(netloc)
+ elif schema == "https":
+ conn = httplib.HTTPSConnection(netloc)
+ else:
+ raise AssertionError("unsupported schema "+schema)
+
+ try:
+ conn.connect()
+ conn.putrequest("POST", url)
+ conn.putheader('Content-type', content_type)
+ conn.putheader('Content-length', str(len(body)))
+ conn.putheader('Authorization', auth)
+ conn.endheaders()
+ conn.send(body)
+ except socket.error, e:
+ self.announce(str(e), log.ERROR)
+ return
+
+ r = conn.getresponse()
+
+ if r.status == 200:
+ self.announce('Server response (%s): %s' % (r.status, r.reason),
+ log.INFO)
+ elif r.status == 301:
+ location = r.getheader('Location')
+ if location is None:
+ location = 'http://packages.python.org/%s/' % name
+ self.announce('Upload successful. Visit %s' % location,
+ log.INFO)
+ else:
+ self.announce('Upload failed (%s): %s' % (r.status, r.reason),
+ log.ERROR)
+
+ if self.show_response:
+ msg = '\n'.join(('-' * 75, r.read(), '-' * 75))
+ self.announce(msg, log.INFO)