diff -r ab13c1d0d5e6 Lib/pdb.py
--- a/Lib/pdb.py Sat Oct 15 19:03:06 2016 -0700
+++ b/Lib/pdb.py Sun Oct 16 16:34:17 2016 +0800
@@ -374,6 +374,9 @@
sys.stdout = self.stdout
sys.displayhook = self.displayhook
exec(code, globals, locals)
+ except NameError as e:
+ self._handle_closure_error(e, locals)
+ raise
finally:
sys.stdout = save_stdout
sys.stdin = save_stdin
@@ -382,6 +385,19 @@
exc_info = sys.exc_info()[:2]
self.error(traceback.format_exception_only(*exc_info)[-1].strip())
+ def _handle_closure_error(self, error, local_vars):
+ var_name = re.findall("'(\w+)' is not defined", str(error))[0]
+ if var_name and var_name in local_vars:
+ _msg = """
+ Pdb can not use the local variable '{0}' to create a closure in
+ your evaluation. Instead, it tries to use '{0}' in globals().
+ This behavior is due to the limitation of dynamic
+ interpretation within a debugger session.
+
+ Hint: type 'help interact' to check 'interact' command.
+ """.format(var_name)
+ self.message(_msg)
+
def precmd(self, line):
"""Handle alias expansion and ';;' separator."""
if not line.strip():
@@ -1153,7 +1169,9 @@
def _getval(self, arg):
try:
return eval(arg, self.curframe.f_globals, self.curframe_locals)
- except:
+ except Exception as e:
+ if isinstance(e, NameError):
+ self._handle_closure_error(e, self.curframe_locals)
exc_info = sys.exc_info()[:2]
self.error(traceback.format_exception_only(*exc_info)[-1].strip())
raise
diff -r ab13c1d0d5e6 Lib/test/test_pdb.py
--- a/Lib/test/test_pdb.py Sat Oct 15 19:03:06 2016 -0700
+++ b/Lib/test/test_pdb.py Sun Oct 16 16:34:17 2016 +0800
@@ -583,7 +583,7 @@
>>> with PdbTestInput(['step','x', 'continue']): # doctest: +ELLIPSIS
... pdb_invoke('run', compile('x=1', '', 'exec'))
- > (1)()...
+ > (1)()
(Pdb) step
--Return--
> (1)()->None
@@ -1110,6 +1110,34 @@
if save_home is not None:
os.environ['HOME'] = save_home
+ def test_show_closure_warning(self):
+ script = """
+ y = 1
+ def f1():
+ x = 1
+ f1()
+ """
+ commands = """
+ break f1
+ continue
+ next
+ (lambda: x)()
+ p (lambda: x)()
+ pp (lambda: x)()
+ source (lambda: x)()
+ whatis (lambda: x)()
+ (lambda: y)()
+ (lambda: z)()
+ quit
+ """
+ stdout, stderr = self.run_pdb(script, commands)
+ self.assertEqual(5, stdout.count("'x' is not defined"))
+ self.assertEqual(5, stdout.count("can not use local variable 'x'"))
+ self.assertEqual(0, stdout.count("'y' is not defined"))
+ self.assertEqual(0, stdout.count("can not use local variable 'y'"))
+ self.assertEqual(1, stdout.count("'z' is not defined"))
+ self.assertEqual(0, stdout.count("can not use local variable 'z'"))
+
def tearDown(self):
support.unlink(support.TESTFN)