Skip to content

Commit

Permalink
Merge pull request #6638 from cjerdonek/debug-command
Browse files Browse the repository at this point in the history
Add a "pip debug" command
  • Loading branch information
cjerdonek authored Jul 2, 2019
2 parents ec73d72 + d67acca commit a8510bc
Show file tree
Hide file tree
Showing 12 changed files with 251 additions and 37 deletions.
2 changes: 2 additions & 0 deletions news/6638.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add a new command ``pip debug`` that can display e.g. the list of compatible
tags for the current Python.
12 changes: 1 addition & 11 deletions src/pip/_internal/cli/base_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,10 +324,7 @@ def _build_package_finder(
self,
options, # type: Values
session, # type: PipSession
platform=None, # type: Optional[str]
py_version_info=None, # type: Optional[Tuple[int, ...]]
abi=None, # type: Optional[str]
implementation=None, # type: Optional[str]
target_python=None, # type: Optional[TargetPython]
ignore_requires_python=None, # type: Optional[bool]
):
# type: (...) -> PackageFinder
Expand All @@ -346,13 +343,6 @@ def _build_package_finder(
ignore_requires_python=ignore_requires_python,
)

target_python = TargetPython(
platform=platform,
py_version_info=py_version_info,
abi=abi,
implementation=implementation,
)

return PackageFinder.create(
search_scope=search_scope,
selection_prefs=selection_prefs,
Expand Down
22 changes: 22 additions & 0 deletions src/pip/_internal/cli/cmdoptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from pip._internal.models.format_control import FormatControl
from pip._internal.models.index import PyPI
from pip._internal.models.search_scope import SearchScope
from pip._internal.models.target_python import TargetPython
from pip._internal.utils.hashes import STRONG_HASHES
from pip._internal.utils.misc import redact_password_from_url
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
Expand Down Expand Up @@ -356,6 +357,7 @@ def find_links():


def make_search_scope(options, suppress_no_index=False):
# type: (Values, bool) -> SearchScope
"""
:param suppress_no_index: Whether to ignore the --no-index option
when constructing the SearchScope object.
Expand Down Expand Up @@ -600,6 +602,26 @@ def _handle_python_version(option, opt_str, value, parser):
) # type: Callable[..., Option]


def add_target_python_options(cmd_opts):
# type: (OptionGroup) -> None
cmd_opts.add_option(platform())
cmd_opts.add_option(python_version())
cmd_opts.add_option(implementation())
cmd_opts.add_option(abi())


def make_target_python(options):
# type: (Values) -> TargetPython
target_python = TargetPython(
platform=options.platform,
py_version_info=options.python_version,
abi=options.abi,
implementation=options.implementation,
)

return target_python


def prefer_binary():
# type: () -> Option
return Option(
Expand Down
10 changes: 2 additions & 8 deletions src/pip/_internal/cli/main_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import os
import sys

from pip import __version__
from pip._internal.cli import cmdoptions
from pip._internal.cli.parser import (
ConfigOptionParser, UpdatingDefaultsHelpFormatter,
Expand All @@ -13,7 +12,7 @@
commands_dict, get_similar_commands, get_summaries,
)
from pip._internal.exceptions import CommandError
from pip._internal.utils.misc import get_prog
from pip._internal.utils.misc import get_pip_version, get_prog
from pip._internal.utils.typing import MYPY_CHECK_RUNNING

if MYPY_CHECK_RUNNING:
Expand All @@ -39,12 +38,7 @@ def create_main_parser():
parser = ConfigOptionParser(**parser_kw)
parser.disable_interspersed_args()

pip_pkg_dir = os.path.abspath(os.path.join(
os.path.dirname(__file__), "..", "..",
))
parser.version = 'pip %s from %s (python %s)' % (
__version__, pip_pkg_dir, sys.version[:3],
)
parser.version = get_pip_version()

# add the general options
gen_opts = cmdoptions.make_option_group(cmdoptions.general_group, parser)
Expand Down
2 changes: 2 additions & 0 deletions src/pip/_internal/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from pip._internal.commands.completion import CompletionCommand
from pip._internal.commands.configuration import ConfigurationCommand
from pip._internal.commands.debug import DebugCommand
from pip._internal.commands.download import DownloadCommand
from pip._internal.commands.freeze import FreezeCommand
from pip._internal.commands.hash import HashCommand
Expand Down Expand Up @@ -36,6 +37,7 @@
WheelCommand,
HashCommand,
CompletionCommand,
DebugCommand,
HelpCommand,
] # type: List[Type[Command]]

Expand Down
102 changes: 102 additions & 0 deletions src/pip/_internal/commands/debug.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
from __future__ import absolute_import

import logging
import sys

from pip._internal.cli import cmdoptions
from pip._internal.cli.base_command import Command
from pip._internal.cli.cmdoptions import make_target_python
from pip._internal.cli.status_codes import SUCCESS
from pip._internal.utils.logging import indent_log
from pip._internal.utils.misc import get_pip_version
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
from pip._internal.wheel import format_tag

if MYPY_CHECK_RUNNING:
from typing import Any, List
from optparse import Values

logger = logging.getLogger(__name__)


def show_value(name, value):
# type: (str, str) -> None
logger.info('{}: {}'.format(name, value))


def show_sys_implementation():
# type: () -> None
logger.info('sys.implementation:')
if hasattr(sys, 'implementation'):
implementation = sys.implementation # type: ignore
implementation_name = implementation.name
else:
implementation_name = ''

with indent_log():
show_value('name', implementation_name)


def show_tags(options):
# type: (Values) -> None
tag_limit = 10

target_python = make_target_python(options)
tags = target_python.get_tags()

# Display the target options that were explicitly provided.
formatted_target = target_python.format_given()
suffix = ''
if formatted_target:
suffix = ' (target: {})'.format(formatted_target)

msg = 'Compatible tags: {}{}'.format(len(tags), suffix)
logger.info(msg)

if options.verbose < 1 and len(tags) > tag_limit:
tags_limited = True
tags = tags[:tag_limit]
else:
tags_limited = False

with indent_log():
for tag in tags:
logger.info(format_tag(tag))

if tags_limited:
msg = (
'...\n'
'[First {tag_limit} tags shown. Pass --verbose to show all.]'
).format(tag_limit=tag_limit)
logger.info(msg)


class DebugCommand(Command):
"""
Display debug information.
"""

name = 'debug'
usage = """
%prog <options>"""
summary = 'Show information useful for debugging.'
ignore_require_venv = True

def __init__(self, *args, **kw):
super(DebugCommand, self).__init__(*args, **kw)

cmd_opts = self.cmd_opts
cmdoptions.add_target_python_options(cmd_opts)
self.parser.insert_option_group(0, cmd_opts)

def run(self, options, args):
# type: (Values, List[Any]) -> int
show_value('pip version', get_pip_version())
show_value('sys.version', sys.version)
show_value('sys.executable', sys.executable)
show_value('sys.platform', sys.platform)
show_sys_implementation()

show_tags(options)

return SUCCESS
12 changes: 4 additions & 8 deletions src/pip/_internal/commands/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from pip._internal.cli import cmdoptions
from pip._internal.cli.base_command import RequirementCommand
from pip._internal.cli.cmdoptions import make_target_python
from pip._internal.legacy_resolve import Resolver
from pip._internal.operations.prepare import RequirementPreparer
from pip._internal.req import RequirementSet
Expand Down Expand Up @@ -69,10 +70,7 @@ def __init__(self, *args, **kw):
help=("Download packages into <dir>."),
)

cmd_opts.add_option(cmdoptions.platform())
cmd_opts.add_option(cmdoptions.python_version())
cmd_opts.add_option(cmdoptions.implementation())
cmd_opts.add_option(cmdoptions.abi())
cmdoptions.add_target_python_options(cmd_opts)

index_opts = cmdoptions.make_option_group(
cmdoptions.index_group,
Expand All @@ -96,13 +94,11 @@ def run(self, options, args):
ensure_dir(options.download_dir)

with self._build_session(options) as session:
target_python = make_target_python(options)
finder = self._build_package_finder(
options=options,
session=session,
platform=options.platform,
py_version_info=options.python_version,
abi=options.abi,
implementation=options.implementation,
target_python=target_python,
)
build_delete = (not (options.no_clean or options.build_dir))
if options.cache_dir and not check_path_owner(options.cache_dir):
Expand Down
12 changes: 4 additions & 8 deletions src/pip/_internal/commands/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from pip._internal.cache import WheelCache
from pip._internal.cli import cmdoptions
from pip._internal.cli.base_command import RequirementCommand
from pip._internal.cli.cmdoptions import make_target_python
from pip._internal.cli.status_codes import ERROR
from pip._internal.exceptions import (
CommandError, InstallationError, PreviousBuildDirError,
Expand Down Expand Up @@ -114,10 +115,7 @@ def __init__(self, *args, **kw):
'<dir>. Use --upgrade to replace existing packages in <dir> '
'with new versions.'
)
cmd_opts.add_option(cmdoptions.platform())
cmd_opts.add_option(cmdoptions.python_version())
cmd_opts.add_option(cmdoptions.implementation())
cmd_opts.add_option(cmdoptions.abi())
cmdoptions.add_target_python_options(cmd_opts)

cmd_opts.add_option(
'--user',
Expand Down Expand Up @@ -285,13 +283,11 @@ def run(self, options, args):
global_options = options.global_options or []

with self._build_session(options) as session:
target_python = make_target_python(options)
finder = self._build_package_finder(
options=options,
session=session,
platform=options.platform,
py_version_info=options.python_version,
abi=options.abi,
implementation=options.implementation,
target_python=target_python,
ignore_requires_python=options.ignore_requires_python,
)
build_delete = (not (options.no_clean or options.build_dir))
Expand Down
28 changes: 26 additions & 2 deletions src/pip/_internal/models/target_python.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
from pip._internal.utils.typing import MYPY_CHECK_RUNNING

if MYPY_CHECK_RUNNING:
from typing import Optional, Tuple
from typing import List, Optional, Tuple
from pip._internal.pep425tags import Pep425Tag


class TargetPython(object):
Expand Down Expand Up @@ -54,9 +55,32 @@ def __init__(
self.py_version_info = py_version_info

# This is used to cache the return value of get_tags().
self._valid_tags = None
self._valid_tags = None # type: Optional[List[Pep425Tag]]

def format_given(self):
# type: () -> str
"""
Format the given, non-None attributes for display.
"""
display_version = None
if self._given_py_version_info is not None:
display_version = '.'.join(
str(part) for part in self._given_py_version_info
)

key_values = [
('platform', self.platform),
('version_info', display_version),
('abi', self.abi),
('implementation', self.implementation),
]
return ' '.join(
'{}={!r}'.format(key, value) for key, value in key_values
if value is not None
)

def get_tags(self):
# type: () -> List[Pep425Tag]
"""
Return the supported tags to check wheel candidates against.
"""
Expand Down
13 changes: 13 additions & 0 deletions src/pip/_internal/utils/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from pip._vendor.six.moves.urllib import request as urllib_request
from pip._vendor.six.moves.urllib.parse import unquote as urllib_unquote

from pip import __version__
from pip._internal.exceptions import CommandError, InstallationError
from pip._internal.locations import (
running_under_virtualenv, site_packages, user_site, virtualenv_no_global,
Expand Down Expand Up @@ -104,6 +105,18 @@ def cast(typ, val):
logger.debug('lzma module is not available')


def get_pip_version():
# type: () -> str
pip_pkg_dir = os.path.join(os.path.dirname(__file__), "..", "..")
pip_pkg_dir = os.path.abspath(pip_pkg_dir)

return (
'pip {} from {} (python {})'.format(
__version__, pip_pkg_dir, sys.version[:3],
)
)


def normalize_version_info(py_version_info):
# type: (Optional[Tuple[int, ...]]) -> Optional[Tuple[int, int, int]]
"""
Expand Down
Loading

0 comments on commit a8510bc

Please sign in to comment.