Skip to content

Commit

Permalink
Add --debug flag to base command
Browse files Browse the repository at this point in the history
This flag makes the main subroutine (cli.base_command.Command.run)
withold from intercepting unhandled exceptions. This means, that
debugging via "python -m pdb -m pip" is now possible.
  • Loading branch information
MrMino committed Feb 4, 2021
1 parent c188d1f commit f3cf5c1
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 37 deletions.
1 change: 1 addition & 0 deletions news/9349.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add "--debug" flag.
88 changes: 51 additions & 37 deletions src/pip/_internal/cli/base_command.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Base Command class, and related routines"""

import functools
import logging
import logging.config
import optparse
Expand Down Expand Up @@ -34,7 +35,7 @@

if MYPY_CHECK_RUNNING:
from optparse import Values
from typing import Any, List, Optional, Tuple
from typing import Any, Callable, List, Optional, Tuple

from pip._internal.utils.temp_dir import (
TempDirectoryTypeRegistry as TempDirRegistry,
Expand Down Expand Up @@ -185,42 +186,55 @@ def _main(self, args):
"This will become an error in pip 21.0."
)

def intercepts_unhandled_exc(run_func):
# type: (Callable[..., int]) -> Callable[..., int]
@functools.wraps(run_func)
def exc_logging_wrapper(*args):
# type: (*Any) -> int
try:
status = run_func(*args)
assert isinstance(status, int)
return status
except PreviousBuildDirError as exc:
logger.critical(str(exc))
logger.debug('Exception information:', exc_info=True)

return PREVIOUS_BUILD_DIR_ERROR
except (InstallationError, UninstallationError, BadCommand,
NetworkConnectionError) as exc:
logger.critical(str(exc))
logger.debug('Exception information:', exc_info=True)

return ERROR
except CommandError as exc:
logger.critical('%s', exc)
logger.debug('Exception information:', exc_info=True)

return ERROR
except BrokenStdoutLoggingError:
# Bypass our logger and write any remaining messages to
# stderr because stdout no longer works.
print('ERROR: Pipe to stdout was broken', file=sys.stderr)
if level_number <= logging.DEBUG:
traceback.print_exc(file=sys.stderr)

return ERROR
except KeyboardInterrupt:
logger.critical('Operation cancelled by user')
logger.debug('Exception information:', exc_info=True)

return ERROR
except BaseException:
logger.critical('Exception:', exc_info=True)

return UNKNOWN_ERROR
return exc_logging_wrapper

try:
status = self.run(options, args)
assert isinstance(status, int)
return status
except PreviousBuildDirError as exc:
logger.critical(str(exc))
logger.debug('Exception information:', exc_info=True)

return PREVIOUS_BUILD_DIR_ERROR
except (InstallationError, UninstallationError, BadCommand,
NetworkConnectionError) as exc:
logger.critical(str(exc))
logger.debug('Exception information:', exc_info=True)

return ERROR
except CommandError as exc:
logger.critical('%s', exc)
logger.debug('Exception information:', exc_info=True)

return ERROR
except BrokenStdoutLoggingError:
# Bypass our logger and write any remaining messages to stderr
# because stdout no longer works.
print('ERROR: Pipe to stdout was broken', file=sys.stderr)
if level_number <= logging.DEBUG:
traceback.print_exc(file=sys.stderr)

return ERROR
except KeyboardInterrupt:
logger.critical('Operation cancelled by user')
logger.debug('Exception information:', exc_info=True)

return ERROR
except BaseException:
logger.critical('Exception:', exc_info=True)

return UNKNOWN_ERROR
if not options.debug_mode:
run = intercepts_unhandled_exc(self.run)
else:
run = self.run
return run(options, args)
finally:
self.handle_pip_version_check(options)
13 changes: 13 additions & 0 deletions src/pip/_internal/cli/cmdoptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,18 @@ class PipOption(Option):
help='Show help.',
) # type: Callable[..., Option]

debug_mode = partial(
Option,
'--debug',
dest='debug_mode',
action="store_true",
default=False,
help=(
"Let unhandled exceptions propagate outside the main subroutine, "
"instead of logging them to stderr."
)
) # type: Callable[..., Option]

isolated_mode = partial(
Option,
"--isolated",
Expand Down Expand Up @@ -934,6 +946,7 @@ def check_list_path_option(options):
'name': 'General Options',
'options': [
help_,
debug_mode,
isolated_mode,
require_virtualenv,
verbose,
Expand Down

0 comments on commit f3cf5c1

Please sign in to comment.