]>
Commit | Line | Data |
---|---|---|
53e6db90 DC |
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 .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 | ||
22 | class 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', "url of repository [default: %s]" % DEFAULT_REPOSITORY), | |
32 | ('show-response', None, 'display full response text from server'), | |
33 | ] | |
34 | ||
35 | boolean_options = ['show-response'] | |
36 | ||
37 | def _get_rc_file(self): | |
38 | """Returns rc file path.""" | |
39 | return os.path.join(os.path.expanduser('~'), '.pypirc') | |
40 | ||
41 | def _store_pypirc(self, username, password): | |
42 | """Creates a default .pypirc file.""" | |
43 | rc = self._get_rc_file() | |
44 | with os.fdopen(os.open(rc, os.O_CREAT | os.O_WRONLY, 0o600), 'w') as f: | |
45 | f.write(DEFAULT_PYPIRC % (username, password)) | |
46 | ||
47 | def _read_pypirc(self): # noqa: C901 | |
48 | """Reads the .pypirc file.""" | |
49 | rc = self._get_rc_file() | |
50 | if os.path.exists(rc): | |
51 | self.announce('Using PyPI login from %s' % rc) | |
52 | repository = self.repository or self.DEFAULT_REPOSITORY | |
53 | ||
54 | config = RawConfigParser() | |
55 | config.read(rc) | |
56 | sections = config.sections() | |
57 | if 'distutils' in sections: | |
58 | # let's get the list of servers | |
59 | index_servers = config.get('distutils', 'index-servers') | |
60 | _servers = [ | |
61 | server.strip() | |
62 | for server in index_servers.split('\n') | |
63 | if server.strip() != '' | |
64 | ] | |
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 ( | |
79 | ('repository', self.DEFAULT_REPOSITORY), | |
80 | ('realm', self.DEFAULT_REALM), | |
81 | ('password', None), | |
82 | ): | |
83 | if config.has_option(server, key): | |
84 | current[key] = config.get(server, key) | |
85 | else: | |
86 | current[key] = default | |
87 | ||
88 | # work around people having "repository" for the "pypi" | |
89 | # section of their config set to the HTTP (rather than | |
90 | # HTTPS) URL | |
91 | if server == 'pypi' and repository in ( | |
92 | self.DEFAULT_REPOSITORY, | |
93 | 'pypi', | |
94 | ): | |
95 | current['repository'] = self.DEFAULT_REPOSITORY | |
96 | return current | |
97 | ||
98 | if ( | |
99 | current['server'] == repository | |
100 | or current['repository'] == repository | |
101 | ): | |
102 | return current | |
103 | elif 'server-login' in sections: | |
104 | # old format | |
105 | server = 'server-login' | |
106 | if config.has_option(server, 'repository'): | |
107 | repository = config.get(server, 'repository') | |
108 | else: | |
109 | repository = self.DEFAULT_REPOSITORY | |
110 | return { | |
111 | 'username': config.get(server, 'username'), | |
112 | 'password': config.get(server, 'password'), | |
113 | 'repository': repository, | |
114 | 'server': server, | |
115 | 'realm': self.DEFAULT_REALM, | |
116 | } | |
117 | ||
118 | return {} | |
119 | ||
120 | def _read_pypi_response(self, response): | |
121 | """Read and decode a PyPI HTTP response.""" | |
122 | import cgi | |
123 | ||
124 | content_type = response.getheader('content-type', 'text/plain') | |
125 | encoding = cgi.parse_header(content_type)[1].get('charset', 'ascii') | |
126 | return response.read().decode(encoding) | |
127 | ||
128 | def initialize_options(self): | |
129 | """Initialize options.""" | |
130 | self.repository = None | |
131 | self.realm = None | |
132 | self.show_response = 0 | |
133 | ||
134 | def finalize_options(self): | |
135 | """Finalizes options.""" | |
136 | if self.repository is None: | |
137 | self.repository = self.DEFAULT_REPOSITORY | |
138 | if self.realm is None: | |
139 | self.realm = self.DEFAULT_REALM |