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