python-2.5.2/win32/Lib/distutils/command/register.py
changeset 0 ae805ac0140d
equal deleted inserted replaced
-1:000000000000 0:ae805ac0140d
       
     1 """distutils.command.register
       
     2 
       
     3 Implements the Distutils 'register' command (register with the repository).
       
     4 """
       
     5 
       
     6 # created 2002/10/21, Richard Jones
       
     7 
       
     8 __revision__ = "$Id: register.py 56542 2007-07-25 16:24:08Z martin.v.loewis $"
       
     9 
       
    10 import sys, os, string, urllib2, getpass, urlparse
       
    11 import StringIO, ConfigParser
       
    12 
       
    13 from distutils.core import Command
       
    14 from distutils.errors import *
       
    15 
       
    16 class register(Command):
       
    17 
       
    18     description = ("register the distribution with the Python package index")
       
    19 
       
    20     DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi'
       
    21 
       
    22     user_options = [
       
    23         ('repository=', 'r',
       
    24          "url of repository [default: %s]"%DEFAULT_REPOSITORY),
       
    25         ('list-classifiers', None,
       
    26          'list the valid Trove classifiers'),
       
    27         ('show-response', None,
       
    28          'display full response text from server'),
       
    29         ]
       
    30     boolean_options = ['verify', 'show-response', 'list-classifiers']
       
    31 
       
    32     def initialize_options(self):
       
    33         self.repository = None
       
    34         self.show_response = 0
       
    35         self.list_classifiers = 0
       
    36 
       
    37     def finalize_options(self):
       
    38         if self.repository is None:
       
    39             self.repository = self.DEFAULT_REPOSITORY
       
    40 
       
    41     def run(self):
       
    42         self.check_metadata()
       
    43         if self.dry_run:
       
    44             self.verify_metadata()
       
    45         elif self.list_classifiers:
       
    46             self.classifiers()
       
    47         else:
       
    48             self.send_metadata()
       
    49 
       
    50     def check_metadata(self):
       
    51         """Ensure that all required elements of meta-data (name, version,
       
    52            URL, (author and author_email) or (maintainer and
       
    53            maintainer_email)) are supplied by the Distribution object; warn if
       
    54            any are missing.
       
    55         """
       
    56         metadata = self.distribution.metadata
       
    57 
       
    58         missing = []
       
    59         for attr in ('name', 'version', 'url'):
       
    60             if not (hasattr(metadata, attr) and getattr(metadata, attr)):
       
    61                 missing.append(attr)
       
    62 
       
    63         if missing:
       
    64             self.warn("missing required meta-data: " +
       
    65                       string.join(missing, ", "))
       
    66 
       
    67         if metadata.author:
       
    68             if not metadata.author_email:
       
    69                 self.warn("missing meta-data: if 'author' supplied, " +
       
    70                           "'author_email' must be supplied too")
       
    71         elif metadata.maintainer:
       
    72             if not metadata.maintainer_email:
       
    73                 self.warn("missing meta-data: if 'maintainer' supplied, " +
       
    74                           "'maintainer_email' must be supplied too")
       
    75         else:
       
    76             self.warn("missing meta-data: either (author and author_email) " +
       
    77                       "or (maintainer and maintainer_email) " +
       
    78                       "must be supplied")
       
    79 
       
    80     def classifiers(self):
       
    81         ''' Fetch the list of classifiers from the server.
       
    82         '''
       
    83         response = urllib2.urlopen(self.repository+'?:action=list_classifiers')
       
    84         print response.read()
       
    85 
       
    86     def verify_metadata(self):
       
    87         ''' Send the metadata to the package index server to be checked.
       
    88         '''
       
    89         # send the info to the server and report the result
       
    90         (code, result) = self.post_to_server(self.build_post_data('verify'))
       
    91         print 'Server response (%s): %s'%(code, result)
       
    92 
       
    93     def send_metadata(self):
       
    94         ''' Send the metadata to the package index server.
       
    95 
       
    96             Well, do the following:
       
    97             1. figure who the user is, and then
       
    98             2. send the data as a Basic auth'ed POST.
       
    99 
       
   100             First we try to read the username/password from $HOME/.pypirc,
       
   101             which is a ConfigParser-formatted file with a section
       
   102             [server-login] containing username and password entries (both
       
   103             in clear text). Eg:
       
   104 
       
   105                 [server-login]
       
   106                 username: fred
       
   107                 password: sekrit
       
   108 
       
   109             Otherwise, to figure who the user is, we offer the user three
       
   110             choices:
       
   111 
       
   112              1. use existing login,
       
   113              2. register as a new user, or
       
   114              3. set the password to a random string and email the user.
       
   115 
       
   116         '''
       
   117         choice = 'x'
       
   118         username = password = ''
       
   119 
       
   120         # see if we can short-cut and get the username/password from the
       
   121         # config
       
   122         config = None
       
   123         if os.environ.has_key('HOME'):
       
   124             rc = os.path.join(os.environ['HOME'], '.pypirc')
       
   125             if os.path.exists(rc):
       
   126                 print 'Using PyPI login from %s'%rc
       
   127                 config = ConfigParser.ConfigParser()
       
   128                 config.read(rc)
       
   129                 username = config.get('server-login', 'username')
       
   130                 password = config.get('server-login', 'password')
       
   131                 choice = '1'
       
   132 
       
   133         # get the user's login info
       
   134         choices = '1 2 3 4'.split()
       
   135         while choice not in choices:
       
   136             print '''We need to know who you are, so please choose either:
       
   137  1. use your existing login,
       
   138  2. register as a new user,
       
   139  3. have the server generate a new password for you (and email it to you), or
       
   140  4. quit
       
   141 Your selection [default 1]: ''',
       
   142             choice = raw_input()
       
   143             if not choice:
       
   144                 choice = '1'
       
   145             elif choice not in choices:
       
   146                 print 'Please choose one of the four options!'
       
   147 
       
   148         if choice == '1':
       
   149             # get the username and password
       
   150             while not username:
       
   151                 username = raw_input('Username: ')
       
   152             while not password:
       
   153                 password = getpass.getpass('Password: ')
       
   154 
       
   155             # set up the authentication
       
   156             auth = urllib2.HTTPPasswordMgr()
       
   157             host = urlparse.urlparse(self.repository)[1]
       
   158             auth.add_password('pypi', host, username, password)
       
   159 
       
   160             # send the info to the server and report the result
       
   161             code, result = self.post_to_server(self.build_post_data('submit'),
       
   162                 auth)
       
   163             print 'Server response (%s): %s'%(code, result)
       
   164 
       
   165             # possibly save the login
       
   166             if os.environ.has_key('HOME') and config is None and code == 200:
       
   167                 rc = os.path.join(os.environ['HOME'], '.pypirc')
       
   168                 print 'I can store your PyPI login so future submissions will be faster.'
       
   169                 print '(the login will be stored in %s)'%rc
       
   170                 choice = 'X'
       
   171                 while choice.lower() not in 'yn':
       
   172                     choice = raw_input('Save your login (y/N)?')
       
   173                     if not choice:
       
   174                         choice = 'n'
       
   175                 if choice.lower() == 'y':
       
   176                     f = open(rc, 'w')
       
   177                     f.write('[server-login]\nusername:%s\npassword:%s\n'%(
       
   178                         username, password))
       
   179                     f.close()
       
   180                     try:
       
   181                         os.chmod(rc, 0600)
       
   182                     except:
       
   183                         pass
       
   184         elif choice == '2':
       
   185             data = {':action': 'user'}
       
   186             data['name'] = data['password'] = data['email'] = ''
       
   187             data['confirm'] = None
       
   188             while not data['name']:
       
   189                 data['name'] = raw_input('Username: ')
       
   190             while data['password'] != data['confirm']:
       
   191                 while not data['password']:
       
   192                     data['password'] = getpass.getpass('Password: ')
       
   193                 while not data['confirm']:
       
   194                     data['confirm'] = getpass.getpass(' Confirm: ')
       
   195                 if data['password'] != data['confirm']:
       
   196                     data['password'] = ''
       
   197                     data['confirm'] = None
       
   198                     print "Password and confirm don't match!"
       
   199             while not data['email']:
       
   200                 data['email'] = raw_input('   EMail: ')
       
   201             code, result = self.post_to_server(data)
       
   202             if code != 200:
       
   203                 print 'Server response (%s): %s'%(code, result)
       
   204             else:
       
   205                 print 'You will receive an email shortly.'
       
   206                 print 'Follow the instructions in it to complete registration.'
       
   207         elif choice == '3':
       
   208             data = {':action': 'password_reset'}
       
   209             data['email'] = ''
       
   210             while not data['email']:
       
   211                 data['email'] = raw_input('Your email address: ')
       
   212             code, result = self.post_to_server(data)
       
   213             print 'Server response (%s): %s'%(code, result)
       
   214 
       
   215     def build_post_data(self, action):
       
   216         # figure the data to send - the metadata plus some additional
       
   217         # information used by the package server
       
   218         meta = self.distribution.metadata
       
   219         data = {
       
   220             ':action': action,
       
   221             'metadata_version' : '1.0',
       
   222             'name': meta.get_name(),
       
   223             'version': meta.get_version(),
       
   224             'summary': meta.get_description(),
       
   225             'home_page': meta.get_url(),
       
   226             'author': meta.get_contact(),
       
   227             'author_email': meta.get_contact_email(),
       
   228             'license': meta.get_licence(),
       
   229             'description': meta.get_long_description(),
       
   230             'keywords': meta.get_keywords(),
       
   231             'platform': meta.get_platforms(),
       
   232             'classifiers': meta.get_classifiers(),
       
   233             'download_url': meta.get_download_url(),
       
   234             # PEP 314
       
   235             'provides': meta.get_provides(),
       
   236             'requires': meta.get_requires(),
       
   237             'obsoletes': meta.get_obsoletes(),
       
   238         }
       
   239         if data['provides'] or data['requires'] or data['obsoletes']:
       
   240             data['metadata_version'] = '1.1'
       
   241         return data
       
   242 
       
   243     def post_to_server(self, data, auth=None):
       
   244         ''' Post a query to the server, and return a string response.
       
   245         '''
       
   246 
       
   247         # Build up the MIME payload for the urllib2 POST data
       
   248         boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
       
   249         sep_boundary = '\n--' + boundary
       
   250         end_boundary = sep_boundary + '--'
       
   251         body = StringIO.StringIO()
       
   252         for key, value in data.items():
       
   253             # handle multiple entries for the same name
       
   254             if type(value) not in (type([]), type( () )):
       
   255                 value = [value]
       
   256             for value in value:
       
   257                 value = unicode(value).encode("utf-8")
       
   258                 body.write(sep_boundary)
       
   259                 body.write('\nContent-Disposition: form-data; name="%s"'%key)
       
   260                 body.write("\n\n")
       
   261                 body.write(value)
       
   262                 if value and value[-1] == '\r':
       
   263                     body.write('\n')  # write an extra newline (lurve Macs)
       
   264         body.write(end_boundary)
       
   265         body.write("\n")
       
   266         body = body.getvalue()
       
   267 
       
   268         # build the Request
       
   269         headers = {
       
   270             'Content-type': 'multipart/form-data; boundary=%s; charset=utf-8'%boundary,
       
   271             'Content-length': str(len(body))
       
   272         }
       
   273         req = urllib2.Request(self.repository, body, headers)
       
   274 
       
   275         # handle HTTP and include the Basic Auth handler
       
   276         opener = urllib2.build_opener(
       
   277             urllib2.HTTPBasicAuthHandler(password_mgr=auth)
       
   278         )
       
   279         data = ''
       
   280         try:
       
   281             result = opener.open(req)
       
   282         except urllib2.HTTPError, e:
       
   283             if self.show_response:
       
   284                 data = e.fp.read()
       
   285             result = e.code, e.msg
       
   286         except urllib2.URLError, e:
       
   287             result = 500, str(e)
       
   288         else:
       
   289             if self.show_response:
       
   290                 data = result.read()
       
   291             result = 200, 'OK'
       
   292         if self.show_response:
       
   293             print '-'*75, data, '-'*75
       
   294         return result