--- /dev/null
+"""
+Tests that run inside GDB.
+
+Note: debug information is already imported by the file generated by
+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 warnings
+import unittest
+import traceback
+from test import test_support
+
+import gdb
+
+from Cython.Debugger import libcython
+
+class DebugTestCase(unittest.TestCase):
+
+ def __init__(self, name):
+ super(DebugTestCase, self).__init__(name)
+ self.cy = libcython.cy
+ self.module = libcython.cy.cython_namespace['codefile']
+ self.spam_func, self.spam_meth = libcython.cy.functions_by_name['spam']
+ self.ham_func = libcython.cy.functions_by_qualified_name[
+ 'codefile.ham']
+ 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 local_info(self):
+ return gdb.execute('info locals', to_string=True)
+
+class TestDebugInformationClasses(DebugTestCase):
+
+
+ def test_CythonModule(self):
+ "test that debug information was parsed properly into data structures"
+ self.assertEqual(self.module.name, 'codefile')
+ global_vars = ('c_var', 'python_var', 'SomeClass', '__name__',
+ '__builtins__', '__doc__', '__file__')
+ assert set(global_vars).issubset(self.module.globals)
+
+ def test_CythonVariable(self):
+ module_globals = self.module.globals
+ c_var = module_globals['c_var']
+ python_var = module_globals['python_var']
+ self.assertEqual(c_var.type, libcython.CObject)
+ self.assertEqual(python_var.type, libcython.PythonObject)
+ self.assertEqual(c_var.qualified_name, 'codefile.c_var')
+
+ def test_CythonFunction(self):
+ self.assertEqual(self.spam_func.qualified_name, 'codefile.spam')
+ self.assertEqual(self.spam_meth.qualified_name,
+ 'codefile.SomeClass.spam')
+ self.assertEqual(self.spam_func.module, self.module)
+
+ assert self.eggs_func.pf_cname
+ assert not self.ham_func.pf_cname
+ assert not self.spam_func.pf_cname
+ assert not self.spam_meth.pf_cname
+
+ self.assertEqual(self.spam_func.type, libcython.CObject)
+ 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.lineno, 8)
+ self.assertEqual(sorted(self.spam_func.locals), list('abcd'))
+
+
+class TestParameters(unittest.TestCase):
+
+ def test_parameters(self):
+ assert libcython.parameters.colorize_code
+ gdb.execute('set cy_colorize_code off')
+ assert not libcython.parameters.colorize_code
+
+
+class TestBreak(DebugTestCase):
+
+ def test_break(self):
+ result = gdb.execute('cy break codefile.spam', to_string=True)
+ assert self.spam_func.cname in result
+
+ self.assertEqual(len(gdb.breakpoints()), 1)
+ bp, = gdb.breakpoints()
+ self.assertEqual(bp.type, gdb.BP_BREAKPOINT)
+ self.assertEqual(bp.location, self.spam_func.cname)
+ assert bp.enabled
+
+
+class TestStep(DebugTestCase):
+
+ def test_step(self):
+ # Note: breakpoint for spam is still set
+ gdb.execute('run')
+
+ 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')
+
+
+class TestNext(DebugTestCase):
+
+ def test_next(self):
+ pass
+
+def main():
+ 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
from Cython.Debugger import libpython
-
-# Cython module namespace
-cython_namespace = {}
-
# C or Python type
-CObject = object()
-PythonObject = object()
-
-# maps (unique) qualified function names (e.g.
-# cythonmodule.ClassName.method_name) to the CythonFunction object
-functions_by_qualified_name = {}
-
-# unique cnames of Cython functions
-functions_by_cname = {}
-
-# map function names like method_name to a list of all such CythonFunction
-# objects
-functions_by_name = collections.defaultdict(list)
+CObject = 'CObject'
+PythonObject = 'PythonObject'
+_data_types = dict(CObject=CObject, PythonObject=PythonObject)
_filesystemencoding = sys.getfilesystemencoding() or 'UTF-8'
# decorators
def dont_suppress_errors(function):
+ "*sigh*, readline"
@functools.wraps(function)
def wrapper(*args, **kwargs):
try:
return wrapper
-def default_selected_gdb_frame(function):
- @functools.wraps(function)
- def wrapper(self, frame=None, **kwargs):
- frame = frame or gdb.selected_frame()
- if frame.name() is None:
- raise NoFunctionNameInFrameError()
-
- return function(self, frame)
- return wrapper
-
+def default_selected_gdb_frame(err=True):
+ def decorator(function):
+ @functools.wraps(function)
+ def wrapper(self, frame=None, **kwargs):
+ try:
+ frame = frame or gdb.selected_frame()
+ except RuntimeError:
+ raise gdb.GdbError("No frame is currently selected.")
+
+ if err and frame.name() is None:
+ raise NoFunctionNameInFrameError()
+
+ return function(self, frame, **kwargs)
+ return wrapper
+ return decorator
+def require_cython_frame(function):
+ @functools.wraps(function)
+ def wrapper(self, *args, **kwargs):
+ if not self.is_cython_function():
+ raise gdb.GdbError('Selected frame does not correspond with a '
+ 'Cython function we know about.')
+ return function(self, *args, **kwargs)
+ return wrapper
+
# Classes that represent the debug information
# Don't rename the parameters of these classes, they come directly from the XML
def __init__(self, module_name, filename):
self.name = module_name
self.filename = filename
- self.functions = {}
self.globals = {}
# {cython_lineno: min(c_linenos)}
self.lineno_cy2c = {}
# {c_lineno: cython_lineno}
self.lineno_c2cy = {}
+
+ def qualified_name(self, varname):
+ return '.'.join(self.name, varname)
class CythonVariable(object):
type)
self.module = module
self.pf_cname = pf_cname
- self.lineno = lineno
+ self.lineno = int(lineno)
self.locals = {}
self.arguments = []
self.step_into_functions = set()
+
+# General purpose classes
+
+class CythonBase(object):
+
+ @default_selected_gdb_frame(err=False)
+ def is_cython_function(self, frame):
+ return frame.name() in self.cy.functions_by_cname
+
+ @default_selected_gdb_frame(err=False)
+ def is_python_function(self, frame):
+ return frame.name() == 'PyEval_EvalFrameEx'
+
+ @default_selected_gdb_frame()
+ def is_python_function(self, frame):
+ return libpython.Frame(frame).is_evalframeex()
+
+ @default_selected_gdb_frame()
+ def get_c_function_name(self, frame):
+ return frame.name()
+
+ @default_selected_gdb_frame()
+ def get_c_lineno(self, frame):
+ return frame.find_sal().line
+
+ @default_selected_gdb_frame()
+ def get_cython_function(self, frame):
+ result = self.cy.functions_by_cname.get(frame.name())
+ if result is None:
+ raise NoCythonFunctionInFrameError()
+
+ return result
+
+ @default_selected_gdb_frame()
+ def get_cython_lineno(self, frame):
+ cyfunc = self.get_cython_function(frame)
+ return cyfunc.module.lineno_c2cy.get(self.get_c_lineno(frame))
+
+ @default_selected_gdb_frame()
+ def get_source_desc(self, frame):
+ filename = lineno = lexer = None
+ if self.is_cython_function():
+ filename = self.get_cython_function(frame).module.filename
+ lineno = self.get_cython_lineno(frame)
+ if pygments:
+ lexer = pygments.lexers.CythonLexer()
+ elif self.is_python_function():
+ pyframeobject = libpython.Frame(frame).get_pyop()
+
+ if not pyframeobject:
+ raise GdbError('Unable to read information on python frame')
+
+ filename = pyframeobject.filename()
+ lineno = pyframeobject.current_line_num()
+ if pygments:
+ lexer = pygments.lexers.PythonLexer()
+
+ return SourceFileDescriptor(filename, lexer), lineno
+
+ @default_selected_gdb_frame()
+ def get_source_line(self, frame):
+ source_desc, lineno = self.get_source_desc()
+ return source_desc.get_source(lineno)
+
class SourceFileDescriptor(object):
- def __init__(self, filename, lineno, lexer, formatter=None):
+ def __init__(self, filename, lexer, formatter=None):
self.filename = filename
- self.lineno = lineno
self.lexer = lexer
self.formatter = formatter
return self.filename is not None
def lex(self, code):
- if pygments and parameter.colorize_code:
- bg = parameter.terminal_background.value
+ if pygments and self.lexer and parameters.colorize_code:
+ bg = parameters.terminal_background.value
if self.formatter is None:
formatter = pygments.formatters.TerminalFormatter(bg=bg)
else:
return code
- def get_source(self, start=0, stop=None, lex_source=True):
- # todo: have it detect the source file's encoding
- if not self.filename:
- return 'Unable to retrieve source code'
-
- start = max(self.lineno + start, 0)
- if stop is None:
- stop = self.lineno + 1
- else:
- stop = self.lineno + stop
-
+ def _get_source(self, start, stop, lex_source, mark_line):
with open(self.filename) as f:
- source = itertools.islice(f, start, stop)
-
if lex_source:
- return [self.lex(line) for line in source]
+ # to provide proper colouring, the entire code needs to be
+ # lexed
+ lines = self.lex(f.read()).splitlines()
else:
- return list(source)
+ lines = f
+
+ for idx, line in enumerate(itertools.islice(lines, start - 1, stop - 1)):
+ if start + idx == mark_line:
+ prefix = '>'
+ else:
+ prefix = ' '
+
+ yield '%s %4d %s' % (prefix, start + idx, line)
+ def get_source(self, start, stop=None, lex_source=True, mark_line=0):
+ if not self.filename:
+ raise GdbError('Unable to retrieve source code')
+
+ if stop is None:
+ stop = start + 1
+ return '\n'.join(self._get_source(start, stop, lex_source, mark_line))
+
# Errors
Tell cygdb about the user's terminal background (light or dark)
"""
-class Parameter(object):
+class CythonParameters(object):
"""
Simple container class that might get more functionality in the distant
future (mostly to remind us that we're dealing with parameters)
"""
- complete_unqualified = CompleteUnqualifiedFunctionNames(
- 'cy_complete_unqualified',
- gdb.COMMAND_BREAKPOINTS,
- gdb.PARAM_BOOLEAN,
- True)
- colorize_code = ColorizeSourceCode(
- 'cy_colorize_code',
- gdb.COMMAND_FILES,
- gdb.PARAM_BOOLEAN,
- True)
- terminal_background = TerminalBackground(
- 'cy_terminal_background_color',
- gdb.COMMAND_FILES,
- gdb.PARAM_STRING,
- "dark")
-
-parameter = Parameter()
+
+ def __init__(self):
+ self.complete_unqualified = CompleteUnqualifiedFunctionNames(
+ 'cy_complete_unqualified',
+ gdb.COMMAND_BREAKPOINTS,
+ gdb.PARAM_BOOLEAN,
+ True)
+ self.colorize_code = ColorizeSourceCode(
+ 'cy_colorize_code',
+ gdb.COMMAND_FILES,
+ gdb.PARAM_BOOLEAN,
+ True)
+ self.terminal_background = TerminalBackground(
+ 'cy_terminal_background_color',
+ gdb.COMMAND_FILES,
+ gdb.PARAM_STRING,
+ "dark")
+
+parameters = CythonParameters()
+
# Commands
-class CythonCommand(gdb.Command):
+class CythonCommand(gdb.Command, CythonBase):
+ """
+ Base class for Cython commands
+ """
+
+
+class CyCy(CythonCommand):
"""
Invoke a Cython command. Available commands are:
cy locals
cy globals
cy backtrace
- cy info line
+ cy up
+ cy down
"""
- def is_cython_function(self, frame=None):
- func_name = (frame or gdb.selected_frame()).name()
- return func_name is not None and func_name in functions_by_cname
-
- @default_selected_gdb_frame
- def is_python_function(self, frame):
- return libpython.Frame(frame).is_evalframeex()
-
- @default_selected_gdb_frame
- def get_c_function_name(self, frame):
- return frame.name()
-
- @default_selected_gdb_frame
- def get_c_lineno(self, frame):
- return frame.find_sal().line
-
- @default_selected_gdb_frame
- def get_cython_function(self, frame):
- result = functions_by_cname.get(frame.name())
- if result is None:
- raise NoCythonFunctionInFrameError()
+ def __init__(self):
+ super(CythonCommand, self).__init__(
+ 'cy', gdb.COMMAND_NONE, gdb.COMPLETE_COMMAND, prefix=True)
+
+ self.import_ = CyImport(
+ 'cy import', gdb.COMMAND_STATUS, gdb.COMPLETE_FILENAME)
- return result
-
- @default_selected_gdb_frame
- def get_cython_lineno(self, frame):
- cyfunc = self.get_cython_function(frame)
- return cyfunc.module.lineno_c2cy.get(self.get_c_lineno(frame))
-
- @default_selected_gdb_frame
- def get_source_desc(self, frame):
- if self.is_cython_function():
- filename = self.get_cython_function(frame).module.filename
- lineno = self.get_cython_lineno(frame)
- lexer = pygments.lexers.CythonLexer()
- else:
- filename = None
- lineno = -1
- lexer = None
-
- return SourceFileDescriptor(filename, lineno, lexer)
+ self.break_ = CyBreak('cy break', gdb.COMMAND_BREAKPOINTS)
+ self.step = CyStep('cy step', gdb.COMMAND_RUNNING, gdb.COMPLETE_NONE)
+ self.next = CyNext('cy next', gdb.COMMAND_RUNNING, gdb.COMPLETE_NONE)
+ self.list = CyList('cy list', gdb.COMMAND_FILES, gdb.COMPLETE_NONE)
+ self.print_ = CyPrint('cy print', gdb.COMMAND_DATA)
+
+ self.locals = CyLocals(
+ 'cy locals', gdb.COMMAND_STACK, gdb.COMPLETE_NONE)
+ self.globals = CyGlobals(
+ 'cy globals', gdb.COMMAND_STACK, gdb.COMPLETE_NONE)
+
+ self.cy_cname = CyCName('cy_cname')
+
+ objs = (self.import_, self.break_, self.step, self.list, self.print_,
+ self.locals, self.globals, self.cy_cname)
-
-cy = CythonCommand('cy', gdb.COMMAND_NONE, gdb.COMPLETE_COMMAND, prefix=True)
+ for obj in objs:
+ obj.cy = self
+
+ # Cython module namespace
+ self.cython_namespace = {}
+
+ # maps (unique) qualified function names (e.g.
+ # cythonmodule.ClassName.method_name) to the CythonFunction object
+ self.functions_by_qualified_name = {}
+
+ # unique cnames of Cython functions
+ self.functions_by_cname = {}
+
+ # map function names like method_name to a list of all such
+ # CythonFunction objects
+ self.functions_by_name = collections.defaultdict(list)
class CyImport(CythonCommand):
for module in t.getroot():
cython_module = CythonModule(**module.attrib)
- cython_namespace[cython_module.name] = cython_module
+ self.cy.cython_namespace[cython_module.name] = cython_module
for variable in module.find('Globals'):
d = variable.attrib
for function in module.find('Functions'):
cython_function = CythonFunction(module=cython_module,
**function.attrib)
- cython_module.functions[cython_function.name] = \
- cython_function
# update the global function mappings
- functions_by_name[cython_function.name].append(
+ self.cy.functions_by_name[cython_function.name].append(
cython_function)
- functions_by_qualified_name[
+ self.cy.functions_by_qualified_name[
cython_function.qualified_name] = cython_function
- functions_by_cname[cython_function.cname] = cython_function
+ self.cy.functions_by_cname[
+ cython_function.cname] = cython_function
for local in function.find('Locals'):
d = local.attrib
for c_lineno in c_linenos:
cython_module.lineno_c2cy[c_lineno] = cython_lineno
-
-cy.import_ = CyImport('cy import', gdb.COMMAND_STATUS, gdb.COMPLETE_FILENAME)
-
class CyBreak(CythonCommand):
"""
def _break_pyx(self, name):
modulename, _, lineno = name.partition(':')
lineno = int(lineno)
- cython_module = cython_namespace[modulename]
+ cython_module = self.cy.cython_namespace[modulename]
if lineno in cython_module.lineno_cy2c:
c_lineno = cython_module.lineno_cy2c[lineno]
breakpoint = '%s:%s' % (cython_module.name, c_lineno)
gdb.execute('break ' + breakpoint)
else:
- sys.stderr.write("Not a valid line number (does it contain actual code?)\n")
+ raise GdbError("Not a valid line number. "
+ "Does it contain actual code?")
def _break_funcname(self, funcname):
- func = functions_by_qualified_name.get(funcname)
+ func = self.cy.functions_by_qualified_name.get(funcname)
break_funcs = [func]
if not func:
- funcs = functions_by_name.get(funcname)
+ funcs = self.cy.functions_by_name.get(funcname)
if not funcs:
gdb.execute('break ' + funcname)
return
@dont_suppress_errors
def complete(self, text, word):
- names = functions_by_qualified_name
- if parameter.complete_unqualified:
- names = itertools.chain(names, functions_by_name)
+ names = self.cy.functions_by_qualified_name
+ if parameters.complete_unqualified:
+ names = itertools.chain(names, self.cy.functions_by_name)
words = text.strip().split()
if words and '.' in words[-1]:
- compl = [n for n in functions_by_qualified_name
+ compl = [n for n in self.cy.functions_by_qualified_name
if n.startswith(lastword)]
else:
seen = set(text[:-len(word)].split())
return compl
-cy.break_ = CyBreak('cy break', gdb.COMMAND_BREAKPOINTS)
+
+class CodeStepperMixin(object):
+
+ def init_stepping(self):
+ self.cython_func = self.get_cython_function()
+ self.beginline = self.get_cython_lineno()
+ self.curframe = gdb.selected_frame()
+
+ def next_step(self, command):
+ "returns whether to continue stepping"
+ result = gdb.execute(command, to_string=True)
+ newframe = gdb.selected_frame()
+
+ c1 = result.startswith('Breakpoint')
+ c2 = (newframe == self.curframe and
+ self.get_cython_lineno() > self.beginline)
+ return not c1 and not c2
+
+ def end_stepping(self):
+ sys.stdout.write(self.get_source_line())
-class CyStep(CythonCommand):
+class CyStep(CythonCommand, CodeStepperMixin):
- def step(self, from_tty=True, nsteps=1):
+ def step(self, nsteps=1):
for nthstep in xrange(nsteps):
- cython_func = self.get_cython_function()
- beginline = self.get_cython_lineno()
- curframe = gdb.selected_frame()
-
- while True:
- result = gdb.execute('step', False, True)
- if result.startswith('Breakpoint'):
- break
+ self.init_stepping()
+
+ while self.next_step('step'):
newframe = gdb.selected_frame()
- if newframe == curframe:
- # still in the same function
- if self.get_cython_lineno() > beginline:
- break
- else:
+ if newframe != self.curframe:
# we entered a function
funcname = self.get_c_function_name(newframe)
if (self.is_cython_function() or
funcname in cython_function.step_into_functions):
break
- line, = self.get_source_desc().get_source()
- sys.stdout.write(line)
+ self.end_stepping()
def invoke(self, steps, from_tty):
if self.is_cython_function():
if steps:
- self.step(from_tty, int(steps))
+ self.step(int(steps))
else:
- self.step(from_tty)
+ self.step()
else:
- gdb.execute('step ' + steps)
+ gdb.execute('step ' + steps, from_tty)
-cy.step = CyStep('cy step', gdb.COMMAND_RUNNING, gdb.COMPLETE_NONE)
+
+class CyNext(CythonCommand, CodeStepperMixin):
+
+ def next(self, nsteps=1):
+ for nthstep in xrange(nsteps):
+ self.init_stepping()
+
+ while self.next_step('next'):
+ pass
+
+ self.end_stepping()
+
+ def invoke(self, steps, from_tty):
+ if self.is_cython_function():
+ if steps:
+ self.next(int(steps))
+ else:
+ self.next()
+ else:
+ gdb.execute('next ' + steps, from_tty)
class CyList(CythonCommand):
def invoke(self, _, from_tty):
- sd = self.get_source_desc()
- it = enumerate(sd.get_source(-5, +5))
- sys.stdout.write(
- ''.join('%4d %s' % (sd.lineno + i, line) for i, line in it))
-
-cy.list = CyList('cy list', gdb.COMMAND_FILES, gdb.COMPLETE_NONE)
+ sd, lineno = self.get_source_desc()
+ source = sd.get_source(lineno - 5, lineno + 5, mark_line=lineno)
+ print source
class CyPrint(CythonCommand):
"""
Print a Cython variable using 'cy-print x' or 'cy-print module.function.x'
"""
-
def invoke(self, name, from_tty):
- cname = None
- if self.is_cython_function():
- cython_function = self.get_cython_function()
- if name in cython_function.locals:
- cname = cython_function.locals[name].cname
- elif name in cython_function.module.globals:
- cname = cython_function.module.globals[name].cname
-
- # let the pretty printers do the work
- cname = cname or name
+ try:
+ cname = cy.cy_cname.invoke(name)
+ except gdb.GdbError:
+ cname = name
+
gdb.execute('print ' + cname)
+
def complete(self):
if self.is_cython_function():
- cf = self.get_cython_function()
- return list(itertools.chain(cf.locals, cf.globals))
+ f = self.get_cython_function()
+ return list(itertools.chain(f.locals, f.globals))
return []
-
-cy.print_ = CyPrint('cy print', gdb.COMMAND_DATA)
class CyLocals(CythonCommand):
def ns(self):
return self.get_cython_function().locals
-
+
+ @require_cython_frame
def invoke(self, name, from_tty):
try:
ns = self.ns()
return
if ns is None:
- print ('Information of Cython locals could not be obtained. '
- 'Is this an actual Cython function and did you '
- "'cy import' the debug information?")
+ raise gdb.GdbError(
+ 'Information of Cython locals could not be obtained. '
+ 'Is this an actual Cython function and did you '
+ "'cy import' the debug information?")
for var in ns.itervalues():
val = gdb.parse_and_eval(var.cname)
def ns(self):
return self.get_cython_function().globals
+ @require_cython_frame
def invoke(self, name, from_tty):
# include globals from the debug info XML file!
m = gdb.parse_and_eval('__pyx_m')
print d.get_truncated_repr(1000)
-cy.locals = CyLocals('cy locals', gdb.COMMAND_STACK, gdb.COMPLETE_NONE)
-cy.globals = CyGlobals('cy globals', gdb.COMMAND_STACK, gdb.COMPLETE_NONE)
+# Functions
+
+class CyCName(gdb.Function, CythonBase):
+ """
+ Get the C name of a Cython variable.
+ """
+
+ @require_cython_frame
+ def invoke(self, cyname, frame=None):
+ frame = frame or gdb.selected_frame()
+ cname = None
+
+ cyname = cyname.string()
+ if self.is_cython_function(frame):
+ cython_function = self.get_cython_function(frame)
+ if cyname in cython_function.locals:
+ cname = cython_function.locals[cyname].cname
+ elif cyname in cython_function.module.globals:
+ cname = cython_function.module.globals[cyname].cname
+
+ if not cname:
+ raise gdb.GdbError('No such Cython variable: %s' % cyname)
+
+ return cname
+
+
+cy = CyCy()
\ No newline at end of file