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

'pip freeze PKG' outputs the installed version of PKG and its dependencies #1946

Closed
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
982a891
'pip freeze {NAME}' outputs the installed versions of package {NAME} …
benoitbryon Jul 23, 2014
cc94c3a
'pip show {NAME}' also shows the list of recursive dependencies of pa…
benoitbryon Jul 23, 2014
24a2dda
Fixed line count in 'pip show' tests. Renamed 'pip.util.recursive_dep…
benoitbryon Jul 24, 2014
d84fb4a
In 'pip show' tests, shifted output lines to insert 'Requires recursi…
benoitbryon Jul 24, 2014
ffa2694
Psyko-rebased branch feature/freeze-subset-recursively on top of deve…
benoitbryon Oct 26, 2014
4396efa
pip freeze is not recursive by default. Added --recursive option to m…
benoitbryon Oct 26, 2014
28d098c
Updated examples in documentation around 'pip freeze PKG' and 'Requir…
benoitbryon Oct 26, 2014
4b43c91
Psycho-rebased branch feature/freeze-subset-recursively on top of dev…
benoitbryon Nov 13, 2014
f895418
pip.utils.get_recursive_dependencies() takes a list as first argument…
benoitbryon Dec 8, 2014
098ec7e
Improved readability of pip.utils.get_recursive_dependencies().
benoitbryon Dec 8, 2014
5c2efb1
Removed duplicate recursive calls in pip.utils.get_recursive_dependen…
benoitbryon Dec 8, 2014
bdd2fda
'pip show' shows *ordered* list of recursive dependencies (was unorde…
benoitbryon Dec 8, 2014
8fb3bbf
pip.utils.get_recursive_dependencies() returns case-sensitive names, …
benoitbryon Dec 8, 2014
98b004f
Psycho-rebased branch feature/freeze-subset-recursively on top of dev…
benoitbryon Dec 8, 2014
e27ed4b
Used case-insensitive order for recursive dependencies in 'pip show' …
benoitbryon Dec 8, 2014
9bb013d
In 'pip freeze PKG', compare safe names (was lowercase names).
benoitbryon Dec 10, 2014
8cfb9ad
Typos in 'pip freeze' and 'pip show' documentation.
benoitbryon Dec 10, 2014
186ddf2
Merge remote-tracking branch 'origin/develop' into freeze_recursive_r…
msabramo Jan 16, 2015
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
18 changes: 14 additions & 4 deletions pip/commands/freeze.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
from pip.req import InstallRequirement
from pip.log import logger
from pip.basecommand import Command
from pip.util import get_installed_distributions
from pip.util import get_installed_distributions, recursive_dependencies


# packages to exclude from freeze output
freeze_excludes = stdlib_pkgs + ['setuptools', 'pip', 'distribute']
Expand All @@ -20,7 +21,7 @@ class FreezeCommand(Command):
"""
name = 'freeze'
usage = """
%prog [options]"""
%prog [options] [PACKAGE PACKAGE...]"""
summary = 'Output installed packages in requirements format.'

def __init__(self, *args, **kw):
Expand Down Expand Up @@ -72,10 +73,19 @@ def run(self, options, args):
for link in find_links:
f.write('-f %s\n' % link)
installations = {}

only_dists = []
if args:
only_dists = args
only_dists.extend(recursive_dependencies(only_dists))
only_dists = [name.lower() for name in only_dists]

for dist in get_installed_distributions(local_only=local_only,
skip=freeze_excludes):
req = pip.FrozenRequirement.from_dist(dist, find_tags=find_tags)
installations[req.name] = req
if not only_dists or dist.project_name.lower() in only_dists:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than just lowercasing the name, you might want to use the canonical form of the name (- and _ should be treated as the same, IIRC). I think you should be using the safe_name function from setuptools, but pip should probably implement its own "canonical name" function in utils or somewhere. @dstufft?

For now, just a .replace('_', '-') is probably good enough though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That safe_name function that pip might want to provide may be better placed in pypa/packaging since pypa/twine would make good use of it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ultimately I agree. But I don't want to leave these things waiting for ages (practicality beats purity) so I'd suggest .replace() for now, then when someone gets to it we put a function in util, and then, when we get to the point where we vendor in packaging we can switch to using the function from there at that point.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. Sorry. It can be in both places and then removed from pip as soon as packaging becomes a dependency. (That was in my head but not in my writing. ;))

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

req = pip.FrozenRequirement.from_dist(
dist, find_tags=find_tags)
installations[req.name] = req
if requirement:
req_f = open(requirement)
for line in req_f:
Expand Down
3 changes: 3 additions & 0 deletions pip/commands/show.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from pip.basecommand import Command
from pip.log import logger
from pip.util import recursive_dependencies
from pip._vendor import pkg_resources


Expand Down Expand Up @@ -78,6 +79,8 @@ def print_results(distributions, list_all_files):
logger.notify("Version: %s" % dist['version'])
logger.notify("Location: %s" % dist['location'])
logger.notify("Requires: %s" % ', '.join(dist['requires']))
logger.notify("Requires recursive: %s" % ', '.join(
recursive_dependencies([dist['name']])))
if list_all_files:
logger.notify("Files:")
if dist['files'] is not None:
Expand Down
17 changes: 17 additions & 0 deletions pip/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,23 @@ def get_installed_distributions(local_only=True,
]


def recursive_dependencies(query):
"""Return list of dependencies of dists in ``query``, recursively."""
dependencies = set()
installed = dict(
[(p.project_name.lower(), p) for p in pkg_resources.working_set])
query_names = [name.lower() for name in query]
for pkg in query_names:
try:
dist = installed[pkg]
for dep in dist.requires():
dependencies.add(dep.project_name)
dependencies.update(recursive_dependencies([dep.project_name]))
except KeyError:
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This except could be moved at line 412 to not catch exception from anywhere else.

pass # pkg is not installed.
return dependencies


def egg_link_path(dist):
"""
Return the path for the .egg-link file if it exists, otherwise, None.
Expand Down
2 changes: 2 additions & 0 deletions tests/functional/test_show.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def test_show(script):
assert lines[2] == 'Version: %s' % __version__, lines[2]
assert lines[3].startswith('Location: '), lines[3]
assert lines[4] == 'Requires: '
assert lines[5] == 'Requires recursive: '


def test_show_with_files_not_found(script, data):
Expand All @@ -32,6 +33,7 @@ def test_show_with_files_not_found(script, data):
assert lines[2] == 'Version: 0.0.0', lines[2]
assert lines[3].startswith('Location: '), lines[3]
assert lines[4] == 'Requires: ', lines[4]
assert lines[4] == 'Requires recursive: ', lines[5]
assert lines[5] == 'Files:', lines[5]
assert lines[6] == 'Cannot locate installed-files.txt', lines[6]

Expand Down