Example #1
0
    def archive(self, build_dir):
        # type: (str) -> None
        """Saves archive to provided build_dir.

        Used for saving downloaded VCS requirements as part of `pip download`.
        """
        assert self.source_dir

        create_archive = True
        archive_name = '{}-{}.zip'.format(self.name, self.metadata["version"])
        archive_path = os.path.join(build_dir, archive_name)

        if os.path.exists(archive_path):
            response = ask_path_exists(
                'The file {} exists. (i)gnore, (w)ipe, '
                '(b)ackup, (a)bort '.format(
                    display_path(archive_path)),
                ('i', 'w', 'b', 'a'))
            if response == 'i':
                create_archive = False
            elif response == 'w':
                logger.warning('Deleting %s', display_path(archive_path))
                os.remove(archive_path)
            elif response == 'b':
                dest_file = backup_dir(archive_path)
                logger.warning(
                    'Backing up %s to %s',
                    display_path(archive_path),
                    display_path(dest_file),
                )
                shutil.move(archive_path, dest_file)
            elif response == 'a':
                sys.exit(-1)

        if not create_archive:
            return

        zip_output = zipfile.ZipFile(
            archive_path, 'w', zipfile.ZIP_DEFLATED, allowZip64=True,
        )
        with zip_output:
            dir = os.path.normcase(
                os.path.abspath(self.unpacked_source_directory)
            )
            for dirpath, dirnames, filenames in os.walk(dir):
                for dirname in dirnames:
                    dir_arcname = self._get_archive_name(
                        dirname, parentdir=dirpath, rootdir=dir,
                    )
                    zipdir = zipfile.ZipInfo(dir_arcname + '/')
                    zipdir.external_attr = 0x1ED << 16  # 0o755
                    zip_output.writestr(zipdir, '')
                for filename in filenames:
                    file_arcname = self._get_archive_name(
                        filename, parentdir=dirpath, rootdir=dir,
                    )
                    filename = os.path.join(dirpath, filename)
                    zip_output.write(filename, file_arcname)

        logger.info('Saved %s', display_path(archive_path))
Example #2
0
    def fetch_new(self, dest, url, rev_options):
        # type: (str, HiddenText, RevOptions) -> None
        rev_display = rev_options.to_display()
        logger.info('Cloning %s%s to %s', url, rev_display, display_path(dest))
        self.run_command(make_command('clone', '-q', url, dest))

        if rev_options.rev:
            # Then a specific revision was requested.
            rev_options = self.resolve_revision(dest, url, rev_options)
            branch_name = getattr(rev_options, 'branch_name', None)
            if branch_name is None:
                # Only do a checkout if the current commit id doesn't match
                # the requested revision.
                if not self.is_commit_id_equal(dest, rev_options.rev):
                    cmd_args = make_command(
                        'checkout',
                        '-q',
                        rev_options.to_args(),
                    )
                    self.run_command(cmd_args, cwd=dest)
            elif self.get_current_branch(dest) != branch_name:
                # Then a specific branch was requested, and that branch
                # is not yet checked out.
                track_branch = 'origin/{}'.format(branch_name)
                cmd_args = [
                    'checkout',
                    '-b',
                    branch_name,
                    '--track',
                    track_branch,
                ]
                self.run_command(cmd_args, cwd=dest)

        #: repo may contain submodules
        self.update_submodules(dest)
Example #3
0
def get_metadata(dist):
    # type: (Distribution) -> Message
    """
    :raises NoneMetadataError: if the distribution reports `has_metadata()`
        True but `get_metadata()` returns None.
    """
    metadata_name = 'METADATA'
    if (isinstance(dist, pkg_resources.DistInfoDistribution)
            and dist.has_metadata(metadata_name)):
        metadata = dist.get_metadata(metadata_name)
    elif dist.has_metadata('PKG-INFO'):
        metadata_name = 'PKG-INFO'
        metadata = dist.get_metadata(metadata_name)
    else:
        logger.warning("No metadata found in %s", display_path(dist.location))
        metadata = ''

    if metadata is None:
        raise NoneMetadataError(dist, metadata_name)

    feed_parser = FeedParser()
    # The following line errors out if with a "NoneType" TypeError if
    # passed metadata=None.
    feed_parser.feed(metadata)
    return feed_parser.close()
Example #4
0
 def fetch_new(self, dest, url, rev_options):
     # type: (str, HiddenText, RevOptions) -> None
     rev_display = rev_options.to_display()
     logger.info(
         'Checking out %s%s to %s',
         url,
         rev_display,
         display_path(dest),
     )
     cmd_args = (make_command('branch', '-q', rev_options.to_args(), url,
                              dest))
     self.run_command(cmd_args)
Example #5
0
 def fetch_new(self, dest, url, rev_options):
     # type: (str, HiddenText, RevOptions) -> None
     rev_display = rev_options.to_display()
     logger.info(
         'Cloning hg %s%s to %s',
         url,
         rev_display,
         display_path(dest),
     )
     self.run_command(make_command('clone', '--noupdate', '-q', url, dest))
     self.run_command(
         make_command('update', '-q', rev_options.to_args()),
         cwd=dest,
     )
Example #6
0
 def assert_source_matches_version(self):
     # type: () -> None
     assert self.source_dir
     version = self.metadata['version']
     if self.req.specifier and version not in self.req.specifier:
         logger.warning(
             'Requested %s, but installing version %s',
             self,
             version,
         )
     else:
         logger.debug(
             'Source in %s has version %s, which satisfies requirement %s',
             display_path(self.source_dir),
             version,
             self,
         )
Example #7
0
 def __str__(self):
     # type: () -> str
     if self.req:
         s = str(self.req)
         if self.link:
             s += ' from {}'.format(redact_auth_from_url(self.link.url))
     elif self.link:
         s = redact_auth_from_url(self.link.url)
     else:
         s = '<InstallRequirement>'
     if self.satisfied_by is not None:
         s += ' in {}'.format(display_path(self.satisfied_by.location))
     if self.comes_from:
         if isinstance(self.comes_from, six.string_types):
             comes_from = self.comes_from  # type: Optional[str]
         else:
             comes_from = self.comes_from.from_path()
         if comes_from:
             s += ' (from {})'.format(comes_from)
     return s
Example #8
0
    def obtain(self, dest, url):
        # type: (str, HiddenText) -> None
        """
        Install or update in editable mode the package represented by this
        VersionControl object.

        :param dest: the repository directory in which to install or update.
        :param url: the repository URL starting with a vcs prefix.
        """
        url, rev_options = self.get_url_rev_options(url)

        if not os.path.exists(dest):
            self.fetch_new(dest, url, rev_options)
            return

        rev_display = rev_options.to_display()
        if self.is_repository_directory(dest):
            existing_url = self.get_remote_url(dest)
            if self.compare_urls(existing_url, url.secret):
                logger.debug(
                    '%s in %s exists, and has correct URL (%s)',
                    self.repo_name.title(),
                    display_path(dest),
                    url,
                )
                if not self.is_commit_id_equal(dest, rev_options.rev):
                    logger.info(
                        'Updating %s %s%s',
                        display_path(dest),
                        self.repo_name,
                        rev_display,
                    )
                    self.update(dest, url, rev_options)
                else:
                    logger.info('Skipping because already up-to-date.')
                return

            logger.warning(
                '%s %s in %s exists with URL %s',
                self.name,
                self.repo_name,
                display_path(dest),
                existing_url,
            )
            prompt = ('(s)witch, (i)gnore, (w)ipe, (b)ackup ', ('s', 'i', 'w',
                                                                'b'))
        else:
            logger.warning(
                'Directory %s already exists, and is not a %s %s.',
                dest,
                self.name,
                self.repo_name,
            )
            # https://github.com/python/mypy/issues/1174
            prompt = (
                '(i)gnore, (w)ipe, (b)ackup ',  # type: ignore
                ('i', 'w', 'b'))

        logger.warning(
            'The plan is to install the %s repository %s',
            self.name,
            url,
        )
Example #9
0
    def prepare_linked_requirement(
            self,
            req,  # type: InstallRequirement
    ):
        # type: (...) -> AbstractDistribution
        """Prepare a requirement that would be obtained from req.link
        """
        assert req.link
        link = req.link

        # TODO: Breakup into smaller functions
        if link.scheme == 'file':
            path = link.file_path
            logger.info('Processing %s', display_path(path))
        else:
            logger.info('Collecting %s', req.req or req)

        download_dir = self.download_dir
        if link.is_wheel and self.wheel_download_dir:
            # when doing 'pip wheel` we download wheels to a
            # dedicated dir.
            download_dir = self.wheel_download_dir

        if link.is_wheel:
            if download_dir:
                # When downloading, we only unpack wheels to get
                # metadata.
                autodelete_unpacked = True
            else:
                # When installing a wheel, we use the unpacked
                # wheel.
                autodelete_unpacked = False
        else:
            # We always delete unpacked sdists after pip runs.
            autodelete_unpacked = True

        with indent_log():
            # Since source_dir is only set for editable requirements.
            assert req.source_dir is None
            req.ensure_has_source_dir(self.build_dir, autodelete_unpacked)
            # If a checkout exists, it's unwise to keep going.  version
            # inconsistencies are logged later, but do not fail the
            # installation.
            # FIXME: this won't upgrade when there's an existing
            # package unpacked in `req.source_dir`
            if os.path.exists(os.path.join(req.source_dir, 'setup.py')):
                raise PreviousBuildDirError(
                    "pip can't proceed with requirements '{}' due to a"
                    " pre-existing build directory ({}). This is "
                    "likely due to a previous installation that failed"
                    ". pip is being responsible and not assuming it "
                    "can delete this. Please delete it and try again.".format(
                        req, req.source_dir))

            # Now that we have the real link, we can tell what kind of
            # requirements we have and raise some more informative errors
            # than otherwise. (For example, we can raise VcsHashUnsupported
            # for a VCS URL rather than HashMissing.)
            if self.require_hashes:
                # We could check these first 2 conditions inside
                # unpack_url and save repetition of conditions, but then
                # we would report less-useful error messages for
                # unhashable requirements, complaining that there's no
                # hash provided.
                if link.is_vcs:
                    raise VcsHashUnsupported()
                elif link.is_existing_dir():
                    raise DirectoryUrlHashUnsupported()
                if not req.original_link and not req.is_pinned:
                    # Unpinned packages are asking for trouble when a new
                    # version is uploaded. This isn't a security check, but
                    # it saves users a surprising hash mismatch in the
                    # future.
                    #
                    # file:/// URLs aren't pinnable, so don't complain
                    # about them not being pinned.
                    raise HashUnpinned()

            hashes = req.hashes(trust_internet=not self.require_hashes)
            if self.require_hashes and not hashes:
                # Known-good hashes are missing for this requirement, so
                # shim it with a facade object that will provoke hash
                # computation and then raise a HashMissing exception
                # showing the user what the hash should be.
                hashes = MissingHashes()

            try:
                local_file = unpack_url(
                    link,
                    req.source_dir,
                    self.downloader,
                    download_dir,
                    hashes=hashes,
                )
            except requests.HTTPError as exc:
                logger.critical(
                    'Could not install requirement %s because of error %s',
                    req,
                    exc,
                )
                raise InstallationError(
                    'Could not install requirement {} because of HTTP '
                    'error {} for URL {}'.format(req, exc, link))

            # For use in later processing, preserve the file path on the
            # requirement.
            if local_file:
                req.local_file_path = local_file.path

            abstract_dist = _get_prepared_distribution(
                req,
                self.req_tracker,
                self.finder,
                self.build_isolation,
            )

            if download_dir:
                if link.is_existing_dir():
                    logger.info('Link is a directory, ignoring download_dir')
                elif local_file:
                    download_location = os.path.join(download_dir,
                                                     link.filename)
                    if not os.path.exists(download_location):
                        shutil.copy(local_file.path, download_location)
                        logger.info('Saved %s',
                                    display_path(download_location))

            if self._download_should_save:
                # Make a .zip of the source_dir we already created.
                if link.is_vcs:
                    req.archive(self.download_dir)
        return abstract_dist