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