Home | History | Annotate | Download | only in distutils
      1 """distutils.pypirc
      2 
      3 Provides the PyPIRCCommand class, the base class for the command classes
      4 that uses .pypirc in the distutils.command package.
      5 """
      6 import os
      7 from configparser import RawConfigParser
      8 
      9 from distutils.cmd import Command
     10 
     11 DEFAULT_PYPIRC = """\
     12 [distutils]
     13 index-servers =
     14     pypi
     15 
     16 [pypi]
     17 username:%s
     18 password:%s
     19 """
     20 
     21 class PyPIRCCommand(Command):
     22     """Base command that knows how to handle the .pypirc file
     23     """
     24     DEFAULT_REPOSITORY = 'https://upload.pypi.org/legacy/'
     25     DEFAULT_REALM = 'pypi'
     26     repository = None
     27     realm = None
     28 
     29     user_options = [
     30         ('repository=', 'r',
     31          "url of repository [default: %s]" % \
     32             DEFAULT_REPOSITORY),
     33         ('show-response', None,
     34          'display full response text from server')]
     35 
     36     boolean_options = ['show-response']
     37 
     38     def _get_rc_file(self):
     39         """Returns rc file path."""
     40         return os.path.join(os.path.expanduser('~'), '.pypirc')
     41 
     42     def _store_pypirc(self, username, password):
     43         """Creates a default .pypirc file."""
     44         rc = self._get_rc_file()
     45         with os.fdopen(os.open(rc, os.O_CREAT | os.O_WRONLY, 0o600), 'w') as f:
     46             f.write(DEFAULT_PYPIRC % (username, password))
     47 
     48     def _read_pypirc(self):
     49         """Reads the .pypirc file."""
     50         rc = self._get_rc_file()
     51         if os.path.exists(rc):
     52             self.announce('Using PyPI login from %s' % rc)
     53             repository = self.repository or self.DEFAULT_REPOSITORY
     54             realm = self.realm or self.DEFAULT_REALM
     55 
     56             config = RawConfigParser()
     57             config.read(rc)
     58             sections = config.sections()
     59             if 'distutils' in sections:
     60                 # let's get the list of servers
     61                 index_servers = config.get('distutils', 'index-servers')
     62                 _servers = [server.strip() for server in
     63                             index_servers.split('\n')
     64                             if server.strip() != '']
     65                 if _servers == []:
     66                     # nothing set, let's try to get the default pypi
     67                     if 'pypi' in sections:
     68                         _servers = ['pypi']
     69                     else:
     70                         # the file is not properly defined, returning
     71                         # an empty dict
     72                         return {}
     73                 for server in _servers:
     74                     current = {'server': server}
     75                     current['username'] = config.get(server, 'username')
     76 
     77                     # optional params
     78                     for key, default in (('repository',
     79                                           self.DEFAULT_REPOSITORY),
     80                                          ('realm', self.DEFAULT_REALM),
     81                                          ('password', None)):
     82                         if config.has_option(server, key):
     83                             current[key] = config.get(server, key)
     84                         else:
     85                             current[key] = default
     86 
     87                     # work around people having "repository" for the "pypi"
     88                     # section of their config set to the HTTP (rather than
     89                     # HTTPS) URL
     90                     if (server == 'pypi' and
     91                         repository in (self.DEFAULT_REPOSITORY, 'pypi')):
     92                         current['repository'] = self.DEFAULT_REPOSITORY
     93                         return current
     94 
     95                     if (current['server'] == repository or
     96                         current['repository'] == repository):
     97                         return current
     98             elif 'server-login' in sections:
     99                 # old format
    100                 server = 'server-login'
    101                 if config.has_option(server, 'repository'):
    102                     repository = config.get(server, 'repository')
    103                 else:
    104                     repository = self.DEFAULT_REPOSITORY
    105                 return {'username': config.get(server, 'username'),
    106                         'password': config.get(server, 'password'),
    107                         'repository': repository,
    108                         'server': server,
    109                         'realm': self.DEFAULT_REALM}
    110 
    111         return {}
    112 
    113     def _read_pypi_response(self, response):
    114         """Read and decode a PyPI HTTP response."""
    115         import cgi
    116         content_type = response.getheader('content-type', 'text/plain')
    117         encoding = cgi.parse_header(content_type)[1].get('charset', 'ascii')
    118         return response.read().decode(encoding)
    119 
    120     def initialize_options(self):
    121         """Initialize options."""
    122         self.repository = None
    123         self.realm = None
    124         self.show_response = 0
    125 
    126     def finalize_options(self):
    127         """Finalizes options."""
    128         if self.repository is None:
    129             self.repository = self.DEFAULT_REPOSITORY
    130         if self.realm is None:
    131             self.realm = self.DEFAULT_REALM
    132