blob: 7e10fff9d9d32f44d335370350c6a984d96debd8 [file] [log] [blame]
Alexandre Vassalottifc02aef2008-05-15 02:14:05 +00001"""distutils.pypirc
2
3Provides the PyPIRCCommand class, the base class for the command classes
4that uses .pypirc in the distutils.command package.
5"""
Antoine Pitrou335a5122013-12-22 18:13:51 +01006import cgi
Alexandre Vassalottifc02aef2008-05-15 02:14:05 +00007import os
Alexandre Vassalottifc02aef2008-05-15 02:14:05 +00008from configparser import ConfigParser
9
Alexandre Vassalottie9f305f2008-05-16 04:39:54 +000010from distutils.cmd import Command
Alexandre Vassalottifc02aef2008-05-15 02:14:05 +000011
12DEFAULT_PYPIRC = """\
Benjamin Peterson92035012008-12-27 16:00:54 +000013[distutils]
14index-servers =
Alexandre Vassalottifc02aef2008-05-15 02:14:05 +000015 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 """
Antoine Pitrouf60b7df2013-12-22 01:35:53 +010025 DEFAULT_REPOSITORY = 'https://pypi.python.org/pypi'
Alexandre Vassalottifc02aef2008-05-15 02:14:05 +000026 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()
Éric Araujod61926e2012-12-08 14:51:47 -050046 with os.fdopen(os.open(rc, os.O_CREAT | os.O_WRONLY, 0o600), 'w') as f:
Alexandre Vassalottifc02aef2008-05-15 02:14:05 +000047 f.write(DEFAULT_PYPIRC % (username, password))
Alexandre Vassalottifc02aef2008-05-15 02:14:05 +000048
49 def _read_pypirc(self):
50 """Reads the .pypirc file."""
51 rc = self._get_rc_file()
52 if os.path.exists(rc):
Georg Brandl1b466f22008-05-26 17:01:57 +000053 self.announce('Using PyPI login from %s' % rc)
Alexandre Vassalottifc02aef2008-05-15 02:14:05 +000054 repository = self.repository or self.DEFAULT_REPOSITORY
Tarek Ziadé36797272010-07-22 12:50:05 +000055 realm = self.realm or self.DEFAULT_REALM
56
Alexandre Vassalottifc02aef2008-05-15 02:14:05 +000057 config = ConfigParser()
58 config.read(rc)
59 sections = config.sections()
60 if 'distutils' in sections:
61 # let's get the list of servers
62 index_servers = config.get('distutils', 'index-servers')
63 _servers = [server.strip() for server in
64 index_servers.split('\n')
65 if server.strip() != '']
66 if _servers == []:
67 # nothing set, let's try to get the default pypi
68 if 'pypi' in sections:
69 _servers = ['pypi']
70 else:
71 # the file is not properly defined, returning
72 # an empty dict
73 return {}
74 for server in _servers:
75 current = {'server': server}
76 current['username'] = config.get(server, 'username')
Alexandre Vassalottifc02aef2008-05-15 02:14:05 +000077
78 # optional params
79 for key, default in (('repository',
80 self.DEFAULT_REPOSITORY),
Tarek Ziadé13f7c3b2009-01-09 00:15:45 +000081 ('realm', self.DEFAULT_REALM),
82 ('password', None)):
Alexandre Vassalottifc02aef2008-05-15 02:14:05 +000083 if config.has_option(server, key):
84 current[key] = config.get(server, key)
85 else:
86 current[key] = default
87 if (current['server'] == repository or
88 current['repository'] == repository):
89 return current
90 elif 'server-login' in sections:
91 # old format
92 server = 'server-login'
93 if config.has_option(server, 'repository'):
94 repository = config.get(server, 'repository')
95 else:
96 repository = self.DEFAULT_REPOSITORY
97 return {'username': config.get(server, 'username'),
98 'password': config.get(server, 'password'),
99 'repository': repository,
100 'server': server,
101 'realm': self.DEFAULT_REALM}
102
103 return {}
104
Antoine Pitrou335a5122013-12-22 18:13:51 +0100105 def _read_pypi_response(self, response):
106 """Read and decode a PyPI HTTP response."""
107 content_type = response.getheader('content-type', 'text/plain')
108 encoding = cgi.parse_header(content_type)[1].get('charset', 'ascii')
109 return response.read().decode(encoding)
110
Alexandre Vassalottifc02aef2008-05-15 02:14:05 +0000111 def initialize_options(self):
112 """Initialize options."""
113 self.repository = None
114 self.realm = None
115 self.show_response = 0
116
117 def finalize_options(self):
118 """Finalizes options."""
119 if self.repository is None:
120 self.repository = self.DEFAULT_REPOSITORY
121 if self.realm is None:
122 self.realm = self.DEFAULT_REALM