import Errors
# 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
+# conditional metaclass. These options are processed by CmdLine called from
# main() in this file.
# import Parsing
import Version
# include_directories [string]
# future_directives [object]
# language_level int currently 2 or 3 for Python 2/3
-
+
def __init__(self, include_directories, compiler_directives, cpp=False, language_level=2):
import Builtin, CythonScope
self.modules = {"__builtin__" : Builtin.builtin_scope}
self.include_directories = include_directories + [standard_include_path]
self.set_language_level(language_level)
-
+
self.gdb_debug_outputwriter = None
def set_language_level(self, level):
else:
_check_c_declarations = check_c_declarations
_specific_post_parse = None
-
+
if py and not pxd:
_align_function_definitions = AlignFunctionDefinitions(self)
else:
_align_function_definitions = None
-
+
return [
NormalizeTree(self),
PostParse(self),
debug_transform = [DebugTransform(self, options, result)]
else:
debug_transform = []
-
+
return list(itertools.chain(
[create_parse(self)],
self.create_pipeline(pxd=False, py=py),
return [parse_pxd] + self.create_pipeline(pxd=True) + [
ExtractPxdCode(self),
]
-
+
def create_py_pipeline(self, options, result):
return self.create_pyx_pipeline(options, result, py=True)
pipeline = self.create_pxd_pipeline(scope, module_name)
result = self.run_pipeline(pipeline, source_desc)
return result
-
+
def nonfatal_error(self, exc):
return Errors.report_error(exc)
error = err
return (error, data)
- def find_module(self, module_name,
+ def find_module(self, module_name,
relative_to = None, pos = None, need_pxd = 1):
# Finds and returns the module scope corresponding to
# the given relative or absolute module name. If this
except CompileError:
pass
return scope
-
+
def find_pxd_file(self, qualified_name, pos):
# Search include path for the .pxd file corresponding to the
# given fully-qualified module name.
# Search include path for the .pyx file corresponding to the
# given fully-qualified module name, as for find_pxd_file().
return self.search_include_directories(qualified_name, ".pyx", pos)
-
+
def find_include_file(self, filename, pos):
# Search list of include directories for filename.
# Reports an error and returns None if not found.
if not path:
error(pos, "'%s' not found" % filename)
return path
-
+
def search_include_directories(self, qualified_name, suffix, pos,
include=False):
# Search the list of include directories for the given
if dep_path and Utils.file_newer_than(dep_path, c_time):
return 1
return 0
-
+
def find_cimported_module_names(self, source_path):
return [ name for kind, name in self.read_dependency_file(source_path)
if kind == "cimport" ]
def is_package_dir(self, dir_path):
# Return true if the given directory is a package directory.
- for filename in ("__init__.py",
- "__init__.pyx",
+ for filename in ("__init__.py",
+ "__init__.pyx",
"__init__.pxd"):
path = os.path.join(dir_path, filename)
if Utils.path_exists(path):
# Find a top-level module, creating a new one if needed.
scope = self.lookup_submodule(name)
if not scope:
- scope = ModuleScope(name,
+ scope = ModuleScope(name,
parent_module = None, context = self)
self.modules[name] = scope
return scope
# Set up result object
result = create_default_resultobj(source, options)
-
+
# Get pipeline
if source_ext.lower() == '.py':
pipeline = context.create_py_pipeline(options, result)
err, enddata = context.run_pipeline(pipeline, source)
context.teardown_errors(err, options, result)
return result
-
+
#------------------------------------------------------------------------
#
class CompilationOptions(object):
"""
Options to the Cython compiler:
-
+
show_version boolean Display version number
use_listing_file boolean Generate a .lis file
errors_to_stderr boolean Echo errors to stderr when using .lis
compiler_directives dict Overrides for pragma options (see Options.py)
evaluate_tree_assertions boolean Test support: evaluate parse tree assertions
language_level integer The Python language level: 2 or 3
-
+
cplus boolean Compile as c++ code
"""
-
+
def __init__(self, defaults = None, **kw):
self.include_path = []
if defaults:
class CompilationResult(object):
"""
Results from the Cython compiler:
-
+
c_file string or None The generated C source file
h_file string or None The generated C header file
i_file string or None The generated .pxi file
num_errors integer Number of compilation errors
compilation_source CompilationSource
"""
-
+
def __init__(self):
self.c_file = None
self.h_file = None
Results from compiling multiple Pyrex source files. A mapping
from source file paths to CompilationResult instances. Also
has the following attributes:
-
+
num_errors integer Total number of compilation errors
"""
-
+
num_errors = 0
def add(self, source, result):
def compile_single(source, options, full_module_name = None):
"""
compile_single(source, options, full_module_name)
-
+
Compile the given Pyrex implementation file and return a CompilationResult.
Always compiles a single file; does not perform timestamp checking or
recursion.
def compile_multiple(sources, options):
"""
compile_multiple(sources, options)
-
+
Compiles the given sequence of Pyrex implementation files and returns
a CompilationResultSet. Performs timestamp checking and/or recursion
if these are specified in the options.
def compile(source, options = None, full_module_name = None, **kwds):
"""
compile(source [, options], [, <option> = <value>]...)
-
+
Compile one or more Pyrex implementation files, with optional timestamp
checking and recursing on dependecies. The source argument may be a string
or a sequence of strings If it is a string and no recursion or timestamp
class SkipDeclarations(object):
"""
- Variable and function declarations can often have a deep tree structure,
- and yet most transformations don't need to descend to this depth.
-
- Declaration nodes are removed after AnalyseDeclarationsTransform, so there
- is no need to use this for transformations after that point.
+ Variable and function declarations can often have a deep tree structure,
+ and yet most transformations don't need to descend to this depth.
+
+ Declaration nodes are removed after AnalyseDeclarationsTransform, so there
+ is no need to use this for transformations after that point.
"""
def visit_CTypeDefNode(self, node):
return node
-
+
def visit_CVarDefNode(self, node):
return node
-
+
def visit_CDeclaratorNode(self, node):
return node
-
+
def visit_CBaseTypeNode(self, node):
return node
-
+
def visit_CEnumDefNode(self, node):
return node
def visit_ParallelAssignmentNode(self, node):
return self.visit_StatNode(node, True)
-
+
def visit_CEnumDefNode(self, node):
return self.visit_StatNode(node, True)
return []
def visit_CDeclaratorNode(self, node):
- return node
+ return node
class PostParseError(CompileError): pass
- Default values to cdef assignments are turned into single
assignments following the declaration (everywhere but in class
bodies, where they raise a compile error)
-
+
- Interpret some node structures into Python runtime values.
Some nodes take compile-time arguments (currently:
TemplatedTypeNode[args] and __cythonbufferdefaults__ = {args}),
lhs_list = expr_list[:-1]
rhs = expr_list[-1]
if len(lhs_list) == 1:
- node = Nodes.SingleAssignmentNode(rhs.pos,
+ node = Nodes.SingleAssignmentNode(rhs.pos,
lhs = lhs_list[0], rhs = rhs)
else:
node = Nodes.CascadedAssignmentNode(rhs.pos,
- "def" functions are let through only if they fill the
getbuffer/releasebuffer slots
-
+
- cdef functions are let through only if they are on the
top level and are declared "inline"
"""
if (isinstance(node, Nodes.DefNode) and self.scope_type == 'cclass'
and node.name in ('__getbuffer__', '__releasebuffer__')):
err = None # allow these slots
-
+
if isinstance(node, Nodes.CFuncDefNode):
if u'inline' in node.modifiers and self.scope_type == 'pxd':
node.inline_in_pxd = True
return None
else:
return node
-
+
class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
"""
After parsing, directives can be stored in a number of places:
"""
unop_method_nodes = {
'typeof': ExprNodes.TypeofNode,
-
+
'operator.address': ExprNodes.AmpersandNode,
'operator.dereference': ExprNodes.DereferenceNode,
'operator.preincrement' : ExprNodes.inc_dec_constructor(True, '++'),
'operator.predecrement' : ExprNodes.inc_dec_constructor(True, '--'),
'operator.postincrement': ExprNodes.inc_dec_constructor(False, '++'),
'operator.postdecrement': ExprNodes.inc_dec_constructor(False, '--'),
-
+
# For backwards compatability.
'address': ExprNodes.AmpersandNode,
}
binop_method_nodes = {
'operator.comma' : ExprNodes.c_binop_constructor(','),
}
-
+
special_methods = cython.set(['declare', 'union', 'struct', 'typedef', 'sizeof',
'cast', 'pointer', 'compiled', 'NULL'])
special_methods.update(unop_method_nodes.keys())
return False
else:
return True
-
+
# Set up processing and handle the cython: comments.
def visit_ModuleNode(self, node):
for key, value in node.directive_comments.items():
# want to leave the cimport node sitting in the tree
return None
return node
-
+
def visit_FromCImportStatNode(self, node):
if (node.module_name == u"cython") or \
node.module_name.startswith(u"cython."):
return None
node.imported_names = newimp
return node
-
+
def visit_FromImportStatNode(self, node):
if (node.module.module_name.value == u"cython") or \
node.module.module_name.value.startswith(u"cython."):
def visit_SingleAssignmentNode(self, node):
if (isinstance(node.rhs, ExprNodes.ImportNode) and
node.rhs.module_name.value == u'cython'):
- node = Nodes.CImportStatNode(node.pos,
+ node = Nodes.CImportStatNode(node.pos,
module_name = u'cython',
as_name = node.lhs.name)
self.visit_CImportStatNode(node)
else:
self.visitchildren(node)
return node
-
+
def visit_NameNode(self, node):
if node.name in self.cython_module_names:
node.is_cython_module = True
directives=newdirectives)
self.directives = olddirectives
return directive
-
+
# Handle decorators
def visit_FuncDefNode(self, node):
directives = self._extract_directives(node, 'function')
if EXC:
EXIT(None, None, None)
MGR = EXIT = VALUE = EXC = None
-
+
""", temps=[u'MGR', u'EXC', u"EXIT", u"VALUE"],
pipeline=[NormalizeTree(None)])
# TODO: Cleanup badly needed
TemplateTransform.temp_name_counter += 1
handle = "__tmpvar_%d" % TemplateTransform.temp_name_counter
-
+
self.visitchildren(node, ['body'])
excinfo_temp = ExprNodes.NameNode(node.pos, name=handle)#TempHandle(Builtin.tuple_type)
if node.target is not None:
# node.pos, temps=[excinfo_temp], body=try_except)
return result
-
+
def visit_ExprNode(self, node):
# With statements are never inside expressions.
return node
-
+
class DecoratorTransform(CythonTransform, SkipDeclarations):
self.env_stack = [root.scope]
# needed to determine if a cdef var is declared after it's used.
self.seen_vars_stack = []
- return super(AnalyseDeclarationsTransform, self).__call__(root)
-
+ return super(AnalyseDeclarationsTransform, self).__call__(root)
+
def visit_NameNode(self, node):
self.seen_vars_stack[-1].add(node.name)
return node
self.visitchildren(node)
self.env_stack.pop()
return node
-
+
def visit_CClassDefNode(self, node):
node = self.visit_ClassDefNode(node)
if node.scope and node.scope.implemented:
if stats:
node.body.stats += stats
return node
-
+
def visit_FuncDefNode(self, node):
self.seen_vars_stack.append(cython.set())
lenv = node.local_scope
# necessary to ensure that all CNameDeclaratorNodes are visited.
self.visitchildren(node)
return node
-
+
def visit_CTypeDefNode(self, node):
return node
def visit_CBaseTypeNode(self, node):
return None
-
+
def visit_CEnumDefNode(self, node):
if node.visibility == 'public':
return node
# to ensure all CNameDeclaratorNodes are visited.
self.visitchildren(node)
return None
-
+
def create_Property(self, entry):
if entry.visibility == 'public':
if entry.type.is_pyobject:
template = self.basic_property_ro
property = template.substitute({
u"ATTR": ExprNodes.AttributeNode(pos=entry.pos,
- obj=ExprNodes.NameNode(pos=entry.pos, name="self"),
+ obj=ExprNodes.NameNode(pos=entry.pos, name="self"),
attribute=entry.name),
}, pos=entry.pos).stats[0]
property.name = entry.name
# ---------------------------------------
# XXX This should go to AutoDocTransforms
# ---------------------------------------
- if (Options.docstrings and
+ if (Options.docstrings and
self.current_directives['embedsignature']):
attr_name = entry.name
type_name = entry.type.declaration_code("", for_display=1)
node.body.analyse_expressions(node.scope)
self.visitchildren(node)
return node
-
+
def visit_FuncDefNode(self, node):
node.local_scope.infer_types()
node.body.analyse_expressions(node.local_scope)
return node
class ExpandInplaceOperators(EnvTransform):
-
+
def visit_InPlaceAssignmentNode(self, node):
lhs = node.lhs
rhs = node.rhs
except ValueError:
return node
dup = lhs.__class__(**lhs.__dict__)
- binop = ExprNodes.binop_node(node.pos,
+ binop = ExprNodes.binop_node(node.pos,
operator = node.operator,
operand1 = dup,
operand2 = rhs,
dup.analyse_types(env)
binop.analyse_operation(env)
node = Nodes.SingleAssignmentNode(
- node.pos,
+ node.pos,
lhs = lhs,
rhs=binop.coerce_to(lhs.type, env))
# Use LetRefNode to avoid side effects.
class AlignFunctionDefinitions(CythonTransform):
"""
- This class takes the signatures from a .pxd file and applies them to
- the def methods in a .py file.
+ This class takes the signatures from a .pxd file and applies them to
+ the def methods in a .py file.
"""
-
+
def visit_ModuleNode(self, node):
self.scope = node.scope
self.directives = node.directives
self.visitchildren(node)
return node
-
+
def visit_PyClassDefNode(self, node):
pxd_def = self.scope.lookup(node.name)
if pxd_def:
return None
else:
return node
-
+
def visit_CClassDefNode(self, node, pxd_def=None):
if pxd_def is None:
pxd_def = self.scope.lookup(node.class_name)
if pxd_def:
self.scope = outer_scope
return node
-
+
def visit_DefNode(self, node):
pxd_def = self.scope.lookup(node.name)
if pxd_def:
node = node.as_cfunction(pxd_def)
elif self.scope.is_module_scope and self.directives['auto_cpdef']:
node = node.as_cfunction(scope=self.scope)
- # Enable this when internal def functions are allowed.
+ # Enable this when internal def functions are allowed.
# self.visitchildren(node)
return node
-
+
class MarkClosureVisitor(CythonTransform):
node.needs_closure = self.needs_closure
self.needs_closure = True
return node
-
+
def visit_CFuncDefNode(self, node):
self.visit_FuncDefNode(node)
if node.needs_closure:
else:
self.visitchildren(node)
return node
-
+
def visit_AttributeNode(self, node):
self.visitchildren(node)
return self.visit_cython_attribute(node)
def visit_NameNode(self, node):
return self.visit_cython_attribute(node)
-
+
def visit_cython_attribute(self, node):
attribute = node.as_cython_attribute()
if attribute:
node.function = ExprNodes.NameNode(node.pos, name=EncodedString('set'))
else:
error(node.function.pos, u"'%s' not a valid cython language construct" % function)
-
+
self.visitchildren(node)
return node
Create debug information and all functions' visibility to extern in order
to enable debugging.
"""
-
+
def __init__(self, context, options, result):
super(DebugTransform, self).__init__(context)
self.visited = cython.set()
- # our treebuilder and debug output writer
+ # our treebuilder and debug output writer
# (see Cython.Debugger.debug_output.CythonDebugWriter)
self.tb = self.context.gdb_debug_outputwriter
- #self.c_output_file = options.output_file
+ #self.c_output_file = options.output_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
self.register_stepinto = False
-
+
def visit_ModuleNode(self, node):
self.tb.module_name = node.full_module_name
attrs = dict(
module_name=node.full_module_name,
filename=node.pos[0].filename,
c_filename=self.c_output_file)
-
+
self.tb.start('Module', attrs)
-
+
# serialize functions
self.tb.start('Functions')
# First, serialize functions normally...
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')
-
+
# 2.3 compatibility. Serialize global variables
self.tb.start('Globals')
entries = {}
for k, v in node.scope.entries.iteritems():
if (v.qualified_name not in self.visited and not
- v.name.startswith('__pyx_') and not
+ v.name.startswith('__pyx_') and not
v.type.is_cfunction and not
v.type.is_extension_type):
entries[k]= v
-
+
self.serialize_local_variables(entries)
self.tb.end('Globals')
# self.tb.end('Module') # end Module after the line number mapping in
# Cython.Compiler.ModuleNode.ModuleNode._serialize_lineno_map
return node
-
- def visit_FuncDefNode(self, node):
+
+ def visit_FuncDefNode(self, node):
self.visited.add(node.local_scope.qualified_name)
if getattr(node, 'is_wrapper', False):
pf_cname = ''
else:
pf_cname = node.py_func.entry.func_cname
-
+
attrs = dict(
name=node.entry.name,
cname=node.entry.func_cname,
pf_cname=pf_cname,
qualified_name=node.local_scope.qualified_name,
lineno=str(node.pos[1]))
-
+
self.tb.start('Function', attrs=attrs)
-
+
self.tb.start('Locals')
self.serialize_local_variables(node.local_scope.entries)
self.tb.end('Locals')
return node
def visit_NameNode(self, node):
- if (self.register_stepinto and
- node.type.is_cfunction and
+ if (self.register_stepinto and
+ node.type.is_cfunction and
getattr(node, 'is_called', False) and
node.entry.func_cname is not None):
- # don't check node.entry.in_cinclude, as 'cdef extern: ...'
- # declared functions are not 'in_cinclude'.
- # This means we will list called 'cdef' functions as
- # "step into functions", but this is not an issue as they will be
+ # don't check node.entry.in_cinclude, as 'cdef extern: ...'
+ # declared functions are not 'in_cinclude'.
+ # This means we will list called 'cdef' functions as
+ # "step into functions", but this is not an issue as they will be
# recognized as Cython functions anyway.
attrs = dict(name=node.entry.func_cname)
self.tb.start('StepIntoFunction', attrs=attrs)
self.tb.end('StepIntoFunction')
-
+
self.visitchildren(node)
return node
-
+
def serialize_modulenode_as_function(self, node):
"""
Serialize the module-level code as a function so the debugger will know
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
+ # 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.visitchildren(node)
self.register_stepinto = False
self.tb.end('StepIntoFunctions')
-
+
self.tb.end('Function')
-
+
def serialize_local_variables(self, entries):
for entry in entries.values():
if entry.type.is_pyobject:
vartype = 'PythonObject'
else:
vartype = 'CObject'
-
+
if entry.from_closure:
# We're dealing with a closure where a variable from an outer
# scope is accessed, get it from the scope object.
- cname = '%s->%s' % (Naming.cur_scope_cname,
+ 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.scope.name,
entry.name)
elif entry.in_closure:
- cname = '%s->%s' % (Naming.cur_scope_cname,
+ cname = '%s->%s' % (Naming.cur_scope_cname,
entry.cname)
qname = entry.qualified_name
else:
lineno = '0'
else:
lineno = str(entry.pos[1])
-
+
attrs = dict(
name=entry.name,
cname=cname,
qualified_name=qname,
type=vartype,
lineno=lineno)
-
+
self.tb.start('LocalVar', attrs)
self.tb.end('LocalVar')
-
+
body: ExprStatNode
expr: NameNode
""", self.treetypes(t))
-
+
def test_wrap_singlestat(self):
t = self.run_pipeline([NormalizeTree(None)], u"if x: y")
self.assertLines(u"""
stats[0]: ExprStatNode
expr: NameNode
""", self.treetypes(t))
-
+
def test_pass_eliminated(self):
t = self.run_pipeline([NormalizeTree(None)], u"pass")
$0_2(None, None, None)
""", t)
-
+
# TODO: Re-enable once they're more robust.
if sys.version_info[:2] >= (2, 5) and False:
DebuggerTestCase = object
class TestDebugTransform(DebuggerTestCase):
-
+
def elem_hasattrs(self, elem, attrs):
# we shall supporteth python 2.3 !
return all([attr in elem.attrib for attr in attrs])
-
+
def test_debug_info(self):
try:
assert os.path.exists(self.debug_dest)
-
+
t = DebugWriter.etree.parse(self.debug_dest)
# the xpath of the standard ElementTree is primitive, don't use
# anything fancy
xml_globals = dict(
[(e.attrib['name'], e.attrib['type']) for e in L])
self.assertEqual(len(L), len(xml_globals))
-
+
L = list(t.find('/Module/Functions'))
assert L
xml_funcs = dict([(e.attrib['qualified_name'], e) for e in L])
self.assertEqual(len(L), len(xml_funcs))
-
+
# test globals
self.assertEqual('CObject', xml_globals.get('c_var'))
self.assertEqual('PythonObject', xml_globals.get('python_var'))
-
+
# 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'
assert all([f in xml_funcs for f in funcnames])
spam, ham, eggs = [xml_funcs[funcname] for funcname in funcnames]
-
+
self.assertEqual(spam.attrib['name'], 'spam')
self.assertNotEqual('spam', spam.attrib['cname'])
assert self.elem_hasattrs(spam, required_xml_attrs)
names = [e.attrib['name'] for e in spam_locals]
self.assertEqual(list('abcd'), names)
assert self.elem_hasattrs(spam_locals[0], required_xml_attrs)
-
+
# test arguments of functions
spam_arguments = list(spam.find('Arguments'))
assert spam_arguments
self.assertEqual(1, len(list(spam_arguments)))
-
+
# test step-into functions
step_into = spam.find('StepIntoFunctions')
spam_stepinto = [x.attrib['name'] for x in step_into]
except:
print open(self.debug_dest).read()
raise
-
-
+
+
if __name__ == "__main__":
import unittest
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')
# 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()
except Exception:
traceback.print_exc()
raise
-
+
return wrapper
def default_selected_gdb_frame(err=True):
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, *args, **kwargs)
return wrapper
return decorator
raise gdb.GdbError('Selected frame does not correspond with a '
'Cython function we know about.')
return function(self, *args, **kwargs)
- return wrapper
+ return wrapper
def dispatch_on_frame(c_command, python_command=None):
def decorator(function):
def wrapper(self, *args, **kwargs):
is_cy = self.is_cython_function()
is_py = self.is_python_function()
-
+
if is_cy or (is_py and not python_command):
function(self, *args, **kwargs)
elif is_py:
else:
raise gdb.GdbError("Not a function cygdb knows about. "
"Use the normal GDB commands instead.")
-
+
return wrapper
return decorator
gdb.selected_frame()
except RuntimeError:
raise gdb.GdbError("No frame is currently selected.")
-
+
return function(*args, **kwargs)
return wrapper
-
+
def gdb_function_value_to_unicode(function):
@functools.wraps(function)
# {c_lineno: cython_lineno}
self.lineno_c2cy = {}
self.functions = {}
-
+
class CythonVariable(object):
def __init__(self, name, cname, qualified_name, type, lineno):
self.lineno = int(lineno)
class CythonFunction(CythonVariable):
- def __init__(self,
- module,
- name,
- cname,
+ def __init__(self,
+ module,
+ name,
+ cname,
pf_cname,
- qualified_name,
- lineno,
+ qualified_name,
+ lineno,
type=CObject,
is_initmodule_function="False"):
- super(CythonFunction, self).__init__(name,
- cname,
- qualified_name,
+ super(CythonFunction, self).__init__(name,
+ cname,
+ qualified_name,
type,
lineno)
self.module = module
# 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
pyframe = libpython.Frame(frame).get_pyop()
return pyframe and not pyframe.is_optimized_out()
return False
-
+
@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):
"""
- Get the current Cython line number. Returns 0 if there is no
+ Get the current Cython line number. Returns 0 if there is no
correspondence between the C and Cython code.
"""
cyfunc = self.get_cython_function(frame)
return cyfunc.module.lineno_c2cy.get(self.get_c_lineno(frame), 0)
-
+
@default_selected_gdb_frame()
def get_source_desc(self, frame):
filename = lineno = lexer = None
filename = pyframeobject.filename()
lineno = pyframeobject.current_line_num()
-
+
if pygments:
lexer = pygments.lexers.PythonLexer(stripall=False)
else:
lineno = symbol_and_line_obj.line
if pygments:
lexer = pygments.lexers.CLexer(stripall=False)
-
+
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)
-
+
@default_selected_gdb_frame()
def is_relevant_function(self, frame):
"""
return name in cython_func.step_into_functions
return False
-
+
@default_selected_gdb_frame(err=False)
def print_stackframe(self, frame, index, is_c=False):
"""
# raising GdbError when calling self.cy.cy_cvalue.invoke()
selected_frame = gdb.selected_frame()
frame.select()
-
+
try:
source_desc, lineno = self.get_source_desc(frame)
except NoFunctionNameInFrameError:
if pyframe is None or pyframe.is_optimized_out():
# print this python function as a C function
return self.print_stackframe(frame, index, is_c=True)
-
+
func_name = pyframe.co_name
func_cname = 'PyEval_EvalFrameEx'
func_args = []
elif self.is_cython_function(frame):
cyfunc = self.get_cython_function(frame)
f = lambda arg: self.cy.cy_cvalue.invoke(arg, frame=frame)
-
+
func_name = cyfunc.name
func_cname = cyfunc.cname
func_args = [] # [(arg, f(arg)) for arg in cyfunc.arguments]
func_name = frame.name()
func_cname = func_name
func_args = []
-
+
try:
gdb_value = gdb.parse_and_eval(func_cname)
except RuntimeError:
else:
# Seriously? Why is the address not an int?
func_address = int(str(gdb_value.address).split()[0], 0)
-
+
a = ', '.join('%s=%s' % (name, val) for name, val in func_args)
print '#%-2d 0x%016x in %s(%s)' % (index, func_address, func_name, a),
-
+
if source_desc.filename is not None:
print 'at %s:%s' % (source_desc.filename, lineno),
-
+
print
-
+
try:
print ' ' + source_desc.get_source(lineno)
except gdb.GdbError:
pass
-
+
selected_frame.select()
-
+
def get_remote_cython_globals_dict(self):
m = gdb.parse_and_eval('__pyx_m')
-
+
try:
PyModuleObject = gdb.lookup_type('PyModuleObject')
except RuntimeError:
raise gdb.GdbError(textwrap.dedent("""\
- Unable to lookup type PyModuleObject, did you compile python
+ Unable to lookup type PyModuleObject, did you compile python
with debugging support (-g)?"""))
-
+
m = m.cast(PyModuleObject.pointer())
return m['md_dict']
-
-
+
+
def get_cython_globals_dict(self):
"""
Get the Cython globals dict where the remote names are turned into
"""
remote_dict = self.get_remote_cython_globals_dict()
pyobject_dict = libpython.PyObjectPtr.from_pyobject_ptr(remote_dict)
-
+
result = {}
seen = set()
for k, v in pyobject_dict.iteritems():
result[k.proxyval(seen)] = v
-
+
return result
def print_gdb_value(self, name, value, max_name_length=None, prefix=''):
typename = ''
else:
typename = '(%s) ' % (value.type,)
-
+
if max_name_length is None:
print '%s%s = %s%s' % (prefix, name, typename, value)
else:
- print '%s%-*s = %s%s' % (prefix, max_name_length, name, typename,
+ print '%s%-*s = %s%s' % (prefix, max_name_length, name, typename,
value)
def is_initialized(self, cython_func, local_name):
return long(gdb.parse_and_eval(cyvar.cname))
return True
return False
-
+
cur_lineno = self.get_cython_lineno()
return (local_name in cython_func.arguments or
(islocal and cur_lineno > cyvar.lineno))
# to provide "correct" colouring, the entire code needs to be
# lexed. However, this makes a lot of things terribly slow, so
# we decide not to. Besides, it's unlikely to matter.
-
+
if lex_source and lex_entire:
f = self.lex(f.read()).splitlines()
-
+
slice = itertools.islice(f, start - 1, stop - 1)
-
+
for idx, line in enumerate(slice):
if start + idx == mark_line:
prefix = '>'
else:
prefix = ' '
-
+
if lex_source and not lex_entire:
line = self.lex(line)
yield '%s %4d %s' % (prefix, start + idx, line.rstrip())
- def get_source(self, start, stop=None, lex_source=True, mark_line=0,
+ def get_source(self, start, stop=None, lex_source=True, mark_line=0,
lex_entire=False):
exc = gdb.GdbError('Unable to retrieve source code')
-
+
if not self.filename:
raise exc
-
+
start = max(start, 1)
if stop is None:
stop = start + 1
"""
Base class for Cython-command related erorrs
"""
-
+
def __init__(self, *args):
args = args or (self.msg,)
super(CyGDBError, self).__init__(*args)
-
+
class NoCythonFunctionInFrameError(CyGDBError):
"""
- raised when the user requests the current cython function, which is
+ raised when the user requests the current cython function, which is
unavailable
"""
msg = "Current function is a function cygdb doesn't know about"
class NoFunctionNameInFrameError(NoCythonFunctionInFrameError):
"""
- raised when the name of the C function could not be determined
+ raised when the name of the C function could not be determined
in the current C stack frame
"""
msg = ('C function name could not be determined in the current C stack '
"""
Base class for cython parameters
"""
-
+
def __init__(self, name, command_class, parameter_class, default=None):
self.show_doc = self.set_doc = self.__class__.__doc__
- super(CythonParameter, self).__init__(name, command_class,
+ super(CythonParameter, self).__init__(name, command_class,
parameter_class)
if default is not None:
self.value = default
-
+
def __nonzero__(self):
return bool(self.value)
-
+
__bool__ = __nonzero__ # python 3
class CompleteUnqualifiedFunctionNames(CythonParameter):
"""
Have 'cy break' complete unqualified function or method names.
- """
+ """
class ColorizeSourceCode(CythonParameter):
"""
Simple container class that might get more functionality in the distant
future (mostly to remind us that we're dealing with parameters).
"""
-
+
def __init__(self):
self.complete_unqualified = CompleteUnqualifiedFunctionNames(
'cy_complete_unqualified',
gdb.COMMAND_FILES,
gdb.PARAM_STRING,
"dark")
-
+
parameters = CythonParameters()
"""
Base class for Cython commands
"""
-
+
command_class = gdb.COMMAND_NONE
-
+
@classmethod
def _register(cls, clsname, args, kwargs):
if not hasattr(cls, 'completer_class'):
return cls(clsname, cls.command_class, *args, **kwargs)
else:
- return cls(clsname, cls.command_class, cls.completer_class,
+ return cls(clsname, cls.command_class, cls.completer_class,
*args, **kwargs)
-
+
@classmethod
def register(cls, *args, **kwargs):
alias = getattr(cls, 'alias', None)
if alias:
cls._register(cls.alias, args, kwargs)
-
+
return cls._register(cls.name, args, kwargs)
class CyCy(CythonCommand):
"""
Invoke a Cython command. Available commands are:
-
+
cy import
cy break
cy step
cy globals
cy exec
"""
-
+
name = 'cy'
command_class = gdb.COMMAND_NONE
completer_class = gdb.COMPLETE_COMMAND
-
+
def __init__(self, name, command_class, completer_class):
# keep the signature 2.5 compatible (i.e. do not use f(*a, k=v)
- super(CythonCommand, self).__init__(name, command_class,
+ super(CythonCommand, self).__init__(name, command_class,
completer_class, prefix=True)
-
+
commands = dict(
import_ = CyImport.register(),
break_ = CyBreak.register(),
cy_cvalue = CyCValue('cy_cvalue'),
cy_lineno = CyLine('cy_lineno'),
)
-
+
for command_name, command in commands.iteritems():
command.cy = self
setattr(self, command_name, command)
-
+
self.cy = self
-
+
# Cython module namespace
self.cython_namespace = {}
-
- # maps (unique) qualified function names (e.g.
+
+ # 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
+
+ # map function names like method_name to a list of all such
# CythonFunction objects
self.functions_by_name = collections.defaultdict(list)
Import debug information outputted by the Cython compiler
Example: cy import FILE...
"""
-
+
name = 'cy import'
command_class = gdb.COMMAND_STATUS
completer_class = gdb.COMPLETE_FILENAME
-
+
def invoke(self, args, from_tty):
args = args.encode(_filesystemencoding)
for arg in string_to_argv(args):
try:
f = open(arg)
except OSError, e:
- raise gdb.GdbError('Unable to open file %r: %s' %
+ raise gdb.GdbError('Unable to open file %r: %s' %
(args, e.args[1]))
-
+
t = etree.parse(f)
-
+
for module in t.getroot():
cython_module = CythonModule(**module.attrib)
self.cy.cython_namespace[cython_module.name] = cython_module
-
+
for variable in module.find('Globals'):
d = variable.attrib
cython_module.globals[d['name']] = CythonVariable(**d)
-
+
for function in module.find('Functions'):
- cython_function = CythonFunction(module=cython_module,
+ cython_function = CythonFunction(module=cython_module,
**function.attrib)
# update the global function mappings
name = cython_function.name
qname = cython_function.qualified_name
-
+
self.cy.functions_by_name[name].append(cython_function)
self.cy.functions_by_qualified_name[
cython_function.qualified_name] = cython_function
self.cy.functions_by_cname[
cython_function.cname] = cython_function
-
+
d = cython_module.functions[qname] = cython_function
-
+
for local in function.find('Locals'):
d = local.attrib
cython_function.locals[d['name']] = CythonVariable(**d)
for step_into_func in function.find('StepIntoFunctions'):
d = step_into_func.attrib
cython_function.step_into_functions.add(d['name'])
-
+
cython_function.arguments.extend(
funcarg.tag for funcarg in function.find('Arguments'))
class CyBreak(CythonCommand):
"""
Set a breakpoint for Cython code using Cython qualified name notation, e.g.:
-
+
cy break cython_modulename.ClassName.method_name...
-
+
or normal notation:
-
+
cy break function_or_method_name...
-
+
or for a line number:
-
+
cy break cython_module:lineno...
-
+
Set a Python breakpoint:
Break on any function or method named 'func' in module 'modname'
-
+
cy break -p modname.func...
-
+
Break on any function or method named 'func'
-
+
cy break -p func...
"""
-
+
name = 'cy break'
command_class = gdb.COMMAND_BREAKPOINTS
-
+
def _break_pyx(self, name):
modulename, _, lineno = name.partition(':')
lineno = int(lineno)
else:
raise GdbError("Not a valid line number. "
"Does it contain actual code?")
-
+
def _break_funcname(self, funcname):
func = self.cy.functions_by_qualified_name.get(funcname)
-
+
if func and func.is_initmodule_function:
func = None
-
+
break_funcs = [func]
-
+
if not func:
funcs = self.cy.functions_by_name.get(funcname) or []
funcs = [f for f in funcs if not f.is_initmodule_function]
-
+
if not funcs:
gdb.execute('break ' + funcname)
return
-
+
if len(funcs) > 1:
# multiple functions, let the user pick one
print 'There are multiple such functions:'
for idx, func in enumerate(funcs):
print '%3d) %s' % (idx, func.qualified_name)
-
+
while True:
try:
result = raw_input(
elif result.lower() == 'a':
break_funcs = funcs
break
- elif (result.isdigit() and
+ elif (result.isdigit() and
0 <= int(result) < len(funcs)):
break_funcs = [funcs[int(result)]]
break
print 'Not understood...'
else:
break_funcs = [funcs[0]]
-
+
for func in break_funcs:
gdb.execute('break %s' % func.cname)
if func.pf_cname:
gdb.execute('break %s' % func.pf_cname)
-
+
def invoke(self, function_names, from_tty):
argv = string_to_argv(function_names.encode('UTF-8'))
if function_names.startswith('-p'):
python_breakpoints = True
else:
python_breakpoints = False
-
+
for funcname in argv:
if python_breakpoints:
gdb.execute('py-break %s' % funcname)
self._break_pyx(funcname)
else:
self._break_funcname(funcname)
-
+
@dont_suppress_errors
def complete(self, text, word):
- # Filter init-module functions (breakpoints can be set using
+ # Filter init-module functions (breakpoints can be set using
# modulename:linenumber).
- names = [n for n, L in self.cy.functions_by_name.iteritems()
+ names = [n for n, L in self.cy.functions_by_name.iteritems()
if any(not f.is_initmodule_function for f in L)]
qnames = [n for n, f in self.cy.functions_by_qualified_name.iteritems()
if not f.is_initmodule_function]
-
+
if parameters.complete_unqualified:
all_names = itertools.chain(qnames, names)
else:
if not words or '.' not in words[-1]:
# complete unqualified
seen = set(text[:-len(word)].split())
- return [n for n in all_names
+ return [n for n in all_names
if n.startswith(word) and n not in seen]
-
+
# complete qualified name
lastword = words[-1]
compl = [n for n in qnames if n.startswith(lastword)]
-
+
if len(lastword) > len(word):
# readline sees something (e.g. a '.') as a word boundary, so don't
# "recomplete" this prefix
strip_prefix_length = len(lastword) - len(word)
compl = [n[strip_prefix_length:] for n in compl]
-
+
return compl
"""
Implementation of the interface dictated by libpython.LanguageInfo.
"""
-
+
def lineno(self, frame):
# Take care of the Python and Cython levels. We need to care for both
# as we can't simply dispath to 'py-step', since that would work for
if self.is_cython_function(frame):
return self.get_cython_lineno(frame)
return super(CythonInfo, self).lineno(frame)
-
+
def get_source_line(self, frame):
try:
line = super(CythonInfo, self).get_source_line(frame)
if self.is_cython_function():
return self.get_cython_function().step_into_functions
return ()
-
+
def static_break_functions(self):
result = ['PyEval_EvalFrameEx']
result.extend(self.cy.functions_by_cname)
return result
-class CythonExecutionControlCommand(CythonCommand,
+class CythonExecutionControlCommand(CythonCommand,
libpython.ExecutionControlCommandBase):
-
+
@classmethod
def register(cls):
return cls(cls.name, cython_info)
class CyStep(CythonExecutionControlCommand, libpython.PythonStepperMixin):
"Step through Cython, Python or C code."
-
+
name = 'cy -step'
stepinto = True
-
+
def invoke(self, args, from_tty):
if self.is_python_function():
self.python_step(self.stepinto)
command = 'step'
else:
command = 'next'
-
+
self.finish_executing(gdb.execute(command, to_string=True))
else:
self.step(stepinto=self.stepinto)
class CyRun(CythonExecutionControlCommand):
"""
- Run a Cython program. This is like the 'run' command, except that it
+ Run a Cython program. This is like the 'run' command, except that it
displays Cython or Python source lines as well
"""
-
+
name = 'cy run'
-
+
invoke = CythonExecutionControlCommand.run
class CyCont(CythonExecutionControlCommand):
"""
- Continue a Cython program. This is like the 'run' command, except that it
+ Continue a Cython program. This is like the 'run' command, except that it
displays Cython or Python source lines as well.
"""
-
+
name = 'cy cont'
invoke = CythonExecutionControlCommand.cont
"""
name = 'cy up'
_command = 'up'
-
+
def invoke(self, *args):
try:
gdb.execute(self._command, to_string=True)
gdb.execute(self._command, to_string=True)
except RuntimeError, e:
raise gdb.GdbError(*e.args)
-
+
frame = gdb.selected_frame()
index = 0
while frame:
frame = frame.older()
index += 1
-
+
self.print_stackframe(index=index - 1)
"""
Go down a Cython, Python or relevant C frame.
"""
-
+
name = 'cy down'
_command = 'down'
Select a frame. Use frame numbers as listed in `cy backtrace`.
This command is useful because `cy backtrace` prints a reversed backtrace.
"""
-
+
name = 'cy select'
-
+
def invoke(self, stackno, from_tty):
try:
stackno = int(stackno)
except ValueError:
raise gdb.GdbError("Not a valid number: %r" % (stackno,))
-
+
frame = gdb.selected_frame()
while frame.newer():
frame = frame.newer()
-
+
stackdepth = libpython.stackdepth(frame)
-
+
try:
gdb.execute('select %d' % (stackdepth - stackno - 1,))
except RuntimeError, e:
class CyBacktrace(CythonCommand):
'Print the Cython stack'
-
+
name = 'cy bt'
alias = 'cy backtrace'
command_class = gdb.COMMAND_STACK
completer_class = gdb.COMPLETE_NONE
-
+
@require_running_program
def invoke(self, args, from_tty):
# get the first frame
selected_frame = frame = gdb.selected_frame()
while frame.older():
frame = frame.older()
-
+
print_all = args == '-a'
-
+
index = 0
while frame:
is_c = False
-
+
is_relevant = False
try:
is_relevant = self.is_relevant_function(frame)
except CyGDBError:
pass
-
+
if print_all or is_relevant:
self.print_stackframe(frame, index)
-
+
index += 1
frame = frame.newer()
-
+
selected_frame.select()
List Cython source code. To disable to customize colouring see the cy_*
parameters.
"""
-
+
name = 'cy list'
command_class = gdb.COMMAND_FILES
completer_class = gdb.COMPLETE_NONE
-
+
# @dispatch_on_frame(c_command='list')
def invoke(self, _, from_tty):
sd, lineno = self.get_source_desc()
- source = sd.get_source(lineno - 5, lineno + 5, mark_line=lineno,
+ source = sd.get_source(lineno - 5, lineno + 5, mark_line=lineno,
lex_entire=True)
print source
"""
Print a Cython variable using 'cy-print x' or 'cy-print module.function.x'
"""
-
+
name = 'cy print'
command_class = gdb.COMMAND_DATA
-
+
def invoke(self, name, from_tty, max_name_length=None):
if self.is_python_function():
return gdb.execute('py-print ' + name)
value = value.dereference()
else:
break
-
+
self.print_gdb_value(name, value, max_name_length)
else:
gdb.execute('print ' + name)
-
+
def complete(self):
if self.is_cython_function():
f = self.get_cython_function()
"""
List the locals from the current Cython frame.
"""
-
+
name = 'cy locals'
command_class = gdb.COMMAND_STACK
completer_class = gdb.COMPLETE_NONE
-
+
@dispatch_on_frame(c_command='info locals', python_command='py-locals')
def invoke(self, args, from_tty):
cython_function = self.get_cython_function()
-
+
if cython_function.is_initmodule_function:
self.cy.globals.invoke(args, from_tty)
return
if self.is_initialized(self.get_cython_function(), cyvar.name):
value = gdb.parse_and_eval(cyvar.cname)
if not value.is_optimized_out:
- self.print_gdb_value(cyvar.name, value,
+ self.print_gdb_value(cyvar.name, value,
max_name_length, '')
"""
List the globals from the current Cython module.
"""
-
+
name = 'cy globals'
command_class = gdb.COMMAND_STACK
completer_class = gdb.COMPLETE_NONE
-
+
@dispatch_on_frame(c_command='info variables', python_command='py-globals')
def invoke(self, args, from_tty):
global_python_dict = self.get_cython_globals_dict()
module_globals = self.get_cython_function().module.globals
-
+
max_globals_len = 0
max_globals_dict_len = 0
if module_globals:
max_globals_len = len(max(module_globals, key=len))
if global_python_dict:
max_globals_dict_len = len(max(global_python_dict))
-
+
max_name_length = max(max_globals_len, max_globals_dict_len)
-
+
seen = set()
print 'Python globals:'
for k, v in sorted(global_python_dict.iteritems(), key=sortkey):
v = v.get_truncated_repr(libpython.MAX_OUTPUT_LEN)
seen.add(k)
print ' %-*s = %s' % (max_name_length, k, v)
-
+
print 'C globals:'
for name, cyvar in sorted(module_globals.iteritems(), key=sortkey):
if name not in seen:
"""
Execute Python code in the nearest Python or Cython frame.
"""
-
+
name = '-cy-exec'
command_class = gdb.COMMAND_STACK
completer_class = gdb.COMPLETE_NONE
-
+
def _fill_locals_dict(self, executor, local_dict_pointer):
"Fill a remotely allocated dict with values from the Cython C stack"
cython_func = self.get_cython_function()
current_lineno = self.get_cython_lineno()
-
+
for name, cyvar in cython_func.locals.iteritems():
- if (cyvar.type == PythonObject and
+ if (cyvar.type == PythonObject and
self.is_initialized(cython_func, name)):
-
+
try:
val = gdb.parse_and_eval(cyvar.cname)
except RuntimeError:
else:
if val.is_optimized_out:
continue
-
+
pystringp = executor.alloc_pystring(name)
code = '''
(PyObject *) PyDict_SetItem(
finally:
# PyDict_SetItem doesn't steal our reference
executor.decref(pystringp)
-
+
def _find_first_cython_or_python_frame(self):
frame = gdb.selected_frame()
while frame:
- if (self.is_cython_function(frame) or
+ if (self.is_cython_function(frame) or
self.is_python_function(frame)):
return frame
-
+
frame = frame.older()
-
+
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()
-
+
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
global_dict = gdb.parse_and_eval(
'(PyObject *) PyModule_GetDict(__pyx_m)')
local_dict = gdb.parse_and_eval('(PyObject *) PyDict_New()')
-
+
cython_function = self.get_cython_function()
-
+
try:
- self._fill_locals_dict(executor,
+ self._fill_locals_dict(executor,
libpython.pointervalue(local_dict))
executor.evalcode(expr, input_type, global_dict, local_dict)
finally:
"""
Get the C name of a Cython variable in the current context.
Examples:
-
+
print $cy_cname("function")
print $cy_cname("Class.method")
print $cy_cname("module.function")
"""
-
+
@require_cython_frame
@gdb_function_value_to_unicode
def invoke(self, cyname, frame=None):
frame = frame or gdb.selected_frame()
cname = None
-
+
if self.is_cython_function(frame):
cython_function = self.get_cython_function(frame)
if cyname in cython_function.locals:
qname = '%s.%s' % (cython_function.module.name, cyname)
if qname in cython_function.module.functions:
cname = cython_function.module.functions[qname].cname
-
+
if not cname:
cname = self.cy.functions_by_qualified_name.get(cyname)
-
+
if not cname:
raise gdb.GdbError('No such Cython variable: %s' % cyname)
-
+
return cname
"""
Get the value of a Cython variable.
"""
-
+
@require_cython_frame
@gdb_function_value_to_unicode
def invoke(self, cyname, frame=None):
"""
Get the current Cython line.
"""
-
+
@require_cython_frame
def invoke(self):
return self.get_cython_lineno()
define cy step
cy -step
end
-
+
define cy next
cy -next
end
-
+
document cy step
%s
end
-
+
document cy next
%s
end
""") % (CyStep.__doc__, CyNext.__doc__))
-register_defines()
\ No newline at end of file
+register_defines()
all_pretty_typenames = set()
class PrettyPrinterTrackerMeta(type):
-
+
def __init__(self, name, bases, dict):
super(PrettyPrinterTrackerMeta, self).__init__(name, bases, dict)
all_pretty_typenames.add(self._typename)
Note that at every stage the underlying pointer could be NULL, point
to corrupt data, etc; this is the debugger, after all.
"""
-
+
__metaclass__ = PrettyPrinterTrackerMeta
-
+
_typename = 'PyObject'
-
+
def __init__(self, gdbval, cast_to=None):
if cast_to:
self._gdbval = gdbval.cast(cast_to)
#print 'tp_flags = 0x%08x' % tp_flags
#print 'tp_name = %r' % tp_name
-
+
name_map = {'bool': PyBoolObjectPtr,
'classobj': PyClassObjectPtr,
'instance': PyInstanceObjectPtr,
}
if tp_name in name_map:
return name_map[tp_name]
-
+
if tp_flags & (Py_TPFLAGS_HEAPTYPE|Py_TPFLAGS_TYPE_SUBCLASS):
return PyTypeObjectPtr
class PyTypeObjectPtr(PyObjectPtr):
_typename = 'PyTypeObject'
-
+
def get_attr_dict(self):
'''
Get the PyDictObject ptr representing the attribute dictionary
out.write('<...>')
return
visited.add(self.as_address())
-
+
try:
tp_name = self.field('tp_name').string()
except RuntimeError:
tp_name = 'unknown'
-
- out.write('<type %s at remote 0x%x>' % (tp_name,
+
+ out.write('<type %s at remote 0x%x>' % (tp_name,
self.as_address()))
# pyop_attrdict = self.get_attr_dict()
# _write_instance_repr(out, visited,
within the process being debugged.
"""
_typename = 'PyBaseExceptionObject'
-
+
def proxyval(self, visited):
# Guard against infinite loops:
if self.as_address() in visited:
if not pyop_value.is_null():
pyop_key = PyObjectPtr.from_pyobject_ptr(ep['me_key'])
yield (pyop_key, pyop_value)
-
+
def proxyval(self, visited):
# Guard against infinite loops:
if self.as_address() in visited:
class PyInstanceObjectPtr(PyObjectPtr):
_typename = 'PyInstanceObject'
-
+
def proxyval(self, visited):
# Guard against infinite loops:
if self.as_address() in visited:
class PyListObjectPtr(PyObjectPtr):
_typename = 'PyListObject'
-
+
def __getitem__(self, i):
# Get the gdb.Value for the (PyObject*) with the given index:
field_ob_item = self.field('ob_item')
class PyLongObjectPtr(PyObjectPtr):
_typename = 'PyLongObject'
-
+
def proxyval(self, visited):
'''
Python's Include/longobjrep.h has this declaration:
where SHIFT can be either:
#define PyLong_SHIFT 30
#define PyLong_SHIFT 15
- '''
+ '''
ob_size = long(self.field('ob_size'))
if ob_size == 0:
return 0L
<bool> instances (Py_True/Py_False) within the process being debugged.
"""
_typename = 'PyBoolObject'
-
+
def proxyval(self, visited):
castto = gdb.lookup_type('PyLongObject').pointer()
self._gdbval = self._gdbval.cast(castto)
class PyBytesObjectPtr(PyObjectPtr):
_typename = 'PyBytesObject'
-
+
def __str__(self):
field_ob_size = self.field('ob_size')
field_ob_sval = self.field('ob_sval')
- return ''.join(struct.pack('b', field_ob_sval[i])
+ return ''.join(struct.pack('b', field_ob_sval[i])
for i in safe_range(field_ob_size))
def proxyval(self, visited):
quote = "'"
if "'" in proxy and not '"' in proxy:
quote = '"'
-
+
if py3:
out.write('b')
else:
# Python 2, write the 'u'
out.write('u')
-
+
if "'" in proxy and '"' not in proxy:
quote = '"'
else:
namespace = self.get_namespace(pyop_frame)
namespace = [(name.proxyval(set()), val) for name, val in namespace]
-
+
if namespace:
name, val = max(namespace, key=lambda (name, val): len(name))
max_name_length = len(name)
-
+
for name, pyop_value in namespace:
value = pyop_value.get_truncated_repr(MAX_OUTPUT_LEN)
print ('%-*s = %s' % (max_name_length, name, value))
class PyGlobals(PyLocals):
'List all the globals in the currently select Python frame'
-
+
def get_namespace(self, pyop_frame):
return pyop_frame.iter_globals()
class PyNameEquals(gdb.Function):
-
+
def _get_pycurframe_attr(self, attr):
frame = Frame(gdb.selected_frame())
if frame.is_evalframeex():
warnings.warn("Use a Python debug build, Python breakpoints "
"won't work otherwise.")
return None
-
+
return getattr(pyframe, attr).proxyval(set())
-
+
return None
-
+
def invoke(self, funcname):
attr = self._get_pycurframe_attr('co_name')
return attr is not None and attr == funcname.string()
class PyModEquals(PyNameEquals):
-
+
def invoke(self, modname):
attr = self._get_pycurframe_attr('co_filename')
if attr is not None:
class PyBreak(gdb.Command):
"""
Set a Python breakpoint. Examples:
-
+
Break on any function or method named 'func' in module 'modname'
-
- py-break modname.func
-
+
+ py-break modname.func
+
Break on any function or method named 'func'
-
+
py-break func
"""
-
+
def invoke(self, funcname, from_tty):
if '.' in funcname:
modname, dot, funcname = funcname.rpartition('.')
- cond = '$pyname_equals("%s") && $pymod_equals("%s")' % (funcname,
+ cond = '$pyname_equals("%s") && $pymod_equals("%s")' % (funcname,
modname)
else:
cond = '$pyname_equals("%s")' % funcname
"""
State that helps to provide a reentrant gdb.execute() function.
"""
-
+
def __init__(self):
self.fd, self.filename = tempfile.mkstemp()
self.file = os.fdopen(self.fd, 'r+')
_execute("set logging file %s" % self.filename)
self.file_position_stack = []
-
+
atexit.register(os.close, self.fd)
atexit.register(os.remove, self.filename)
-
+
def __enter__(self):
if not self.file_position_stack:
_execute("set logging redirect on")
_execute("set logging on")
_execute("set pagination off")
-
+
self.file_position_stack.append(os.fstat(self.fd).st_size)
return self
-
+
def getoutput(self):
gdb.flush()
self.file.seek(self.file_position_stack[-1])
result = self.file.read()
return result
-
+
def __exit__(self, exc_type, exc_val, tb):
startpos = self.file_position_stack.pop()
self.file.seek(startpos)
def execute(command, from_tty=False, to_string=False):
"""
Replace gdb.execute() with this function and have it accept a 'to_string'
- argument (new in 7.2). Have it properly capture stderr also. Ensure
+ argument (new in 7.2). Have it properly capture stderr also. Ensure
reentrancy.
"""
if to_string:
# Woooh, another bug in gdb! Is there an end in sight?
# http://sourceware.org/bugzilla/show_bug.cgi?id=12212
return gdb.inferiors()[0]
-
+
selected_thread = gdb.selected_thread()
-
+
for inferior in gdb.inferiors():
for thread in inferior.threads():
if thread == selected_thread:
"""
Source a gdb script with script_contents passed as a string. This is useful
to provide defines for py-step and py-next to make them repeatable (this is
- not possible with gdb.execute()). See
+ not possible with gdb.execute()). See
http://sourceware.org/bugzilla/show_bug.cgi?id=12216
"""
fd, filename = tempfile.mkstemp()
define py-step
-py-step
end
-
+
define py-next
-py-next
end
-
+
document py-step
%s
end
-
+
document py-next
%s
end
""") % (PyStep.__doc__, PyNext.__doc__))
-
+
def stackdepth(frame):
"Tells the stackdepth of a gdb frame."
while frame:
frame = frame.older()
depth += 1
-
+
return depth
class ExecutionControlCommandBase(gdb.Command):
"""
Superclass for language specific execution control. Language specific
- features should be implemented by lang_info using the LanguageInfo
+ features should be implemented by lang_info using the LanguageInfo
interface. 'name' is the name of the command.
"""
-
+
def __init__(self, name, lang_info):
super(ExecutionControlCommandBase, self).__init__(
name, gdb.COMMAND_RUNNING, gdb.COMPLETE_NONE)
all_locations = itertools.chain(
self.lang_info.static_break_functions(),
self.lang_info.runtime_break_functions())
-
+
for location in all_locations:
result = gdb.execute('break %s' % location, to_string=True)
yield re.search(r'Breakpoint (\d+)', result).group(1)
-
+
def delete_breakpoints(self, breakpoint_list):
for bp in breakpoint_list:
gdb.execute("delete %s" % bp)
-
+
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
regexes = [
(r'^Program received signal .*', reflags|re.DOTALL),
(r'.*[Ww]arning.*', 0),
(r'^Program exited .*', reflags),
]
-
+
for regex, flags in regexes:
match = re.search(regex, result, flags)
if match:
output.append(match.group(0))
-
+
return '\n'.join(output)
-
+
def stopped(self):
return get_selected_inferior().pid == 0
-
+
def finish_executing(self, result):
"""
After doing some kind of code running in the inferior, print the line
in as the `result` argument).
"""
result = self.filter_output(result)
-
+
if self.stopped():
print result.strip()
else:
print self.lang_info.get_source_line(frame) or result
else:
print result
-
+
def _finish(self):
"""
- Execute until the function returns (or until something else makes it
+ Execute until the function returns (or until something else makes it
stop)
"""
if gdb.selected_frame().older() is not None:
else:
# outermost frame, continue
return gdb.execute('cont', to_string=True)
-
+
def _finish_frame(self):
"""
Execute until the function returns to a relevant caller.
"""
while True:
result = self._finish()
-
+
try:
frame = gdb.selected_frame()
except RuntimeError:
break
-
+
hitbp = re.search(r'Breakpoint (\d+)', result)
is_relevant = self.lang_info.is_relevant_function(frame)
if hitbp or is_relevant or self.stopped():
break
-
+
return result
-
+
def finish(self, *args):
"Implements the finish command."
result = self._finish_frame()
self.finish_executing(result)
-
+
def step(self, stepinto, stepover_command='next'):
"""
Do a single step or step-over. Returns the result of the last gdb
command that made execution stop.
-
- This implementation, for stepping, sets (conditional) breakpoints for
+
+ This implementation, for stepping, sets (conditional) breakpoints for
all functions that are deemed relevant. It then does a step over until
either something halts execution, or until the next line is reached.
-
- If, however, stepover_command is given, it should be a string gdb
- command that continues execution in some way. The idea is that the
- caller has set a (conditional) breakpoint or watchpoint that can work
+
+ If, however, stepover_command is given, it should be a string gdb
+ command that continues execution in some way. The idea is that the
+ caller has set a (conditional) breakpoint or watchpoint that can work
more efficiently than the step-over loop. For Python this means setting
- a watchpoint for f->f_lasti, which means we can then subsequently
+ a watchpoint for f->f_lasti, which means we can then subsequently
"finish" frames.
- We want f->f_lasti instead of f->f_lineno, because the latter only
- works properly with local trace functions, see
+ We want f->f_lasti instead of f->f_lineno, because the latter only
+ works properly with local trace functions, see
PyFrameObjectPtr.current_line_num and PyFrameObjectPtr.addr2line.
"""
if stepinto:
breakpoint_list = list(self.install_breakpoints())
-
+
beginframe = gdb.selected_frame()
-
+
if self.lang_info.is_relevant_function(beginframe):
# If we start in a relevant frame, initialize stuff properly. If
- # we don't start in a relevant frame, the loop will halt
- # immediately. So don't call self.lang_info.lineno() as it may
+ # we don't start in a relevant frame, the loop will halt
+ # immediately. So don't call self.lang_info.lineno() as it may
# raise for irrelevant frames.
beginline = self.lang_info.lineno(beginframe)
-
+
if not stepinto:
depth = stackdepth(beginframe)
-
+
newframe = beginframe
-
+
while True:
if self.lang_info.is_relevant_function(newframe):
result = gdb.execute(stepover_command, to_string=True)
else:
result = self._finish_frame()
-
+
if self.stopped():
break
-
+
newframe = gdb.selected_frame()
is_relevant_function = self.lang_info.is_relevant_function(newframe)
try:
framename = newframe.name()
except RuntimeError:
framename = None
-
+
m = re.search(r'Breakpoint (\d+)', result)
if m:
if is_relevant_function and m.group(1) in breakpoint_list:
# that the function, in case hit by a runtime breakpoint,
# is in the right context
break
-
+
if newframe != beginframe:
# new function
-
+
if not stepinto:
# see if we returned to the caller
newdepth = stackdepth(newframe)
- is_relevant_function = (newdepth < depth and
+ is_relevant_function = (newdepth < depth and
is_relevant_function)
-
+
if is_relevant_function:
break
else:
lineno = self.lang_info.lineno(newframe)
if lineno and lineno != beginline:
break
-
+
if stepinto:
self.delete_breakpoints(breakpoint_list)
self.finish_executing(result)
-
+
def run(self, *args):
self.finish_executing(gdb.execute('run', to_string=True))
-
+
def cont(self, *args):
self.finish_executing(gdb.execute('cont', to_string=True))
"""
This class defines the interface that ExecutionControlCommandBase needs to
provide language-specific execution control.
-
+
Classes that implement this interface should implement:
-
+
lineno(frame)
Tells the current line number (only called for a relevant frame).
If lineno is a false value it is not checked for a difference.
-
+
is_relevant_function(frame)
tells whether we care about frame 'frame'
-
+
get_source_line(frame)
get the line of source code for the current line (only called for a
- relevant frame). If the source code cannot be retrieved this
+ relevant frame). If the source code cannot be retrieved this
function should return None
-
+
exc_info(frame) -- optional
tells whether an exception was raised, if so, it should return a
string representation of the exception value, None otherwise.
-
- static_break_functions()
- returns an iterable of function names that are considered relevant
- and should halt step-into execution. This is needed to provide a
+
+ static_break_functions()
+ returns an iterable of function names that are considered relevant
+ and should halt step-into execution. This is needed to provide a
performing step-into
-
+
runtime_break_functions() -- optional
- list of functions that we should break into depending on the
+ list of functions that we should break into depending on the
context
"""
def runtime_break_functions(self):
"""
- Implement this if the list of step-into functions depends on the
+ Implement this if the list of step-into functions depends on the
context.
"""
return ()
class PythonInfo(LanguageInfo):
-
+
def pyframe(self, frame):
pyframe = Frame(frame).get_pyop()
if pyframe:
raise gdb.RuntimeError(
"Unable to find the Python frame, run your code with a debug "
"build (configure with --with-pydebug or compile with -g).")
-
+
def lineno(self, frame):
return self.pyframe(frame).current_line_num()
-
+
def is_relevant_function(self, frame):
return Frame(frame).is_evalframeex()
def get_source_line(self, frame):
try:
pyframe = self.pyframe(frame)
- return '%4d %s' % (pyframe.current_line_num(),
+ return '%4d %s' % (pyframe.current_line_num(),
pyframe.current_line().rstrip())
except IOError, e:
return None
-
+
def exc_info(self, frame):
try:
tstate = frame.read_var('tstate').dereference()
inf_type = tstate['curexc_type']
inf_value = tstate['curexc_value']
if inf_type:
- return 'An exception was raised: %s(%s)' % (inf_type,
+ return 'An exception was raised: %s(%s)' % (inf_type,
inf_value)
except (ValueError, RuntimeError), e:
# Could not read the variable tstate or it's memory, it's ok
pass
-
+
def static_break_functions(self):
yield 'PyEval_EvalFrameEx'
class PythonStepperMixin(object):
"""
- Make this a mixin so CyStep can also inherit from this and use a
+ Make this a mixin so CyStep can also inherit from this and use a
CythonCodeStepper at the same time.
"""
-
+
def python_step(self, stepinto):
frame = gdb.selected_frame()
framewrapper = Frame(frame)
-
+
output = gdb.execute('watch f->f_lasti', to_string=True)
watchpoint = int(re.search(r'[Ww]atchpoint (\d+):', output).group(1))
self.step(stepinto=stepinto, stepover_command='finish')
class PyStep(ExecutionControlCommandBase, PythonStepperMixin):
"Step through Python code."
-
+
stepinto = True
-
+
def invoke(self, args, from_tty):
self.python_step(stepinto=self.stepinto)
-
+
class PyNext(PyStep):
"Step-over Python code."
-
+
stepinto = False
-
+
class PyFinish(ExecutionControlCommandBase):
"Execute until function returns to a caller."
-
+
invoke = ExecutionControlCommandBase.finish
class PyRun(ExecutionControlCommandBase):
"Run the program."
-
+
invoke = ExecutionControlCommandBase.run
class PyCont(ExecutionControlCommandBase):
-
+
invoke = ExecutionControlCommandBase.cont
def _pointervalue(gdbval):
"""
- Return the value of the pionter as a Python int.
-
+ Return the value of the pionter as a Python int.
+
gdbval.type must be a pointer type
"""
# don't convert with int() as it will raise a RuntimeError
# work around yet another bug in gdb where you get random behaviour
# and tracebacks
pass
-
+
return pointer
def get_inferior_unicode_postfix():
try:
gdb.parse_and_eval('PyUnicode_FromEncodedObject')
except RuntimeError:
- try:
+ try:
gdb.parse_and_eval('PyUnicodeUCS2_FromEncodedObject')
except RuntimeError:
return 'UCS4'
return 'UCS2'
else:
return ''
-
+
class PythonCodeExecutor(object):
-
+
Py_single_input = 256
Py_file_input = 257
Py_eval_input = 258
-
+
def malloc(self, size):
chunk = (gdb.parse_and_eval("(void *) malloc((size_t) %d)" % size))
-
+
pointer = pointervalue(chunk)
if pointer == 0:
raise gdb.GdbError("No memory could be allocated in the inferior.")
-
+
return pointer
-
+
def alloc_string(self, string):
pointer = self.malloc(len(string))
get_selected_inferior().write_memory(pointer, string)
-
+
return pointer
-
+
def alloc_pystring(self, string):
stringp = self.alloc_string(string)
PyString_FromStringAndSize = 'PyString_FromStringAndSize'
-
+
try:
gdb.parse_and_eval(PyString_FromStringAndSize)
except RuntimeError:
# Python 3
- PyString_FromStringAndSize = ('PyUnicode%s_FromStringAndSize' %
+ PyString_FromStringAndSize = ('PyUnicode%s_FromStringAndSize' %
(get_inferior_unicode_postfix,))
try:
PyString_FromStringAndSize, stringp, len(string)))
finally:
self.free(stringp)
-
+
pointer = pointervalue(result)
if pointer == 0:
raise gdb.GdbError("Unable to allocate Python string in "
"the inferior.")
-
+
return pointer
-
+
def free(self, pointer):
gdb.parse_and_eval("free((void *) %d)" % pointer)
-
+
def incref(self, pointer):
"Increment the reference count of a Python object in the inferior."
gdb.parse_and_eval('Py_IncRef((PyObject *) %d)' % pointer)
-
+
def decref(self, pointer):
"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
# to check for NULL. This should also decref all our allocated
# Python strings.
gdb.parse_and_eval('Py_DecRef((PyObject *) %d)' % pointer)
-
+
def evalcode(self, code, input_type, global_dict=None, local_dict=None):
"""
Evaluate python code `code` given as a string in the inferior and
return the result as a gdb.Value. Returns a new reference in the
inferior.
-
+
Of course, executing any code in the inferior may be dangerous and may
leave the debuggee in an unsafe state or terminate it alltogether.
"""
if '\0' in code:
raise gdb.GdbError("String contains NUL byte.")
-
+
code += '\0'
-
+
pointer = self.alloc_string(code)
-
+
globalsp = pointervalue(global_dict)
localsp = pointervalue(local_dict)
-
+
if globalsp == 0 or localsp == 0:
raise gdb.GdbError("Unable to obtain or create locals or globals.")
-
+
code = """
PyRun_String(
(char *) %(code)d,
(int) %(start)d,
(PyObject *) %(globals)s,
(PyObject *) %(locals)d)
- """ % dict(code=pointer, start=input_type,
+ """ % dict(code=pointer, start=input_type,
globals=globalsp, locals=localsp)
-
+
with FetchAndRestoreError():
try:
self.decref(gdb.parse_and_eval(code))
def __init__(self):
self.sizeof_PyObjectPtr = gdb.lookup_type('PyObject').pointer().sizeof
self.pointer = self.malloc(self.sizeof_PyObjectPtr * 3)
-
+
type = self.pointer
value = self.pointer + self.sizeof_PyObjectPtr
traceback = self.pointer + self.sizeof_PyObjectPtr * 2
-
+
self.errstate = type, value, traceback
-
+
def __enter__(self):
gdb.parse_and_eval("PyErr_Fetch(%d, %d, %d)" % self.errstate)
-
+
def __exit__(self, *args):
if gdb.parse_and_eval("(int) PyErr_Occurred()"):
gdb.parse_and_eval("PyErr_Print()")
-
+
pyerr_restore = ("PyErr_Restore("
"(PyObject *) *%d,"
"(PyObject *) *%d,"
"(PyObject *) *%d)")
-
+
try:
gdb.parse_and_eval(pyerr_restore % self.errstate)
finally:
super(FixGdbCommand, self).__init__(command, gdb.COMMAND_DATA,
gdb.COMPLETE_NONE)
self.actual_command = actual_command
-
+
def fix_gdb(self):
"""
- So, you must be wondering what the story is this time! Yeeees, indeed,
+ So, you must be wondering what the story is this time! Yeeees, indeed,
I have quite the story for you! It seems that invoking either 'cy exec'
- and 'py-exec' work perfectly fine, but after this gdb's python API is
- entirely broken. Some unset exception value is still set?
+ and 'py-exec' work perfectly fine, but after this gdb's python API is
+ entirely broken. Some unset exception value is still set?
sys.exc_clear() didn't help. A demonstration:
-
+
(gdb) cy exec 'hello'
'hello'
(gdb) python gdb.execute('cont')
Error while executing Python code.
(gdb) python gdb.execute('cont')
[15148 refs]
-
+
Program exited normally.
"""
- warnings.filterwarnings('ignore', r'.*', RuntimeWarning,
+ warnings.filterwarnings('ignore', r'.*', RuntimeWarning,
re.escape(__name__))
try:
long(gdb.parse_and_eval("(void *) 0")) == 0
except RuntimeError:
pass
# warnings.resetwarnings()
-
+
def invoke(self, args, from_tty):
self.fix_gdb()
try:
class PyExec(gdb.Command):
-
+
def readcode(self, expr):
if expr:
return expr, PythonCodeExecutor.Py_single_input
else:
if line.rstrip() == 'end':
break
-
+
lines.append(line)
-
+
return '\n'.join(lines), Py_file_input
-
+
def invoke(self, expr, from_tty):
expr, input_type = self.readcode(expr)
-
+
executor = PythonCodeExecutor()
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).")
-
+
executor.evalcode(expr, input_type, global_dict, local_dict)
py_exec = FixGdbCommand('py-exec', '-py-exec')
_py_exec = PyExec("-py-exec", gdb.COMMAND_DATA, gdb.COMPLETE_NONE)
else:
- warnings.warn("Use gdb 7.2 or higher to use the py-exec command.")
\ No newline at end of file
+ warnings.warn("Use gdb 7.2 or higher to use the py-exec command.")
def build_extension(self, ext):
if ext.language == 'c++':
try:
- try: # Py2.7+ & Py3.2+
+ try: # Py2.7+ & Py3.2+
compiler_obj = self.compiler_obj
except AttributeError:
compiler_obj = self.compiler
source = self.find_module_source_file(
os.path.join(test_directory, module + '.pyx'))
target = os.path.join(targetdir, self.build_target_filename(module))
-
+
if extra_compile_options is None:
extra_compile_options = {}
-
+
try:
CompilationOptions
except NameError:
from Cython.Compiler.Main import CompilationOptions
from Cython.Compiler.Main import compile as cython_compile
from Cython.Compiler.Main import default_options
-
+
options = CompilationOptions(
default_options,
include_path = include_dirs,
cython_compile(source, options=options,
full_module_name=module)
- def run_distutils(self, test_directory, module, workdir, incdir,
+ def run_distutils(self, test_directory, module, workdir, incdir,
extra_extension_args=None):
cwd = os.getcwd()
os.chdir(workdir)
if match(module):
ext_include_dirs += get_additional_include_dirs()
self.copy_related_files(test_directory, workdir, module)
-
+
if extra_extension_args is None:
extra_extension_args = {}
-
+
extension = Extension(
module,
sources = self.find_source_files(workdir, module),
result.addSkip(self, 'ok')
# Someone wrapped this in a:
-# 'try: import gdb; ... except: include_debugger = False' thing, but don't do
+# 'try: import gdb; ... except: include_debugger = False' thing, but don't do
# this, it doesn't work as gdb is a builtin module in GDB. The tests themselves
-# are doing the skipping. If there's a problem with the tests, please file an
+# are doing the skipping. If there's a problem with the tests, please file an
# issue.
include_debugger = sys.version_info[:2] > (2, 5)
return dirname == "Tests"
loader = unittest.TestLoader()
-
+
if include_debugger:
skipped_dirs = []
else:
return dirname not in ("Mac", "Distutils", "Plex")
def file_matches(filename):
filename, ext = os.path.splitext(filename)
- blacklist = ['libcython', 'libpython', 'test_libcython_in_gdb',
+ blacklist = ['libcython', 'libpython', 'test_libcython_in_gdb',
'TestLibCython']
return (ext == '.py' and not
'~' in filename and not
directory structure and its header gives a list of commands to run.
"""
cython_root = os.path.dirname(os.path.abspath(__file__))
-
+
def __init__(self, treefile, workdir, cleanup_workdir=True):
self.treefile = treefile
self.workdir = os.path.join(workdir, os.path.splitext(treefile)[0])
if self.cleanup_workdir:
shutil.rmtree(self.workdir)
os.chdir(self.old_dir)
-
+
def runTest(self):
commands = (self.commands
.replace("CYTHON", "PYTHON %s" % os.path.join(self.cython_root, 'cython.py'))
# TODO: Windows support.
class EmbedTest(unittest.TestCase):
-
+
working_dir = "Demos/embed"
-
+
def setUp(self):
self.old_dir = os.getcwd()
os.chdir(self.working_dir)
os.system(
"make PYTHON='%s' clean > /dev/null" % sys.executable)
-
+
def tearDown(self):
try:
os.system(
except:
pass
os.chdir(self.old_dir)
-
+
def test_embed(self):
from distutils import sysconfig
libname = sysconfig.get_config_var('LIBRARY')
self.excludes[line.split()[0]] = True
finally:
f.close()
-
+
def __call__(self, testname):
return testname in self.excludes or testname.split('.')[-1] in self.excludes
help="do not run the file based tests")
parser.add_option("--no-pyregr", dest="pyregr",
action="store_false", default=True,
- help="do not run the regression tests of CPython in tests/pyregr/")
+ help="do not run the regression tests of CPython in tests/pyregr/")
parser.add_option("--cython-only", dest="cython_only",
action="store_true", default=False,
help="only compile pyx to c, do not run C compiler or run the tests")
# Chech which external modules are not present and exclude tests
# which depends on them (by prefix)
- missing_dep_excluder = MissingDependencyExcluder(EXT_DEP_MODULES)
- version_dep_excluder = VersionDependencyExcluder(VER_DEP_MODULES)
+ missing_dep_excluder = MissingDependencyExcluder(EXT_DEP_MODULES)
+ version_dep_excluder = VersionDependencyExcluder(VER_DEP_MODULES)
exclude_selectors = [missing_dep_excluder, version_dep_excluder] # want to pring msg at exit
if options.exclude:
exclude_selectors += [ re.compile(r, re.I|re.U).search for r in options.exclude ]
-
+
if not test_bugs:
exclude_selectors += [ FileListExcluder("tests/bugs.txt") ]
-
+
if sys.platform in ['win32', 'cygwin'] and sys.version_info < (2,6):
exclude_selectors += [ lambda x: x == "run.specialfloat" ]
ignored_modules = ('Options', 'Version', 'DebugFlags', 'CmdLine')
modules = [ module for name, module in sys.modules.items()
if module is not None and
- name.startswith('Cython.Compiler.') and
+ name.startswith('Cython.Compiler.') and
name[len('Cython.Compiler.'):] not in ignored_modules ]
if options.coverage:
coverage.report(modules, show_missing=0)
'Cython' : [ p[7:] for p in pxd_include_patterns ],
}
-# This dict is used for passing extra arguments that are setuptools
+# This dict is used for passing extra arguments that are setuptools
# specific to setup
setuptools_extra_args = {}