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)