Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
register.py324 linesDownload Raw Back to command
1"""distutils.command.register2 3Implements the Distutils 'register' command (register with the repository).4"""5 6# created 2002/10/21, Richard Jones7 8import getpass9import io10import logging11import urllib.parse12import urllib.request13from distutils._log import log14from warnings import warn15 16from .._itertools import always_iterable17from ..core import PyPIRCCommand18 19 20class register(PyPIRCCommand):21    description = "register the distribution with the Python package index"22    user_options = PyPIRCCommand.user_options + [23        ('list-classifiers', None, 'list the valid Trove classifiers'),24        (25            'strict',26            None,27            'Will stop the registering if the meta-data are not fully compliant',28        ),29    ]30    boolean_options = PyPIRCCommand.boolean_options + [31        'verify',32        'list-classifiers',33        'strict',34    ]35 36    sub_commands = [('check', lambda self: True)]37 38    def initialize_options(self):39        PyPIRCCommand.initialize_options(self)40        self.list_classifiers = 041        self.strict = 042 43    def finalize_options(self):44        PyPIRCCommand.finalize_options(self)45        # setting options for the `check` subcommand46        check_options = {47            'strict': ('register', self.strict),48            'restructuredtext': ('register', 1),49        }50        self.distribution.command_options['check'] = check_options51 52    def run(self):53        self.finalize_options()54        self._set_config()55 56        # Run sub commands57        for cmd_name in self.get_sub_commands():58            self.run_command(cmd_name)59 60        if self.dry_run:61            self.verify_metadata()62        elif self.list_classifiers:63            self.classifiers()64        else:65            self.send_metadata()66 67    def check_metadata(self):68        """Deprecated API."""69        warn(70            "distutils.command.register.check_metadata is deprecated; "71            "use the check command instead",72            DeprecationWarning,73        )74        check = self.distribution.get_command_obj('check')75        check.ensure_finalized()76        check.strict = self.strict77        check.restructuredtext = 178        check.run()79 80    def _set_config(self):81        """Reads the configuration file and set attributes."""82        config = self._read_pypirc()83        if config != {}:84            self.username = config['username']85            self.password = config['password']86            self.repository = config['repository']87            self.realm = config['realm']88            self.has_config = True89        else:90            if self.repository not in ('pypi', self.DEFAULT_REPOSITORY):91                raise ValueError('%s not found in .pypirc' % self.repository)92            if self.repository == 'pypi':93                self.repository = self.DEFAULT_REPOSITORY94            self.has_config = False95 96    def classifiers(self):97        """Fetch the list of classifiers from the server."""98        url = self.repository + '?:action=list_classifiers'99        response = urllib.request.urlopen(url)100        log.info(self._read_pypi_response(response))101 102    def verify_metadata(self):103        """Send the metadata to the package index server to be checked."""104        # send the info to the server and report the result105        (code, result) = self.post_to_server(self.build_post_data('verify'))106        log.info('Server response (%s): %s', code, result)107 108    def send_metadata(self):  # noqa: C901109        """Send the metadata to the package index server.110 111        Well, do the following:112        1. figure who the user is, and then113        2. send the data as a Basic auth'ed POST.114 115        First we try to read the username/password from $HOME/.pypirc,116        which is a ConfigParser-formatted file with a section117        [distutils] containing username and password entries (both118        in clear text). Eg:119 120            [distutils]121            index-servers =122                pypi123 124            [pypi]125            username: fred126            password: sekrit127 128        Otherwise, to figure who the user is, we offer the user three129        choices:130 131         1. use existing login,132         2. register as a new user, or133         3. set the password to a random string and email the user.134 135        """136        # see if we can short-cut and get the username/password from the137        # config138        if self.has_config:139            choice = '1'140            username = self.username141            password = self.password142        else:143            choice = 'x'144            username = password = ''145 146        # get the user's login info147        choices = '1 2 3 4'.split()148        while choice not in choices:149            self.announce(150                """\151We need to know who you are, so please choose either:152 1. use your existing login,153 2. register as a new user,154 3. have the server generate a new password for you (and email it to you), or155 4. quit156Your selection [default 1]: """,157                logging.INFO,158            )159            choice = input()160            if not choice:161                choice = '1'162            elif choice not in choices:163                print('Please choose one of the four options!')164 165        if choice == '1':166            # get the username and password167            while not username:168                username = input('Username: ')169            while not password:170                password = getpass.getpass('Password: ')171 172            # set up the authentication173            auth = urllib.request.HTTPPasswordMgr()174            host = urllib.parse.urlparse(self.repository)[1]175            auth.add_password(self.realm, host, username, password)176            # send the info to the server and report the result177            code, result = self.post_to_server(self.build_post_data('submit'), auth)178            self.announce(f'Server response ({code}): {result}', logging.INFO)179 180            # possibly save the login181            if code == 200:182                if self.has_config:183                    # sharing the password in the distribution instance184                    # so the upload command can reuse it185                    self.distribution.password = password186                else:187                    self.announce(188                        (189                            'I can store your PyPI login so future '190                            'submissions will be faster.'191                        ),192                        logging.INFO,193                    )194                    self.announce(195                        '(the login will be stored in %s)' % self._get_rc_file(),196                        logging.INFO,197                    )198                    choice = 'X'199                    while choice.lower() not in 'yn':200                        choice = input('Save your login (y/N)?')201                        if not choice:202                            choice = 'n'203                    if choice.lower() == 'y':204                        self._store_pypirc(username, password)205 206        elif choice == '2':207            data = {':action': 'user'}208            data['name'] = data['password'] = data['email'] = ''209            data['confirm'] = None210            while not data['name']:211                data['name'] = input('Username: ')212            while data['password'] != data['confirm']:213                while not data['password']:214                    data['password'] = getpass.getpass('Password: ')215                while not data['confirm']:216                    data['confirm'] = getpass.getpass(' Confirm: ')217                if data['password'] != data['confirm']:218                    data['password'] = ''219                    data['confirm'] = None220                    print("Password and confirm don't match!")221            while not data['email']:222                data['email'] = input('   EMail: ')223            code, result = self.post_to_server(data)224            if code != 200:225                log.info('Server response (%s): %s', code, result)226            else:227                log.info('You will receive an email shortly.')228                log.info('Follow the instructions in it to ' 'complete registration.')229        elif choice == '3':230            data = {':action': 'password_reset'}231            data['email'] = ''232            while not data['email']:233                data['email'] = input('Your email address: ')234            code, result = self.post_to_server(data)235            log.info('Server response (%s): %s', code, result)236 237    def build_post_data(self, action):238        # figure the data to send - the metadata plus some additional239        # information used by the package server240        meta = self.distribution.metadata241        data = {242            ':action': action,243            'metadata_version': '1.0',244            'name': meta.get_name(),245            'version': meta.get_version(),246            'summary': meta.get_description(),247            'home_page': meta.get_url(),248            'author': meta.get_contact(),249            'author_email': meta.get_contact_email(),250            'license': meta.get_licence(),251            'description': meta.get_long_description(),252            'keywords': meta.get_keywords(),253            'platform': meta.get_platforms(),254            'classifiers': meta.get_classifiers(),255            'download_url': meta.get_download_url(),256            # PEP 314257            'provides': meta.get_provides(),258            'requires': meta.get_requires(),259            'obsoletes': meta.get_obsoletes(),260        }261        if data['provides'] or data['requires'] or data['obsoletes']:262            data['metadata_version'] = '1.1'263        return data264 265    def post_to_server(self, data, auth=None):  # noqa: C901266        """Post a query to the server, and return a string response."""267        if 'name' in data:268            self.announce(269                'Registering {} to {}'.format(data['name'], self.repository),270                logging.INFO,271            )272        # Build up the MIME payload for the urllib2 POST data273        boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'274        sep_boundary = '\n--' + boundary275        end_boundary = sep_boundary + '--'276        body = io.StringIO()277        for key, values in data.items():278            for value in map(str, make_iterable(values)):279                body.write(sep_boundary)280                body.write('\nContent-Disposition: form-data; name="%s"' % key)281                body.write("\n\n")282                body.write(value)283                if value and value[-1] == '\r':284                    body.write('\n')  # write an extra newline (lurve Macs)285        body.write(end_boundary)286        body.write("\n")287        body = body.getvalue().encode("utf-8")288 289        # build the Request290        headers = {291            'Content-type': 'multipart/form-data; boundary=%s; charset=utf-8'292            % boundary,293            'Content-length': str(len(body)),294        }295        req = urllib.request.Request(self.repository, body, headers)296 297        # handle HTTP and include the Basic Auth handler298        opener = urllib.request.build_opener(299            urllib.request.HTTPBasicAuthHandler(password_mgr=auth)300        )301        data = ''302        try:303            result = opener.open(req)304        except urllib.error.HTTPError as e:305            if self.show_response:306                data = e.fp.read()307            result = e.code, e.msg308        except urllib.error.URLError as e:309            result = 500, str(e)310        else:311            if self.show_response:312                data = self._read_pypi_response(result)313            result = 200, 'OK'314        if self.show_response:315            msg = '\n'.join(('-' * 75, data, '-' * 75))316            self.announce(msg, logging.INFO)317        return result318 319 320def make_iterable(values):321    if values is None:322        return [None]323    return always_iterable(values)324 
codekingpro/portable-devtools · Team Ai