Commit 27961df2 authored by Robert Bradshaw's avatar Robert Bradshaw

Merge branch 'master' of github.com:cython/cython

parents 6cca314b 89974e5c
...@@ -650,17 +650,27 @@ class GlobalState(object): ...@@ -650,17 +650,27 @@ class GlobalState(object):
if self.should_declare(entry.cname, entry): if self.should_declare(entry.cname, entry):
self.put_pyobject_decl(entry) self.put_pyobject_decl(entry)
w = self.parts['cached_builtins'] w = self.parts['cached_builtins']
conditional_name = False
if entry.name == 'xrange': if entry.name == 'xrange':
# replaced by range() in Py3 # replaced by range() in Py3
conditional_name = True
w.putln('#if PY_MAJOR_VERSION >= 3') w.putln('#if PY_MAJOR_VERSION >= 3')
self.put_cached_builtin_init( self.put_cached_builtin_init(
entry.pos, StringEncoding.EncodedString('range'), entry.pos, StringEncoding.EncodedString('range'),
entry.cname) entry.cname)
elif entry.name == 'BaseException':
# replace BaseException by Exception in Py<2.5
conditional_name = True
w.putln('#if PY_VERSION_HEX < 0x02050000')
self.put_cached_builtin_init(
entry.pos, StringEncoding.EncodedString('Exception'),
entry.cname)
if conditional_name:
w.putln('#else') w.putln('#else')
self.put_cached_builtin_init( self.put_cached_builtin_init(
entry.pos, StringEncoding.EncodedString(entry.name), entry.pos, StringEncoding.EncodedString(entry.name),
entry.cname) entry.cname)
if entry.name == 'xrange': if conditional_name:
w.putln('#endif') w.putln('#endif')
def put_cached_builtin_init(self, pos, name, cname): def put_cached_builtin_init(self, pos, name, cname):
......
...@@ -902,8 +902,8 @@ class ModuleScope(Scope): ...@@ -902,8 +902,8 @@ class ModuleScope(Scope):
return self.outer_scope.lookup(name, language_level = self.context.language_level) return self.outer_scope.lookup(name, language_level = self.context.language_level)
def declare_builtin(self, name, pos): def declare_builtin(self, name, pos):
if not hasattr(builtins, name) and name != 'xrange': if not hasattr(builtins, name) and name not in ('xrange', 'BaseException'):
# 'xrange' is special cased in Code.py # 'xrange' and 'BaseException' are special cased in Code.py
if self.has_import_star: if self.has_import_star:
entry = self.declare_var(name, py_object_type, pos) entry = self.declare_var(name, py_object_type, pos)
return entry return entry
......
...@@ -378,6 +378,32 @@ class TestExec(DebugTestCase): ...@@ -378,6 +378,32 @@ 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 CySet(DebugTestCase):
def test_cyset(self):
self.break_and_run('os.path.join("foo", "bar")')
gdb.execute('cy set a = $cy_eval("{None: []}")')
stringvalue = self.read_var("a", cast_to=str)
self.assertEqual(stringvalue, "{None: []}")
class TestCyEval(DebugTestCase):
"Test the $cy_eval() gdb function."
def test_cy_eval(self):
# This function leaks a few objects in the GDB python process. This
# is no biggie
self.break_and_run('os.path.join("foo", "bar")')
result = gdb.execute('print $cy_eval("None")', to_string=True)
assert re.match(r'\$\d+ = None\n', result), result
result = gdb.execute('print $cy_eval("[a]")', to_string=True)
assert re.match(r'\$\d+ = \[0\]', result), result
class TestClosure(DebugTestCase): class TestClosure(DebugTestCase):
def break_and_run_func(self, funcname): def break_and_run_func(self, funcname):
......
...@@ -113,5 +113,3 @@ class TestPrettyPrinters(test_libcython_in_gdb.DebugTestCase): ...@@ -113,5 +113,3 @@ class TestPrettyPrinters(test_libcython_in_gdb.DebugTestCase):
frame = self.pyobject_fromcode('PyEval_GetFrame()') frame = self.pyobject_fromcode('PyEval_GetFrame()')
self.assertEqual(type(frame), libpython.PyFrameObjectPtr) self.assertEqual(type(frame), libpython.PyFrameObjectPtr)
...@@ -592,6 +592,7 @@ class CyCy(CythonCommand): ...@@ -592,6 +592,7 @@ class CyCy(CythonCommand):
cy bt / cy backtrace cy bt / cy backtrace
cy list cy list
cy print cy print
cy set
cy locals cy locals
cy globals cy globals
cy exec cy exec
...@@ -607,6 +608,7 @@ class CyCy(CythonCommand): ...@@ -607,6 +608,7 @@ class CyCy(CythonCommand):
completer_class, prefix=True) completer_class, prefix=True)
commands = dict( commands = dict(
# GDB commands
import_ = CyImport.register(), import_ = CyImport.register(),
break_ = CyBreak.register(), break_ = CyBreak.register(),
step = CyStep.register(), step = CyStep.register(),
...@@ -624,9 +626,13 @@ class CyCy(CythonCommand): ...@@ -624,9 +626,13 @@ class CyCy(CythonCommand):
globals = CyGlobals.register(), globals = CyGlobals.register(),
exec_ = libpython.FixGdbCommand('cy exec', '-cy-exec'), exec_ = libpython.FixGdbCommand('cy exec', '-cy-exec'),
_exec = CyExec.register(), _exec = CyExec.register(),
set = CySet.register(),
# GDB functions
cy_cname = CyCName('cy_cname'), cy_cname = CyCName('cy_cname'),
cy_cvalue = CyCValue('cy_cvalue'), cy_cvalue = CyCValue('cy_cvalue'),
cy_lineno = CyLine('cy_lineno'), cy_lineno = CyLine('cy_lineno'),
cy_eval = CyEval('cy_eval'),
) )
for command_name, command in commands.iteritems(): for command_name, command in commands.iteritems():
...@@ -1169,15 +1175,14 @@ class CyGlobals(CyLocals): ...@@ -1169,15 +1175,14 @@ class CyGlobals(CyLocals):
max_name_length, ' ') max_name_length, ' ')
class CyExec(CythonCommand, libpython.PyExec):
class EvaluateOrExecuteCodeMixin(object):
""" """
Execute Python code in the nearest Python or Cython frame. Evaluate or execute Python code in a Cython or Python frame. The 'evalcode'
method evaluations Python code, prints a traceback if an exception went
uncaught, and returns any return value as a gdb.Value (NULL on exception).
""" """
name = '-cy-exec'
command_class = gdb.COMMAND_STACK
completer_class = gdb.COMPLETE_NONE
def _fill_locals_dict(self, executor, local_dict_pointer): def _fill_locals_dict(self, executor, local_dict_pointer):
"Fill a remotely allocated dict with values from the Cython C stack" "Fill a remotely allocated dict with values from the Cython C stack"
cython_func = self.get_cython_function() cython_func = self.get_cython_function()
...@@ -1208,28 +1213,22 @@ class CyExec(CythonCommand, libpython.PyExec): ...@@ -1208,28 +1213,22 @@ class CyExec(CythonCommand, libpython.PyExec):
raise gdb.GdbError("Unable to execute Python code.") raise gdb.GdbError("Unable to execute Python code.")
finally: finally:
# PyDict_SetItem doesn't steal our reference # PyDict_SetItem doesn't steal our reference
executor.decref(pystringp) executor.xdecref(pystringp)
def _find_first_cython_or_python_frame(self): def _find_first_cython_or_python_frame(self):
frame = gdb.selected_frame() frame = gdb.selected_frame()
while frame: while frame:
if (self.is_cython_function(frame) or if (self.is_cython_function(frame) or
self.is_python_function(frame)): self.is_python_function(frame)):
frame.select()
return frame return frame
frame = frame.older() frame = frame.older()
raise gdb.GdbError("There is no Cython or Python frame on the stack.") raise gdb.GdbError("There is no Cython or Python frame on the stack.")
def invoke(self, expr, from_tty):
frame = self._find_first_cython_or_python_frame()
if self.is_python_function(frame):
libpython.py_exec.invoke(expr, from_tty)
return
expr, input_type = self.readcode(expr)
executor = libpython.PythonCodeExecutor()
def _evalcode_cython(self, executor, code, input_type):
with libpython.FetchAndRestoreError(): with libpython.FetchAndRestoreError():
# get the dict of Cython globals and construct a dict in the # get the dict of Cython globals and construct a dict in the
# inferior with Cython locals # inferior with Cython locals
...@@ -1240,9 +1239,65 @@ class CyExec(CythonCommand, libpython.PyExec): ...@@ -1240,9 +1239,65 @@ class CyExec(CythonCommand, libpython.PyExec):
try: try:
self._fill_locals_dict(executor, self._fill_locals_dict(executor,
libpython.pointervalue(local_dict)) libpython.pointervalue(local_dict))
executor.evalcode(expr, input_type, global_dict, local_dict) result = executor.evalcode(code, input_type, global_dict,
local_dict)
finally: finally:
executor.decref(libpython.pointervalue(local_dict)) executor.xdecref(libpython.pointervalue(local_dict))
return result
def evalcode(self, code, input_type):
"""
Evaluate `code` in a Python or Cython stack frame using the given
`input_type`.
"""
frame = self._find_first_cython_or_python_frame()
executor = libpython.PythonCodeExecutor()
if self.is_python_function(frame):
return libpython._evalcode_python(executor, code, input_type)
return self._evalcode_cython(executor, code, input_type)
class CyExec(CythonCommand, libpython.PyExec, EvaluateOrExecuteCodeMixin):
"""
Execute Python code in the nearest Python or Cython frame.
"""
name = '-cy-exec'
command_class = gdb.COMMAND_STACK
completer_class = gdb.COMPLETE_NONE
def invoke(self, expr, from_tty):
expr, input_type = self.readcode(expr)
executor = libpython.PythonCodeExecutor()
executor.xdecref(self.evalcode(expr, executor.Py_single_input))
class CySet(CythonCommand):
"""
Set a Cython variable to a certain value
cy set my_cython_c_variable = 10
cy set my_cython_py_variable = $cy_eval("{'doner': 'kebab'}")
This is equivalent to
set $cy_value("my_cython_variable") = 10
"""
name = 'cy set'
command_class = gdb.COMMAND_DATA
completer_class = gdb.COMPLETE_NONE
@require_cython_frame
def invoke(self, expr, from_tty):
name_and_expr = expr.split('=', 1)
if len(name_and_expr) != 2:
raise gdb.GdbError("Invalid expression. Use 'cy set var = expr'.")
varname, expr = name_and_expr
cname = self.cy.cy_cname.invoke(varname.strip())
gdb.execute("set %s = %s" % (cname, expr))
# Functions # Functions
...@@ -1312,6 +1367,18 @@ class CyLine(gdb.Function, CythonBase): ...@@ -1312,6 +1367,18 @@ class CyLine(gdb.Function, CythonBase):
def invoke(self): def invoke(self):
return self.get_cython_lineno() return self.get_cython_lineno()
class CyEval(gdb.Function, CythonBase, EvaluateOrExecuteCodeMixin):
"""
Evaluate Python code in the nearest Python or Cython frame and return
"""
@gdb_function_value_to_unicode
def invoke(self, python_expression):
input_type = libpython.PythonCodeExecutor.Py_eval_input
return self.evalcode(python_expression, input_type)
cython_info = CythonInfo() cython_info = CythonInfo()
cy = CyCy.register() cy = CyCy.register()
cython_info.cy = cy cython_info.cy = cy
......
...@@ -1949,27 +1949,41 @@ class ExecutionControlCommandBase(gdb.Command): ...@@ -1949,27 +1949,41 @@ class ExecutionControlCommandBase(gdb.Command):
gdb.execute("delete %s" % bp) gdb.execute("delete %s" % bp)
def filter_output(self, result): def filter_output(self, result):
output = []
match_finish = re.search(r'^Value returned is \$\d+ = (.*)', result,
re.MULTILINE)
if match_finish:
output.append('Value returned: %s' % match_finish.group(1))
reflags = re.MULTILINE reflags = re.MULTILINE
regexes = [
output_on_halt = [
(r'^Program received signal .*', reflags|re.DOTALL), (r'^Program received signal .*', reflags|re.DOTALL),
(r'.*[Ww]arning.*', 0), (r'.*[Ww]arning.*', 0),
(r'^Program exited .*', reflags), (r'^Program exited .*', reflags),
] ]
output_always = [
# output when halting on a watchpoint
(r'^(Old|New) value = .*', reflags),
# output from the 'display' command
(r'^\d+: \w+ = .*', reflags),
]
def filter_output(regexes):
output = []
for regex, flags in regexes: for regex, flags in regexes:
match = re.search(regex, result, flags) for match in re.finditer(regex, result, flags):
if match:
output.append(match.group(0)) output.append(match.group(0))
return '\n'.join(output) return '\n'.join(output)
# Filter the return value output of the 'finish' command
match_finish = re.search(r'^Value returned is \$\d+ = (.*)', result,
re.MULTILINE)
if match_finish:
finish_output = 'Value returned: %s\n' % match_finish.group(1)
else:
finish_output = ''
return (filter_output(output_on_halt),
finish_output + filter_output(output_always))
def stopped(self): def stopped(self):
return get_selected_inferior().pid == 0 return get_selected_inferior().pid == 0
...@@ -1979,17 +1993,23 @@ class ExecutionControlCommandBase(gdb.Command): ...@@ -1979,17 +1993,23 @@ class ExecutionControlCommandBase(gdb.Command):
of source code or the result of the last executed gdb command (passed of source code or the result of the last executed gdb command (passed
in as the `result` argument). in as the `result` argument).
""" """
result = self.filter_output(result) output_on_halt, output_always = self.filter_output(result)
if self.stopped(): if self.stopped():
print result.strip() print output_always
print output_on_halt
else: else:
frame = gdb.selected_frame() frame = gdb.selected_frame()
source_line = self.lang_info.get_source_line(frame)
if self.lang_info.is_relevant_function(frame): if self.lang_info.is_relevant_function(frame):
raised_exception = self.lang_info.exc_info(frame) raised_exception = self.lang_info.exc_info(frame)
if raised_exception: if raised_exception:
print raised_exception print raised_exception
print self.lang_info.get_source_line(frame) or result
if source_line:
if output_always.rstrip():
print output_always.rstrip()
print source_line
else: else:
print result print result
...@@ -2190,12 +2210,12 @@ class PythonInfo(LanguageInfo): ...@@ -2190,12 +2210,12 @@ class PythonInfo(LanguageInfo):
try: try:
tstate = frame.read_var('tstate').dereference() tstate = frame.read_var('tstate').dereference()
if gdb.parse_and_eval('tstate->frame == f'): if gdb.parse_and_eval('tstate->frame == f'):
# tstate local variable initialized # tstate local variable initialized, check for an exception
inf_type = tstate['curexc_type'] inf_type = tstate['curexc_type']
inf_value = tstate['curexc_value'] inf_value = tstate['curexc_value']
if inf_type: if inf_type:
return 'An exception was raised: %s(%s)' % (inf_type, return 'An exception was raised: %s' % (inf_value,)
inf_value)
except (ValueError, RuntimeError), e: except (ValueError, RuntimeError), e:
# Could not read the variable tstate or it's memory, it's ok # Could not read the variable tstate or it's memory, it's ok
pass pass
...@@ -2342,7 +2362,7 @@ class PythonCodeExecutor(object): ...@@ -2342,7 +2362,7 @@ class PythonCodeExecutor(object):
"Increment the reference count of a Python object in the inferior." "Increment the reference count of a Python object in the inferior."
gdb.parse_and_eval('Py_IncRef((PyObject *) %d)' % pointer) gdb.parse_and_eval('Py_IncRef((PyObject *) %d)' % pointer)
def decref(self, pointer): def xdecref(self, pointer):
"Decrement the reference count of a Python object in the inferior." "Decrement the reference count of a Python object in the inferior."
# Py_DecRef is like Py_XDECREF, but a function. So we don't have # Py_DecRef is like Py_XDECREF, but a function. So we don't have
# to check for NULL. This should also decref all our allocated # to check for NULL. This should also decref all our allocated
...@@ -2382,10 +2402,11 @@ class PythonCodeExecutor(object): ...@@ -2382,10 +2402,11 @@ class PythonCodeExecutor(object):
with FetchAndRestoreError(): with FetchAndRestoreError():
try: try:
self.decref(gdb.parse_and_eval(code)) pyobject_return_value = gdb.parse_and_eval(code)
finally: finally:
self.free(pointer) self.free(pointer)
return pyobject_return_value
class FetchAndRestoreError(PythonCodeExecutor): class FetchAndRestoreError(PythonCodeExecutor):
""" """
...@@ -2462,6 +2483,20 @@ class FixGdbCommand(gdb.Command): ...@@ -2462,6 +2483,20 @@ class FixGdbCommand(gdb.Command):
self.fix_gdb() self.fix_gdb()
def _evalcode_python(executor, code, input_type):
"""
Execute Python code in the most recent stack frame.
"""
global_dict = gdb.parse_and_eval('PyEval_GetGlobals()')
local_dict = gdb.parse_and_eval('PyEval_GetLocals()')
if (pointervalue(global_dict) == 0 or pointervalue(local_dict) == 0):
raise gdb.GdbError("Unable to find the locals or globals of the "
"most recent Python function (relative to the "
"selected frame).")
return executor.evalcode(code, input_type, global_dict, local_dict)
class PyExec(gdb.Command): class PyExec(gdb.Command):
def readcode(self, expr): def readcode(self, expr):
...@@ -2484,17 +2519,9 @@ class PyExec(gdb.Command): ...@@ -2484,17 +2519,9 @@ class PyExec(gdb.Command):
def invoke(self, expr, from_tty): def invoke(self, expr, from_tty):
expr, input_type = self.readcode(expr) expr, input_type = self.readcode(expr)
executor = PythonCodeExecutor() executor = PythonCodeExecutor()
global_dict = gdb.parse_and_eval('PyEval_GetGlobals()') executor.xdecref(_evalcode_python(executor, input_type, global_dict,
local_dict = gdb.parse_and_eval('PyEval_GetLocals()') local_dict))
if pointervalue(global_dict) == 0 or pointervalue(local_dict) == 0:
raise gdb.GdbError("Unable to find the locals or globals of the "
"most recent Python function (relative to the "
"selected frame).")
executor.evalcode(expr, input_type, global_dict, local_dict)
gdb.execute('set breakpoint pending on') gdb.execute('set breakpoint pending on')
......
...@@ -894,7 +894,7 @@ class EmbedTest(unittest.TestCase): ...@@ -894,7 +894,7 @@ class EmbedTest(unittest.TestCase):
# report the error for the original directory # report the error for the original directory
libdir = sysconfig.get_config_var('LIBDIR') libdir = sysconfig.get_config_var('LIBDIR')
cython = 'cython.py' cython = 'cython.py'
if sys.version_info[0] >=3: if sys.version_info[0] >=3 and CY3_DIR:
cython = os.path.join(CY3_DIR, cython) cython = os.path.join(CY3_DIR, cython)
cython = os.path.abspath(os.path.join('..', '..', cython)) cython = os.path.abspath(os.path.join('..', '..', cython))
self.assert_(os.system( self.assert_(os.system(
......
...@@ -56,3 +56,14 @@ def test_for_in_range(arg): ...@@ -56,3 +56,14 @@ def test_for_in_range(arg):
for c in range(arg): for c in range(arg):
l.append(c) l.append(c)
return l return l
def raise_and_catch_BaseException():
"""
>>> raise_and_catch_BaseException()
1
"""
try:
raise BaseException
except BaseException:
return 1
return 2
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