Commit 7b515269 authored by Vitja Makarov's avatar Vitja Makarov

Merge remote branch 'upstream/master'

Conflicts:
	runtests.py
parents 4b64c5b0 7f45b0b7
...@@ -18,7 +18,11 @@ from time import time ...@@ -18,7 +18,11 @@ from time import time
import Code import Code
import Errors import Errors
import Parsing # Do not import Parsing here, import it when needed, because Parsing imports
# Nodes, which globally needs debug command line options initialized to set a
# conditional metaclass. These options are processed by CmdLine called from
# main() in this file.
# import Parsing
import Version import Version
from Scanning import PyrexScanner, FileSourceDescriptor from Scanning import PyrexScanner, FileSourceDescriptor
from Errors import PyrexError, CompileError, InternalError, AbortError, error, warning from Errors import PyrexError, CompileError, InternalError, AbortError, error, warning
...@@ -496,6 +500,7 @@ class Context(object): ...@@ -496,6 +500,7 @@ class Context(object):
try: try:
f = Utils.open_source_file(source_filename, "rU") f = Utils.open_source_file(source_filename, "rU")
try: try:
import Parsing
s = PyrexScanner(f, source_desc, source_encoding = f.encoding, s = PyrexScanner(f, source_desc, source_encoding = f.encoding,
scope = scope, context = self) scope = scope, context = self)
tree = Parsing.p_module(s, pxd, full_module_name) tree = Parsing.p_module(s, pxd, full_module_name)
......
...@@ -1728,6 +1728,10 @@ class DebugTransform(CythonTransform): ...@@ -1728,6 +1728,10 @@ class DebugTransform(CythonTransform):
#self.c_output_file = options.output_file #self.c_output_file = options.output_file
self.c_output_file = result.c_file self.c_output_file = result.c_file
# Closure support, basically treat nested functions as if the AST were
# never nested
self.nested_funcdefs = []
# tells visit_NameNode whether it should register step-into functions # tells visit_NameNode whether it should register step-into functions
self.register_stepinto = False self.register_stepinto = False
...@@ -1742,7 +1746,16 @@ class DebugTransform(CythonTransform): ...@@ -1742,7 +1746,16 @@ class DebugTransform(CythonTransform):
# serialize functions # serialize functions
self.tb.start('Functions') self.tb.start('Functions')
# First, serialize functions normally...
self.visitchildren(node) self.visitchildren(node)
# ... then, serialize nested functions
for nested_funcdef in self.nested_funcdefs:
self.visit_FuncDefNode(nested_funcdef)
self.register_stepinto = True
self.serialize_modulenode_as_function(node)
self.register_stepinto = False
self.tb.end('Functions') self.tb.end('Functions')
# 2.3 compatibility. Serialize global variables # 2.3 compatibility. Serialize global variables
...@@ -1764,6 +1777,14 @@ class DebugTransform(CythonTransform): ...@@ -1764,6 +1777,14 @@ class DebugTransform(CythonTransform):
def visit_FuncDefNode(self, node): def visit_FuncDefNode(self, node):
self.visited.add(node.local_scope.qualified_name) self.visited.add(node.local_scope.qualified_name)
if getattr(node, 'is_wrapper', False):
return node
if self.register_stepinto:
self.nested_funcdefs.append(node)
return node
# node.entry.visibility = 'extern' # node.entry.visibility = 'extern'
if node.py_func is None: if node.py_func is None:
pf_cname = '' pf_cname = ''
...@@ -1815,6 +1836,51 @@ class DebugTransform(CythonTransform): ...@@ -1815,6 +1836,51 @@ class DebugTransform(CythonTransform):
self.visitchildren(node) self.visitchildren(node)
return node return node
def serialize_modulenode_as_function(self, node):
"""
Serialize the module-level code as a function so the debugger will know
it's a "relevant frame" and it will know where to set the breakpoint
for 'break modulename'.
"""
name = node.full_module_name.rpartition('.')[-1]
cname_py2 = 'init' + name
cname_py3 = 'PyInit_' + name
py2_attrs = dict(
name=name,
cname=cname_py2,
pf_cname='',
# Ignore the qualified_name, breakpoints should be set using
# `cy break modulename:lineno` for module-level breakpoints.
qualified_name='',
lineno='1',
is_initmodule_function="True",
)
py3_attrs = dict(py2_attrs, cname=cname_py3)
self._serialize_modulenode_as_function(node, py2_attrs)
self._serialize_modulenode_as_function(node, py3_attrs)
def _serialize_modulenode_as_function(self, node, attrs):
self.tb.start('Function', attrs=attrs)
self.tb.start('Locals')
self.serialize_local_variables(node.scope.entries)
self.tb.end('Locals')
self.tb.start('Arguments')
self.tb.end('Arguments')
self.tb.start('StepIntoFunctions')
self.register_stepinto = True
self.visitchildren(node)
self.register_stepinto = False
self.tb.end('StepIntoFunctions')
self.tb.end('Function')
def serialize_local_variables(self, entries): def serialize_local_variables(self, entries):
for entry in entries.values(): for entry in entries.values():
if entry.type.is_pyobject: if entry.type.is_pyobject:
...@@ -1822,9 +1888,22 @@ class DebugTransform(CythonTransform): ...@@ -1822,9 +1888,22 @@ class DebugTransform(CythonTransform):
else: else:
vartype = 'CObject' vartype = 'CObject'
cname = entry.cname if entry.from_closure:
# if entry.type.is_extension_type: # We're dealing with a closure where a variable from an outer
# cname = entry.type.typeptr_cname # scope is accessed, get it from the scope object.
cname = '%s->%s' % (Naming.cur_scope_cname,
entry.outer_entry.cname)
qname = '%s.%s.%s' % (entry.scope.outer_scope.qualified_name,
entry.scope.name,
entry.name)
elif entry.in_closure:
cname = '%s->%s' % (Naming.cur_scope_cname,
entry.cname)
qname = entry.qualified_name
else:
cname = entry.cname
qname = entry.qualified_name
if not entry.pos: if not entry.pos:
# this happens for variables that are not in the user's code, # this happens for variables that are not in the user's code,
...@@ -1837,7 +1916,7 @@ class DebugTransform(CythonTransform): ...@@ -1837,7 +1916,7 @@ class DebugTransform(CythonTransform):
attrs = dict( attrs = dict(
name=entry.name, name=entry.name,
cname=cname, cname=cname,
qualified_name=entry.qualified_name, qualified_name=qname,
type=vartype, type=vartype,
lineno=lineno) lineno=lineno)
......
...@@ -182,7 +182,8 @@ class TestDebugTransform(DebuggerTestCase): ...@@ -182,7 +182,8 @@ class TestDebugTransform(DebuggerTestCase):
self.assertEqual('PythonObject', xml_globals.get('python_var')) self.assertEqual('PythonObject', xml_globals.get('python_var'))
# test functions # test functions
funcnames = 'codefile.spam', 'codefile.ham', 'codefile.eggs' funcnames = ('codefile.spam', 'codefile.ham', 'codefile.eggs',
'codefile.closure', 'codefile.inner')
required_xml_attrs = 'name', 'cname', 'qualified_name' required_xml_attrs = 'name', 'cname', 'qualified_name'
assert all([f in xml_funcs for f in funcnames]) assert all([f in xml_funcs for f in funcnames])
spam, ham, eggs = [xml_funcs[funcname] for funcname in funcnames] spam, ham, eggs = [xml_funcs[funcname] for funcname in funcnames]
......
...@@ -16,6 +16,7 @@ from distutils import ccompiler ...@@ -16,6 +16,7 @@ from distutils import ccompiler
import runtests import runtests
import Cython.Distutils.extension import Cython.Distutils.extension
import Cython.Distutils.build_ext
from Cython.Debugger import Cygdb as cygdb from Cython.Debugger import Cygdb as cygdb
root = os.path.dirname(os.path.abspath(__file__)) root = os.path.dirname(os.path.abspath(__file__))
...@@ -24,6 +25,10 @@ cfuncs_file = os.path.join(root, 'cfuncs.c') ...@@ -24,6 +25,10 @@ cfuncs_file = os.path.join(root, 'cfuncs.c')
with open(codefile) as f: with open(codefile) as f:
source_to_lineno = dict((line.strip(), i + 1) for i, line in enumerate(f)) source_to_lineno = dict((line.strip(), i + 1) for i, line in enumerate(f))
# Cython.Distutils.__init__ imports build_ext from build_ext which means we
# can't access the module anymore. Get it from sys.modules instead.
build_ext = sys.modules['Cython.Distutils.build_ext']
class DebuggerTestCase(unittest.TestCase): class DebuggerTestCase(unittest.TestCase):
def setUp(self): def setUp(self):
...@@ -52,6 +57,9 @@ class DebuggerTestCase(unittest.TestCase): ...@@ -52,6 +57,9 @@ class DebuggerTestCase(unittest.TestCase):
module='codefile', module='codefile',
) )
optimization_disabler = build_ext.Optimization()
optimization_disabler.disable_optimization()
cython_compile_testcase = runtests.CythonCompileTestCase( cython_compile_testcase = runtests.CythonCompileTestCase(
workdir=self.tempdir, workdir=self.tempdir,
# we clean up everything (not only compiled files) # we clean up everything (not only compiled files)
...@@ -77,6 +85,8 @@ class DebuggerTestCase(unittest.TestCase): ...@@ -77,6 +85,8 @@ class DebuggerTestCase(unittest.TestCase):
**opts **opts
) )
optimization_disabler.restore_state()
# ext = Cython.Distutils.extension.Extension( # ext = Cython.Distutils.extension.Extension(
# 'codefile', # 'codefile',
# ['codefile.pyx'], # ['codefile.pyx'],
...@@ -95,6 +105,7 @@ class DebuggerTestCase(unittest.TestCase): ...@@ -95,6 +105,7 @@ class DebuggerTestCase(unittest.TestCase):
class GdbDebuggerTestCase(DebuggerTestCase): class GdbDebuggerTestCase(DebuggerTestCase):
def setUp(self): def setUp(self):
super(GdbDebuggerTestCase, self).setUp() super(GdbDebuggerTestCase, self).setUp()
......
...@@ -21,16 +21,30 @@ def spam(a=0): ...@@ -21,16 +21,30 @@ def spam(a=0):
puts("spam") puts("spam")
os.path.join("foo", "bar") os.path.join("foo", "bar")
some_c_function() some_c_function()
cpdef eggs():
pass
cdef ham(): cdef ham():
pass pass
cpdef eggs():
pass
cdef class SomeClass(object): cdef class SomeClass(object):
def spam(self): def spam(self):
pass pass
def outer():
cdef object a = "an object"
def inner():
b = 2
# access closed over variables
print a, b
return inner
outer()()
spam() spam()
print "bye!" print "bye!"
def use_ham():
ham()
...@@ -14,6 +14,7 @@ import warnings ...@@ -14,6 +14,7 @@ import warnings
import unittest import unittest
import textwrap import textwrap
import tempfile import tempfile
import functools
import traceback import traceback
import itertools import itertools
from test import test_support from test import test_support
...@@ -28,12 +29,35 @@ from Cython.Debugger.Tests import TestLibCython as test_libcython ...@@ -28,12 +29,35 @@ from Cython.Debugger.Tests import TestLibCython as test_libcython
sys.argv = ['gdb'] sys.argv = ['gdb']
def print_on_call_decorator(func):
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
_debug(type(self).__name__, func.__name__)
try:
return func(self, *args, **kwargs)
except Exception, e:
_debug("An exception occurred:", traceback.format_exc(e))
raise
return wrapper
class TraceMethodCallMeta(type):
def __init__(self, name, bases, dict):
for func_name, func in dict.iteritems():
if inspect.isfunction(func):
setattr(self, func_name, print_on_call_decorator(func))
class DebugTestCase(unittest.TestCase): class DebugTestCase(unittest.TestCase):
""" """
Base class for test cases. On teardown it kills the inferior and unsets Base class for test cases. On teardown it kills the inferior and unsets
all breakpoints. all breakpoints.
""" """
__metaclass__ = TraceMethodCallMeta
def __init__(self, name): def __init__(self, name):
super(DebugTestCase, self).__init__(name) super(DebugTestCase, self).__init__(name)
self.cy = libcython.cy self.cy = libcython.cy
...@@ -58,7 +82,7 @@ class DebugTestCase(unittest.TestCase): ...@@ -58,7 +82,7 @@ class DebugTestCase(unittest.TestCase):
if source_line is not None: if source_line is not None:
lineno = test_libcython.source_to_lineno[source_line] lineno = test_libcython.source_to_lineno[source_line]
frame = gdb.selected_frame() frame = gdb.selected_frame()
self.assertEqual(libcython.cy.step.lineno(frame), lineno) self.assertEqual(libcython.cython_info.lineno(frame), lineno)
def break_and_run(self, source_line): def break_and_run(self, source_line):
break_lineno = test_libcython.source_to_lineno[source_line] break_lineno = test_libcython.source_to_lineno[source_line]
...@@ -74,9 +98,6 @@ class DebugTestCase(unittest.TestCase): ...@@ -74,9 +98,6 @@ class DebugTestCase(unittest.TestCase):
gdb.execute('set args -c "import codefile"') gdb.execute('set args -c "import codefile"')
libcython.cy.step.static_breakpoints.clear()
libcython.cy.step.runtime_breakpoints.clear()
libcython.cy.step.init_breakpoints()
class TestDebugInformationClasses(DebugTestCase): class TestDebugInformationClasses(DebugTestCase):
...@@ -101,7 +122,7 @@ class TestDebugInformationClasses(DebugTestCase): ...@@ -101,7 +122,7 @@ class TestDebugInformationClasses(DebugTestCase):
'codefile.SomeClass.spam') 'codefile.SomeClass.spam')
self.assertEqual(self.spam_func.module, self.module) self.assertEqual(self.spam_func.module, self.module)
assert self.eggs_func.pf_cname assert self.eggs_func.pf_cname, (self.eggs_func, self.eggs_func.pf_cname)
assert not self.ham_func.pf_cname assert not self.ham_func.pf_cname
assert not self.spam_func.pf_cname assert not self.spam_func.pf_cname
assert not self.spam_meth.pf_cname assert not self.spam_meth.pf_cname
...@@ -130,7 +151,7 @@ class TestParameters(unittest.TestCase): ...@@ -130,7 +151,7 @@ class TestParameters(unittest.TestCase):
class TestBreak(DebugTestCase): class TestBreak(DebugTestCase):
def test_break(self): def test_break(self):
breakpoint_amount = len(gdb.breakpoints()) breakpoint_amount = len(gdb.breakpoints() or ())
gdb.execute('cy break codefile.spam') gdb.execute('cy break codefile.spam')
self.assertEqual(len(gdb.breakpoints()), breakpoint_amount + 1) self.assertEqual(len(gdb.breakpoints()), breakpoint_amount + 1)
...@@ -143,6 +164,16 @@ class TestBreak(DebugTestCase): ...@@ -143,6 +164,16 @@ class TestBreak(DebugTestCase):
gdb.execute('cy break -p join') gdb.execute('cy break -p join')
assert 'def join(' in gdb.execute('cy run', to_string=True) assert 'def join(' in gdb.execute('cy run', to_string=True)
def test_break_lineno(self):
beginline = 'import os'
nextline = 'cdef int c_var = 12'
self.break_and_run(beginline)
self.lineno_equals(beginline)
step_result = gdb.execute('cy step', to_string=True)
self.lineno_equals(nextline)
assert step_result.rstrip().endswith(nextline)
class TestKilled(DebugTestCase): class TestKilled(DebugTestCase):
...@@ -151,6 +182,7 @@ class TestKilled(DebugTestCase): ...@@ -151,6 +182,7 @@ class TestKilled(DebugTestCase):
output = gdb.execute('cy run', to_string=True) output = gdb.execute('cy run', to_string=True)
assert 'abort' in output.lower() assert 'abort' in output.lower()
class DebugStepperTestCase(DebugTestCase): class DebugStepperTestCase(DebugTestCase):
def step(self, varnames_and_values, source_line=None, lineno=None): def step(self, varnames_and_values, source_line=None, lineno=None):
...@@ -262,7 +294,7 @@ class TestBacktrace(DebugTestCase): ...@@ -262,7 +294,7 @@ class TestBacktrace(DebugTestCase):
gdb.execute('cy bt') gdb.execute('cy bt')
result = gdb.execute('cy bt -a', to_string=True) result = gdb.execute('cy bt -a', to_string=True)
assert re.search(r'\#0 *0x.* in main\(\) at', result), result assert re.search(r'\#0 *0x.* in main\(\)', result), result
class TestFunctions(DebugTestCase): class TestFunctions(DebugTestCase):
...@@ -344,6 +376,36 @@ class TestExec(DebugTestCase): ...@@ -344,6 +376,36 @@ class TestExec(DebugTestCase):
gdb.execute('cy exec some_random_var = 14') gdb.execute('cy exec some_random_var = 14')
self.assertEqual('14', self.eval_command('some_random_var')) self.assertEqual('14', self.eval_command('some_random_var'))
class TestClosure(DebugTestCase):
def break_and_run_func(self, funcname):
gdb.execute('cy break ' + funcname)
gdb.execute('cy run')
def test_inner(self):
self.break_and_run_func('inner')
self.assertEqual('', gdb.execute('cy locals', to_string=True))
# Allow the Cython-generated code to initialize the scope variable
gdb.execute('cy step')
self.assertEqual(str(self.read_var('a')), "'an object'")
print_result = gdb.execute('cy print a', to_string=True).strip()
self.assertEqual(print_result, "a = 'an object'")
def test_outer(self):
self.break_and_run_func('outer')
self.assertEqual('', gdb.execute('cy locals', to_string=True))
# Initialize scope with 'a' uninitialized
gdb.execute('cy step')
self.assertEqual('', gdb.execute('cy locals', to_string=True))
# Initialize 'a' to 1
gdb.execute('cy step')
print_result = gdb.execute('cy print a', to_string=True).strip()
self.assertEqual(print_result, "a = 'an object'")
_do_debug = os.environ.get('GDB_DEBUG') _do_debug = os.environ.get('GDB_DEBUG')
if _do_debug: if _do_debug:
......
This diff is collapsed.
This diff is collapsed.
...@@ -675,12 +675,7 @@ class CythonPyregrTestCase(CythonRunTestCase): ...@@ -675,12 +675,7 @@ class CythonPyregrTestCase(CythonRunTestCase):
except (unittest.SkipTest, support.ResourceDenied): except (unittest.SkipTest, support.ResourceDenied):
result.addSkip(self, 'ok') result.addSkip(self, 'ok')
include_debugger = sys.version_info[:2] > (2, 5)
try:
import gdb
include_debugger = sys.version_info[:2] > (2, 5)
except:
include_debugger = False
def collect_unittests(path, module_prefix, suite, selectors): def collect_unittests(path, module_prefix, suite, selectors):
def file_matches(filename): def file_matches(filename):
...@@ -750,6 +745,9 @@ def collect_doctests(path, module_prefix, suite, selectors): ...@@ -750,6 +745,9 @@ def collect_doctests(path, module_prefix, suite, selectors):
modulename = module_prefix + filepath[len(path)+1:].replace(os.path.sep, '.') modulename = module_prefix + filepath[len(path)+1:].replace(os.path.sep, '.')
if not [ 1 for match in selectors if match(modulename) ]: if not [ 1 for match in selectors if match(modulename) ]:
continue continue
if 'in_gdb' in modulename:
# These should only be imported from gdb.
continue
module = __import__(modulename) module = __import__(modulename)
for x in modulename.split('.')[1:]: for x in modulename.split('.')[1:]:
module = getattr(module, x) module = getattr(module, x)
......
...@@ -100,7 +100,7 @@ def compile_cython_modules(profile=False, compile_more=False, cython_with_refnan ...@@ -100,7 +100,7 @@ def compile_cython_modules(profile=False, compile_more=False, cython_with_refnan
"Cython.Compiler.Parsing", "Cython.Compiler.Parsing",
"Cython.Compiler.Visitor", "Cython.Compiler.Visitor",
"Cython.Compiler.Code", "Cython.Compiler.Code",
"Cython.Runtime.refnanny"] "Cython.Runtime.refnanny",]
if compile_more: if compile_more:
compiled_modules.extend([ compiled_modules.extend([
"Cython.Compiler.ParseTreeTransforms", "Cython.Compiler.ParseTreeTransforms",
......
...@@ -210,14 +210,14 @@ def builtin_type_methods(): ...@@ -210,14 +210,14 @@ def builtin_type_methods():
append(1) append(1)
assert l == [1], str(l) assert l == [1], str(l)
cdef int func(int x): cdef int cfunc(int x):
return x+1 return x+1
def c_functions(): def c_functions():
""" """
>>> c_functions() >>> c_functions()
""" """
f = func f = cfunc
assert typeof(f) == 'int (*)(int)', typeof(f) assert typeof(f) == 'int (*)(int)', typeof(f)
assert 2 == f(1) assert 2 == f(1)
...@@ -424,6 +424,15 @@ def safe_only(): ...@@ -424,6 +424,15 @@ def safe_only():
res = -j res = -j
assert typeof(j) == "Python object", typeof(j) assert typeof(j) == "Python object", typeof(j)
@infer_types(None)
def safe_c_functions():
"""
>>> safe_c_functions()
"""
f = cfunc
assert typeof(f) == 'int (*)(int)', typeof(f)
assert 2 == f(1)
@infer_types(None) @infer_types(None)
def args_tuple_keywords(*args, **kwargs): def args_tuple_keywords(*args, **kwargs):
""" """
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment