Skip to content

Commit e243b4f

Browse files
author
Mathew Jennings
committed
Add pip download command. This has the same functionality that pip install --download does (which now has a deprecated warning, but still functions as expected).
1 parent 0027580 commit e243b4f

File tree

11 files changed

+231
-55
lines changed

11 files changed

+231
-55
lines changed

CHANGES.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
**7.2.0 (unreleased)**
22

3+
* Implement a top-level pip download command and add a deprecation warning to
4+
pip install --download.
5+
6+
37
**7.1.0 (2015-06-30)**
48

59
* Allow constraining versions globally without having to know exactly what will

docs/reference/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Reference Guide
77

88
pip
99
pip_install
10+
pip_download
1011
pip_uninstall
1112
pip_freeze
1213
pip_list

docs/reference/pip_download.rst

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
2+
.. _`pip download`:
3+
4+
pip download
5+
------------
6+
7+
.. contents::
8+
9+
Usage
10+
*****
11+
12+
.. pip-command-usage:: download
13+
14+
``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.
15+
16+
Description
17+
***********
18+
19+
.. pip-command-description:: download
20+
21+
22+
23+
Options
24+
*******
25+
26+
.. pip-command-options:: download
27+
28+
.. pip-index-options::
29+
30+
31+
Examples
32+
********
33+
34+
1. Download a package and all of its dependencies
35+
36+
::
37+
38+
$ pip download -d /tmp/wheelhouse SomePackage
39+
$ pip download --no-index --find-links=/tmp/wheelhouse -d /tmp/otherwheelhouse SomePackage
40+
41+

pip/basecommand.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
import warnings
99

1010
from pip import cmdoptions
11+
from pip.index import PackageFinder
1112
from pip.locations import running_under_virtualenv
1213
from pip.download import PipSession
1314
from pip.exceptions import (BadCommand, InstallationError, UninstallationError,
1415
CommandError, PreviousBuildDirError)
16+
1517
from pip.compat import logging_dictConfig
1618
from pip.baseparser import ConfigOptionParser, UpdatingDefaultsHelpFormatter
1719
from pip.req import InstallRequirement, parse_requirements
@@ -303,3 +305,25 @@ def populate_requirement_set(requirement_set, args, options, finder,
303305
msg = ('You must give at least one requirement '
304306
'to %(name)s (see "pip help %(name)s")' % opts)
305307
logger.warning(msg)
308+
309+
def _build_package_finder(self, options, session):
310+
"""
311+
Create a package finder appropriate to this requirement command.
312+
"""
313+
index_urls = [options.index_url] + options.extra_index_urls
314+
if options.no_index:
315+
logger.info('Ignoring indexes: %s', ','.join(index_urls))
316+
index_urls = []
317+
318+
return PackageFinder(
319+
find_links=options.find_links,
320+
format_control=options.format_control,
321+
index_urls=index_urls,
322+
allow_external=options.allow_external,
323+
allow_unverified=options.allow_unverified,
324+
allow_all_external=options.allow_all_external,
325+
trusted_hosts=options.trusted_hosts,
326+
allow_all_prereleases=options.pre,
327+
process_dependency_links=options.process_dependency_links,
328+
session=session,
329+
)

pip/commands/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from __future__ import absolute_import
55

66
from pip.commands.completion import CompletionCommand
7+
from pip.commands.download import DownloadCommand
78
from pip.commands.freeze import FreezeCommand
89
from pip.commands.help import HelpCommand
910
from pip.commands.list import ListCommand
@@ -22,13 +23,15 @@
2223
ShowCommand.name: ShowCommand,
2324
InstallCommand.name: InstallCommand,
2425
UninstallCommand.name: UninstallCommand,
26+
DownloadCommand.name: DownloadCommand,
2527
ListCommand.name: ListCommand,
2628
WheelCommand.name: WheelCommand,
2729
}
2830

2931

3032
commands_order = [
3133
InstallCommand,
34+
DownloadCommand,
3235
UninstallCommand,
3336
FreezeCommand,
3437
ListCommand,

pip/commands/download.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
from __future__ import absolute_import
2+
3+
import logging
4+
import os
5+
6+
from pip.req import RequirementSet
7+
from pip.basecommand import RequirementCommand
8+
from pip import cmdoptions
9+
from pip.utils.build import BuildDirectory
10+
from pip.utils.filesystem import check_path_owner
11+
12+
13+
logger = logging.getLogger(__name__)
14+
15+
16+
class DownloadCommand(RequirementCommand):
17+
"""
18+
Download packages from:
19+
20+
- PyPI (and other indexes) using requirement specifiers.
21+
- VCS project urls.
22+
- Local project directories.
23+
- Local or remote source archives.
24+
25+
pip also supports downloading from "requirements files", which provide
26+
an easy way to specify a whole environment to be downloaded.
27+
"""
28+
name = 'download'
29+
30+
usage = """
31+
%prog [options] <requirement specifier> [package-index-options] ...
32+
%prog [options] -r <requirements file> [package-index-options] ...
33+
%prog [options] [-e] <vcs project url> ...
34+
%prog [options] [-e] <local project path> ...
35+
%prog [options] <archive url/path> ..."""
36+
37+
summary = 'Download packages.'
38+
39+
def __init__(self, *args, **kw):
40+
super(DownloadCommand, self).__init__(*args, **kw)
41+
42+
cmd_opts = self.cmd_opts
43+
44+
cmd_opts.add_option(cmdoptions.constraints())
45+
cmd_opts.add_option(cmdoptions.editable())
46+
cmd_opts.add_option(cmdoptions.requirements())
47+
cmd_opts.add_option(cmdoptions.build_dir())
48+
cmd_opts.add_option(cmdoptions.no_deps())
49+
cmd_opts.add_option(cmdoptions.global_options())
50+
cmd_opts.add_option(cmdoptions.no_binary())
51+
cmd_opts.add_option(cmdoptions.only_binary())
52+
cmd_opts.add_option(cmdoptions.src())
53+
cmd_opts.add_option(cmdoptions.no_clean())
54+
cmd_opts.add_option(
55+
'-d', '--dest', '--destination-dir', '--destination-directory',
56+
dest='download_dir',
57+
metavar='dir',
58+
default=None,
59+
help=("Download packages into <dir>."),
60+
)
61+
cmd_opts.add_option(
62+
'--pre',
63+
action='store_true',
64+
default=False,
65+
help="Include pre-release and development versions. By default, "
66+
"pip only finds stable versions.")
67+
68+
index_opts = cmdoptions.make_option_group(
69+
cmdoptions.index_group,
70+
self.parser,
71+
)
72+
73+
self.parser.insert_option_group(0, index_opts)
74+
self.parser.insert_option_group(0, cmd_opts)
75+
76+
def run(self, options, args):
77+
options.ignore_installed = True
78+
79+
options.src_dir = os.path.abspath(options.src_dir)
80+
81+
with self._build_session(options) as session:
82+
83+
finder = self._build_package_finder(options, session)
84+
build_delete = (not (options.no_clean or options.build_dir))
85+
if options.cache_dir and not check_path_owner(options.cache_dir):
86+
logger.warning(
87+
"The directory '%s' or its parent directory is not owned "
88+
"by the current user and caching wheels has been "
89+
"disabled. check the permissions and owner of that "
90+
"directory. If executing pip with sudo, you may want "
91+
"sudo's -H flag.",
92+
options.cache_dir,
93+
)
94+
options.cache_dir = None
95+
96+
with BuildDirectory(options.build_dir,
97+
delete=build_delete) as build_dir:
98+
99+
requirement_set = RequirementSet(
100+
build_dir=build_dir,
101+
src_dir=options.src_dir,
102+
download_dir=options.download_dir,
103+
ignore_installed=True,
104+
ignore_dependencies=options.ignore_dependencies,
105+
session=session,
106+
isolated=options.isolated_mode,
107+
)
108+
self.populate_requirement_set(
109+
requirement_set,
110+
args,
111+
options,
112+
finder,
113+
session,
114+
self.name,
115+
None
116+
)
117+
118+
if not requirement_set.has_requirements:
119+
return
120+
121+
requirement_set.prepare_files(finder)
122+
123+
downloaded = ' '.join([
124+
req.name for req in requirement_set.successfully_downloaded
125+
])
126+
if downloaded:
127+
logger.info(
128+
'Successfully downloaded %s', downloaded
129+
)
130+
131+
# Clean up
132+
if not options.no_clean:
133+
requirement_set.cleanup_files()
134+
135+
return requirement_set

pip/commands/install.py

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
from pip.req import RequirementSet
1515
from pip.basecommand import RequirementCommand
1616
from pip.locations import virtualenv_no_global, distutils_scheme
17-
from pip.index import PackageFinder
1817
from pip.exceptions import (
1918
InstallationError, CommandError, PreviousBuildDirError,
2019
)
@@ -175,30 +174,17 @@ def __init__(self, *args, **kw):
175174
self.parser.insert_option_group(0, index_opts)
176175
self.parser.insert_option_group(0, cmd_opts)
177176

178-
def _build_package_finder(self, options, index_urls, session):
179-
"""
180-
Create a package finder appropriate to this install command.
181-
This method is meant to be overridden by subclasses, not
182-
called directly.
183-
"""
184-
return PackageFinder(
185-
find_links=options.find_links,
186-
format_control=options.format_control,
187-
index_urls=index_urls,
188-
allow_external=options.allow_external,
189-
allow_unverified=options.allow_unverified,
190-
allow_all_external=options.allow_all_external,
191-
trusted_hosts=options.trusted_hosts,
192-
allow_all_prereleases=options.pre,
193-
process_dependency_links=options.process_dependency_links,
194-
session=session,
195-
)
196-
197177
def run(self, options, args):
198178
cmdoptions.resolve_wheel_no_use_binary(options)
199179
cmdoptions.check_install_build_global(options)
200180

201181
if options.download_dir:
182+
warnings.warn(
183+
"pip install --download has been deprecated and will be "
184+
"removed in the future. Pip now has a download command that "
185+
"should be used instead.",
186+
RemovedInPip8Warning,
187+
)
202188
options.ignore_installed = True
203189

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

231217
global_options = options.global_options or []
232-
index_urls = [options.index_url] + options.extra_index_urls
233-
if options.no_index:
234-
logger.info('Ignoring indexes: %s', ','.join(index_urls))
235-
index_urls = []
236218

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

245227
with self._build_session(options) as session:
246228

247-
finder = self._build_package_finder(options, index_urls, session)
229+
finder = self._build_package_finder(options, session)
248230
build_delete = (not (options.no_clean or options.build_dir))
249231
wheel_cache = WheelCache(options.cache_dir, options.format_control)
250232
if options.cache_dir and not check_path_owner(options.cache_dir):

pip/commands/wheel.py

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import warnings
77

88
from pip.basecommand import RequirementCommand
9-
from pip.index import PackageFinder
109
from pip.exceptions import CommandError, PreviousBuildDirError
1110
from pip.req import RequirementSet
1211
from pip.utils import import_or_raise, normalize_path
@@ -128,11 +127,6 @@ def run(self, options, args):
128127
cmdoptions.resolve_wheel_no_use_binary(options)
129128
cmdoptions.check_install_build_global(options)
130129

131-
index_urls = [options.index_url] + options.extra_index_urls
132-
if options.no_index:
133-
logger.info('Ignoring indexes: %s', ','.join(index_urls))
134-
index_urls = []
135-
136130
if options.download_cache:
137131
warnings.warn(
138132
"--download-cache has been deprecated and will be removed in "
@@ -146,19 +140,7 @@ def run(self, options, args):
146140

147141
with self._build_session(options) as session:
148142

149-
finder = PackageFinder(
150-
find_links=options.find_links,
151-
format_control=options.format_control,
152-
index_urls=index_urls,
153-
allow_external=options.allow_external,
154-
allow_unverified=options.allow_unverified,
155-
allow_all_external=options.allow_all_external,
156-
allow_all_prereleases=options.pre,
157-
trusted_hosts=options.trusted_hosts,
158-
process_dependency_links=options.process_dependency_links,
159-
session=session,
160-
)
161-
143+
finder = self._build_package_finder(options, session)
162144
build_delete = (not (options.no_clean or options.build_dir))
163145
wheel_cache = WheelCache(options.cache_dir, options.format_control)
164146
with BuildDirectory(options.build_dir,

0 commit comments

Comments
 (0)