Browse Source

add PyPI publisher, for pushing to packages.python.org

main
Ryan Kelly 13 years ago
parent
commit
fad2e9d7b0
1 changed files with 140 additions and 0 deletions
  1. +140
    -0
      hyde/ext/publishers/pypi.py

+ 140
- 0
hyde/ext/publishers/pypi.py View File

@@ -0,0 +1,140 @@
"""
Contains classes and utilities that help publishing a hyde website to
the documentation hosting on http://packages.python.org/.

"""

import os
import getpass
import zipfile
import tempfile
import httplib
import urlparse
from base64 import standard_b64encode
import ConfigParser

from hyde.fs import File, Folder
from hyde.publisher import Publisher

from hyde.util import getLoggerWithNullHandler
logger = getLoggerWithNullHandler('hyde.ext.publishers.pypi')




class PyPI(Publisher):

def initialize(self, settings):
self.settings = settings
self.project = settings.project
self.url = getattr(settings,"url","https://pypi.python.org/pypi/")
self.username = getattr(settings,"username",None)
self.password = getattr(settings,"password",None)
self.prompt_for_credentials()

def prompt_for_credentials(self):
pypirc_file = os.path.expanduser("~/.pypirc")
if not os.path.isfile(pypirc_file):
pypirc = None
else:
pypirc = ConfigParser.RawConfigParser()
pypirc.read([pypirc_file])
missing_errs = (ConfigParser.NoSectionError,ConfigParser.NoOptionError)
# Try to find username in .pypirc
if self.username is None:
if pypirc is not None:
try:
self.username = pypirc.get("server-login","username")
except missing_errs:
pass
# Prompt for username on command-line
if self.username is None:
print "Username: ",
self.username = raw_input().strip()
# Try to find password in .pypirc
if self.password is None:
if pypirc is not None:
try:
self.password = pypirc.get("server-login","password")
except missing_errs:
pass
# Prompt for username on command-line
if self.password is None:
self.password = getpass.getpass("Password: ")
# Validate the values.
if not self.username:
raise ValueError("PyPI requires a username")
if not self.password:
raise ValueError("PyPI requires a password")

def publish(self):
super(PyPI, self).publish()
tf = tempfile.TemporaryFile()
try:
# Bundle it up into a zipfile
logger.info("building the zipfile")
root = self.site.config.deploy_root_path
zf = zipfile.ZipFile(tf,"w",zipfile.ZIP_DEFLATED)
try:
for item in root.walker.walk_files():
logger.info(" adding file: %s",item.path)
zf.write(item.path,item.get_relative_path(root))
finally:
zf.close()
# Formulate the necessary bits for the HTTP POST.
# Multipart/form-data encoding. Yuck.
authz = self.username + ":" + self.password
authz = "Basic " + standard_b64encode(authz)
boundary = "-----------" + os.urandom(20).encode("hex")
sep_boundary = "\r\n--" + boundary
end_boundary = "\r\n--" + boundary + "--\r\n"
content_type = "multipart/form-data; boundary=%s" % (boundary,)
items = ((":action","doc_upload"),("name",self.project))
body_prefix = ""
for (name,value) in items:
body_prefix += "--" + boundary + "\r\n"
body_prefix += "Content-Disposition: form-data; name=\""
body_prefix += name + "\"\r\n\r\n"
body_prefix += value + "\r\n"
body_prefix += "--" + boundary + "\r\n"
body_prefix += "Content-Disposition: form-data; name=\"content\""
body_prefix += "; filename=\"website.zip\"\r\n\r\n"
body_suffix = "\r\n--" + boundary + "--\r\n"
content_length = len(body_prefix) + tf.tell() + len(body_suffix)
# POST it up to PyPI
logger.info("uploading to PyPI")
url = urlparse.urlparse(self.url)
if url.scheme == "https":
con = httplib.HTTPSConnection(url.netloc)
else:
con = httplib.HTTPConnection(url.netloc)
con.connect()
try:
con.putrequest("POST", self.url)
con.putheader("Content-Type",content_type)
con.putheader("Content-Length",str(content_length))
con.putheader("Authorization",authz)
con.endheaders()
con.send(body_prefix)
tf.seek(0)
data = tf.read(1024*32)
while data:
con.send(data)
data = tf.read(1024*32)
con.send(body_suffix)
r = con.getresponse()
try:
# PyPI tries to redirect to the page on success.
if r.status in (200,301,):
logger.info("success!")
else:
msg = "Upload failed: %s %s" % (r.status,r.reason,)
raise Exception(msg)
finally:
r.close()
finally:
con.close()
finally:
tf.close()


Loading…
Cancel
Save