mirror of https://github.com/pypa/pip
156 lines
5.4 KiB
Python
156 lines
5.4 KiB
Python
from __future__ import absolute_import
|
|
|
|
import logging
|
|
import re
|
|
import sys
|
|
|
|
|
|
import pip
|
|
|
|
from pip.compat import stdlib_pkgs
|
|
from pip.req import InstallRequirement
|
|
from pip.basecommand import Command
|
|
from pip.utils import get_installed_distributions
|
|
from pip._vendor import pkg_resources
|
|
|
|
# packages to exclude from freeze output
|
|
freeze_excludes = stdlib_pkgs + ['setuptools', 'pip', 'distribute']
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class FreezeCommand(Command):
|
|
"""
|
|
Output installed packages in requirements format.
|
|
|
|
packages are listed in a case-insensitive sorted order.
|
|
"""
|
|
name = 'freeze'
|
|
usage = """
|
|
%prog [options]"""
|
|
summary = 'Output installed packages in requirements format.'
|
|
log_stream = "ext://sys.stderr"
|
|
|
|
def __init__(self, *args, **kw):
|
|
super(FreezeCommand, self).__init__(*args, **kw)
|
|
|
|
self.cmd_opts.add_option(
|
|
'-r', '--requirement',
|
|
dest='requirement',
|
|
action='store',
|
|
default=None,
|
|
metavar='file',
|
|
help="Use the order in the given requirements file and its "
|
|
"comments when generating output.")
|
|
self.cmd_opts.add_option(
|
|
'-f', '--find-links',
|
|
dest='find_links',
|
|
action='append',
|
|
default=[],
|
|
metavar='URL',
|
|
help='URL for finding packages, which will be added to the '
|
|
'output.')
|
|
self.cmd_opts.add_option(
|
|
'-l', '--local',
|
|
dest='local',
|
|
action='store_true',
|
|
default=False,
|
|
help='If in a virtualenv that has global access, do not output '
|
|
'globally-installed packages.')
|
|
|
|
self.parser.insert_option_group(0, self.cmd_opts)
|
|
|
|
def run(self, options, args):
|
|
requirement = options.requirement
|
|
find_links = options.find_links or []
|
|
local_only = options.local
|
|
# FIXME: Obviously this should be settable:
|
|
find_tags = False
|
|
skip_match = None
|
|
|
|
skip_regex = options.skip_requirements_regex
|
|
if skip_regex:
|
|
skip_match = re.compile(skip_regex)
|
|
|
|
dependency_links = []
|
|
|
|
f = sys.stdout
|
|
|
|
for dist in pkg_resources.working_set:
|
|
if dist.has_metadata('dependency_links.txt'):
|
|
dependency_links.extend(
|
|
dist.get_metadata_lines('dependency_links.txt')
|
|
)
|
|
for link in find_links:
|
|
if '#egg=' in link:
|
|
dependency_links.append(link)
|
|
for link in find_links:
|
|
f.write('-f %s\n' % link)
|
|
installations = {}
|
|
for dist in get_installed_distributions(local_only=local_only,
|
|
skip=freeze_excludes):
|
|
req = pip.FrozenRequirement.from_dist(
|
|
dist,
|
|
dependency_links,
|
|
find_tags=find_tags,
|
|
)
|
|
installations[req.name] = req
|
|
if requirement:
|
|
req_f = open(requirement)
|
|
for line in req_f:
|
|
if not line.strip() or line.strip().startswith('#'):
|
|
f.write(line)
|
|
continue
|
|
if skip_match and skip_match.search(line):
|
|
f.write(line)
|
|
continue
|
|
elif line.startswith('-e') or line.startswith('--editable'):
|
|
if line.startswith('-e'):
|
|
line = line[2:].strip()
|
|
else:
|
|
line = line[len('--editable'):].strip().lstrip('=')
|
|
line_req = InstallRequirement.from_editable(
|
|
line,
|
|
default_vcs=options.default_vcs
|
|
)
|
|
elif (line.startswith('-r')
|
|
or line.startswith('--requirement')
|
|
or line.startswith('-Z')
|
|
or line.startswith('--always-unzip')
|
|
or line.startswith('-f')
|
|
or line.startswith('-i')
|
|
or line.startswith('--extra-index-url')
|
|
or line.startswith('--find-links')
|
|
or line.startswith('--index-url')):
|
|
f.write(line)
|
|
continue
|
|
else:
|
|
line_req = InstallRequirement.from_line(line)
|
|
if not line_req.name:
|
|
logger.info(
|
|
"Skipping line because it's not clear what it would "
|
|
"install: %s",
|
|
line.strip(),
|
|
)
|
|
logger.info(
|
|
" (add #egg=PackageName to the URL to avoid"
|
|
" this warning)"
|
|
)
|
|
continue
|
|
if line_req.name not in installations:
|
|
logger.warning(
|
|
"Requirement file contains %s, but that package is not"
|
|
" installed",
|
|
line.strip(),
|
|
)
|
|
continue
|
|
f.write(str(installations[line_req.name]))
|
|
del installations[line_req.name]
|
|
f.write(
|
|
'## The following requirements were added by pip --freeze:\n'
|
|
)
|
|
for installation in sorted(
|
|
installations.values(), key=lambda x: x.name.lower()):
|
|
f.write(str(installation))
|