Skip to content

Commit b9a6ad7

Browse files
committed
Separate pip freeze command from operation
By extracting the logic into pip.operations.freeze, the hope is that folks could do a freeze programmatically more easily.
1 parent d251fe5 commit b9a6ad7

File tree

4 files changed

+121
-107
lines changed

4 files changed

+121
-107
lines changed

pip/commands/freeze.py

+8-106
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,9 @@
11
from __future__ import absolute_import
22

3-
import logging
4-
import re
53
import sys
64

7-
8-
import pip
9-
10-
from pip.compat import stdlib_pkgs
11-
from pip.req import InstallRequirement
125
from pip.basecommand import Command
13-
from pip.utils import get_installed_distributions
14-
from pip._vendor import pkg_resources
15-
16-
# packages to exclude from freeze output
17-
freeze_excludes = stdlib_pkgs + ['setuptools', 'pip', 'distribute']
18-
19-
20-
logger = logging.getLogger(__name__)
6+
from pip.operations.freeze import freeze_to_file
217

228

239
class FreezeCommand(Command):
@@ -68,94 +54,10 @@ def __init__(self, *args, **kw):
6854
self.parser.insert_option_group(0, self.cmd_opts)
6955

7056
def run(self, options, args):
71-
requirement = options.requirement
72-
find_links = options.find_links or []
73-
local_only = options.local
74-
user_only = options.user
75-
# FIXME: Obviously this should be settable:
76-
find_tags = False
77-
skip_match = None
78-
79-
skip_regex = options.skip_requirements_regex
80-
if skip_regex:
81-
skip_match = re.compile(skip_regex)
82-
83-
dependency_links = []
84-
85-
f = sys.stdout
86-
87-
for dist in pkg_resources.working_set:
88-
if dist.has_metadata('dependency_links.txt'):
89-
dependency_links.extend(
90-
dist.get_metadata_lines('dependency_links.txt')
91-
)
92-
for link in find_links:
93-
if '#egg=' in link:
94-
dependency_links.append(link)
95-
for link in find_links:
96-
f.write('-f %s\n' % link)
97-
installations = {}
98-
for dist in get_installed_distributions(local_only=local_only,
99-
skip=freeze_excludes,
100-
user_only=user_only):
101-
req = pip.FrozenRequirement.from_dist(
102-
dist,
103-
dependency_links,
104-
find_tags=find_tags,
105-
)
106-
installations[req.name] = req
107-
108-
if requirement:
109-
with open(requirement) as req_file:
110-
for line in req_file:
111-
if (not line.strip()
112-
or line.strip().startswith('#')
113-
or (skip_match and skip_match.search(line))
114-
or line.startswith((
115-
'-r', '--requirement',
116-
'-Z', '--always-unzip',
117-
'-f', '--find-links',
118-
'-i', '--index-url',
119-
'--extra-index-url'))):
120-
f.write(line)
121-
continue
122-
123-
if line.startswith('-e') or line.startswith('--editable'):
124-
if line.startswith('-e'):
125-
line = line[2:].strip()
126-
else:
127-
line = line[len('--editable'):].strip().lstrip('=')
128-
line_req = InstallRequirement.from_editable(
129-
line,
130-
default_vcs=options.default_vcs
131-
)
132-
else:
133-
line_req = InstallRequirement.from_line(line)
134-
135-
if not line_req.name:
136-
logger.info(
137-
"Skipping line because it's not clear what it "
138-
"would install: %s",
139-
line.strip(),
140-
)
141-
logger.info(
142-
" (add #egg=PackageName to the URL to avoid"
143-
" this warning)"
144-
)
145-
elif line_req.name not in installations:
146-
logger.warning(
147-
"Requirement file contains %s, but that package is"
148-
" not installed",
149-
line.strip(),
150-
)
151-
else:
152-
f.write(str(installations[line_req.name]))
153-
del installations[line_req.name]
154-
155-
f.write(
156-
'## The following requirements were added by '
157-
'pip %(name)s:\n' % dict(name=self.name)
158-
)
159-
for installation in sorted(
160-
installations.values(), key=lambda x: x.name.lower()):
161-
f.write(str(installation))
57+
freeze_to_file(
58+
f=sys.stdout,
59+
requirement=options.requirement,
60+
find_links=options.find_links,
61+
local_only=options.local,
62+
user_only=options.user,
63+
skip_regex=options.skip_requirements_regex)

pip/operations/__init__.py

Whitespace-only changes.

pip/operations/freeze.py

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
from __future__ import absolute_import
2+
3+
import logging
4+
import re
5+
import sys
6+
7+
import pip
8+
from pip.compat import stdlib_pkgs
9+
from pip.req import InstallRequirement
10+
from pip.utils import get_installed_distributions
11+
from pip._vendor import pkg_resources
12+
13+
14+
logger = logging.getLogger(__name__)
15+
16+
# packages to exclude from freeze output
17+
freeze_excludes = stdlib_pkgs + ['setuptools', 'pip', 'distribute']
18+
19+
20+
def freeze_to_file(f=sys.stdout, *args, **kwargs):
21+
for line in get_freeze_lines(*args, **kwargs):
22+
f.write(line + '\n')
23+
24+
25+
def get_freeze_lines(
26+
requirement=None,
27+
find_links=None, local_only=None, user_only=None, skip_regex=None,
28+
find_tags=False,
29+
default_vcs=None):
30+
find_links = find_links or []
31+
skip_match = None
32+
33+
if skip_regex:
34+
skip_match = re.compile(skip_regex)
35+
36+
dependency_links = []
37+
38+
for dist in pkg_resources.working_set:
39+
if dist.has_metadata('dependency_links.txt'):
40+
dependency_links.extend(
41+
dist.get_metadata_lines('dependency_links.txt')
42+
)
43+
for link in find_links:
44+
if '#egg=' in link:
45+
dependency_links.append(link)
46+
for link in find_links:
47+
yield '-f %s' % link
48+
installations = {}
49+
for dist in get_installed_distributions(local_only=local_only,
50+
skip=freeze_excludes,
51+
user_only=user_only):
52+
req = pip.FrozenRequirement.from_dist(
53+
dist,
54+
dependency_links,
55+
find_tags=find_tags,
56+
)
57+
installations[req.name] = req
58+
59+
if requirement:
60+
with open(requirement) as req_file:
61+
for line in req_file:
62+
if (not line.strip()
63+
or line.strip().startswith('#')
64+
or (skip_match and skip_match.search(line))
65+
or line.startswith((
66+
'-r', '--requirement',
67+
'-Z', '--always-unzip',
68+
'-f', '--find-links',
69+
'-i', '--index-url',
70+
'--extra-index-url'))):
71+
yield line.rstrip()
72+
continue
73+
74+
if line.startswith('-e') or line.startswith('--editable'):
75+
if line.startswith('-e'):
76+
line = line[2:].strip()
77+
else:
78+
line = line[len('--editable'):].strip().lstrip('=')
79+
line_req = InstallRequirement.from_editable(
80+
line,
81+
default_vcs=default_vcs,
82+
)
83+
else:
84+
line_req = InstallRequirement.from_line(line)
85+
86+
if not line_req.name:
87+
logger.info(
88+
"Skipping line because it's not clear what it "
89+
"would install: %s",
90+
line.strip(),
91+
)
92+
logger.info(
93+
" (add #egg=PackageName to the URL to avoid"
94+
" this warning)"
95+
)
96+
elif line_req.name not in installations:
97+
logger.warning(
98+
"Requirement file contains %s, but that package is"
99+
" not installed",
100+
line.strip(),
101+
)
102+
else:
103+
yield str(installations[line_req.name]).rstrip()
104+
del installations[line_req.name]
105+
106+
yield(
107+
'## The following requirements were added by '
108+
'pip freeze:'
109+
)
110+
for installation in sorted(
111+
installations.values(), key=lambda x: x.name.lower()):
112+
yield str(installation).rstrip()

tests/unit/test_utils.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from pip.exceptions import BadCommand
1515
from pip.utils import (egg_link_path, Inf, get_installed_distributions,
1616
find_command, untar_file, unzip_file)
17-
from pip.commands.freeze import freeze_excludes
17+
from pip.operations.freeze import freeze_excludes
1818

1919

2020
class Tests_EgglinkPath:

0 commit comments

Comments
 (0)