You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
pdb "args" crashes when an arg is not printable #65052
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee=Noneclosed_at=Nonecreated_at=<Date2014-03-05.10:45:57.026>labels= ['type-feature', 'library']
title='pdb "args" crashes when an arg is not printable'updated_at=<Date2021-09-17.02:50:21.987>user='https://github.com/jneb'
The "args" command in pdb crashes when an argument cannot be printed.
Fortunately, this is easy to fix.
For version 3.3.3:
In function Pdb.do_args (lib/pdb.py, line 1120)
Change line 1131
self.message('%s = %r' % (name, dict[name]))
to
try: r = repr(dict[name])
except: r = "(Cannot print object)"
self.message('%s = %s' % (name, r))
Why is the object not printable? Can you provide a reproducer?
Also, the bare except is buggy, it would catch things like KeyboardInterrupt that should not be caught there. 'except Exception' would be appropriate in this case.
jneb commented on Mar 12, 2014
jnebmannequin
MannequinAuthor
More actions
Thanks for your reaction.
The object is not printable, since I was debugging an __init__ of an object, and some fields where being initialized:
class foo:
def __init__(self):
foo.bar = "hello"
def repr(self): return foo.bar
I tried to make a useable patch file (with neater layout and using Exeption), see attach.
jneb commented on Mar 12, 2014
jnebmannequin
MannequinAuthor
More actions
Oops. Here the correct example:
>>> classfoo:
... def__init__(self):
... foo.bar ="hello"
... def__repr__(self): return foo.bar
...
>>> pdb.runcall(foo)
> <stdin>(3)__init__()
(Pdb) a
Traceback (most recent call last):
File ".\pdb.py", line 1132, in do_args
self.message('%s = %r' % (name, dict[name]))
File "<stdin>", line 4, in __repr__
AttributeError: type object 'foo' has no attribute 'bar'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File ".\pdb.py", line 1580, in runcall
return Pdb().runcall(*args, **kwds)
File "C:\Python33\lib\bdb.py", line 439, in runcall
res = func(*args, **kwds)
File "<stdin>", line 3, in __init__
File "<stdin>", line 3, in __init__
File "C:\Python33\lib\bdb.py", line 47, in trace_dispatch
return self.dispatch_line(frame)
File "C:\Python33\lib\bdb.py", line 65, in dispatch_line
self.user_line(frame)
File ".\pdb.py", line 266, in user_line
self.interaction(frame, None)
File ".\pdb.py", line 345, in interaction
self._cmdloop()
File ".\pdb.py", line 318, in _cmdloop
self.cmdloop()
File "C:\Python33\lib\cmd.py", line 138, in cmdloop
stop = self.onecmd(line)
File ".\pdb.py", line 411, in onecmd
return cmd.Cmd.onecmd(self, line)
File "C:\Python33\lib\cmd.py", line 217, in onecmd
return func(arg)
File ".\pdb.py", line 1134, in do_args
self.message('%s = *** repr failed: %s ***' % (name,))
TypeError: not enough arguments for format string
At least, I expect pdb to not crash, but a clearer error (as in the patch) is nice to have.
jneb commented on Mar 12, 2014
jnebmannequin
MannequinAuthor
More actions
I am not good at this. Sorry for the mess.
Here is a good example, and a good patch:
>>> classfoo:
... def__init__(self):
... foo.bar ="hello"
... def__repr__(self): return foo.bar
...
>>> pdb.runcall(foo)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'pdb' is not defined
>>> import pdb
>>> pdb.runcall(foo)
> <stdin>(3)__init__()
(Pdb) a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python33\lib\pdb.py", line 1577, in runcall
return Pdb().runcall(*args, **kwds)
File "C:\Python33\lib\bdb.py", line 439, in runcall
res = func(*args, **kwds)
File "<stdin>", line 3, in __init__
File "<stdin>", line 3, in __init__
File "C:\Python33\lib\bdb.py", line 47, in trace_dispatch
return self.dispatch_line(frame)
File "C:\Python33\lib\bdb.py", line 65, in dispatch_line
self.user_line(frame)
File "C:\Python33\lib\pdb.py", line 266, in user_line
self.interaction(frame, None)
File "C:\Python33\lib\pdb.py", line 345, in interaction
self._cmdloop()
File "C:\Python33\lib\pdb.py", line 318, in _cmdloop
self.cmdloop()
File "C:\Python33\lib\cmd.py", line 138, in cmdloop
stop = self.onecmd(line)
File "C:\Python33\lib\pdb.py", line 411, in onecmd
return cmd.Cmd.onecmd(self, line)
File "C:\Python33\lib\cmd.py", line 217, in onecmd
return func(arg)
File "C:\Python33\lib\pdb.py", line 1131, in do_args
self.message('%s = %r' % (name, dict[name]))
File "<stdin>", line 4, in __repr__
AttributeError: type object 'foo' has no attribute 'bar'
There is at least one other place (do_break) where this same problem could crop up. Unittest handles this by having a 'safe_repr' function. pdb doesn't need the same function unittest does, but it could do something similar, and then use %s and this function in the places where it currently uses repr to print an arbitrary object:
Maybe we could use Pdb._getval_except(arg, frame=None) in the routine do_args.
If I understand the code, do_args does quite some work to get the value of name in the context of the current frame, maybe just calling
self._getval_except(name, frame=self.curframe)
plus or minus some code would do the job?
I guess the code would actually become shorter...
I'll try to figure it out.
jneb commented on Mar 17, 2014
jnebmannequin
MannequinAuthor
More actions
I did figure it out.
It almost works, except when a argument lost its value, and the same name exists in the global context.
To be more specific: I simplified do_args to the following code
(that obviously ugly by explicitly evaluating repr in context, but
that is not the point)
def do_args(self, arg):
"""a(rgs)
Print the argument list of the current function.
Modified by Jurjen
"""
co = self.curframe.f_code
n = co.co_argcount
if co.co_flags & 4: n = n+1
if co.co_flags & 8: n = n+1
for i in range(n):
name = co.co_varnames[i]
expr = 'repr(%s)' % (name,)
self.message('%s = %s' % (name, self._getval_except(expr)))
At it works perfectly, except for this little surprise:
>>> bar ="BAR"
>>> deffoo(bar):
... del bar
... return5
>>> pdb.runcall(f, 10)
> <stdin>(2)f()
-> del bar
(Pdb) a
bar = 5
(Pdb) n
> <stdin>(3)f()
-> return 5
(Pdb) a
bar = 'BAR' ##### Huh? Expected undefined
I'll leave it to the experts to patch this in proper Python coding style.
So, the conclusion is we need a way to safely evaluate the call to repr() in context, with self.curframe_locals[co.co_varnames[i]] as argument.
I did not find a good supporting routine for that elsewhere in pdb.
xdegaye commented on Mar 18, 2014
xdegayemannequin
Mannequin
More actions
There is at least one other place (do_break) where this same problem could crop up.
Also in do_retval. And the exception is silently ignored in do_p and do_pp when repr() fails, which is not correct.
A solution could be to have a message_safe method to be used in such cases. For example, substitute in do_args:
Commands that silently fail when an object is not printable: p, pp
Commands that crash when an object is not printable:
args, retval
Python 3: display
Python 2: on a 'return' trace event when the return value is not printable
Activity
jneb commentedon Mar 5, 2014
The "args" command in pdb crashes when an argument cannot be printed.
Fortunately, this is easy to fix.
For version 3.3.3:
In function Pdb.do_args (lib/pdb.py, line 1120)
Change line 1131
self.message('%s = %r' % (name, dict[name]))
to
try: r = repr(dict[name])
except: r = "(Cannot print object)"
self.message('%s = %s' % (name, r))
bitdancer commentedon Mar 5, 2014
Why is the object not printable? Can you provide a reproducer?
Also, the bare except is buggy, it would catch things like KeyboardInterrupt that should not be caught there. 'except Exception' would be appropriate in this case.
jneb commentedon Mar 12, 2014
Thanks for your reaction.
The object is not printable, since I was debugging an __init__ of an object, and some fields where being initialized:
class foo:
def __init__(self):
foo.bar = "hello"
def repr(self): return foo.bar
I tried to make a useable patch file (with neater layout and using Exeption), see attach.
jneb commentedon Mar 12, 2014
At least, I expect pdb to not crash, but a clearer error (as in the patch) is nice to have.
jneb commentedon Mar 12, 2014
bitdancer commentedon Mar 12, 2014
There is at least one other place (do_break) where this same problem could crop up. Unittest handles this by having a 'safe_repr' function. pdb doesn't need the same function unittest does, but it could do something similar, and then use %s and this function in the places where it currently uses repr to print an arbitrary object:
jneb commentedon Mar 14, 2014
Maybe we could use Pdb._getval_except(arg, frame=None) in the routine do_args.
If I understand the code, do_args does quite some work to get the value of name in the context of the current frame, maybe just calling
self._getval_except(name, frame=self.curframe)
plus or minus some code would do the job?
I guess the code would actually become shorter...
I'll try to figure it out.
jneb commentedon Mar 17, 2014
I did figure it out.
It almost works, except when a argument lost its value, and the same name exists in the global context.
To be more specific: I simplified do_args to the following code
(that obviously ugly by explicitly evaluating repr in context, but
that is not the point)
def do_args(self, arg):
"""a(rgs)
Print the argument list of the current function.
Modified by Jurjen
"""
co = self.curframe.f_code
n = co.co_argcount
if co.co_flags & 4: n = n+1
if co.co_flags & 8: n = n+1
for i in range(n):
name = co.co_varnames[i]
expr = 'repr(%s)' % (name,)
self.message('%s = %s' % (name, self._getval_except(expr)))
I'll leave it to the experts to patch this in proper Python coding style.
So, the conclusion is we need a way to safely evaluate the call to repr() in context, with self.curframe_locals[co.co_varnames[i]] as argument.
I did not find a good supporting routine for that elsewhere in pdb.
xdegaye commentedon Mar 18, 2014
Also in do_retval. And the exception is silently ignored in do_p and do_pp when repr() fails, which is not correct.
A solution could be to have a message_safe method to be used in such cases. For example, substitute in do_args:
xdegaye commentedon May 23, 2014
Commands that silently fail when an object is not printable: p, pp
Commands that crash when an object is not printable:
args, retval
Python 3: display
Python 2: on a 'return' trace event when the return value is not printable
The attached script illustrates all these cases.
akulakov commentedon Sep 17, 2021
p/pp commands were fixed in this commit:
6544b2532df
gh-65052: Prevent pdb from crashing when trying to display objects (#…
gaogaotiantian commentedon Oct 11, 2023
args, retval and display fixed in #110578, close issue.
pythongh-65052: Prevent pdb from crashing when trying to display obje…
[3.12] gh-65052: Prevent pdb from crashing when trying to display obj…
[3.11] pythongh-65052: Prevent pdb from crashing when trying to displ…
[3.11] GH-65052: Prevent pdb from crashing when trying to display obj…
pythongh-65052: Prevent pdb from crashing when trying to display obje…