10c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi"""distutils.command.register
20c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
30c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiImplements the Distutils 'register' command (register with the repository).
40c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi"""
50c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
60c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# created 2002/10/21, Richard Jones
70c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
80c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi__revision__ = "$Id$"
90c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport urllib2
110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport getpass
120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport urlparse
130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yifrom warnings import warn
140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yifrom distutils.core import PyPIRCCommand
160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yifrom distutils import log
170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiclass register(PyPIRCCommand):
190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    description = ("register the distribution with the Python package index")
210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    user_options = PyPIRCCommand.user_options + [
220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        ('list-classifiers', None,
230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi         'list the valid Trove classifiers'),
240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        ('strict', None ,
250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi         'Will stop the registering if the meta-data are not fully compliant')
260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        ]
270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    boolean_options = PyPIRCCommand.boolean_options + [
280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        'verify', 'list-classifiers', 'strict']
290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    sub_commands = [('check', lambda self: True)]
310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def initialize_options(self):
330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        PyPIRCCommand.initialize_options(self)
340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.list_classifiers = 0
350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.strict = 0
360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def finalize_options(self):
380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        PyPIRCCommand.finalize_options(self)
390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # setting options for the `check` subcommand
400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        check_options = {'strict': ('register', self.strict),
410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                         'restructuredtext': ('register', 1)}
420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.distribution.command_options['check'] = check_options
430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def run(self):
450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.finalize_options()
460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self._set_config()
470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # Run sub commands
490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        for cmd_name in self.get_sub_commands():
500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.run_command(cmd_name)
510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if self.dry_run:
530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.verify_metadata()
540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        elif self.list_classifiers:
550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.classifiers()
560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        else:
570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.send_metadata()
580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def check_metadata(self):
600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        """Deprecated API."""
610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        warn("distutils.command.register.check_metadata is deprecated, \
620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi              use the check command instead", PendingDeprecationWarning)
630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        check = self.distribution.get_command_obj('check')
640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        check.ensure_finalized()
650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        check.strict = self.strict
660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        check.restructuredtext = 1
670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        check.run()
680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def _set_config(self):
700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        ''' Reads the configuration file and set attributes.
710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        '''
720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        config = self._read_pypirc()
730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if config != {}:
740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.username = config['username']
750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.password = config['password']
760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.repository = config['repository']
770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.realm = config['realm']
780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.has_config = True
790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        else:
800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if self.repository not in ('pypi', self.DEFAULT_REPOSITORY):
810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                raise ValueError('%s not found in .pypirc' % self.repository)
820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if self.repository == 'pypi':
830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self.repository = self.DEFAULT_REPOSITORY
840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.has_config = False
850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def classifiers(self):
870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        ''' Fetch the list of classifiers from the server.
880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        '''
890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        response = urllib2.urlopen(self.repository+'?:action=list_classifiers')
900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        log.info(response.read())
910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def verify_metadata(self):
930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        ''' Send the metadata to the package index server to be checked.
940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        '''
950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # send the info to the server and report the result
960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        (code, result) = self.post_to_server(self.build_post_data('verify'))
970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        log.info('Server response (%s): %s' % (code, result))
980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def send_metadata(self):
1010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        ''' Send the metadata to the package index server.
1020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            Well, do the following:
1040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            1. figure who the user is, and then
1050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            2. send the data as a Basic auth'ed POST.
1060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            First we try to read the username/password from $HOME/.pypirc,
1080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            which is a ConfigParser-formatted file with a section
1090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            [distutils] containing username and password entries (both
1100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            in clear text). Eg:
1110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                [distutils]
1130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                index-servers =
1140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    pypi
1150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                [pypi]
1170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                username: fred
1180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                password: sekrit
1190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            Otherwise, to figure who the user is, we offer the user three
1210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            choices:
1220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi             1. use existing login,
1240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi             2. register as a new user, or
1250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi             3. set the password to a random string and email the user.
1260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        '''
1280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # see if we can short-cut and get the username/password from the
1290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # config
1300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if self.has_config:
1310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            choice = '1'
1320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            username = self.username
1330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            password = self.password
1340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        else:
1350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            choice = 'x'
1360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            username = password = ''
1370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # get the user's login info
1390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        choices = '1 2 3 4'.split()
1400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        while choice not in choices:
1410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.announce('''\
1420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiWe need to know who you are, so please choose either:
1430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1. use your existing login,
1440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 2. register as a new user,
1450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 3. have the server generate a new password for you (and email it to you), or
1460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 4. quit
1470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiYour selection [default 1]: ''', log.INFO)
1480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            choice = raw_input()
1500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if not choice:
1510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                choice = '1'
1520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            elif choice not in choices:
1530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                print 'Please choose one of the four options!'
1540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if choice == '1':
1560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # get the username and password
1570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            while not username:
1580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                username = raw_input('Username: ')
1590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            while not password:
1600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                password = getpass.getpass('Password: ')
1610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # set up the authentication
1630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            auth = urllib2.HTTPPasswordMgr()
1640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            host = urlparse.urlparse(self.repository)[1]
1650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            auth.add_password(self.realm, host, username, password)
1660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # send the info to the server and report the result
1670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            code, result = self.post_to_server(self.build_post_data('submit'),
1680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                auth)
1690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.announce('Server response (%s): %s' % (code, result),
1700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                          log.INFO)
1710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # possibly save the login
1730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if code == 200:
1740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                if self.has_config:
1750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    # sharing the password in the distribution instance
1760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    # so the upload command can reuse it
1770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    self.distribution.password = password
1780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                else:
1790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    self.announce(('I can store your PyPI login so future '
1800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                                   'submissions will be faster.'), log.INFO)
1810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    self.announce('(the login will be stored in %s)' % \
1820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                                  self._get_rc_file(), log.INFO)
1830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    choice = 'X'
1840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    while choice.lower() not in 'yn':
1850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                        choice = raw_input('Save your login (y/N)?')
1860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                        if not choice:
1870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                            choice = 'n'
1880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    if choice.lower() == 'y':
1890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                        self._store_pypirc(username, password)
1900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        elif choice == '2':
1920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            data = {':action': 'user'}
1930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            data['name'] = data['password'] = data['email'] = ''
1940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            data['confirm'] = None
1950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            while not data['name']:
1960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                data['name'] = raw_input('Username: ')
1970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            while data['password'] != data['confirm']:
1980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                while not data['password']:
1990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    data['password'] = getpass.getpass('Password: ')
2000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                while not data['confirm']:
2010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    data['confirm'] = getpass.getpass(' Confirm: ')
2020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                if data['password'] != data['confirm']:
2030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    data['password'] = ''
2040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    data['confirm'] = None
2050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    print "Password and confirm don't match!"
2060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            while not data['email']:
2070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                data['email'] = raw_input('   EMail: ')
2080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            code, result = self.post_to_server(data)
2090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if code != 200:
2100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                log.info('Server response (%s): %s' % (code, result))
2110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            else:
2120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                log.info('You will receive an email shortly.')
2130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                log.info(('Follow the instructions in it to '
2140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                          'complete registration.'))
2150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        elif choice == '3':
2160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            data = {':action': 'password_reset'}
2170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            data['email'] = ''
2180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            while not data['email']:
2190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                data['email'] = raw_input('Your email address: ')
2200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            code, result = self.post_to_server(data)
2210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            log.info('Server response (%s): %s' % (code, result))
2220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def build_post_data(self, action):
2240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # figure the data to send - the metadata plus some additional
2250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # information used by the package server
2260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        meta = self.distribution.metadata
2270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        data = {
2280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            ':action': action,
2290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            'metadata_version' : '1.0',
2300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            'name': meta.get_name(),
2310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            'version': meta.get_version(),
2320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            'summary': meta.get_description(),
2330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            'home_page': meta.get_url(),
2340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            'author': meta.get_contact(),
2350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            'author_email': meta.get_contact_email(),
2360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            'license': meta.get_licence(),
2370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            'description': meta.get_long_description(),
2380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            'keywords': meta.get_keywords(),
2390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            'platform': meta.get_platforms(),
2400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            'classifiers': meta.get_classifiers(),
2410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            'download_url': meta.get_download_url(),
2420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # PEP 314
2430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            'provides': meta.get_provides(),
2440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            'requires': meta.get_requires(),
2450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            'obsoletes': meta.get_obsoletes(),
2460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        }
2470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if data['provides'] or data['requires'] or data['obsoletes']:
2480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            data['metadata_version'] = '1.1'
2490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return data
2500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def post_to_server(self, data, auth=None):
2520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        ''' Post a query to the server, and return a string response.
2530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        '''
2540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if 'name' in data:
2550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.announce('Registering %s to %s' % (data['name'],
2560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                                                   self.repository),
2570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                                                   log.INFO)
2580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # Build up the MIME payload for the urllib2 POST data
2590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
2600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        sep_boundary = '\n--' + boundary
2610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        end_boundary = sep_boundary + '--'
2620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        chunks = []
2630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        for key, value in data.items():
2640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # handle multiple entries for the same name
2650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if type(value) not in (type([]), type( () )):
2660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                value = [value]
2670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            for value in value:
2680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                chunks.append(sep_boundary)
2690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                chunks.append('\nContent-Disposition: form-data; name="%s"'%key)
2700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                chunks.append("\n\n")
2710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                chunks.append(value)
2720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                if value and value[-1] == '\r':
2730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    chunks.append('\n')  # write an extra newline (lurve Macs)
2740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        chunks.append(end_boundary)
2750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        chunks.append("\n")
2760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # chunks may be bytes (str) or unicode objects that we need to encode
2780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        body = []
2790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        for chunk in chunks:
2800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if isinstance(chunk, unicode):
2810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                body.append(chunk.encode('utf-8'))
2820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            else:
2830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                body.append(chunk)
2840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        body = ''.join(body)
2860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # build the Request
2880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        headers = {
2890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            'Content-type': 'multipart/form-data; boundary=%s; charset=utf-8'%boundary,
2900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            'Content-length': str(len(body))
2910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        }
2920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        req = urllib2.Request(self.repository, body, headers)
2930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # handle HTTP and include the Basic Auth handler
2950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        opener = urllib2.build_opener(
2960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            urllib2.HTTPBasicAuthHandler(password_mgr=auth)
2970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        )
2980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        data = ''
2990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        try:
3000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            result = opener.open(req)
3010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        except urllib2.HTTPError, e:
3020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if self.show_response:
3030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                data = e.fp.read()
3040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            result = e.code, e.msg
3050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        except urllib2.URLError, e:
3060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            result = 500, str(e)
3070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        else:
3080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if self.show_response:
3090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                data = result.read()
3100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            result = 200, 'OK'
3110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if self.show_response:
3120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            dashes = '-' * 75
3130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.announce('%s%s%s' % (dashes, data, dashes))
3140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return result
316