aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/tools/python3/src/Lib/pdb.py
diff options
context:
space:
mode:
authorshadchin <shadchin@yandex-team.com>2024-02-12 07:53:52 +0300
committerDaniil Cherednik <dcherednik@ydb.tech>2024-02-14 14:26:16 +0000
commit31f2a419764a8ba77c2a970cfc80056c6cd06756 (patch)
treec1995d239eba8571cefc640f6648e1d5dd4ce9e2 /contrib/tools/python3/src/Lib/pdb.py
parentfe2ef02b38d9c85d80060963b265a1df9f38c3bb (diff)
downloadydb-31f2a419764a8ba77c2a970cfc80056c6cd06756.tar.gz
Update Python from 3.11.8 to 3.12.2
Diffstat (limited to 'contrib/tools/python3/src/Lib/pdb.py')
-rwxr-xr-xcontrib/tools/python3/src/Lib/pdb.py235
1 files changed, 201 insertions, 34 deletions
diff --git a/contrib/tools/python3/src/Lib/pdb.py b/contrib/tools/python3/src/Lib/pdb.py
index 4a4a0b9d90..a838a26b03 100755
--- a/contrib/tools/python3/src/Lib/pdb.py
+++ b/contrib/tools/python3/src/Lib/pdb.py
@@ -76,6 +76,7 @@ import bdb
import dis
import code
import glob
+import token
import pprint
import signal
import inspect
@@ -276,6 +277,8 @@ class Pdb(bdb.Bdb, cmd.Cmd):
self.lineno = None
self.stack = []
self.curindex = 0
+ if hasattr(self, 'curframe') and self.curframe:
+ self.curframe.f_globals.pop('__pdb_convenience_variables', None)
self.curframe = None
self.tb_lineno.clear()
@@ -294,6 +297,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
# locals whenever the .f_locals accessor is called, so we
# cache it here to ensure that modifications are not overwritten.
self.curframe_locals = self.curframe.f_locals
+ self.set_convenience_variable(self.curframe, '_frame', self.curframe)
return self.execRcLines()
# Can be executed earlier than 'setup' if desired
@@ -365,6 +369,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
if self._wait_for_mainpyfile:
return
frame.f_locals['__return__'] = return_value
+ self.set_convenience_variable(frame, '_retval', return_value)
self.message('--Return--')
self.interaction(frame, None)
@@ -375,6 +380,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
return
exc_type, exc_value, exc_traceback = exc_info
frame.f_locals['__exception__'] = exc_type, exc_value
+ self.set_convenience_variable(frame, '_exception', exc_value)
# An 'Internal StopIteration' exception is an exception debug event
# issued by the interpreter when handling a subgenerator run with
@@ -383,8 +389,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
# stop when the debuggee is returning from such generators.
prefix = 'Internal ' if (not exc_traceback
and exc_type is StopIteration) else ''
- self.message('%s%s' % (prefix,
- traceback.format_exception_only(exc_type, exc_value)[-1].strip()))
+ self.message('%s%s' % (prefix, self._format_exc(exc_value)))
self.interaction(frame, exc_traceback)
# General interaction function
@@ -401,6 +406,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
self.message('--KeyboardInterrupt--')
# Called before loop, handles display expressions
+ # Set up convenience variable containers
def preloop(self):
displaying = self.displaying.get(self.curframe)
if displaying:
@@ -442,7 +448,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
self.message(repr(obj))
def default(self, line):
- if line[:1] == '!': line = line[1:]
+ if line[:1] == '!': line = line[1:].strip()
locals = self.curframe_locals
globals = self.curframe.f_globals
try:
@@ -462,6 +468,39 @@ class Pdb(bdb.Bdb, cmd.Cmd):
except:
self._error_exc()
+ def _replace_convenience_variables(self, line):
+ """Replace the convenience variables in 'line' with their values.
+ e.g. $foo is replaced by __pdb_convenience_variables["foo"].
+ Note: such pattern in string literals will be skipped"""
+
+ if "$" not in line:
+ return line
+
+ dollar_start = dollar_end = -1
+ replace_variables = []
+ try:
+ for t in tokenize.generate_tokens(io.StringIO(line).readline):
+ token_type, token_string, start, end, _ = t
+ if token_type == token.OP and token_string == '$':
+ dollar_start, dollar_end = start, end
+ elif start == dollar_end and token_type == token.NAME:
+ # line is a one-line command so we only care about column
+ replace_variables.append((dollar_start[1], end[1], token_string))
+ except tokenize.TokenError:
+ return line
+
+ if not replace_variables:
+ return line
+
+ last_end = 0
+ line_pieces = []
+ for start, end, name in replace_variables:
+ line_pieces.append(line[last_end:start] + f'__pdb_convenience_variables["{name}"]')
+ last_end = end
+ line_pieces.append(line[last_end:])
+
+ return ''.join(line_pieces)
+
def precmd(self, line):
"""Handle alias expansion and ';;' separator."""
if not line.strip():
@@ -485,6 +524,10 @@ class Pdb(bdb.Bdb, cmd.Cmd):
next = line[marker+2:].lstrip()
self.cmdqueue.append(next)
line = line[:marker].rstrip()
+
+ # Replace all the convenience variables
+ line = self._replace_convenience_variables(line)
+
return line
def onecmd(self, line):
@@ -535,6 +578,13 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def error(self, msg):
print('***', msg, file=self.stdout)
+ # convenience variables
+
+ def set_convenience_variable(self, frame, name, value):
+ if '__pdb_convenience_variables' not in frame.f_globals:
+ frame.f_globals['__pdb_convenience_variables'] = {}
+ frame.f_globals['__pdb_convenience_variables'][name] = value
+
# Generic completion functions. Individual complete_foo methods can be
# assigned below to one of these functions.
@@ -594,7 +644,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
# Return true to exit from the command loop
def do_commands(self, arg):
- """commands [bpnumber]
+ """(Pdb) commands [bpnumber]
(com) ...
(com) end
(Pdb)
@@ -680,6 +730,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def do_break(self, arg, temporary = 0):
"""b(reak) [ ([filename:]lineno | function) [, condition] ]
+
Without argument, list all breaks.
With a line number argument, set a break at this line in the
@@ -709,6 +760,9 @@ class Pdb(bdb.Bdb, cmd.Cmd):
if comma > 0:
# parse stuff after comma: "condition"
cond = arg[comma+1:].lstrip()
+ if err := self._compile_error_message(cond):
+ self.error('Invalid condition %s: %r' % (cond, err))
+ return
arg = arg[:comma].rstrip()
# parse stuff before comma: [filename:]lineno | function
colon = arg.rfind(':')
@@ -785,6 +839,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def do_tbreak(self, arg):
"""tbreak [ ([filename:]lineno | function) [, condition] ]
+
Same arguments as break, but sets a temporary breakpoint: it
is automatically deleted when first hit.
"""
@@ -849,6 +904,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def do_enable(self, arg):
"""enable bpnumber [bpnumber ...]
+
Enables the breakpoints given as a space separated list of
breakpoint numbers.
"""
@@ -866,6 +922,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def do_disable(self, arg):
"""disable bpnumber [bpnumber ...]
+
Disables the breakpoints given as a space separated list of
breakpoint numbers. Disabling a breakpoint means it cannot
cause the program to stop execution, but unlike clearing a
@@ -886,6 +943,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def do_condition(self, arg):
"""condition bpnumber [condition]
+
Set a new condition for the breakpoint, an expression which
must evaluate to true before the breakpoint is honored. If
condition is absent, any existing condition is removed; i.e.,
@@ -894,6 +952,9 @@ class Pdb(bdb.Bdb, cmd.Cmd):
args = arg.split(' ', 1)
try:
cond = args[1]
+ if err := self._compile_error_message(cond):
+ self.error('Invalid condition %s: %r' % (cond, err))
+ return
except IndexError:
cond = None
try:
@@ -913,6 +974,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def do_ignore(self, arg):
"""ignore bpnumber [count]
+
Set the ignore count for the given breakpoint number. If
count is omitted, the ignore count is set to 0. A breakpoint
becomes active when the ignore count is zero. When non-zero,
@@ -947,7 +1009,8 @@ class Pdb(bdb.Bdb, cmd.Cmd):
complete_ignore = _complete_bpnumber
def do_clear(self, arg):
- """cl(ear) filename:lineno\ncl(ear) [bpnumber [bpnumber...]]
+ """cl(ear) [filename:lineno | bpnumber ...]
+
With a space separated list of breakpoint numbers, clear
those breakpoints. Without argument, clear all breaks (but
first ask confirmation). With a filename:lineno argument,
@@ -999,6 +1062,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def do_where(self, arg):
"""w(here)
+
Print a stack trace, with the most recent frame at the bottom.
An arrow indicates the "current frame", which determines the
context of most commands. 'bt' is an alias for this command.
@@ -1012,11 +1076,13 @@ class Pdb(bdb.Bdb, cmd.Cmd):
self.curindex = number
self.curframe = self.stack[self.curindex][0]
self.curframe_locals = self.curframe.f_locals
+ self.set_convenience_variable(self.curframe, '_frame', self.curframe)
self.print_stack_entry(self.stack[self.curindex])
self.lineno = None
def do_up(self, arg):
"""u(p) [count]
+
Move the current frame count (default one) levels up in the
stack trace (to an older frame).
"""
@@ -1037,6 +1103,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def do_down(self, arg):
"""d(own) [count]
+
Move the current frame count (default one) levels down in the
stack trace (to a newer frame).
"""
@@ -1057,6 +1124,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def do_until(self, arg):
"""unt(il) [lineno]
+
Without argument, continue execution until the line with a
number greater than the current one is reached. With a line
number, continue execution until a line with a number greater
@@ -1081,6 +1149,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def do_step(self, arg):
"""s(tep)
+
Execute the current line, stop at the first possible occasion
(either in a function that is called or in the current
function).
@@ -1091,6 +1160,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def do_next(self, arg):
"""n(ext)
+
Continue execution until the next line in the current function
is reached or it returns.
"""
@@ -1100,6 +1170,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def do_run(self, arg):
"""run [args...]
+
Restart the debugged python program. If a string is supplied
it is split with "shlex", and the result is used as the new
sys.argv. History, breakpoints, actions and debugger options
@@ -1121,6 +1192,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def do_return(self, arg):
"""r(eturn)
+
Continue execution until the current function returns.
"""
self.set_return(self.curframe)
@@ -1129,6 +1201,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def do_continue(self, arg):
"""c(ont(inue))
+
Continue execution, only stop when a breakpoint is encountered.
"""
if not self.nosigint:
@@ -1147,6 +1220,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def do_jump(self, arg):
"""j(ump) lineno
+
Set the next line that will be executed. Only available in
the bottom-most frame. This lets you jump back and execute
code again, or jump forward to skip code that you don't want
@@ -1176,6 +1250,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def do_debug(self, arg):
"""debug code
+
Enter a recursive debugger that steps through the code
argument (which is an arbitrary expression or statement to be
executed in the current environment).
@@ -1197,7 +1272,8 @@ class Pdb(bdb.Bdb, cmd.Cmd):
complete_debug = _complete_expression
def do_quit(self, arg):
- """q(uit)\nexit
+ """q(uit) | exit
+
Quit from the debugger. The program being executed is aborted.
"""
self._user_requested_quit = True
@@ -1209,6 +1285,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def do_EOF(self, arg):
"""EOF
+
Handles the receipt of EOF as a command.
"""
self.message('')
@@ -1218,6 +1295,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def do_args(self, arg):
"""a(rgs)
+
Print the argument list of the current function.
"""
co = self.curframe.f_code
@@ -1235,6 +1313,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def do_retval(self, arg):
"""retval
+
Print the return value for the last return of a function.
"""
if '__return__' in self.curframe_locals:
@@ -1256,14 +1335,12 @@ class Pdb(bdb.Bdb, cmd.Cmd):
return eval(arg, self.curframe.f_globals, self.curframe_locals)
else:
return eval(arg, frame.f_globals, frame.f_locals)
- except:
- exc_info = sys.exc_info()[:2]
- err = traceback.format_exception_only(*exc_info)[-1].strip()
- return _rstr('** raised %s **' % err)
+ except BaseException as exc:
+ return _rstr('** raised %s **' % self._format_exc(exc))
def _error_exc(self):
- exc_info = sys.exc_info()[:2]
- self.error(traceback.format_exception_only(*exc_info)[-1].strip())
+ exc = sys.exception()
+ self.error(self._format_exc(exc))
def _msg_val_func(self, arg, func):
try:
@@ -1283,12 +1360,14 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def do_p(self, arg):
"""p expression
+
Print the value of the expression.
"""
self._msg_val_func(arg, repr)
def do_pp(self, arg):
"""pp expression
+
Pretty-print the value of the expression.
"""
self._msg_val_func(arg, pprint.pformat)
@@ -1298,7 +1377,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
complete_pp = _complete_expression
def do_list(self, arg):
- """l(ist) [first [,last] | .]
+ """l(ist) [first[, last] | .]
List source code for the current file. Without arguments,
list 11 lines around the current line or continue the previous
@@ -1355,7 +1434,8 @@ class Pdb(bdb.Bdb, cmd.Cmd):
do_l = do_list
def do_longlist(self, arg):
- """longlist | ll
+ """ll | longlist
+
List the whole source code for the current function or frame.
"""
filename = self.curframe.f_code.co_filename
@@ -1370,6 +1450,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def do_source(self, arg):
"""source expression
+
Try to get source code for the given object and display it.
"""
try:
@@ -1407,7 +1488,8 @@ class Pdb(bdb.Bdb, cmd.Cmd):
self.message(s + '\t' + line.rstrip())
def do_whatis(self, arg):
- """whatis arg
+ """whatis expression
+
Print the type of the argument.
"""
try:
@@ -1450,13 +1532,19 @@ class Pdb(bdb.Bdb, cmd.Cmd):
Without expression, list all display expressions for the current frame.
"""
if not arg:
- self.message('Currently displaying:')
- for key, val in self.displaying.get(self.curframe, {}).items():
- self.message('%s: %s' % (key, self._safe_repr(val, key)))
+ if self.displaying:
+ self.message('Currently displaying:')
+ for key, val in self.displaying.get(self.curframe, {}).items():
+ self.message('%s: %s' % (key, self._safe_repr(val, key)))
+ else:
+ self.message('No expression is being displayed')
else:
- val = self._getval_except(arg)
- self.displaying.setdefault(self.curframe, {})[arg] = val
- self.message('display %s: %s' % (arg, self._safe_repr(val, arg)))
+ if err := self._compile_error_message(arg):
+ self.error('Unable to display %s: %r' % (arg, err))
+ else:
+ val = self._getval_except(arg)
+ self.displaying.setdefault(self.curframe, {})[arg] = val
+ self.message('display %s: %s' % (arg, self._safe_repr(val, arg)))
complete_display = _complete_expression
@@ -1489,7 +1577,8 @@ class Pdb(bdb.Bdb, cmd.Cmd):
code.interact("*interactive*", local=ns)
def do_alias(self, arg):
- """alias [name [command [parameter parameter ...] ]]
+ """alias [name [command]]
+
Create an alias called 'name' that executes 'command'. The
command must *not* be enclosed in quotes. Replaceable
parameters can be indicated by %1, %2, and so on, while %* is
@@ -1528,6 +1617,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def do_unalias(self, arg):
"""unalias name
+
Delete the specified alias.
"""
args = arg.split()
@@ -1570,6 +1660,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def do_help(self, arg):
"""h(elp)
+
Without argument, print the list of available commands.
With a command name as argument, print help about that command.
"help pdb" shows the full pdb documentation.
@@ -1593,17 +1684,21 @@ class Pdb(bdb.Bdb, cmd.Cmd):
if command.__doc__ is None:
self.error('No help for %r; __doc__ string missing' % arg)
return
- self.message(command.__doc__.rstrip())
+ self.message(self._help_message_from_doc(command.__doc__))
do_h = do_help
def help_exec(self):
"""(!) statement
+
Execute the (one-line) statement in the context of the current
stack frame. The exclamation point can be omitted unless the
- first word of the statement resembles a debugger command. To
- assign to a global variable you must always prefix the command
- with a 'global' command, e.g.:
+ first word of the statement resembles a debugger command, e.g.:
+ (Pdb) ! n=42
+ (Pdb)
+
+ To assign to a global variable you must always prefix the command with
+ a 'global' command, e.g.:
(Pdb) global list_options; list_options = ['-l']
(Pdb)
"""
@@ -1661,6 +1756,14 @@ class Pdb(bdb.Bdb, cmd.Cmd):
def _format_exc(self, exc: BaseException):
return traceback.format_exception_only(exc)[-1].strip()
+ def _compile_error_message(self, expr):
+ """Return the error message as string if compiling `expr` fails."""
+ try:
+ compile(expr, "<stdin>", "eval")
+ except SyntaxError as exc:
+ return _rstr(self._format_exc(exc))
+ return ""
+
def _getsourcelines(self, obj):
# GH-103319
# inspect.getsourcelines() returns lineno = 0 for
@@ -1671,6 +1774,26 @@ class Pdb(bdb.Bdb, cmd.Cmd):
lineno = max(1, lineno)
return lines, lineno
+ def _help_message_from_doc(self, doc):
+ lines = [line.strip() for line in doc.rstrip().splitlines()]
+ if not lines:
+ return "No help message found."
+ if "" in lines:
+ usage_end = lines.index("")
+ else:
+ usage_end = 1
+ formatted = []
+ indent = " " * len(self.prompt)
+ for i, line in enumerate(lines):
+ if i == 0:
+ prefix = "Usage: "
+ elif i < usage_end:
+ prefix = " "
+ else:
+ prefix = ""
+ formatted.append(indent + prefix + line)
+ return "\n".join(formatted)
+
# Collect all command help into docstring, if not run with -OO
if __doc__ is not None:
@@ -1693,9 +1816,27 @@ if __doc__ is not None:
# Simplified interface
def run(statement, globals=None, locals=None):
+ """Execute the *statement* (given as a string or a code object)
+ under debugger control.
+
+ The debugger prompt appears before any code is executed; you can set
+ breakpoints and type continue, or you can step through the statement
+ using step or next.
+
+ The optional *globals* and *locals* arguments specify the
+ environment in which the code is executed; by default the
+ dictionary of the module __main__ is used (see the explanation of
+ the built-in exec() or eval() functions.).
+ """
Pdb().run(statement, globals, locals)
def runeval(expression, globals=None, locals=None):
+ """Evaluate the *expression* (given as a string or a code object)
+ under debugger control.
+
+ When runeval() returns, it returns the value of the expression.
+ Otherwise this function is similar to run().
+ """
return Pdb().runeval(expression, globals, locals)
def runctx(statement, globals, locals):
@@ -1703,9 +1844,23 @@ def runctx(statement, globals, locals):
run(statement, globals, locals)
def runcall(*args, **kwds):
+ """Call the function (a function or method object, not a string)
+ with the given arguments.
+
+ When runcall() returns, it returns whatever the function call
+ returned. The debugger prompt appears as soon as the function is
+ entered.
+ """
return Pdb().runcall(*args, **kwds)
def set_trace(*, header=None):
+ """Enter the debugger at the calling stack frame.
+
+ This is useful to hard-code a breakpoint at a given point in a
+ program, even if the code is not otherwise being debugged (e.g. when
+ an assertion fails). If given, *header* is printed to the console
+ just before debugging begins.
+ """
pdb = Pdb()
if header is not None:
pdb.message(header)
@@ -1714,11 +1869,18 @@ def set_trace(*, header=None):
# Post-Mortem interface
def post_mortem(t=None):
+ """Enter post-mortem debugging of the given *traceback* object.
+
+ If no traceback is given, it uses the one of the exception that is
+ currently being handled (an exception must be being handled if the
+ default is to be used).
+ """
# handling the default
if t is None:
- # sys.exc_info() returns (type, value, traceback) if an exception is
- # being handled, otherwise it returns None
- t = sys.exc_info()[2]
+ exc = sys.exception()
+ if exc is not None:
+ t = exc.__traceback__
+
if t is None:
raise ValueError("A valid traceback must be passed if no "
"exception is being handled")
@@ -1728,7 +1890,12 @@ def post_mortem(t=None):
p.interaction(None, t)
def pm():
- post_mortem(sys.last_traceback)
+ """Enter post-mortem debugging of the traceback found in sys.last_traceback."""
+ if hasattr(sys, 'last_exc'):
+ tb = sys.last_exc.__traceback__
+ else:
+ tb = sys.last_traceback
+ post_mortem(tb)
# Main program for testing
@@ -1797,18 +1964,18 @@ def main():
except Restart:
print("Restarting", target, "with arguments:")
print("\t" + " ".join(sys.argv[1:]))
- except SystemExit:
+ except SystemExit as e:
# In most cases SystemExit does not warrant a post-mortem session.
print("The program exited via sys.exit(). Exit status:", end=' ')
- print(sys.exc_info()[1])
+ print(e)
except SyntaxError:
traceback.print_exc()
sys.exit(1)
- except:
+ except BaseException as e:
traceback.print_exc()
print("Uncaught exception. Entering post mortem debugging")
print("Running 'cont' or 'step' will restart the program")
- t = sys.exc_info()[2]
+ t = e.__traceback__
pdb.interaction(None, t)
print("Post mortem debugger finished. The " + target +
" will be restarted")