PageRenderTime 129ms CodeModel.GetById 13ms app.highlight 71ms RepoModel.GetById 8ms app.codeStats 1ms

/Lib/distutils/command/upload.py

http://unladen-swallow.googlecode.com/
Python | 185 lines | 181 code | 1 blank | 3 comment | 8 complexity | e149a9da546ee94900093e047f3e355c MD5 | raw file
  1"""distutils.command.upload
  2
  3Implements the Distutils 'upload' subcommand (upload package to PyPI)."""
  4
  5from distutils.errors import *
  6from distutils.core import PyPIRCCommand
  7from distutils.spawn import spawn
  8from distutils import log
  9from hashlib import md5
 10import os
 11import socket
 12import platform
 13import httplib
 14import base64
 15import urlparse
 16import cStringIO as StringIO
 17from ConfigParser import ConfigParser
 18
 19
 20class upload(PyPIRCCommand):
 21
 22    description = "upload binary package to PyPI"
 23
 24    user_options = PyPIRCCommand.user_options + [
 25        ('sign', 's',
 26         'sign files to upload using gpg'),
 27        ('identity=', 'i', 'GPG identity used to sign files'),
 28        ]
 29
 30    boolean_options = PyPIRCCommand.boolean_options + ['sign']
 31
 32    def initialize_options(self):
 33        PyPIRCCommand.initialize_options(self)
 34        self.username = ''
 35        self.password = ''
 36        self.show_response = 0
 37        self.sign = False
 38        self.identity = None
 39
 40    def finalize_options(self):
 41        PyPIRCCommand.finalize_options(self)
 42        if self.identity and not self.sign:
 43            raise DistutilsOptionError(
 44                "Must use --sign for --identity to have meaning"
 45            )
 46        config = self._read_pypirc()
 47        if config != {}:
 48            self.username = config['username']
 49            self.password = config['password']
 50            self.repository = config['repository']
 51            self.realm = config['realm']
 52
 53    def run(self):
 54        if not self.distribution.dist_files:
 55            raise DistutilsOptionError("No dist file created in earlier command")
 56        for command, pyversion, filename in self.distribution.dist_files:
 57            self.upload_file(command, pyversion, filename)
 58
 59    def upload_file(self, command, pyversion, filename):
 60        # Sign if requested
 61        if self.sign:
 62            gpg_args = ["gpg", "--detach-sign", "-a", filename]
 63            if self.identity:
 64                gpg_args[2:2] = ["--local-user", self.identity]
 65            spawn(gpg_args,
 66                  dry_run=self.dry_run)
 67
 68        # Fill in the data - send all the meta-data in case we need to
 69        # register a new release
 70        content = open(filename,'rb').read()
 71        meta = self.distribution.metadata
 72        data = {
 73            # action
 74            ':action': 'file_upload',
 75            'protcol_version': '1',
 76
 77            # identify release
 78            'name': meta.get_name(),
 79            'version': meta.get_version(),
 80
 81            # file content
 82            'content': (os.path.basename(filename),content),
 83            'filetype': command,
 84            'pyversion': pyversion,
 85            'md5_digest': md5(content).hexdigest(),
 86
 87            # additional meta-data
 88            'metadata_version' : '1.0',
 89            'summary': meta.get_description(),
 90            'home_page': meta.get_url(),
 91            'author': meta.get_contact(),
 92            'author_email': meta.get_contact_email(),
 93            'license': meta.get_licence(),
 94            'description': meta.get_long_description(),
 95            'keywords': meta.get_keywords(),
 96            'platform': meta.get_platforms(),
 97            'classifiers': meta.get_classifiers(),
 98            'download_url': meta.get_download_url(),
 99            # PEP 314
100            'provides': meta.get_provides(),
101            'requires': meta.get_requires(),
102            'obsoletes': meta.get_obsoletes(),
103            }
104        comment = ''
105        if command == 'bdist_rpm':
106            dist, version, id = platform.dist()
107            if dist:
108                comment = 'built for %s %s' % (dist, version)
109        elif command == 'bdist_dumb':
110            comment = 'built for %s' % platform.platform(terse=1)
111        data['comment'] = comment
112
113        if self.sign:
114            data['gpg_signature'] = (os.path.basename(filename) + ".asc",
115                                     open(filename+".asc").read())
116
117        # set up the authentication
118        auth = "Basic " + base64.encodestring(self.username + ":" + self.password).strip()
119
120        # Build up the MIME payload for the POST data
121        boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
122        sep_boundary = '\n--' + boundary
123        end_boundary = sep_boundary + '--'
124        body = StringIO.StringIO()
125        for key, value in data.items():
126            # handle multiple entries for the same name
127            if type(value) != type([]):
128                value = [value]
129            for value in value:
130                if type(value) is tuple:
131                    fn = ';filename="%s"' % value[0]
132                    value = value[1]
133                else:
134                    fn = ""
135                value = str(value)
136                body.write(sep_boundary)
137                body.write('\nContent-Disposition: form-data; name="%s"'%key)
138                body.write(fn)
139                body.write("\n\n")
140                body.write(value)
141                if value and value[-1] == '\r':
142                    body.write('\n')  # write an extra newline (lurve Macs)
143        body.write(end_boundary)
144        body.write("\n")
145        body = body.getvalue()
146
147        self.announce("Submitting %s to %s" % (filename, self.repository), log.INFO)
148
149        # build the Request
150        # We can't use urllib2 since we need to send the Basic
151        # auth right with the first request
152        schema, netloc, url, params, query, fragments = \
153            urlparse.urlparse(self.repository)
154        assert not params and not query and not fragments
155        if schema == 'http':
156            http = httplib.HTTPConnection(netloc)
157        elif schema == 'https':
158            http = httplib.HTTPSConnection(netloc)
159        else:
160            raise AssertionError, "unsupported schema "+schema
161
162        data = ''
163        loglevel = log.INFO
164        try:
165            http.connect()
166            http.putrequest("POST", url)
167            http.putheader('Content-type',
168                           'multipart/form-data; boundary=%s'%boundary)
169            http.putheader('Content-length', str(len(body)))
170            http.putheader('Authorization', auth)
171            http.endheaders()
172            http.send(body)
173        except socket.error, e:
174            self.announce(str(e), log.ERROR)
175            return
176
177        r = http.getresponse()
178        if r.status == 200:
179            self.announce('Server response (%s): %s' % (r.status, r.reason),
180                          log.INFO)
181        else:
182            self.announce('Upload failed (%s): %s' % (r.status, r.reason),
183                          log.ERROR)
184        if self.show_response:
185            print '-'*75, r.read(), '-'*75