return fullname
return None
+ def _runmodule(self, module_name):
+ self._wait_for_mainpyfile = True
+ self._user_requested_quit = False
+ import runpy
+ mod_name, mod_spec, code = runpy._get_module_details(module_name)
+ self.mainpyfile = self.canonic(code.co_filename)
+ import __main__
+ __main__.__dict__.clear()
+ __main__.__dict__.update({
+ "__name__": "__main__",
+ "__file__": self.mainpyfile,
+ "__package__": module_name,
+ "__loader__": mod_spec.loader,
+ "__spec__": mod_spec,
+ "__builtins__": __builtins__,
+ })
+ self.run(code)
+
def _runscript(self, filename):
# The script has to run in __main__ namespace (or imports from
# __main__ will break).
def main():
import getopt
- opts, args = getopt.getopt(sys.argv[1:], 'hc:', ['--help', '--command='])
+ opts, args = getopt.getopt(sys.argv[1:], 'mhc:', ['--help', '--command='])
if not args:
print(_usage)
sys.exit(2)
commands = []
+ run_as_module = False
for opt, optarg in opts:
if opt in ['-h', '--help']:
print(_usage)
sys.exit()
elif opt in ['-c', '--command']:
commands.append(optarg)
+ elif opt in ['-m']:
+ run_as_module = True
mainpyfile = args[0] # Get script filename
- if not os.path.exists(mainpyfile):
+ if not run_as_module and not os.path.exists(mainpyfile):
print('Error:', mainpyfile, 'does not exist')
sys.exit(1)
sys.argv[:] = args # Hide "pdb.py" and pdb options from argument list
# Replace pdb's dir with script's dir in front of module search path.
- sys.path[0] = os.path.dirname(mainpyfile)
+ if not run_as_module:
+ sys.path[0] = os.path.dirname(mainpyfile)
# Note on saving/restoring sys.argv: it's a good idea when sys.argv was
# modified by the script being debugged. It's a bad idea when it was
pdb.rcLines.extend(commands)
while True:
try:
- pdb._runscript(mainpyfile)
+ if run_as_module:
+ pdb._runmodule(mainpyfile)
+ else:
+ pdb._runscript(mainpyfile)
if pdb._user_requested_quit:
break
print("The program finished and will be restarted")
pdb 2: <built-in function default_int_handler>
"""
+
class PdbTestCase(unittest.TestCase):
+ def tearDown(self):
+ support.unlink(support.TESTFN)
- def run_pdb(self, script, commands):
- """Run 'script' lines with pdb and the pdb 'commands'."""
- filename = 'main.py'
- with open(filename, 'w') as f:
- f.write(textwrap.dedent(script))
- self.addCleanup(support.unlink, filename)
+ def _run_pdb(self, pdb_args, commands):
self.addCleanup(support.rmtree, '__pycache__')
- cmd = [sys.executable, '-m', 'pdb', filename]
- stdout = stderr = None
- with subprocess.Popen(cmd, stdout=subprocess.PIPE,
- stdin=subprocess.PIPE,
- stderr=subprocess.STDOUT,
- ) as proc:
+ cmd = [sys.executable, '-m', 'pdb'] + pdb_args
+ with subprocess.Popen(
+ cmd,
+ stdout=subprocess.PIPE,
+ stdin=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ ) as proc:
stdout, stderr = proc.communicate(str.encode(commands))
stdout = stdout and bytes.decode(stdout)
stderr = stderr and bytes.decode(stderr)
return stdout, stderr
+ def run_pdb_script(self, script, commands):
+ """Run 'script' lines with pdb and the pdb 'commands'."""
+ filename = 'main.py'
+ with open(filename, 'w') as f:
+ f.write(textwrap.dedent(script))
+ self.addCleanup(support.unlink, filename)
+ return self._run_pdb([filename], commands)
+
+ def run_pdb_module(self, script, commands):
+ """Runs the script code as part of a module"""
+ self.module_name = 't_main'
+ support.rmtree(self.module_name)
+ main_file = self.module_name + '/__main__.py'
+ init_file = self.module_name + '/__init__.py'
+ os.mkdir(self.module_name)
+ with open(init_file, 'w') as f:
+ pass
+ with open(main_file, 'w') as f:
+ f.write(textwrap.dedent(script))
+ self.addCleanup(support.rmtree, self.module_name)
+ return self._run_pdb(['-m', self.module_name], commands)
+
def _assert_find_function(self, file_content, func_name, expected):
file_content = textwrap.dedent(file_content)
with open('bar.py', 'w') as f:
f.write(textwrap.dedent(bar))
self.addCleanup(support.unlink, 'bar.py')
- stdout, stderr = self.run_pdb(script, commands)
+ stdout, stderr = self.run_pdb_script(script, commands)
self.assertTrue(
any('main.py(5)foo()->None' in l for l in stdout.splitlines()),
'Fail to step into the caller after a return')
script = "def f: pass\n"
commands = ''
expected = "SyntaxError:"
- stdout, stderr = self.run_pdb(script, commands)
+ stdout, stderr = self.run_pdb_script(script, commands)
self.assertIn(expected, stdout,
'\n\nExpected:\n{}\nGot:\n{}\n'
'Fail to handle a syntax error in the debuggee.'
pdb.set_trace(header=header)
self.assertEqual(stdout.getvalue(), header + '\n')
- def tearDown(self):
- support.unlink(support.TESTFN)
+ def test_run_module(self):
+ script = """print("SUCCESS")"""
+ commands = """
+ continue
+ quit
+ """
+ stdout, stderr = self.run_pdb_module(script, commands)
+ self.assertTrue(any("SUCCESS" in l for l in stdout.splitlines()), stdout)
+
+ def test_module_is_run_as_main(self):
+ script = """
+ if __name__ == '__main__':
+ print("SUCCESS")
+ """
+ commands = """
+ continue
+ quit
+ """
+ stdout, stderr = self.run_pdb_module(script, commands)
+ self.assertTrue(any("SUCCESS" in l for l in stdout.splitlines()), stdout)
+
+ def test_breakpoint(self):
+ script = """
+ if __name__ == '__main__':
+ pass
+ print("SUCCESS")
+ pass
+ """
+ commands = """
+ b 3
+ quit
+ """
+ stdout, stderr = self.run_pdb_module(script, commands)
+ self.assertTrue(any("Breakpoint 1 at" in l for l in stdout.splitlines()), stdout)
+ self.assertTrue(all("SUCCESS" not in l for l in stdout.splitlines()), stdout)
+
+ def test_run_pdb_with_pdb(self):
+ commands = """
+ c
+ quit
+ """
+ stdout, stderr = self._run_pdb(["-m", "pdb"], commands)
+ self.assertIn("Debug the Python program given by pyfile.", stdout.splitlines())
+
+ def test_module_without_a_main(self):
+ module_name = 't_main'
+ support.rmtree(module_name)
+ init_file = module_name + '/__init__.py'
+ os.mkdir(module_name)
+ with open(init_file, 'w') as f:
+ pass
+ self.addCleanup(support.rmtree, module_name)
+ stdout, stderr = self._run_pdb(['-m', module_name], "")
+ self.assertIn("ImportError: No module named t_main.__main__",
+ stdout.splitlines())
+
+ def test_blocks_at_first_code_line(self):
+ script = """
+ #This is a comment, on line 2
+
+ print("SUCCESS")
+ """
+ commands = """
+ quit
+ """
+ stdout, stderr = self.run_pdb_module(script, commands)
+ self.assertTrue(any("__main__.py(4)<module>()"
+ in l for l in stdout.splitlines()), stdout)
+
+ def test_relative_imports(self):
+ self.module_name = 't_main'
+ support.rmtree(self.module_name)
+ main_file = self.module_name + '/__main__.py'
+ init_file = self.module_name + '/__init__.py'
+ module_file = self.module_name + '/module.py'
+ self.addCleanup(support.rmtree, self.module_name)
+ os.mkdir(self.module_name)
+ with open(init_file, 'w') as f:
+ f.write(textwrap.dedent("""
+ top_var = "VAR from top"
+ """))
+ with open(main_file, 'w') as f:
+ f.write(textwrap.dedent("""
+ from . import top_var
+ from .module import var
+ from . import module
+ pass # We'll stop here and print the vars
+ """))
+ with open(module_file, 'w') as f:
+ f.write(textwrap.dedent("""
+ var = "VAR from module"
+ var2 = "second var"
+ """))
+ commands = """
+ b 5
+ c
+ p top_var
+ p var
+ p module.var2
+ quit
+ """
+ stdout, _ = self._run_pdb(['-m', self.module_name], commands)
+ self.assertTrue(any("VAR from module" in l for l in stdout.splitlines()))
+ self.assertTrue(any("VAR from top" in l for l in stdout.splitlines()))
+ self.assertTrue(any("second var" in l for l in stdout.splitlines()))
def load_tests(*args):
from test import test_pdb
- suites = [unittest.makeSuite(PdbTestCase), doctest.DocTestSuite(test_pdb)]
+ suites = [
+ unittest.makeSuite(PdbTestCase),
+ doctest.DocTestSuite(test_pdb)
+ ]
return unittest.TestSuite(suites)