Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a top level pip download command. #2995

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
**7.2.0 (unreleased)**

* Implement a top-level pip download command and add a deprecation warning to
pip install --download.


**7.1.0 (2015-06-30)**

* Allow constraining versions globally without having to know exactly what will
Expand Down
1 change: 1 addition & 0 deletions docs/reference/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Reference Guide

pip
pip_install
pip_download
pip_uninstall
pip_freeze
pip_list
Expand Down
41 changes: 41 additions & 0 deletions docs/reference/pip_download.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

.. _`pip download`:

pip download
------------

.. contents::

Usage
*****

.. pip-command-usage:: download

``pip download`` is used just like ``pip install --download`` was used in the past. The same rules apply to this command: it should be very comfortable to use.

Description
***********

.. pip-command-description:: download



Options
*******

.. pip-command-options:: download

.. pip-index-options::


Examples
********

1. Download a package and all of its dependencies

::

$ pip download -d /tmp/wheelhouse SomePackage
$ pip download --no-index --find-links=/tmp/wheelhouse -d /tmp/otherwheelhouse SomePackage


24 changes: 24 additions & 0 deletions pip/basecommand.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
import warnings

from pip import cmdoptions
from pip.index import PackageFinder
from pip.locations import running_under_virtualenv
from pip.download import PipSession
from pip.exceptions import (BadCommand, InstallationError, UninstallationError,
CommandError, PreviousBuildDirError)

from pip.compat import logging_dictConfig
from pip.baseparser import ConfigOptionParser, UpdatingDefaultsHelpFormatter
from pip.req import InstallRequirement, parse_requirements
Expand Down Expand Up @@ -303,3 +305,25 @@ def populate_requirement_set(requirement_set, args, options, finder,
msg = ('You must give at least one requirement '
'to %(name)s (see "pip help %(name)s")' % opts)
logger.warning(msg)

def _build_package_finder(self, options, session):
"""
Create a package finder appropriate to this requirement command.
"""
index_urls = [options.index_url] + options.extra_index_urls
if options.no_index:
logger.info('Ignoring indexes: %s', ','.join(index_urls))
index_urls = []

return PackageFinder(
find_links=options.find_links,
format_control=options.format_control,
index_urls=index_urls,
allow_external=options.allow_external,
allow_unverified=options.allow_unverified,
allow_all_external=options.allow_all_external,
trusted_hosts=options.trusted_hosts,
allow_all_prereleases=options.pre,
process_dependency_links=options.process_dependency_links,
session=session,
)
3 changes: 3 additions & 0 deletions pip/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from __future__ import absolute_import

from pip.commands.completion import CompletionCommand
from pip.commands.download import DownloadCommand
from pip.commands.freeze import FreezeCommand
from pip.commands.help import HelpCommand
from pip.commands.list import ListCommand
Expand All @@ -22,13 +23,15 @@
ShowCommand.name: ShowCommand,
InstallCommand.name: InstallCommand,
UninstallCommand.name: UninstallCommand,
DownloadCommand.name: DownloadCommand,
ListCommand.name: ListCommand,
WheelCommand.name: WheelCommand,
}


commands_order = [
InstallCommand,
DownloadCommand,
UninstallCommand,
FreezeCommand,
ListCommand,
Expand Down
135 changes: 135 additions & 0 deletions pip/commands/download.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
from __future__ import absolute_import

import logging
import os

from pip.req import RequirementSet
from pip.basecommand import RequirementCommand
from pip import cmdoptions
from pip.utils.build import BuildDirectory
from pip.utils.filesystem import check_path_owner


logger = logging.getLogger(__name__)


class DownloadCommand(RequirementCommand):
"""
Download packages from:

- PyPI (and other indexes) using requirement specifiers.
- VCS project urls.
- Local project directories.
- Local or remote source archives.

pip also supports downloading from "requirements files", which provide
an easy way to specify a whole environment to be downloaded.
"""
name = 'download'

usage = """
%prog [options] <requirement specifier> [package-index-options] ...
%prog [options] -r <requirements file> [package-index-options] ...
%prog [options] [-e] <vcs project url> ...
%prog [options] [-e] <local project path> ...
%prog [options] <archive url/path> ..."""

summary = 'Download packages.'

def __init__(self, *args, **kw):
super(DownloadCommand, self).__init__(*args, **kw)

cmd_opts = self.cmd_opts

cmd_opts.add_option(cmdoptions.constraints())
cmd_opts.add_option(cmdoptions.editable())
cmd_opts.add_option(cmdoptions.requirements())
cmd_opts.add_option(cmdoptions.build_dir())
cmd_opts.add_option(cmdoptions.no_deps())
cmd_opts.add_option(cmdoptions.global_options())
cmd_opts.add_option(cmdoptions.no_binary())
cmd_opts.add_option(cmdoptions.only_binary())
cmd_opts.add_option(cmdoptions.src())
cmd_opts.add_option(cmdoptions.no_clean())
cmd_opts.add_option(
'-d', '--dest', '--destination-dir', '--destination-directory',
dest='download_dir',
metavar='dir',
default=None,
help=("Download packages into <dir>."),
)
cmd_opts.add_option(
'--pre',
action='store_true',
default=False,
help="Include pre-release and development versions. By default, "
"pip only finds stable versions.")

index_opts = cmdoptions.make_option_group(
cmdoptions.index_group,
self.parser,
)

self.parser.insert_option_group(0, index_opts)
self.parser.insert_option_group(0, cmd_opts)

def run(self, options, args):
options.ignore_installed = True

options.src_dir = os.path.abspath(options.src_dir)

with self._build_session(options) as session:

finder = self._build_package_finder(options, session)
build_delete = (not (options.no_clean or options.build_dir))
if options.cache_dir and not check_path_owner(options.cache_dir):
logger.warning(
"The directory '%s' or its parent directory is not owned "
"by the current user and caching wheels has been "
"disabled. check the permissions and owner of that "
"directory. If executing pip with sudo, you may want "
"sudo's -H flag.",
options.cache_dir,
)
options.cache_dir = None

with BuildDirectory(options.build_dir,
delete=build_delete) as build_dir:

requirement_set = RequirementSet(
build_dir=build_dir,
src_dir=options.src_dir,
download_dir=options.download_dir,
ignore_installed=True,
ignore_dependencies=options.ignore_dependencies,
session=session,
isolated=options.isolated_mode,
)
self.populate_requirement_set(
requirement_set,
args,
options,
finder,
session,
self.name,
None
)

if not requirement_set.has_requirements:
return

requirement_set.prepare_files(finder)

downloaded = ' '.join([
req.name for req in requirement_set.successfully_downloaded
])
if downloaded:
logger.info(
'Successfully downloaded %s', downloaded
)

# Clean up
if not options.no_clean:
requirement_set.cleanup_files()

return requirement_set
32 changes: 7 additions & 25 deletions pip/commands/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
from pip.req import RequirementSet
from pip.basecommand import RequirementCommand
from pip.locations import virtualenv_no_global, distutils_scheme
from pip.index import PackageFinder
from pip.exceptions import (
InstallationError, CommandError, PreviousBuildDirError,
)
Expand Down Expand Up @@ -175,30 +174,17 @@ def __init__(self, *args, **kw):
self.parser.insert_option_group(0, index_opts)
self.parser.insert_option_group(0, cmd_opts)

def _build_package_finder(self, options, index_urls, session):
"""
Create a package finder appropriate to this install command.
This method is meant to be overridden by subclasses, not
called directly.
"""
return PackageFinder(
find_links=options.find_links,
format_control=options.format_control,
index_urls=index_urls,
allow_external=options.allow_external,
allow_unverified=options.allow_unverified,
allow_all_external=options.allow_all_external,
trusted_hosts=options.trusted_hosts,
allow_all_prereleases=options.pre,
process_dependency_links=options.process_dependency_links,
session=session,
)

def run(self, options, args):
cmdoptions.resolve_wheel_no_use_binary(options)
cmdoptions.check_install_build_global(options)

if options.download_dir:
warnings.warn(
"pip install --download has been deprecated and will be "
"removed in the future. Pip now has a download command that "
"should be used instead.",
RemovedInPip8Warning,
)
options.ignore_installed = True

if options.build_dir:
Expand Down Expand Up @@ -229,10 +215,6 @@ def run(self, options, args):
install_options.append('--home=' + temp_target_dir)

global_options = options.global_options or []
index_urls = [options.index_url] + options.extra_index_urls
if options.no_index:
logger.info('Ignoring indexes: %s', ','.join(index_urls))
index_urls = []

if options.download_cache:
warnings.warn(
Expand All @@ -244,7 +226,7 @@ def run(self, options, args):

with self._build_session(options) as session:

finder = self._build_package_finder(options, index_urls, session)
finder = self._build_package_finder(options, session)
build_delete = (not (options.no_clean or options.build_dir))
wheel_cache = WheelCache(options.cache_dir, options.format_control)
if options.cache_dir and not check_path_owner(options.cache_dir):
Expand Down
20 changes: 1 addition & 19 deletions pip/commands/wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import warnings

from pip.basecommand import RequirementCommand
from pip.index import PackageFinder
from pip.exceptions import CommandError, PreviousBuildDirError
from pip.req import RequirementSet
from pip.utils import import_or_raise, normalize_path
Expand Down Expand Up @@ -128,11 +127,6 @@ def run(self, options, args):
cmdoptions.resolve_wheel_no_use_binary(options)
cmdoptions.check_install_build_global(options)

index_urls = [options.index_url] + options.extra_index_urls
if options.no_index:
logger.info('Ignoring indexes: %s', ','.join(index_urls))
index_urls = []

if options.download_cache:
warnings.warn(
"--download-cache has been deprecated and will be removed in "
Expand All @@ -146,19 +140,7 @@ def run(self, options, args):

with self._build_session(options) as session:

finder = PackageFinder(
find_links=options.find_links,
format_control=options.format_control,
index_urls=index_urls,
allow_external=options.allow_external,
allow_unverified=options.allow_unverified,
allow_all_external=options.allow_all_external,
allow_all_prereleases=options.pre,
trusted_hosts=options.trusted_hosts,
process_dependency_links=options.process_dependency_links,
session=session,
)

finder = self._build_package_finder(options, session)
build_delete = (not (options.no_clean or options.build_dir))
wheel_cache = WheelCache(options.cache_dir, options.format_control)
with BuildDirectory(options.build_dir,
Expand Down
Loading