import subprocess
import distutils.core
from distutils import sysconfig
+from distutils import ccompiler
import Cython.Distutils.extension
from Cython.Debugger import Cygdb as cygdb
+root = os.path.dirname(os.path.abspath(__file__))
+codefile = os.path.join(root, 'codefile')
+cfuncs_file = os.path.join(root, 'cfuncs.c')
+with open(codefile) as f:
+ source_to_lineno = dict((line.strip(), i + 1) for i, line in enumerate(f))
class DebuggerTestCase(unittest.TestCase):
self.debug_dest = os.path.join(self.tempdir,
'cython_debug',
'cython_debug_info_codefile')
-
- code = textwrap.dedent("""
- cdef extern from "stdio.h":
- int puts(char *s)
-
- cdef int c_var = 0
- python_var = 0
-
- def spam(a=0):
- cdef:
- int b, c, d
-
- b = c = d = 0
-
- b = 1
- c = 2
- d = 3
- int(10)
- puts("spam")
-
- cdef ham():
- pass
-
- cpdef eggs():
- pass
-
- cdef class SomeClass(object):
- def spam(self):
- pass
-
- spam()
- """)
+ self.cfuncs_destfile = os.path.join(self.tempdir, 'cfuncs')
self.cwd = os.getcwd()
os.chdir(self.tempdir)
- open(self.destfile, 'w').write(code)
+ shutil.copy(codefile, self.destfile)
+ shutil.copy(cfuncs_file, self.cfuncs_destfile + '.c')
+
+ compiler = ccompiler.new_compiler()
+ compiler.compile(['cfuncs.c'], debug=True)
ext = Cython.Distutils.extension.Extension(
'codefile',
['codefile.pyx'],
- pyrex_debug=True)
+ pyrex_debug=True,
+ extra_objects=['cfuncs.o'])
distutils.core.setup(
script_args=['build_ext', '--inplace'],
- ext_modules=[ext],
+ ext_modules=[ext],
cmdclass=dict(build_ext=Cython.Distutils.build_ext)
)
def setUp(self):
super(GdbDebuggerTestCase, self).setUp()
- self.gdb_command_file = cygdb.make_command_file(self.tempdir)
- with open(self.gdb_command_file, 'a') as f:
- f.write('python '
- 'from Cython.Debugger.Tests import test_libcython_in_gdb;'
- 'test_libcython_in_gdb.main()\n')
+ prefix_code = textwrap.dedent('''\
+ python
+ import os
+ import sys
+ import traceback
+
+ def excepthook(type, value, tb):
+ traceback.print_exception(type, value, tb)
+ os._exit(1)
+
+ sys.excepthook = excepthook
+
+ # Have tracebacks end up on sys.stderr (gdb replaces sys.stderr
+ # with an object that calls gdb.write())
+ sys.stderr = sys.__stderr__
+
+ end
+ ''')
+
+ code = textwrap.dedent('''\
+ python
+
+ from Cython.Debugger.Tests import test_libcython_in_gdb
+ test_libcython_in_gdb.main()
+
+ end
+ ''')
+
+ self.gdb_command_file = cygdb.make_command_file(self.tempdir,
+ prefix_code)
+ open(self.gdb_command_file, 'a').write(code)
+
args = ['gdb', '-batch', '-x', self.gdb_command_file, '-n', '--args',
sys.executable, '-c', 'import codefile']
paths.append(os.path.dirname(os.path.dirname(
os.path.abspath(Cython.__file__))))
env = dict(os.environ, PYTHONPATH=os.pathsep.join(paths))
+
self.p = subprocess.Popen(
args,
stdout=open(os.devnull, 'w'),
Cython.Debugger.Cygdb.make_command_file()
"""
-import sys
-
-# First, fix gdb's python. Make sure to do this before importing modules
-# that bind output streams as default parameters
-
-# for some reason sys.argv is missing in gdb
-sys.argv = ['gdb']
-
-# Allow gdb to capture output, but have errors end up on stderr
-# sys.stdout = sys.__stdout__
-sys.stderr = sys.__stderr__
-
import os
+import sys
+import trace
import warnings
import unittest
import traceback
import gdb
from Cython.Debugger import libcython
+from Cython.Debugger import libpython
+from Cython.Debugger.Tests import TestLibCython as test_libcython
+
+# for some reason sys.argv is missing in gdb
+sys.argv = ['gdb']
+
class DebugTestCase(unittest.TestCase):
self.eggs_func = libcython.cy.functions_by_qualified_name[
'codefile.eggs']
- def read_var(self, varname):
- return gdb.parse_and_eval('$cy_cname("%s")' % varname)
+ def read_var(self, varname, cast_to=None):
+ result = gdb.parse_and_eval('$cy_cname("%s")' % varname)
+ if cast_to:
+ result = cast_to(result)
+
+ return result
def local_info(self):
return gdb.execute('info locals', to_string=True)
-class TestDebugInformationClasses(DebugTestCase):
+ def lineno_equals(self, source_line=None, lineno=None):
+ if source_line is not None:
+ lineno = test_libcython.source_to_lineno[source_line]
+ frame = gdb.selected_frame()
+ self.assertEqual(libcython.cy.step.lineno(frame), lineno)
+ def tearDown(self):
+ gdb.execute('delete breakpoints', to_string=True)
+ try:
+ gdb.execute('kill inferior 1', to_string=True)
+ except RuntimeError:
+ pass
+
+ def break_and_run(self, source_line):
+ break_lineno = test_libcython.source_to_lineno[source_line]
+ gdb.execute('cy break codefile:%d' % break_lineno, to_string=True)
+ gdb.execute('run', to_string=True)
+
+class TestDebugInformationClasses(DebugTestCase):
def test_CythonModule(self):
"test that debug information was parsed properly into data structures"
self.assertEqual(self.ham_func.type, libcython.CObject)
self.assertEqual(self.spam_func.arguments, ['a'])
- self.assertEqual(self.spam_func.step_into_functions, set(['puts']))
+ self.assertEqual(self.spam_func.step_into_functions,
+ set(['puts', 'some_c_function']))
- self.assertEqual(self.spam_func.lineno, 8)
+ expected_lineno = test_libcython.source_to_lineno['def spam(a=0):']
+ self.assertEqual(self.spam_func.lineno, expected_lineno)
self.assertEqual(sorted(self.spam_func.locals), list('abcd'))
class TestBreak(DebugTestCase):
def test_break(self):
- result = gdb.execute('cy break codefile.spam', to_string=True)
+ result = libpython._execute('cy break codefile.spam', to_string=True)
+ print >>sys.stderr, repr(result)
assert self.spam_func.cname in result
self.assertEqual(len(gdb.breakpoints()), 1)
self.assertEqual(bp.location, self.spam_func.cname)
assert bp.enabled
-
-class TestStep(DebugTestCase):
+
+class DebugStepperTestCase(DebugTestCase):
- def test_step(self):
- # Note: breakpoint for spam is still set
- gdb.execute('run')
+ def step(self, varnames_and_values, source_line=None, lineno=None):
+ gdb.execute(self.command, to_string=True)
+ for varname, value in varnames_and_values:
+ self.assertEqual(self.read_var(varname), value, self.local_info())
- gdb.execute('cy step', to_string=True) # b = c = d = 0
- gdb.execute('cy step', to_string=True) # b = 1
- self.assertEqual(self.read_var('b'), 1, self.local_info())
- self.assertRaises(RuntimeError, self.read_var('b'))
- gdb.execute('cont')
+ self.lineno_equals(source_line, lineno)
-class TestNext(DebugTestCase):
+class TestStep(DebugStepperTestCase):
+ """
+ Test stepping. Stepping happens in the code found in
+ Cython/Debugger/Tests/codefile.
+ """
+ def test_cython_step(self):
+ gdb.execute('cy break codefile.spam')
+ libcython.parameters.step_into_c_code.value = False
+
+ gdb.execute('run', to_string=True)
+ self.lineno_equals('def spam(a=0):')
+
+ gdb.execute('cy step', to_string=True)
+ self.lineno_equals('b = c = d = 0')
+
+ self.command = 'cy step'
+ self.step([('b', 0)], source_line='b = 1')
+ self.step([('b', 1), ('c', 0)], source_line='c = 2')
+ self.step([('c', 2)], source_line='int(10)')
+ self.step([], source_line='puts("spam")')
+ self.step([], source_line='os.path.join("foo", "bar")')
+
+ gdb.execute('cont', to_string=True)
+ self.assertEqual(len(gdb.inferiors()), 1)
+ self.assertEqual(gdb.inferiors()[0].pid, 0)
- def test_next(self):
- pass
+ def test_c_step(self):
+ libcython.parameters.step_into_c_code.value = True
+ self.break_and_run('some_c_function()')
+ gdb.execute('cy step', to_string=True)
+ self.assertEqual(gdb.selected_frame().name(), 'some_c_function')
-def main():
+ def test_python_step(self):
+ self.break_and_run('os.path.join("foo", "bar")')
+
+ gdb.execute('cy step', to_string=True)
+
+ curframe = gdb.selected_frame()
+ self.assertEqual(curframe.name(), 'PyEval_EvalFrameEx')
+
+ pyframe = libpython.Frame(curframe).get_pyop()
+ self.assertEqual(str(pyframe.co_name), 'join')
+
+
+class TestNext(DebugStepperTestCase):
+
+ def test_cython_next(self):
+ libcython.parameters.step_into_c_code.value = True
+ self.break_and_run('c = 2')
+
+ lines = (
+ 'int(10)',
+ 'puts("spam")',
+ 'os.path.join("foo", "bar")',
+ 'some_c_function()',
+ )
+
+ for line in lines:
+ gdb.execute('cy next')
+ self.lineno_equals(line)
+
+
+def _main():
+ # unittest.main(module=__import__(__name__, fromlist=['']))
try:
- # unittest.main(module=__import__(__name__, fromlist=['']))
- try:
- gdb.lookup_type('PyModuleObject')
- except RuntimeError:
- msg = ("Unable to run tests, Python was not compiled with "
- "debugging information. Either compile python with "
- "-g or get a debug build (configure with --with-pydebug).")
- warnings.warn(msg)
- else:
- tests = (
- TestDebugInformationClasses,
- TestParameters,
- TestBreak,
- TestStep
- )
- # test_support.run_unittest(tests)
- test_loader = unittest.TestLoader()
- suite = unittest.TestSuite(
- [test_loader.loadTestsFromTestCase(cls) for cls in tests])
-
- unittest.TextTestRunner(verbosity=1).run(suite)
- except Exception:
- traceback.print_exc()
- os._exit(1)
\ No newline at end of file
+ gdb.lookup_type('PyModuleObject')
+ except RuntimeError:
+ msg = ("Unable to run tests, Python was not compiled with "
+ "debugging information. Either compile python with "
+ "-g or get a debug build (configure with --with-pydebug).")
+ warnings.warn(msg)
+ else:
+ tests = (
+ TestDebugInformationClasses,
+ TestParameters,
+ TestBreak,
+ TestStep,
+ TestNext,
+ )
+ # test_support.run_unittest(tests)
+ test_loader = unittest.TestLoader()
+ suite = unittest.TestSuite(
+ [test_loader.loadTestsFromTestCase(cls) for cls in tests])
+
+ result = unittest.TextTestRunner(verbosity=1).run(suite)
+ if not result.wasSuccessful():
+ os._exit(1)
+
+def main(trace_code=False):
+ if trace_code:
+ tracer = trace.Trace(count=False, trace=True, outfile=sys.stderr,
+ ignoredirs=[sys.prefix, sys.exec_prefix])
+ tracer.runfunc(_main)
+ else:
+ _main()
\ No newline at end of file
--- /dev/null
+import unittest
+
+from Cython import StringIOTree as stringtree
+
+code = """
+cdef int spam # line 1
+
+cdef ham():
+ a = 1
+ b = 2
+ c = 3
+ d = 4
+
+def eggs():
+ pass
+
+cpdef bacon():
+ print spam
+ print 'scotch'
+ print 'tea?'
+ print 'or coffee?' # line 16
+"""
+
+linemap = dict(enumerate(code.splitlines()))
+
+class TestStringIOTree(unittest.TestCase):
+
+ def setUp(self):
+ self.tree = stringtree.StringIOTree()
+
+ def test_markers(self):
+ assert not self.tree.allmarkers()
+
+ def test_insertion(self):
+ self.write_lines((1, 2, 3))
+ line_4_to_6_insertion_point = self.tree.insertion_point()
+ self.write_lines((7, 8))
+ line_9_to_13_insertion_point = self.tree.insertion_point()
+ self.write_lines((14, 15, 16))
+
+ line_4_insertion_point = line_4_to_6_insertion_point.insertion_point()
+ self.write_lines((5, 6), tree=line_4_to_6_insertion_point)
+
+ line_9_to_12_insertion_point = (
+ line_9_to_13_insertion_point.insertion_point())
+ self.write_line(13, tree=line_9_to_13_insertion_point)
+
+ self.write_line(4, tree=line_4_insertion_point)
+ self.write_line(9, tree=line_9_to_12_insertion_point)
+ line_10_insertion_point = line_9_to_12_insertion_point.insertion_point()
+ self.write_line(11, tree=line_9_to_12_insertion_point)
+ self.write_line(10, tree=line_10_insertion_point)
+ self.write_line(12, tree=line_9_to_12_insertion_point)
+
+ self.assertEqual(self.tree.allmarkers(), range(1, 17))
+ self.assertEqual(code.strip(), self.tree.getvalue().strip())
+
+
+ def write_lines(self, linenos, tree=None):
+ for lineno in linenos:
+ self.write_line(lineno, tree=tree)
+
+ def write_line(self, lineno, tree=None):
+ if tree is None:
+ tree = self.tree
+ tree.markers.append(lineno)
+ tree.write(linemap[lineno] + '\n')
\ No newline at end of file