def ask_yn(question, default=None, helptext=None): question += ' (y/n)' while True: answer = ask(question, default, helptext, required=True) if answer and answer[0].lower() in ('y', 'n'): return answer[0].lower() logger.error('You must select "Y" or "N".')
def _search(dispatcher, args, **kw): """The search action. It is able to search for a specific index (specified with --index), using the simple or xmlrpc index types (with --type xmlrpc / --type simple) """ #opts = _parse_args(args[1:], '', ['simple', 'xmlrpc']) # 1. what kind of index is requested ? (xmlrpc / simple) logger.error('not implemented') return 1
def run(self): name = self.distribution.metadata['Name'] version = self.distribution.metadata['Version'] zip_file = zip_dir(self.upload_dir) fields = [(':action', 'doc_upload'), ('name', name), ('version', version)] files = [('content', name, zip_file.getvalue())] content_type, body = encode_multipart(fields, files) credentials = self.username + ':' + self.password # FIXME should use explicit encoding auth = b"Basic " + base64.encodebytes(credentials.encode()).strip() logger.info("Submitting documentation to %s", self.repository) scheme, netloc, url, params, query, fragments = urllib.parse.urlparse( self.repository) if scheme == "http": conn = http.client.HTTPConnection(netloc) elif scheme == "https": conn = http.client.HTTPSConnection(netloc) else: raise AssertionError("unsupported scheme %r" % scheme) try: conn.connect() conn.putrequest("POST", url) conn.putheader('Content-type', content_type) conn.putheader('Content-length', str(len(body))) conn.putheader('Authorization', auth) conn.endheaders() conn.send(body) except socket.error as e: logger.error(e) return r = conn.getresponse() if r.status == 200: logger.info('Server response (%s): %s', r.status, r.reason) elif r.status == 301: location = r.getheader('Location') if location is None: location = 'http://packages.python.org/%s/' % name logger.info('Upload successful. Visit %s', location) else: logger.error('Upload failed (%s): %s', r.status, r.reason) if self.show_response and logger.isEnabledFor(logging.INFO): sep = '-' * 75 logger.info('%s\n%s\n%s', sep, r.read().decode('utf-8'), sep)
def install(project): """Installs a project. Returns True on success, False on failure """ if is_python_build(): # Python would try to install into the site-packages directory under # $PREFIX, but when running from an uninstalled code checkout we don't # want to create directories under the installation root message = ('installing third-party projects from an uninstalled ' 'Python is not supported') logger.error(message) return False logger.info('Checking the installation location...') purelib_path = get_path('purelib') # trying to write a file there try: testfile = tempfile.NamedTemporaryFile(suffix=project, dir=purelib_path) try: testfile.write('test') finally: testfile.close() except OSError: # FIXME this should check the errno, or be removed altogether (race # condition: the directory permissions could be changed between here # and the actual install) logger.info('Unable to write in "%s". Do you have the permissions ?' % purelib_path) return False logger.info('Getting information about %r...', project) try: info = get_infos(project) except InstallationException: logger.info('Cound not find %r', project) return False if info['install'] == []: logger.info('Nothing to install') return False install_path = get_config_var('base') try: install_from_infos(install_path, info['install'], info['remove'], info['conflict']) except InstallationConflict, e: if logger.isEnabledFor(logging.INFO): projects = ('%r %s' % (p.name, p.version) for p in e.args[0]) logger.info('%r conflicts with %s', project, ','.join(projects))
def install(project): """Installs a project. Returns True on success, False on failure """ if is_python_build(): # Python would try to install into the site-packages directory under # $PREFIX, but when running from an uninstalled code checkout we don't # want to create directories under the installation root message = ('installing third-party projects from an uninstalled ' 'Python is not supported') logger.error(message) return False logger.info('Checking the installation location...') purelib_path = get_path('purelib') # trying to write a file there try: with tempfile.NamedTemporaryFile(suffix=project, dir=purelib_path) as testfile: testfile.write(b'test') except OSError: # FIXME this should check the errno, or be removed altogether (race # condition: the directory permissions could be changed between here # and the actual install) logger.info('Unable to write in "%s". Do you have the permissions ?' % purelib_path) return False logger.info('Getting information about %r...', project) try: info = get_infos(project) except InstallationException: logger.info('Cound not find %r', project) return False if info['install'] == []: logger.info('Nothing to install') return False install_path = get_config_var('base') try: install_from_infos(install_path, info['install'], info['remove'], info['conflict']) except InstallationConflict as e: if logger.isEnabledFor(logging.INFO): projects = ('%r %s' % (p.name, p.version) for p in e.args[0]) logger.info('%r conflicts with %s', project, ','.join(projects)) return True
def set_license(self, classifiers): while True: license = ask('What license do you use?', helptext=_helptext['trove_license'], required=False) if not license: return license_words = license.lower().split(' ') found_list = [] for index, licence in LICENCES: for word in license_words: if word in licence: found_list.append(index) break if len(found_list) == 0: logger.error('Could not find a matching license for "%s"' % license) continue question = 'Matching licenses:\n\n' for index, list_index in enumerate(found_list): question += ' %s) %s\n' % (index + 1, _CLASSIFIERS_LIST[list_index]) question += ('\nType the number of the license you wish to use or ' '? to try again:') choice = ask(question, required=False) if choice == '?': continue if choice == '': return try: index = found_list[int(choice) - 1] except ValueError: logger.error( "Invalid selection, type a number from the list above.") classifiers.add(_CLASSIFIERS_LIST[index])
def set_maturity_status(self, classifiers): maturity_name = lambda mat: mat.split('- ')[-1] maturity_question = '''\ Please select the project status: %s Status''' % '\n'.join('%s - %s' % (i, maturity_name(n)) for i, n in enumerate(PROJECT_MATURITY)) while True: choice = ask(dedent(maturity_question), required=False) if choice: try: choice = int(choice) - 1 key = PROJECT_MATURITY[choice] classifiers.add(key) return except (IndexError, ValueError): logger.error( "Invalid selection, type a single digit number.")
def run(self): name = self.distribution.metadata['Name'] version = self.distribution.metadata['Version'] zip_file = zip_dir(self.upload_dir) fields = [(':action', 'doc_upload'), ('name', name), ('version', version)] files = [('content', name, zip_file.getvalue())] content_type, body = encode_multipart(fields, files) credentials = self.username + ':' + self.password # FIXME should use explicit encoding auth = "Basic " + base64.encodestring(credentials.encode()).strip() logger.info("Submitting documentation to %s", self.repository) scheme, netloc, url, params, query, fragments = urlparse.urlparse( self.repository) if scheme == "http": conn = httplib.HTTPConnection(netloc) elif scheme == "https": conn = httplib.HTTPSConnection(netloc) else: raise AssertionError("unsupported scheme %r" % scheme) try: conn.connect() conn.putrequest("POST", url) conn.putheader('Content-type', content_type) conn.putheader('Content-length', str(len(body))) conn.putheader('Authorization', auth) conn.endheaders() conn.send(body) except socket.error, e: logger.error(e) return
def _write_cfg(self): if os.path.exists(_FILENAME): if os.path.exists('%s.old' % _FILENAME): message = ("ERROR: %(name)s.old backup exists, please check " "that current %(name)s is correct and remove " "%(name)s.old" % { 'name': _FILENAME }) logger.error(message) return shutil.move(_FILENAME, '%s.old' % _FILENAME) with open(_FILENAME, 'w', encoding='utf-8') as fp: fp.write('[metadata]\n') # TODO use metadata module instead of hard-coding field-specific # behavior here # simple string entries for name in ('name', 'version', 'summary', 'download_url'): fp.write('%s = %s\n' % (name, self.data.get(name, 'UNKNOWN'))) # optional string entries if 'keywords' in self.data and self.data['keywords']: # XXX shoud use comma to separate, not space fp.write('keywords = %s\n' % ' '.join(self.data['keywords'])) for name in ('home_page', 'author', 'author_email', 'maintainer', 'maintainer_email', 'description-file'): if name in self.data and self.data[name]: fp.write('%s = %s\n' % (name, self.data[name])) if 'description' in self.data: fp.write( 'description = %s\n' % '\n |'.join(self.data['description'].split('\n'))) # multiple use string entries for name in ('platform', 'supported-platform', 'classifier', 'requires-dist', 'provides-dist', 'obsoletes-dist', 'requires-external'): if not (name in self.data and self.data[name]): continue fp.write('%s = ' % name) fp.write(''.join(' %s\n' % val for val in self.data[name]).lstrip()) fp.write('\n[files]\n') for name in ('packages', 'modules', 'scripts', 'extra_files'): if not (name in self.data and self.data[name]): continue fp.write('%s = %s\n' % (name, '\n '.join(self.data[name]).strip())) if self.data.get('package_data'): fp.write('package_data =\n') for pkg, spec in sorted(self.data['package_data'].items()): # put one spec per line, indented under the package name indent = ' ' * (len(pkg) + 7) spec = ('\n' + indent).join(spec) fp.write(' %s = %s\n' % (pkg, spec)) fp.write('\n') if self.data.get('resources'): fp.write('resources =\n') for src, dest in self.data['resources']: fp.write(' %s = %s\n' % (src, dest)) fp.write('\n') os.chmod(_FILENAME, 0o644) logger.info('Wrote "%s".' % _FILENAME)
content_type, body = encode_multipart(data.items(), files) logger.info("Submitting %s to %s", filename, self.repository) # build the Request headers = {'Content-type': content_type, 'Content-length': str(len(body)), 'Authorization': auth} request = Request(self.repository, body, headers) # send the data try: result = urlopen(request) status = result.code reason = result.msg except socket.error, e: logger.error(e) return except HTTPError, e: status = e.code reason = e.msg if status == 200: logger.info('Server response (%s): %s', status, reason) else: logger.error('Upload failed (%s): %s', status, reason) if self.show_response and logger.isEnabledFor(logging.INFO): sep = '-' * 75 logger.info('%s\n%s\n%s', sep, result.read().decode(), sep)
def _write_cfg(self): if os.path.exists(_FILENAME): if os.path.exists('%s.old' % _FILENAME): message = ("ERROR: %(name)s.old backup exists, please check " "that current %(name)s is correct and remove " "%(name)s.old" % {'name': _FILENAME}) logger.error(message) return shutil.move(_FILENAME, '%s.old' % _FILENAME) fp = codecs.open(_FILENAME, 'w', encoding='utf-8') try: fp.write(u'[metadata]\n') # TODO use metadata module instead of hard-coding field-specific # behavior here # simple string entries for name in ('name', 'version', 'summary', 'download_url'): fp.write(u'%s = %s\n' % (name, self.data.get(name, 'UNKNOWN'))) # optional string entries if 'keywords' in self.data and self.data['keywords']: # XXX shoud use comma to separate, not space fp.write(u'keywords = %s\n' % ' '.join(self.data['keywords'])) for name in ('home_page', 'author', 'author_email', 'maintainer', 'maintainer_email', 'description-file'): if name in self.data and self.data[name]: fp.write(u'%s = %s\n' % (name.decode('utf-8'), self.data[name].decode('utf-8'))) if 'description' in self.data: fp.write( u'description = %s\n' % u'\n |'.join(self.data['description'].split('\n'))) # multiple use string entries for name in ('platform', 'supported-platform', 'classifier', 'requires-dist', 'provides-dist', 'obsoletes-dist', 'requires-external'): if not(name in self.data and self.data[name]): continue fp.write(u'%s = ' % name) fp.write(u''.join(' %s\n' % val for val in self.data[name]).lstrip()) fp.write(u'\n[files]\n') for name in ('packages', 'modules', 'scripts', 'extra_files'): if not(name in self.data and self.data[name]): continue fp.write(u'%s = %s\n' % (name, u'\n '.join(self.data[name]).strip())) if self.data.get('package_data'): fp.write(u'package_data =\n') for pkg, spec in sorted(self.data['package_data'].items()): # put one spec per line, indented under the package name indent = u' ' * (len(pkg) + 7) spec = (u'\n' + indent).join(spec) fp.write(u' %s = %s\n' % (pkg, spec)) fp.write(u'\n') if self.data.get('resources'): fp.write(u'resources =\n') for src, dest in self.data['resources']: fp.write(u' %s = %s\n' % (src, dest)) fp.write(u'\n') finally: fp.close() os.chmod(_FILENAME, 0644) logger.info('Wrote "%s".' % _FILENAME)
def upload_file(self, command, pyversion, filename): # Makes sure the repository URL is compliant scheme, netloc, url, params, query, fragments = \ urllib.parse.urlparse(self.repository) if params or query or fragments: raise AssertionError("Incompatible url %s" % self.repository) if scheme not in ('http', 'https'): raise AssertionError("unsupported scheme " + scheme) # Sign if requested if self.sign: gpg_args = ["gpg", "--detach-sign", "-a", filename] if self.identity: gpg_args[2:2] = ["--local-user", self.identity] spawn(gpg_args, dry_run=self.dry_run) # Fill in the data - send all the metadata in case we need to # register a new release with open(filename, 'rb') as f: content = f.read() data = self.distribution.metadata.todict() # extra upload infos data[':action'] = 'file_upload' data['protcol_version'] = '1' data['content'] = (os.path.basename(filename), content) data['filetype'] = command data['pyversion'] = pyversion data['md5_digest'] = md5(content).hexdigest() if command == 'bdist_dumb': data['comment'] = 'built for %s' % platform.platform(terse=True) if self.sign: with open(filename + '.asc') as fp: sig = fp.read() data['gpg_signature'] = [ (os.path.basename(filename) + ".asc", sig)] # set up the authentication # The exact encoding of the authentication string is debated. # Anyway PyPI only accepts ascii for both username or password. user_pass = (self.username + ":" + self.password).encode('ascii') auth = b"Basic " + standard_b64encode(user_pass) # Build up the MIME payload for the POST data files = [] for key in ('content', 'gpg_signature'): if key in data: filename_, value = data.pop(key) files.append((key, filename_, value)) content_type, body = encode_multipart(data.items(), files) logger.info("Submitting %s to %s", filename, self.repository) # build the Request headers = {'Content-type': content_type, 'Content-length': str(len(body)), 'Authorization': auth} request = Request(self.repository, body, headers) # send the data try: result = urlopen(request) status = result.code reason = result.msg except socket.error as e: logger.error(e) return except HTTPError as e: status = e.code reason = e.msg if status == 200: logger.info('Server response (%s): %s', status, reason) else: logger.error('Upload failed (%s): %s', status, reason) if self.show_response and logger.isEnabledFor(logging.INFO): sep = '-' * 75 logger.info('%s\n%s\n%s', sep, result.read().decode(), sep)
def upload_file(self, command, pyversion, filename): # Makes sure the repository URL is compliant scheme, netloc, url, params, query, fragments = \ urlparse.urlparse(self.repository) if params or query or fragments: raise AssertionError("Incompatible url %s" % self.repository) if scheme not in ('http', 'https'): raise AssertionError("unsupported scheme " + scheme) # Sign if requested if self.sign: gpg_args = ["gpg", "--detach-sign", "-a", filename] if self.identity: gpg_args[2:2] = ["--local-user", self.identity] spawn(gpg_args, dry_run=self.dry_run) # Fill in the data - send all the metadata in case we need to # register a new release f = open(filename, 'rb') try: content = f.read() finally: f.close() data = self.distribution.metadata.todict() # extra upload infos data[':action'] = 'file_upload' data['protcol_version'] = '1' data['content'] = (os.path.basename(filename), content) data['filetype'] = command data['pyversion'] = pyversion data['md5_digest'] = md5(content).hexdigest() if command == 'bdist_dumb': data['comment'] = 'built for %s' % platform.platform(terse=True) if self.sign: fp = open(filename + '.asc') try: sig = fp.read() finally: fp.close() data['gpg_signature'] = [ (os.path.basename(filename) + ".asc", sig)] # set up the authentication # The exact encoding of the authentication string is debated. # Anyway PyPI only accepts ascii for both username or password. user_pass = (self.username + ":" + self.password).encode('ascii') auth = "Basic " + standard_b64encode(user_pass) # Build up the MIME payload for the POST data files = [] for key in ('content', 'gpg_signature'): if key in data: filename_, value = data.pop(key) files.append((key, filename_, value)) content_type, body = encode_multipart(data.items(), files) logger.info("Submitting %s to %s", filename, self.repository) # build the Request headers = {'Content-type': content_type, 'Content-length': str(len(body)), 'Authorization': auth} request = Request(self.repository, body, headers) # send the data try: result = urlopen(request) status = result.code reason = result.msg except socket.error, e: logger.error(e) return