Skip to content

Commit d67acca

Browse files
committed
Add "pip debug" command.
1 parent a360649 commit d67acca

File tree

4 files changed

+156
-0
lines changed

4 files changed

+156
-0
lines changed

news/6638.feature

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add a new command ``pip debug`` that can display e.g. the list of compatible
2+
tags for the current Python.

src/pip/_internal/commands/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from pip._internal.commands.completion import CompletionCommand
77
from pip._internal.commands.configuration import ConfigurationCommand
8+
from pip._internal.commands.debug import DebugCommand
89
from pip._internal.commands.download import DownloadCommand
910
from pip._internal.commands.freeze import FreezeCommand
1011
from pip._internal.commands.hash import HashCommand
@@ -36,6 +37,7 @@
3637
WheelCommand,
3738
HashCommand,
3839
CompletionCommand,
40+
DebugCommand,
3941
HelpCommand,
4042
] # type: List[Type[Command]]
4143

src/pip/_internal/commands/debug.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
from __future__ import absolute_import
2+
3+
import logging
4+
import sys
5+
6+
from pip._internal.cli import cmdoptions
7+
from pip._internal.cli.base_command import Command
8+
from pip._internal.cli.cmdoptions import make_target_python
9+
from pip._internal.cli.status_codes import SUCCESS
10+
from pip._internal.utils.logging import indent_log
11+
from pip._internal.utils.misc import get_pip_version
12+
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
13+
from pip._internal.wheel import format_tag
14+
15+
if MYPY_CHECK_RUNNING:
16+
from typing import Any, List
17+
from optparse import Values
18+
19+
logger = logging.getLogger(__name__)
20+
21+
22+
def show_value(name, value):
23+
# type: (str, str) -> None
24+
logger.info('{}: {}'.format(name, value))
25+
26+
27+
def show_sys_implementation():
28+
# type: () -> None
29+
logger.info('sys.implementation:')
30+
if hasattr(sys, 'implementation'):
31+
implementation = sys.implementation # type: ignore
32+
implementation_name = implementation.name
33+
else:
34+
implementation_name = ''
35+
36+
with indent_log():
37+
show_value('name', implementation_name)
38+
39+
40+
def show_tags(options):
41+
# type: (Values) -> None
42+
tag_limit = 10
43+
44+
target_python = make_target_python(options)
45+
tags = target_python.get_tags()
46+
47+
# Display the target options that were explicitly provided.
48+
formatted_target = target_python.format_given()
49+
suffix = ''
50+
if formatted_target:
51+
suffix = ' (target: {})'.format(formatted_target)
52+
53+
msg = 'Compatible tags: {}{}'.format(len(tags), suffix)
54+
logger.info(msg)
55+
56+
if options.verbose < 1 and len(tags) > tag_limit:
57+
tags_limited = True
58+
tags = tags[:tag_limit]
59+
else:
60+
tags_limited = False
61+
62+
with indent_log():
63+
for tag in tags:
64+
logger.info(format_tag(tag))
65+
66+
if tags_limited:
67+
msg = (
68+
'...\n'
69+
'[First {tag_limit} tags shown. Pass --verbose to show all.]'
70+
).format(tag_limit=tag_limit)
71+
logger.info(msg)
72+
73+
74+
class DebugCommand(Command):
75+
"""
76+
Display debug information.
77+
"""
78+
79+
name = 'debug'
80+
usage = """
81+
%prog <options>"""
82+
summary = 'Show information useful for debugging.'
83+
ignore_require_venv = True
84+
85+
def __init__(self, *args, **kw):
86+
super(DebugCommand, self).__init__(*args, **kw)
87+
88+
cmd_opts = self.cmd_opts
89+
cmdoptions.add_target_python_options(cmd_opts)
90+
self.parser.insert_option_group(0, cmd_opts)
91+
92+
def run(self, options, args):
93+
# type: (Values, List[Any]) -> int
94+
show_value('pip version', get_pip_version())
95+
show_value('sys.version', sys.version)
96+
show_value('sys.executable', sys.executable)
97+
show_value('sys.platform', sys.platform)
98+
show_sys_implementation()
99+
100+
show_tags(options)
101+
102+
return SUCCESS

tests/functional/test_debug.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import pytest
2+
3+
from pip._internal import pep425tags
4+
5+
6+
@pytest.mark.parametrize(
7+
'args',
8+
[
9+
[],
10+
['--verbose'],
11+
]
12+
)
13+
def test_debug(script, args):
14+
"""
15+
Check simple option cases.
16+
"""
17+
args = ['debug'] + args
18+
result = script.pip(*args)
19+
stdout = result.stdout
20+
21+
assert 'sys.executable: ' in stdout
22+
assert 'sys.platform: ' in stdout
23+
assert 'sys.implementation:' in stdout
24+
25+
tags = pep425tags.get_supported()
26+
expected_tag_header = 'Compatible tags: {}'.format(len(tags))
27+
assert expected_tag_header in stdout
28+
29+
show_verbose_note = '--verbose' not in args
30+
assert (
31+
'...\n [First 10 tags shown. Pass --verbose to show all.]' in stdout
32+
) == show_verbose_note
33+
34+
35+
@pytest.mark.parametrize(
36+
'args, expected',
37+
[
38+
(['--python-version', '3.7'], "(target: version_info='3.7')"),
39+
]
40+
)
41+
def test_debug__target_options(script, args, expected):
42+
"""
43+
Check passing target-related options.
44+
"""
45+
args = ['debug'] + args
46+
result = script.pip(*args)
47+
stdout = result.stdout
48+
49+
assert 'Compatible tags: ' in stdout
50+
assert expected in stdout

0 commit comments

Comments
 (0)