Beispiel #1
0
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".')
Beispiel #2
0
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".')
Beispiel #3
0
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
Beispiel #4
0
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
Beispiel #5
0
    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)
Beispiel #6
0
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))
Beispiel #7
0
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
Beispiel #8
0
    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])
Beispiel #9
0
    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])
Beispiel #10
0
    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.")
Beispiel #11
0
    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.")
Beispiel #12
0
    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
Beispiel #13
0
    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)
Beispiel #14
0
        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)
Beispiel #15
0
    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)
Beispiel #16
0
    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)
Beispiel #17
0
    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