示例#1
0
    def decorate_arguments(self, arguments):
        """
        Change pathnames of local files into ``file://`` URLs with ``#md5=...`` fragments.

        :param arguments: The command line arguments to ``pip install ...`` (a
                          list of strings).
        :returns: A copy of the command line arguments with pathnames of local
                  files rewritten to ``file://`` URLs.

        When pip-accel calls pip to download missing distribution archives and
        the user specified the pathname of a local distribution archive on the
        command line, pip will (by default) *not* copy the archive into the
        download directory if an archive for the same package name and
        version is already present.

        This can lead to the confusing situation where the user specifies a
        local distribution archive to install, a different (older) archive for
        the same package and version is present in the download directory and
        `pip-accel` installs the older archive instead of the newer archive.

        To avoid this confusing behavior, the :func:`decorate_arguments()`
        method rewrites the command line arguments given to ``pip install`` so
        that pathnames of local archives are changed into ``file://`` URLs that
        include a fragment with the hash of the file's contents. Here's an
        example:

        - Local pathname: ``/tmp/pep8-1.6.3a0.tar.gz``
        - File URL: ``file:///tmp/pep8-1.6.3a0.tar.gz#md5=19cbf0b633498ead63fb3c66e5f1caf6``

        When pip fills the download directory and encounters a previously
        cached distribution archive it will check the hash, realize the
        contents have changed and replace the archive in the download
        directory.
        """
        arguments = list(arguments)
        for i, value in enumerate(arguments):
            is_constraint_file = (i >= 1 and match_option(
                arguments[i - 1], '-c', '--constraint'))
            is_requirement_file = (i >= 1 and match_option(
                arguments[i - 1], '-r', '--requirement'))
            if not is_constraint_file and not is_requirement_file and os.path.isfile(
                    value):
                arguments[i] = '%s#md5=%s' % (create_file_url(value),
                                              hash_files('md5', value))
        return arguments
示例#2
0
    def decorate_arguments(self, arguments):
        """
        Change pathnames of local files into ``file://`` URLs with ``#md5=...`` fragments.

        :param arguments: The command line arguments to ``pip install ...`` (a
                          list of strings).
        :returns: A copy of the command line arguments with pathnames of local
                  files rewritten to ``file://`` URLs.

        When pip-accel calls pip to download missing distribution archives and
        the user specified the pathname of a local distribution archive on the
        command line, pip will (by default) *not* copy the archive into the
        download directory if an archive for the same package name and
        version is already present.

        This can lead to the confusing situation where the user specifies a
        local distribution archive to install, a different (older) archive for
        the same package and version is present in the download directory and
        `pip-accel` installs the older archive instead of the newer archive.

        To avoid this confusing behavior, the :func:`decorate_arguments()`
        method rewrites the command line arguments given to ``pip install`` so
        that pathnames of local archives are changed into ``file://`` URLs that
        include a fragment with the hash of the file's contents. Here's an
        example:

        - Local pathname: ``/tmp/pep8-1.6.3a0.tar.gz``
        - File URL: ``file:///tmp/pep8-1.6.3a0.tar.gz#md5=19cbf0b633498ead63fb3c66e5f1caf6``

        When pip fills the download directory and encounters a previously
        cached distribution archive it will check the hash, realize the
        contents have changed and replace the archive in the download
        directory.
        """
        arguments = list(arguments)
        for i, value in enumerate(arguments):
            is_constraint_file = (i >= 1 and match_option(arguments[i - 1], '-c', '--constraint'))
            is_requirement_file = (i >= 1 and match_option(arguments[i - 1], '-r', '--requirement'))
            if not is_constraint_file and not is_requirement_file and os.path.isfile(value):
                arguments[i] = '%s#md5=%s' % (create_file_url(value), hash_files('md5', value))
        return arguments
示例#3
0
    def get_pip_requirement_set(self, arguments, use_remote_index, use_wheels=False):
        """
        Get the unpacked requirement(s) specified by the caller by running pip.

        :param arguments: The command line arguments to ``pip install ...`` (a
                          list of strings).
        :param use_remote_index: A boolean indicating whether pip is allowed to
                                 connect to the main package index
                                 (http://pypi.python.org by default).
        :param use_wheels: Whether pip and pip-accel are allowed to use wheels_
                           (:data:`False` by default for backwards compatibility
                           with callers that use pip-accel as a Python API).
        :returns: A :class:`pip.req.RequirementSet` object created by pip.
        :raises: Any exceptions raised by pip.
        """
        # Compose the pip command line arguments. This is where a lot of the
        # core logic of pip-accel is hidden and it uses some esoteric features
        # of pip so this method is heavily commented.
        command_line = []
        # Use `--download' to instruct pip to download requirement(s) into
        # pip-accel's local source distribution index directory. This has the
        # following documented side effects (see `pip install --help'):
        #  1. It disables the installation of requirements (without using the
        #     `--no-install' option which is deprecated and slated for removal
        #     in pip 7.x).
        #  2. It ignores requirements that are already installed (because
        #     pip-accel doesn't actually need to re-install requirements that
        #     are already installed we will have work around this later, but
        #     that seems fairly simple to do).
        command_line.append('--download=%s' % self.config.source_index)
        # Use `--find-links' to point pip at pip-accel's local source
        # distribution index directory. This ensures that source distribution
        # archives are never downloaded more than once (regardless of the HTTP
        # cache that was introduced in pip 6.x).
        command_line.append('--find-links=%s' % create_file_url(self.config.source_index))
        # Use `--no-binary=:all:' to ignore wheel distributions by default in
        # order to preserve backwards compatibility with callers that expect a
        # requirement set consisting only of source distributions that can be
        # converted to `dumb binary distributions'.
        if not use_wheels and self.arguments_allow_wheels(arguments):
            command_line.append('--no-binary=:all:')
        # Use `--no-index' to force pip to only consider source distribution
        # archives contained in pip-accel's local source distribution index
        # directory. This enables pip-accel to ask pip "Can the local source
        # distribution index satisfy all requirements in the given requirement
        # set?" which enables pip-accel to keep pip off the internet unless
        # absolutely necessary :-).
        if not use_remote_index:
            command_line.append('--no-index')
        # Use `--no-clean' to instruct pip to unpack the source distribution
        # archives and *not* clean up the unpacked source distributions
        # afterwards. This enables pip-accel to replace pip's installation
        # logic with cached binary distribution archives.
        command_line.append('--no-clean')
        # Use `--build-directory' to instruct pip to unpack the source
        # distribution archives to a temporary directory managed by pip-accel.
        # We will clean up the build directory when we're done using the
        # unpacked source distributions.
        command_line.append('--build-directory=%s' % self.build_directory)
        # Append the user's `pip install ...' arguments to the command line
        # that we just assembled.
        command_line.extend(arguments)
        logger.info("Executing command: pip install %s", ' '.join(command_line))
        # Clear the build directory to prevent PreviousBuildDirError exceptions.
        self.clear_build_directory()
        # During the pip 6.x upgrade pip-accel switched to using `pip install
        # --download' which can produce an interactive prompt as described in
        # issue 51 [1]. The documented way [2] to get rid of this interactive
        # prompt is pip's --exists-action option, but due to what is most
        # likely a bug in pip this doesn't actually work. The environment
        # variable $PIP_EXISTS_ACTION does work however, so if the user didn't
        # set it we will set a reasonable default for them.
        # [1] https://github.com/paylogic/pip-accel/issues/51
        # [2] https://pip.pypa.io/en/latest/reference/pip.html#exists-action-option
        os.environ.setdefault('PIP_EXISTS_ACTION', 'w')
        # Initialize and run the `pip install' command.
        command = InstallCommand()
        opts, args = command.parse_args(command_line)
        if not opts.ignore_installed:
            # If the user didn't supply the -I, --ignore-installed option we
            # will forcefully disable the option. Refer to the documentation of
            # the AttributeOverrides class for further details.
            opts = AttributeOverrides(opts, ignore_installed=False)
        requirement_set = command.run(opts, args)
        # Make sure the output of pip and pip-accel are not intermingled.
        sys.stdout.flush()
        if requirement_set is None:
            raise NothingToDoError("""
                pip didn't generate a requirement set, most likely you
                specified an empty requirements file?
            """)
        else:
            return self.transform_pip_requirement_set(requirement_set)
示例#4
0
    def get_pip_requirement_set(self,
                                arguments,
                                use_remote_index,
                                use_wheels=False):
        """
        Get the unpacked requirement(s) specified by the caller by running pip.

        :param arguments: The command line arguments to ``pip install ...`` (a
                          list of strings).
        :param use_remote_index: A boolean indicating whether pip is allowed to
                                 connect to the main package index
                                 (http://pypi.python.org by default).
        :param use_wheels: Whether pip and pip-accel are allowed to use wheels_
                           (:data:`False` by default for backwards compatibility
                           with callers that use pip-accel as a Python API).
        :returns: A :class:`pip.req.RequirementSet` object created by pip.
        :raises: Any exceptions raised by pip.
        """
        # Compose the pip command line arguments. This is where a lot of the
        # core logic of pip-accel is hidden and it uses some esoteric features
        # of pip so this method is heavily commented.
        command_line = []
        # Use `--download' to instruct pip to download requirement(s) into
        # pip-accel's local source distribution index directory. This has the
        # following documented side effects (see `pip install --help'):
        #  1. It disables the installation of requirements (without using the
        #     `--no-install' option which is deprecated and slated for removal
        #     in pip 7.x).
        #  2. It ignores requirements that are already installed (because
        #     pip-accel doesn't actually need to re-install requirements that
        #     are already installed we will have work around this later, but
        #     that seems fairly simple to do).
        command_line.append('--download=%s' % self.config.source_index)
        # Use `--find-links' to point pip at pip-accel's local source
        # distribution index directory. This ensures that source distribution
        # archives are never downloaded more than once (regardless of the HTTP
        # cache that was introduced in pip 6.x).
        command_line.append('--find-links=%s' %
                            create_file_url(self.config.source_index))
        # Use `--no-binary=:all:' to ignore wheel distributions by default in
        # order to preserve backwards compatibility with callers that expect a
        # requirement set consisting only of source distributions that can be
        # converted to `dumb binary distributions'.
        if not use_wheels and self.arguments_allow_wheels(arguments):
            command_line.append('--no-binary=:all:')
        # Use `--no-index' to force pip to only consider source distribution
        # archives contained in pip-accel's local source distribution index
        # directory. This enables pip-accel to ask pip "Can the local source
        # distribution index satisfy all requirements in the given requirement
        # set?" which enables pip-accel to keep pip off the internet unless
        # absolutely necessary :-).
        if not use_remote_index:
            command_line.append('--no-index')
        # Use `--no-clean' to instruct pip to unpack the source distribution
        # archives and *not* clean up the unpacked source distributions
        # afterwards. This enables pip-accel to replace pip's installation
        # logic with cached binary distribution archives.
        command_line.append('--no-clean')
        # Use `--build-directory' to instruct pip to unpack the source
        # distribution archives to a temporary directory managed by pip-accel.
        # We will clean up the build directory when we're done using the
        # unpacked source distributions.
        command_line.append('--build-directory=%s' % self.build_directory)
        # Append the user's `pip install ...' arguments to the command line
        # that we just assembled.
        command_line.extend(arguments)
        logger.info("Executing command: pip install %s",
                    ' '.join(command_line))
        # Clear the build directory to prevent PreviousBuildDirError exceptions.
        self.clear_build_directory()
        # During the pip 6.x upgrade pip-accel switched to using `pip install
        # --download' which can produce an interactive prompt as described in
        # issue 51 [1]. The documented way [2] to get rid of this interactive
        # prompt is pip's --exists-action option, but due to what is most
        # likely a bug in pip this doesn't actually work. The environment
        # variable $PIP_EXISTS_ACTION does work however, so if the user didn't
        # set it we will set a reasonable default for them.
        # [1] https://github.com/paylogic/pip-accel/issues/51
        # [2] https://pip.pypa.io/en/latest/reference/pip.html#exists-action-option
        os.environ.setdefault('PIP_EXISTS_ACTION', 'w')
        # Initialize and run the `pip install' command.
        command = InstallCommand()
        opts, args = command.parse_args(command_line)
        if not opts.ignore_installed:
            # If the user didn't supply the -I, --ignore-installed option we
            # will forcefully disable the option. Refer to the documentation of
            # the AttributeOverrides class for further details.
            opts = AttributeOverrides(opts, ignore_installed=False)
        requirement_set = command.run(opts, args)
        # Make sure the output of pip and pip-accel are not intermingled.
        sys.stdout.flush()
        if requirement_set is None:
            raise NothingToDoError("""
                pip didn't generate a requirement set, most likely you
                specified an empty requirements file?
            """)
        else:
            return self.transform_pip_requirement_set(requirement_set)