1"""distutils.pypirc
2
3Provides the PyPIRCCommand class, the base class for the command classes
4that uses .pypirc in the distutils.command package.
5"""
6import os
7from configparser import RawConfigParser
8import warnings
9
10from distutils.cmd import Command
11
12DEFAULT_PYPIRC = """\
13[distutils]
14index-servers =
15    pypi
16
17[pypi]
18username:%s
19password:%s
20"""
21
22class PyPIRCCommand(Command):
23    """Base command that knows how to handle the .pypirc file
24    """
25    DEFAULT_REPOSITORY = 'https://upload.pypi.org/legacy/'
26    DEFAULT_REALM = 'pypi'
27    repository = None
28    realm = None
29
30    user_options = [
31        ('repository=', 'r',
32         "url of repository [default: %s]" % \
33            DEFAULT_REPOSITORY),
34        ('show-response', None,
35         'display full response text from server')]
36
37    boolean_options = ['show-response']
38
39    def _get_rc_file(self):
40        """Returns rc file path."""
41        return os.path.join(os.path.expanduser('~'), '.pypirc')
42
43    def _store_pypirc(self, username, password):
44        """Creates a default .pypirc file."""
45        rc = self._get_rc_file()
46        with os.fdopen(os.open(rc, os.O_CREAT | os.O_WRONLY, 0o600), 'w') as f:
47            f.write(DEFAULT_PYPIRC % (username, password))
48
49    def _read_pypirc(self):
50        """Reads the .pypirc file."""
51        rc = self._get_rc_file()
52        if os.path.exists(rc):
53            self.announce('Using PyPI login from %s' % rc)
54            repository = self.repository or self.DEFAULT_REPOSITORY
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        with warnings.catch_warnings():
116            warnings.simplefilter("ignore", DeprecationWarning)
117            import cgi
118        content_type = response.getheader('content-type', 'text/plain')
119        encoding = cgi.parse_header(content_type)[1].get('charset', 'ascii')
120        return response.read().decode(encoding)
121
122    def initialize_options(self):
123        """Initialize options."""
124        self.repository = None
125        self.realm = None
126        self.show_response = 0
127
128    def finalize_options(self):
129        """Finalizes options."""
130        if self.repository is None:
131            self.repository = self.DEFAULT_REPOSITORY
132        if self.realm is None:
133            self.realm = self.DEFAULT_REALM
134