merge from the gccxml-to-python branch
This commit is contained in:
commit
e39021291a
11 files changed
+776
-366
No files matched your search
+11
-6
@@ -362,6 +362,7 @@ class CppClass(object):
|
||||
self.name = name
|
||||
self.python_name = python_name
|
||||
self.mangled_name = None
|
||||
self.mangled_full_name = None
|
||||
self.template_parameters = template_parameters
|
||||
self.custom_template_class_name = custom_template_class_name
|
||||
self.is_singleton = is_singleton
|
||||
@@ -740,17 +741,18 @@ class CppClass(object):
|
||||
"make a name like::This look LikeThis"
|
||||
return ''.join([make_upper(mangle(s)) for s in name.split('::')])
|
||||
|
||||
flat_name = flatten(self.full_name)
|
||||
self.mangled_name = flatten(self.name)
|
||||
self.mangled_full_name = flatten(self.full_name)
|
||||
|
||||
if self.template_parameters:
|
||||
self.full_name += "< %s >" % (', '.join(self.template_parameters))
|
||||
flat_name += '__' + '_'.join([flatten(s) for s in self.template_parameters])
|
||||
self.mangled_name += '__' + '_'.join([flatten(s) for s in self.template_parameters])
|
||||
mangled_template_params = '__' + '_'.join([flatten(s) for s in self.template_parameters])
|
||||
self.mangled_name += mangled_template_params
|
||||
self.mangled_full_name += mangled_template_params
|
||||
|
||||
self._pystruct = "Py%s%s" % (prefix, flat_name)
|
||||
self.metaclass_name = "%sMeta" % flat_name
|
||||
self.pytypestruct = "Py%s%s_Type" % (prefix, flat_name)
|
||||
self._pystruct = "Py%s%s" % (prefix, self.mangled_full_name)
|
||||
self.metaclass_name = "%sMeta" % self.mangled_full_name
|
||||
self.pytypestruct = "Py%s%s_Type" % (prefix, self.mangled_full_name)
|
||||
|
||||
self.instance_attributes.cname = "%s__getsets" % self._pystruct
|
||||
self.static_attributes.cname = "%s__getsets" % self.metaclass_name
|
||||
@@ -766,6 +768,9 @@ class CppClass(object):
|
||||
def register_alias(self, alias):
|
||||
"""Re-register the class with another base name, in addition to any
|
||||
registrations that might have already been done."""
|
||||
|
||||
self.module.register_type(None, alias, self)
|
||||
|
||||
self.ThisClassParameter.CTYPES.append(alias)
|
||||
try:
|
||||
param_type_matcher.register(alias, self.ThisClassParameter)
|
||||
|
||||
+426
-112
@@ -7,7 +7,7 @@ import re
|
||||
from pygccxml import parser
|
||||
from pygccxml import declarations
|
||||
from module import Module
|
||||
from typehandlers.codesink import FileCodeSink
|
||||
from typehandlers.codesink import FileCodeSink, CodeSink, NullCodeSink
|
||||
import typehandlers.base
|
||||
from typehandlers.base import ReturnValue, Parameter, TypeLookupError, TypeConfigurationError, NotSupportedError
|
||||
from pygccxml.declarations.enumeration import enumeration_t
|
||||
@@ -25,6 +25,8 @@ import settings
|
||||
# destructor_t, constructor_t, member_function_t
|
||||
from pygccxml.declarations.variable import variable_t
|
||||
|
||||
ALWAYS_USE_PARAMETER_NEW = True
|
||||
|
||||
## ------------------------
|
||||
|
||||
class ErrorHandler(settings.ErrorHandler):
|
||||
@@ -64,35 +66,38 @@ def normalize_name(decl_string):
|
||||
else:
|
||||
return "%s< %s >" % (cls_name, ', '.join([normalize_name(name) for name in template_parameters]))
|
||||
|
||||
def normalize_class_name(class_name, module_namespace):
|
||||
if not class_name.startswith(module_namespace):
|
||||
class_name = module_namespace + class_name
|
||||
class_name = normalize_name(class_name)
|
||||
return class_name
|
||||
|
||||
|
||||
def _pygen_kwargs(kwargs):
|
||||
l = []
|
||||
for key, val in kwargs.iteritems():
|
||||
if isinstance(val, CppClass):
|
||||
l.append("%s=root_module[%r]" % (key, val.full_name))
|
||||
else:
|
||||
l.append("%s=%r" % (key, val))
|
||||
return l
|
||||
|
||||
|
||||
|
||||
class GccXmlTypeRegistry(object):
|
||||
def __init__(self):
|
||||
self.classes = {} # registered classes dictionary
|
||||
def __init__(self, root_module):
|
||||
"""
|
||||
@root_module: the root L{Module} object
|
||||
"""
|
||||
assert isinstance(root_module, Module)
|
||||
assert root_module.parent is None
|
||||
self.root_module = root_module
|
||||
self.ordered_classes = [] # registered classes list, by registration order
|
||||
self._root_ns_rx = re.compile(r"(^|\s)(::)")
|
||||
|
||||
def register_class(self, cpp_class, alias=None):
|
||||
def class_registered(self, cpp_class):
|
||||
assert isinstance(cpp_class, CppClass)
|
||||
full_name = normalize_name(cpp_class.full_name)
|
||||
#print >> sys.stderr, "******** registering class %s as %s" \
|
||||
# % (cpp_class.gccxml_definition, full_name)
|
||||
self.classes[full_name] = cpp_class
|
||||
self.ordered_classes.append(cpp_class)
|
||||
if alias is not None:
|
||||
alias = normalize_name(alias)
|
||||
self.classes[alias] = cpp_class
|
||||
cpp_class.register_alias(alias)
|
||||
#print >> sys.stderr, "******** registering class %s also as alias %s" \
|
||||
# % (cpp_class.gccxml_definition, alias)
|
||||
|
||||
def find_class(self, class_name, module_namespace):
|
||||
if not class_name.startswith(module_namespace):
|
||||
class_name = module_namespace + class_name
|
||||
class_name = normalize_name(class_name)
|
||||
#if '<' in class_name:
|
||||
# print >> sys.stderr, "******** looking for class %s" \
|
||||
# % (class_name)
|
||||
return self.classes[class_name]
|
||||
|
||||
def get_class_type_traits(self, type_info):
|
||||
assert isinstance(type_info, cpptypes.type_t)
|
||||
@@ -106,14 +111,14 @@ class GccXmlTypeRegistry(object):
|
||||
pointer_is_const = False
|
||||
if isinstance(base_type, cpptypes.declarated_t):
|
||||
class_name = normalize_name(base_type.decl_string)
|
||||
#if '<' in class_name:
|
||||
# print >> sys.stderr, "******** looking for class %s" \
|
||||
# % (class_name)
|
||||
try:
|
||||
cpp_class = self.classes[class_name]
|
||||
cpp_class = self.root_module[class_name]
|
||||
except KeyError:
|
||||
return (None, is_const, is_pointer, is_reference, pointer_is_const)
|
||||
|
||||
if not isinstance(cpp_class, CppClass):
|
||||
return (None, is_const, is_pointer, is_reference, pointer_is_const)
|
||||
|
||||
try:
|
||||
type_tmp = decomposed.pop()
|
||||
except IndexError:
|
||||
@@ -173,25 +178,56 @@ class GccXmlTypeRegistry(object):
|
||||
|
||||
if cpp_class is None:
|
||||
if isinstance(type_traits.remove_declarated(type_info), enumeration_t):
|
||||
return ReturnValue.new(normalize_name(type_info.decl_string), **kwargs)
|
||||
name = normalize_name(type_info.decl_string)
|
||||
else:
|
||||
return ReturnValue.new(self._fixed_std_type_name(type_info), **kwargs)
|
||||
name = self._fixed_std_type_name(type_info)
|
||||
retval = ReturnValue.new(name, **kwargs)
|
||||
retval._pygen_repr = ("ReturnValue.new(%s)"
|
||||
% (", ".join([repr(name)] + _pygen_kwargs(kwargs))))
|
||||
return retval
|
||||
|
||||
## class value
|
||||
if not is_pointer and not is_reference:
|
||||
return cpp_class.ThisClassReturn(type_info.decl_string)
|
||||
if is_pointer and not is_reference:
|
||||
if is_const:
|
||||
## a pointer to const object usually means caller_owns_return=False
|
||||
return cpp_class.ThisClassPtrReturn(type_info.decl_string, caller_owns_return=False,
|
||||
**kwargs)
|
||||
if ALWAYS_USE_PARAMETER_NEW:
|
||||
retval = ReturnValue.new(cpp_class.full_name, **kwargs)
|
||||
retval._pygen_repr = ("ReturnValue.new(%s)"
|
||||
% (", ".join([repr(cpp_class.full_name)] + _pygen_kwargs(kwargs))))
|
||||
else:
|
||||
## This will fail, "missing caller_owns_return
|
||||
## parameter", but the lack of a const does not always
|
||||
## imply caller_owns_return=True, so trying to guess
|
||||
## here is a Bad Idea™
|
||||
return cpp_class.ThisClassPtrReturn(type_info.decl_string, **kwargs)
|
||||
retval = cpp_class.ThisClassReturn(type_info.decl_string)
|
||||
retval._pygen_repr = ("root_module[%r].ThisClassReturn(%r)" % (cpp_class.full_name, type_info.decl_string))
|
||||
return retval
|
||||
|
||||
## pointer to class
|
||||
if is_pointer and not is_reference:
|
||||
if is_const and 'caller_owns_return' not in kwargs:
|
||||
## a pointer to const object "usually" means caller_owns_return=False
|
||||
## some guessing going on here, though..
|
||||
kwargs['caller_owns_return'] = False
|
||||
if ALWAYS_USE_PARAMETER_NEW:
|
||||
retval = ReturnValue.new(cpp_class.full_name+'*', **kwargs)
|
||||
retval._pygen_repr = ("ReturnValue.new(%s)"
|
||||
% (", ".join([repr(cpp_class.full_name + '*')] + _pygen_kwargs(kwargs))))
|
||||
else:
|
||||
retval = cpp_class.ThisClassPtrReturn(type_info.decl_string, **kwargs)
|
||||
retval._pygen_repr = ("root_module[%r].ThisClassPtrReturn(%s)"
|
||||
% (cpp_class.full_name,
|
||||
", ".join([repr(type_info.decl_string)] + _pygen_kwargs(kwargs))))
|
||||
|
||||
return retval
|
||||
|
||||
## reference of class
|
||||
if not is_pointer and is_reference:
|
||||
return cpp_class.ThisClassRefReturn(type_info.decl_string, **kwargs)
|
||||
if ALWAYS_USE_PARAMETER_NEW:
|
||||
retval = ReturnValue.new(cpp_class.full_name+'&', **kwargs)
|
||||
retval._pygen_repr = ("ReturnValue.new(%s)"
|
||||
% (", ".join([repr(cpp_class.full_name + '&')] + _pygen_kwargs(kwargs))))
|
||||
else:
|
||||
retval = cpp_class.ThisClassRefReturn(type_info.decl_string, **kwargs)
|
||||
retval._pygen_repr = ("root_module[%r].ThisClassRefReturn(%s)"
|
||||
% (cpp_class.full_name,
|
||||
", ".join([repr(type_info.decl_string)] + _pygen_kwargs(kwargs))))
|
||||
return retval
|
||||
|
||||
assert 0, "this line should not be reached"
|
||||
|
||||
def lookup_parameter(self, type_info, param_name, annotations={}, default_value=None):
|
||||
@@ -215,41 +251,78 @@ class GccXmlTypeRegistry(object):
|
||||
else:
|
||||
warnings.warn("invalid annotation name %r" % name)
|
||||
|
||||
cpp_class, is_const, is_pointer, is_reference, pointer_is_const = \
|
||||
cpp_class, is_const, is_pointer, is_reference, dummy_pointer_is_const = \
|
||||
self.get_class_type_traits(type_info)
|
||||
|
||||
if is_const:
|
||||
kwargs['is_const'] = True
|
||||
if default_value:
|
||||
kwargs['default_value'] = default_value
|
||||
|
||||
if cpp_class is None:
|
||||
if isinstance(type_traits.remove_declarated(type_info), enumeration_t):
|
||||
return Parameter.new(normalize_name(type_info.decl_string), param_name, **kwargs)
|
||||
type_name = normalize_name(type_info.decl_string)
|
||||
else:
|
||||
return Parameter.new(self._fixed_std_type_name(type_info), param_name, **kwargs)
|
||||
type_name = self._fixed_std_type_name(type_info)
|
||||
|
||||
retval = Parameter.new(type_name, param_name, **kwargs)
|
||||
retval._pygen_repr = ("Parameter.new(%s)"
|
||||
% (", ".join([repr(type_name), repr(param_name)]
|
||||
+ _pygen_kwargs(kwargs))))
|
||||
return retval
|
||||
|
||||
assert isinstance(cpp_class, CppClass)#, cpp_class.full_name
|
||||
|
||||
## class value
|
||||
if not is_pointer and not is_reference:
|
||||
return cpp_class.ThisClassParameter(type_info.decl_string, param_name, **kwargs)
|
||||
if ALWAYS_USE_PARAMETER_NEW:
|
||||
retval = Parameter.new(cpp_class.full_name, param_name, **kwargs)
|
||||
retval._pygen_repr = ("Parameter.new(%s)"
|
||||
% (", ".join([repr(cpp_class.full_name), repr(param_name)] + _pygen_kwargs(kwargs))))
|
||||
else:
|
||||
retval = cpp_class.ThisClassParameter(type_info.decl_string, param_name, **kwargs)
|
||||
retval._pygen_repr = ("root_module[%r].ThisClassParameter(%s)"
|
||||
% (cpp_class.full_name,
|
||||
", ".join([repr(type_info.decl_string), repr(param_name)]
|
||||
+ _pygen_kwargs(kwargs))))
|
||||
return retval
|
||||
|
||||
## pointer to class
|
||||
if is_pointer and not is_reference:
|
||||
if is_const:
|
||||
## a pointer to const object usually means transfer_ownership=False
|
||||
kwargs.setdefault('transfer_ownership', False)
|
||||
return cpp_class.ThisClassPtrParameter(type_info.decl_string, param_name,
|
||||
**kwargs)
|
||||
if ALWAYS_USE_PARAMETER_NEW:
|
||||
retval = Parameter.new(cpp_class.full_name+'*', param_name, **kwargs)
|
||||
retval._pygen_repr = ("Parameter.new(%s)"
|
||||
% (", ".join([repr(cpp_class.full_name+'*'), repr(param_name)] + _pygen_kwargs(kwargs))))
|
||||
else:
|
||||
## This will fail, "missing param_name
|
||||
## parameter", but the lack of a const does not always
|
||||
## imply transfer_ownership=True, so trying to guess
|
||||
## here is a Bad Idea™
|
||||
return cpp_class.ThisClassPtrParameter(type_info.decl_string, param_name, **kwargs)
|
||||
retval = cpp_class.ThisClassPtrParameter(type_info.decl_string, param_name, **kwargs)
|
||||
retval._pygen_repr = ("root_module[%r].ThisClassPtrParameter(%s)"
|
||||
% (cpp_class.full_name,
|
||||
", ".join([repr(type_info.decl_string), repr(param_name)]
|
||||
+ _pygen_kwargs(kwargs))))
|
||||
return retval
|
||||
|
||||
## reference to class
|
||||
if not is_pointer and is_reference:
|
||||
try:
|
||||
return cpp_class.ThisClassRefParameter(type_info.decl_string, param_name, **kwargs)
|
||||
if ALWAYS_USE_PARAMETER_NEW:
|
||||
retval = Parameter.new(cpp_class.full_name+'&', param_name, **kwargs)
|
||||
retval._pygen_repr = ("Parameter.new(%s)"
|
||||
% (", ".join([repr(cpp_class.full_name+'&'), repr(param_name)] + _pygen_kwargs(kwargs))))
|
||||
else:
|
||||
retval = cpp_class.ThisClassRefParameter(type_info.decl_string, param_name, **kwargs)
|
||||
retval._pygen_repr = ("root_module[%r].ThisClassRefParameter(%s)"
|
||||
% (cpp_class.full_name,
|
||||
", ".join([repr(type_info.decl_string), repr(param_name)]
|
||||
+ _pygen_kwargs(kwargs))))
|
||||
return retval
|
||||
except TypeError:
|
||||
print >> sys.stderr, "** Error in %s class ref parameter" % cpp_class.full_name
|
||||
raise
|
||||
assert 0, "this line should not be reached"
|
||||
|
||||
type_registry = GccXmlTypeRegistry()
|
||||
|
||||
|
||||
class AnnotationsScanner(object):
|
||||
def __init__(self):
|
||||
@@ -371,6 +444,8 @@ class ModuleParser(object):
|
||||
self._types_scanned = False
|
||||
self._pre_scan_hooks = []
|
||||
self._post_scan_hooks = []
|
||||
self.pygen_sink = NullCodeSink()
|
||||
self.type_registry = None
|
||||
|
||||
def add_pre_scan_hook(self, hook):
|
||||
"""
|
||||
@@ -431,7 +506,7 @@ class ModuleParser(object):
|
||||
return True
|
||||
return False
|
||||
|
||||
def parse(self, header_files, include_paths=None, whitelist_paths=None):
|
||||
def parse(self, header_files, include_paths=None, whitelist_paths=None, includes=(), pygen_sink=None):
|
||||
"""
|
||||
parses a set of header files and returns a pybindgen Module instance.
|
||||
It is equivalent to calling the following methods:
|
||||
@@ -441,14 +516,15 @@ class ModuleParser(object):
|
||||
4. scan_functions()
|
||||
5. parse_finalize()
|
||||
"""
|
||||
self.parse_init(header_files, include_paths, whitelist_paths)
|
||||
self.parse_init(header_files, include_paths, whitelist_paths, includes, pygen_sink)
|
||||
self.scan_types()
|
||||
self.scan_methods()
|
||||
self.scan_functions()
|
||||
self.parse_finalize()
|
||||
return self.module
|
||||
|
||||
def parse_init(self, header_files, include_paths=None, whitelist_paths=None):
|
||||
def parse_init(self, header_files, include_paths=None,
|
||||
whitelist_paths=None, includes=(), pygen_sink=None):
|
||||
"""
|
||||
Prepares to parse a set of header files. The following
|
||||
methods should then be called in order to finish the rest of
|
||||
@@ -459,8 +535,12 @@ class ModuleParser(object):
|
||||
3. parse_finalize()
|
||||
"""
|
||||
assert isinstance(header_files, list)
|
||||
assert isinstance(includes, (list, tuple))
|
||||
assert pygen_sink is None or isinstance(pygen_sink, CodeSink)
|
||||
self.header_files = [os.path.abspath(f) for f in header_files]
|
||||
self.location_filter = declarations.custom_matcher_t(self.__location_match)
|
||||
if pygen_sink is not None:
|
||||
self.pygen_sink = pygen_sink
|
||||
|
||||
if whitelist_paths is not None:
|
||||
assert isinstance(whitelist_paths, list)
|
||||
@@ -479,24 +559,72 @@ class ModuleParser(object):
|
||||
self.module_namespace = declarations.get_global_namespace(self.declarations).\
|
||||
namespace(self.module_namespace_name)
|
||||
self.module = Module(self.module_name, cpp_namespace=self.module_namespace.decl_string)
|
||||
for inc in includes:
|
||||
self.module.add_include(inc)
|
||||
self.pygen_sink.writeln("import sys")
|
||||
self.pygen_sink.writeln("from pybindgen import Module, FileCodeSink, write_preamble")
|
||||
self.pygen_sink.writeln("from pybindgen.cppclass import CppClass")
|
||||
self.pygen_sink.writeln("from pybindgen.cppmethod import CppMethod, CppConstructor, CppNoConstructor")
|
||||
self.pygen_sink.writeln("from pybindgen import ReturnValue, Parameter, Function, Enum")
|
||||
self.pygen_sink.writeln()
|
||||
self.pygen_sink.writeln("def module_init():")
|
||||
self.pygen_sink.indent()
|
||||
self.pygen_sink.writeln("root_module = Module(%r, cpp_namespace=%r)"
|
||||
% (self.module_name, self.module_namespace.decl_string))
|
||||
for inc in includes:
|
||||
self.pygen_sink.writeln("root_module.add_include(%r)" % inc)
|
||||
self.pygen_sink.writeln("return root_module")
|
||||
self.pygen_sink.unindent()
|
||||
self.pygen_sink.writeln()
|
||||
self.type_registry = GccXmlTypeRegistry(self.module)
|
||||
|
||||
def scan_types(self):
|
||||
self._scan_namespace_types(self.module, self.module_namespace)
|
||||
self._scan_namespace_types(self.module, self.module_namespace, pygen_register_function_name="register_types")
|
||||
self._types_scanned = True
|
||||
|
||||
def scan_methods(self):
|
||||
assert self._types_scanned
|
||||
for class_wrapper in type_registry.ordered_classes:
|
||||
self.pygen_sink.writeln("def register_methods(root_module):")
|
||||
self.pygen_sink.indent()
|
||||
|
||||
for class_wrapper in self.type_registry.ordered_classes:
|
||||
if isinstance(class_wrapper.gccxml_definition, class_declaration_t):
|
||||
continue # skip classes not fully defined
|
||||
register_methods_func = "register_%s_methods" % (class_wrapper.mangled_full_name,)
|
||||
self.pygen_sink.writeln("%s(root_module, root_module[%r])" % (register_methods_func, class_wrapper.full_name))
|
||||
|
||||
self.pygen_sink.unindent()
|
||||
self.pygen_sink.writeln()
|
||||
|
||||
for class_wrapper in self.type_registry.ordered_classes:
|
||||
if isinstance(class_wrapper.gccxml_definition, class_declaration_t):
|
||||
continue # skip classes not fully defined
|
||||
register_methods_func = "register_%s_methods" % (class_wrapper.mangled_full_name,)
|
||||
self.pygen_sink.writeln("def %s(root_module, cls):" % (register_methods_func,))
|
||||
self.pygen_sink.indent()
|
||||
self._scan_class_methods(class_wrapper.gccxml_definition, class_wrapper)
|
||||
self.pygen_sink.writeln("return")
|
||||
self.pygen_sink.unindent()
|
||||
self.pygen_sink.writeln()
|
||||
|
||||
def scan_functions(self):
|
||||
assert self._types_scanned
|
||||
self._scan_namespace_functions(self.module, self.module_namespace)
|
||||
|
||||
def parse_finalize(self):
|
||||
annotations_scanner.warn_unused_annotations()
|
||||
|
||||
self.pygen_sink.writeln("def main():")
|
||||
self.pygen_sink.indent()
|
||||
self.pygen_sink.writeln("out = FileCodeSink(sys.stdout)")
|
||||
self.pygen_sink.writeln("root_module = module_init()")
|
||||
self.pygen_sink.writeln("register_types(root_module)")
|
||||
self.pygen_sink.writeln("register_methods(root_module)")
|
||||
self.pygen_sink.writeln("register_functions(root_module)")
|
||||
self.pygen_sink.writeln("write_preamble(out)")
|
||||
self.pygen_sink.writeln("root_module.generate(out)")
|
||||
self.pygen_sink.unindent()
|
||||
self.pygen_sink.writeln()
|
||||
self.pygen_sink.writeln("if __name__ == '__main__':\n main()")
|
||||
self.pygen_sink.writeln()
|
||||
|
||||
return self.module
|
||||
|
||||
def _apply_class_annotations(self, cls, annotations, kwargs):
|
||||
@@ -532,8 +660,16 @@ class ModuleParser(object):
|
||||
if not self._class_has_public_destructor(cls):
|
||||
kwargs.setdefault('is_singleton', True)
|
||||
|
||||
def _scan_namespace_types(self, module, module_namespace, outer_class=None, pygen_register_function_name=None):
|
||||
root_module = module.get_root()
|
||||
|
||||
if pygen_register_function_name:
|
||||
self.pygen_sink.writeln("def %s(module):" % pygen_register_function_name)
|
||||
self.pygen_sink.indent()
|
||||
self.pygen_sink.writeln("root_module = module.get_root()")
|
||||
self.pygen_sink.writeln()
|
||||
|
||||
|
||||
def _scan_namespace_types(self, module, module_namespace, outer_class=None):
|
||||
## scan enumerations
|
||||
if outer_class is None:
|
||||
enums = module_namespace.enums(function=self.location_filter,
|
||||
@@ -557,6 +693,12 @@ class ModuleParser(object):
|
||||
module.add_enum(Enum(enum.name, [name for name, dummy_val in enum.values],
|
||||
outer_class=outer_class))
|
||||
|
||||
enum_values_repr = '[' + ', '.join([repr(name) for name, dummy_val in enum.values]) + ']'
|
||||
l = [repr(enum.name), enum_values_repr]
|
||||
if outer_class is not None:
|
||||
l.append('outer_class=root_module[%r]' % outer_class.full_name)
|
||||
self.pygen_sink.writeln('module.add_enum(Enum(%s))' % ', '.join(l))
|
||||
|
||||
registered_classes = {} # class_t -> CppClass
|
||||
|
||||
## Look for forward declarations of class/structs like
|
||||
@@ -564,7 +706,7 @@ class ModuleParser(object):
|
||||
## pygccxml by a typedef whose .type.declaration is a
|
||||
## class_declaration_t instead of class_t.
|
||||
for alias in module_namespace.typedefs(function=self.location_filter,
|
||||
recursive=False, allow_empty=True):
|
||||
recursive=False, allow_empty=True):
|
||||
|
||||
## handle "typedef int Something;"
|
||||
if isinstance(alias.type, cpptypes.int_t):
|
||||
@@ -599,11 +741,17 @@ class ModuleParser(object):
|
||||
kwargs.setdefault("incomplete_type", True)
|
||||
kwargs.setdefault("automatic_type_narrowing", False)
|
||||
kwargs.setdefault("allow_subclassing", False)
|
||||
|
||||
class_wrapper = CppClass(alias.name, **kwargs)
|
||||
self.pygen_sink.writeln("module.add_class(CppClass(%s))" %
|
||||
", ".join([repr(alias.name)] + _pygen_kwargs(kwargs)))
|
||||
|
||||
class_wrapper.gccxml_definition = cls
|
||||
module.add_class(class_wrapper)
|
||||
registered_classes[cls] = class_wrapper
|
||||
type_registry.register_class(class_wrapper, alias=cls.name)
|
||||
if cls.name != alias.name:
|
||||
class_wrapper.register_alias(normalize_name(cls.name))
|
||||
self.type_registry.class_registered(class_wrapper)
|
||||
|
||||
## scan classes
|
||||
if outer_class is None:
|
||||
@@ -621,6 +769,22 @@ class ModuleParser(object):
|
||||
continue
|
||||
unregistered_classes.append(cls)
|
||||
|
||||
def postpone_class(cls, reason):
|
||||
## detect the case of a class being postponed many times; that
|
||||
## is almost certainly an error and a sign of an infinite
|
||||
## loop.
|
||||
count = getattr(cls, "_pybindgen_postpone_count", 0)
|
||||
count += 1
|
||||
cls._pybindgen_postpone_count = count
|
||||
if count >= 100:
|
||||
raise AssertionError("The class %s registration is being postponed for "
|
||||
"the %ith time (last reason: %r, current reason: %r);"
|
||||
" something is wrong, please file a bug report"
|
||||
" (https://bugs.launchpad.net/pybindgen/+filebug) with a test case."
|
||||
% (cls, cls._pybindgen_postpone_reason, reason))
|
||||
cls._pybindgen_postpone_reason = reason
|
||||
unregistered_classes.append(cls)
|
||||
|
||||
while unregistered_classes:
|
||||
cls = unregistered_classes.pop(0)
|
||||
typedef = None
|
||||
@@ -662,7 +826,7 @@ class ModuleParser(object):
|
||||
% (cls.decl_string, base_cls.decl_string),
|
||||
Warning, cls.location.file_name, cls.location.line)
|
||||
continue
|
||||
unregistered_classes.append(cls)
|
||||
postpone_class(cls, "waiting for base class %s to be registered first" % base_cls)
|
||||
continue
|
||||
else:
|
||||
base_class_wrapper = None
|
||||
@@ -674,14 +838,15 @@ class ModuleParser(object):
|
||||
if not isinstance(target_type, class_t):
|
||||
continue
|
||||
try:
|
||||
type_registry.find_class(operator.return_type.decl_string, '::')
|
||||
dummy = root_module[normalize_class_name(operator.return_type.decl_string, '::')]
|
||||
except KeyError:
|
||||
ok = False
|
||||
break
|
||||
else:
|
||||
ok = True
|
||||
if not ok:
|
||||
unregistered_classes.append(cls)
|
||||
postpone_class(cls, ("waiting for implicit conversion target class %s to be registered first"
|
||||
% (operator.return_type.decl_string,)))
|
||||
continue
|
||||
|
||||
self._apply_class_annotations(cls, global_annotations, kwargs)
|
||||
@@ -709,14 +874,25 @@ class ModuleParser(object):
|
||||
cls_name = typedef.name
|
||||
alias = '::'.join([module.cpp_namespace_prefix, cls.name])
|
||||
|
||||
class_wrapper = CppClass(cls_name, parent=base_class_wrapper, outer_class=outer_class,
|
||||
template_parameters=template_parameters,
|
||||
custom_template_class_name=custom_template_class_name,
|
||||
**kwargs)
|
||||
if base_class_wrapper is not None:
|
||||
kwargs["parent"] = base_class_wrapper
|
||||
if outer_class is not None:
|
||||
kwargs["outer_class"] = outer_class
|
||||
if template_parameters:
|
||||
kwargs["template_parameters"] = template_parameters
|
||||
if custom_template_class_name:
|
||||
kwargs["custom_template_class_name"] = custom_template_class_name
|
||||
class_wrapper = CppClass(cls_name, **kwargs)
|
||||
|
||||
self.pygen_sink.writeln("module.add_class(CppClass(%s))" %
|
||||
", ".join([repr(cls_name)] + _pygen_kwargs(kwargs)))
|
||||
|
||||
class_wrapper.gccxml_definition = cls
|
||||
module.add_class(class_wrapper)
|
||||
registered_classes[cls] = class_wrapper
|
||||
type_registry.register_class(class_wrapper, alias)
|
||||
if alias:
|
||||
class_wrapper.register_alias(normalize_name(alias))
|
||||
self.type_registry.class_registered(class_wrapper)
|
||||
|
||||
for hook in self._post_scan_hooks:
|
||||
hook(self, cls, class_wrapper)
|
||||
@@ -730,16 +906,53 @@ class ModuleParser(object):
|
||||
target_type = type_traits.remove_declarated(operator.return_type)
|
||||
if not isinstance(target_type, class_t):
|
||||
continue
|
||||
other_class = type_registry.find_class(operator.return_type.decl_string, '::')
|
||||
#other_class = type_registry.find_class(operator.return_type.decl_string, '::')
|
||||
other_class = root_module[normalize_class_name(operator.return_type.decl_string, '::')]
|
||||
class_wrapper.implicitly_converts_to(other_class)
|
||||
self.pygen_sink.writeln("root_module[%r].implicitly_converts_to(root_module[%r])"
|
||||
% (class_wrapper.full_name, other_class.full_name))
|
||||
|
||||
pygen_function_closed = False
|
||||
if outer_class is None:
|
||||
## scan nested namespaces (mapped as python submodules)
|
||||
nested_modules = []
|
||||
for nested_namespace in module_namespace.namespaces(allow_empty=True, recursive=False):
|
||||
if nested_namespace.name.startswith('__'):
|
||||
continue
|
||||
|
||||
if pygen_register_function_name:
|
||||
self.pygen_sink.writeln()
|
||||
self.pygen_sink.writeln("## Register a nested module for the namespace %s" % nested_namespace.name)
|
||||
self.pygen_sink.writeln()
|
||||
nested_module = Module(name=nested_namespace.name, parent=module, cpp_namespace=nested_namespace.name)
|
||||
nested_modules.append(nested_module)
|
||||
self.pygen_sink.writeln(
|
||||
"nested_module = Module(name=%r, parent=module, cpp_namespace=%r)"
|
||||
% (nested_namespace.name, nested_namespace.name))
|
||||
nested_module_type_init_func = "register_types_" + "_".join(nested_module.get_namespace_path())
|
||||
self.pygen_sink.writeln("%s(nested_module)" % nested_module_type_init_func)
|
||||
self.pygen_sink.writeln()
|
||||
|
||||
self.pygen_sink.unindent()
|
||||
self.pygen_sink.writeln()
|
||||
pygen_function_closed = True
|
||||
|
||||
## scan nested namespaces (mapped as python submodules)
|
||||
for nested_namespace in module_namespace.namespaces(allow_empty=True, recursive=False):
|
||||
if nested_namespace.name.startswith('__'):
|
||||
continue
|
||||
nested_module = Module(name=nested_namespace.name, parent=module, cpp_namespace=nested_namespace.name)
|
||||
self._scan_namespace_types(nested_module, nested_namespace)
|
||||
|
||||
if pygen_register_function_name:
|
||||
nested_module = nested_modules.pop(0)
|
||||
nested_module_type_init_func = "register_types_" + "_".join(nested_module.get_namespace_path())
|
||||
self._scan_namespace_types(nested_module, nested_namespace,
|
||||
pygen_register_function_name=nested_module_type_init_func)
|
||||
assert not nested_modules # make sure all have been consumed by the second for loop
|
||||
|
||||
if pygen_register_function_name and not pygen_function_closed:
|
||||
self.pygen_sink.unindent()
|
||||
self.pygen_sink.writeln()
|
||||
|
||||
|
||||
def _class_has_virtual_methods(self, cls):
|
||||
"""return True if cls has at least one virtual method, else False"""
|
||||
@@ -775,7 +988,7 @@ class ModuleParser(object):
|
||||
elif len(member.arguments) == 1:
|
||||
(cpp_class, dummy_is_const, dummy_is_pointer,
|
||||
is_reference, dummy_pointer_is_const) = \
|
||||
type_registry.get_class_type_traits(member.arguments[0].type)
|
||||
self.type_registry.get_class_type_traits(member.arguments[0].type)
|
||||
if cpp_class is class_wrapper and is_reference:
|
||||
have_copy_constructor = True
|
||||
|
||||
@@ -808,8 +1021,8 @@ class ModuleParser(object):
|
||||
Warning, member.location.file_name, member.location.line)
|
||||
|
||||
try:
|
||||
return_type = type_registry.lookup_return(member.return_type,
|
||||
parameter_annotations.get('return', {}))
|
||||
return_type = self.type_registry.lookup_return(member.return_type,
|
||||
parameter_annotations.get('return', {}))
|
||||
except (TypeLookupError, TypeConfigurationError), ex:
|
||||
warnings.warn_explicit("Return value '%s' error (used in %s): %r"
|
||||
% (member.return_type.decl_string, member, ex),
|
||||
@@ -817,14 +1030,17 @@ class ModuleParser(object):
|
||||
if pure_virtual:
|
||||
class_wrapper.set_cannot_be_constructed("pure virtual method not wrapped")
|
||||
class_wrapper.set_helper_class_disabled(True)
|
||||
self.pygen_sink.writeln('cls.set_cannot_be_constructed("pure virtual method not wrapped")')
|
||||
self.pygen_sink.writeln('cls.set_helper_class_disabled(True)')
|
||||
continue
|
||||
arguments = []
|
||||
ok = True
|
||||
for arg in member.arguments:
|
||||
try:
|
||||
arguments.append(type_registry.lookup_parameter(arg.type, arg.name,
|
||||
parameter_annotations.get(arg.name, {}),
|
||||
arg.default_value))
|
||||
arguments.append(self.type_registry.lookup_parameter(arg.type, arg.name,
|
||||
parameter_annotations.get(arg.name, {}),
|
||||
arg.default_value))
|
||||
assert hasattr(arguments[-1], "_pygen_repr")
|
||||
except (TypeLookupError, TypeConfigurationError), ex:
|
||||
warnings.warn_explicit("Parameter '%s %s' error (used in %s): %r"
|
||||
% (arg.type.decl_string, arg.name, member, ex),
|
||||
@@ -834,10 +1050,13 @@ class ModuleParser(object):
|
||||
if pure_virtual:
|
||||
class_wrapper.set_cannot_be_constructed("pure virtual method not wrapped")
|
||||
class_wrapper.set_helper_class_disabled(True)
|
||||
self.pygen_sink.writeln('cls.set_cannot_be_constructed("pure virtual method not wrapped")')
|
||||
self.pygen_sink.writeln('cls.set_helper_class_disabled(True)')
|
||||
continue
|
||||
|
||||
if pure_virtual and not class_wrapper.allow_subclassing:
|
||||
class_wrapper.set_cannot_be_constructed("pure virtual method and subclassing disabled")
|
||||
self.pygen_sink.writeln('cls.set_cannot_be_constructed("pure virtual method not wrapped")')
|
||||
|
||||
custom_template_method_name = None
|
||||
if templates.is_instantiation(member.demangled_name):
|
||||
@@ -853,21 +1072,50 @@ class ModuleParser(object):
|
||||
else:
|
||||
template_parameters = ()
|
||||
|
||||
method_wrapper = CppMethod(return_type, member.name, arguments,
|
||||
is_const=member.has_const,
|
||||
is_static=member.has_static,
|
||||
is_virtual=is_virtual,
|
||||
is_pure_virtual=pure_virtual,
|
||||
template_parameters=template_parameters,
|
||||
custom_template_method_name=custom_template_method_name,
|
||||
visibility=member.access_type)
|
||||
kwargs = {}
|
||||
if member.has_const:
|
||||
kwargs['is_const'] = True
|
||||
if member.has_static:
|
||||
kwargs['is_static'] = True
|
||||
if is_virtual:
|
||||
kwargs['is_virtual'] = True
|
||||
if pure_virtual:
|
||||
kwargs['is_pure_virtual'] = True
|
||||
if template_parameters:
|
||||
kwargs['template_parameters'] = template_parameters
|
||||
if custom_template_method_name:
|
||||
kwargs['custom_template_method_name'] = custom_template_method_name
|
||||
if member.access_type != 'public':
|
||||
kwargs['visibility'] = member.access_type
|
||||
|
||||
## ignore methods that are private and not virtual or
|
||||
## pure virtual, as they do not affect the bindings in
|
||||
## any way and only clutter the generated python script.
|
||||
if (kwargs.get('visibility', 'public') == 'private'
|
||||
and not (kwargs.get('is_virtual', False) or kwargs.get('is_pure_virtual', False))):
|
||||
continue
|
||||
|
||||
method_wrapper = CppMethod(return_type, member.name, arguments, **kwargs)
|
||||
method_wrapper.gccxml_definition = member
|
||||
|
||||
def _pygen_method():
|
||||
arglist_repr = ("[" + ', '.join([arg._pygen_repr for arg in arguments]) + "]")
|
||||
self.pygen_sink.writeln("cls.add_method(CppMethod(%s))" %
|
||||
", ".join(
|
||||
[return_type._pygen_repr, repr(member.name), arglist_repr]
|
||||
+ _pygen_kwargs(kwargs)))
|
||||
|
||||
try:
|
||||
class_wrapper.add_method(method_wrapper)
|
||||
except NotSupportedError, ex:
|
||||
_pygen_method()
|
||||
if pure_virtual:
|
||||
class_wrapper.set_cannot_be_constructed("pure virtual method not wrapped")
|
||||
class_wrapper.set_cannot_be_constructed("pure virtual method %r not wrapped" % member.name)
|
||||
class_wrapper.set_helper_class_disabled(True)
|
||||
self.pygen_sink.writeln('cls.set_cannot_be_constructed("pure virtual method %%r not wrapped" %% %r)'
|
||||
% member.name)
|
||||
self.pygen_sink.writeln('cls.set_helper_class_disabled(True)')
|
||||
|
||||
warnings.warn_explicit("Error adding method %s: %r"
|
||||
% (member, ex),
|
||||
Warning, member.location.file_name, member.location.line)
|
||||
@@ -876,9 +1124,11 @@ class ModuleParser(object):
|
||||
% (member, ex),
|
||||
Warning, member.location.file_name, member.location.line)
|
||||
raise
|
||||
else:
|
||||
else: # no exception, add method succeeded
|
||||
_pygen_method()
|
||||
for hook in self._post_scan_hooks:
|
||||
hook(self, member, method_wrapper)
|
||||
|
||||
|
||||
## ------------ constructor --------------------
|
||||
elif isinstance(member, calldef.constructor_t):
|
||||
@@ -891,8 +1141,8 @@ class ModuleParser(object):
|
||||
arguments = []
|
||||
for arg in member.arguments:
|
||||
try:
|
||||
arguments.append(type_registry.lookup_parameter(arg.type, arg.name,
|
||||
default_value=arg.default_value))
|
||||
arguments.append(self.type_registry.lookup_parameter(arg.type, arg.name,
|
||||
default_value=arg.default_value))
|
||||
except (TypeLookupError, TypeConfigurationError), ex:
|
||||
warnings.warn_explicit("Parameter '%s %s' error (used in %s): %r"
|
||||
% (arg.type.decl_string, arg.name, member, ex),
|
||||
@@ -913,6 +1163,10 @@ class ModuleParser(object):
|
||||
and isinstance(arguments[0], class_wrapper.ThisClassRefParameter)):
|
||||
have_copy_constructor = True
|
||||
|
||||
arglist_repr = ("[" + ', '.join([arg._pygen_repr for arg in arguments]) + "]")
|
||||
self.pygen_sink.writeln("cls.add_constructor(CppConstructor(%s))" %
|
||||
", ".join([arglist_repr, "visibility=%r" % member.access_type]))
|
||||
|
||||
## ------------ attribute --------------------
|
||||
elif isinstance(member, variable_t):
|
||||
if member.access_type == 'protected':
|
||||
@@ -925,7 +1179,7 @@ class ModuleParser(object):
|
||||
continue
|
||||
|
||||
try:
|
||||
return_type = type_registry.lookup_return(member.type)
|
||||
return_type = self.type_registry.lookup_return(member.type)
|
||||
except (TypeLookupError, TypeConfigurationError), ex:
|
||||
warnings.warn_explicit("Return value '%s' error (used in %s): %r"
|
||||
% (member.type.decl_string, member, ex),
|
||||
@@ -934,9 +1188,13 @@ class ModuleParser(object):
|
||||
if member.type_qualifiers.has_static:
|
||||
class_wrapper.add_static_attribute(return_type, member.name,
|
||||
is_const=type_traits.is_const(member.type))
|
||||
self.pygen_sink.writeln("cls.add_static_attribute(%s, %r, is_const=%r)" %
|
||||
(return_type._pygen_repr, member.name, type_traits.is_const(member.type)))
|
||||
else:
|
||||
class_wrapper.add_instance_attribute(return_type, member.name,
|
||||
is_const=type_traits.is_const(member.type))
|
||||
self.pygen_sink.writeln("cls.add_instance_attribute(%s, %r, is_const=%r)" %
|
||||
(return_type._pygen_repr, member.name, type_traits.is_const(member.type)))
|
||||
## TODO: invoke post_scan_hooks
|
||||
elif isinstance(member, calldef.destructor_t):
|
||||
pass
|
||||
@@ -946,6 +1204,8 @@ class ModuleParser(object):
|
||||
if not have_trivial_constructor:
|
||||
if type_traits.has_trivial_constructor(cls):
|
||||
class_wrapper.add_constructor(CppConstructor([]))
|
||||
self.pygen_sink.writeln("cls.add_constructor(CppConstructor([]))")
|
||||
|
||||
if not have_copy_constructor:
|
||||
try: # pygccxml > 0.9
|
||||
has_copy_constructor = type_traits.has_copy_constructor(cls)
|
||||
@@ -956,8 +1216,17 @@ class ModuleParser(object):
|
||||
class_wrapper.ThisClassRefParameter("%s const &" % class_wrapper.full_name,
|
||||
'ctor_arg', is_const=True)]))
|
||||
|
||||
|
||||
def scan_functions(self):
|
||||
assert self._types_scanned
|
||||
self.pygen_sink.writeln("def register_functions(root_module):")
|
||||
self.pygen_sink.indent()
|
||||
self.pygen_sink.writeln("module = root_module")
|
||||
self._scan_namespace_functions(self.module, self.module_namespace)
|
||||
|
||||
def _scan_namespace_functions(self, module, module_namespace):
|
||||
root_module = module.get_root()
|
||||
|
||||
for fun in module_namespace.free_functions(function=self.location_filter,
|
||||
allow_empty=True, recursive=False):
|
||||
if fun.name.startswith('__'):
|
||||
@@ -996,7 +1265,7 @@ class ModuleParser(object):
|
||||
if is_constructor_of:
|
||||
return_annotations['caller_owns_return'] = 'true'
|
||||
try:
|
||||
return_type = type_registry.lookup_return(fun.return_type, return_annotations)
|
||||
return_type = self.type_registry.lookup_return(fun.return_type, return_annotations)
|
||||
except (TypeLookupError, TypeConfigurationError), ex:
|
||||
warnings.warn_explicit("Return value '%s' error (used in %s): %r"
|
||||
% (fun.return_type.decl_string, fun, ex),
|
||||
@@ -1014,7 +1283,7 @@ class ModuleParser(object):
|
||||
and isinstance(arg.type, cpptypes.pointer_t):
|
||||
annotations.setdefault("transfer_ownership", "false")
|
||||
try:
|
||||
arguments.append(type_registry.lookup_parameter(arg.type, arg.name,
|
||||
arguments.append(self.type_registry.lookup_parameter(arg.type, arg.name,
|
||||
annotations,
|
||||
default_value=arg.default_value))
|
||||
except (TypeLookupError, TypeConfigurationError), ex:
|
||||
@@ -1034,39 +1303,84 @@ class ModuleParser(object):
|
||||
if not ok:
|
||||
continue
|
||||
|
||||
arglist_repr = ("[" + ', '.join([arg._pygen_repr for arg in arguments]) + "]")
|
||||
|
||||
if as_method is not None:
|
||||
assert of_class is not None
|
||||
cpp_class = type_registry.find_class(of_class, (self.module_namespace_name or '::'))
|
||||
#cpp_class = type_registry.find_class(of_class, (self.module_namespace_name or '::'))
|
||||
cpp_class = root_module[normalize_class_name(of_class, (self.module_namespace_name or '::'))]
|
||||
|
||||
function_wrapper = Function(return_type, fun.name, arguments)
|
||||
cpp_class.add_method(function_wrapper, name=as_method)
|
||||
function_wrapper.gccxml_definition = fun
|
||||
continue
|
||||
if is_constructor_of is not None:
|
||||
cpp_class = type_registry.find_class(is_constructor_of, (self.module_namespace_name or '::'))
|
||||
function_wrapper = Function(return_type, fun.name, arguments)
|
||||
cpp_class.add_constructor(function_wrapper)
|
||||
function_wrapper.gccxml_definition = fun
|
||||
|
||||
self.pygen_sink.writeln("root_module[%r].add_method(Function(%s), name=%r)" %
|
||||
(cpp_class.full_name,
|
||||
", ".join([return_type._pygen_repr, repr(fun.name), arglist_repr]),
|
||||
as_method))
|
||||
|
||||
continue
|
||||
|
||||
if is_constructor_of is not None:
|
||||
#cpp_class = type_registry.find_class(is_constructor_of, (self.module_namespace_name or '::'))
|
||||
cpp_class = root_module[normalize_class_name(is_constructor_of, (self.module_namespace_name or '::'))]
|
||||
function_wrapper = Function(return_type, fun.name, arguments)
|
||||
cpp_class.add_constructor(function_wrapper)
|
||||
|
||||
function_wrapper.gccxml_definition = fun
|
||||
|
||||
self.pygen_sink.writeln("root_module[%r].add_constructor(Function(%s))" %
|
||||
(cpp_class.full_name,
|
||||
", ".join([return_type._pygen_repr, repr(fun.name), arglist_repr]),))
|
||||
|
||||
continue
|
||||
|
||||
kwargs = {}
|
||||
|
||||
if templates.is_instantiation(fun.demangled_name):
|
||||
template_parameters = templates.args(fun.demangled_name)
|
||||
else:
|
||||
template_parameters = ()
|
||||
|
||||
func_wrapper = Function(return_type, fun.name, arguments,
|
||||
template_parameters=template_parameters)
|
||||
kwargs['template_parameters'] = templates.args(fun.demangled_name)
|
||||
|
||||
func_wrapper = Function(return_type, fun.name, arguments, **kwargs)
|
||||
func_wrapper.gccxml_definition = fun
|
||||
module.add_function(func_wrapper, name=alt_name)
|
||||
|
||||
if alt_name is None:
|
||||
_pygen_altname_arg = ''
|
||||
else:
|
||||
_pygen_altname_arg = ', name=%r' % alt_name
|
||||
self.pygen_sink.writeln("module.add_function(Function(%s)%s)" %
|
||||
(", ".join([return_type._pygen_repr, repr(fun.name), arglist_repr] + _pygen_kwargs(kwargs)),
|
||||
_pygen_altname_arg))
|
||||
|
||||
for hook in self._post_scan_hooks:
|
||||
hook(self, fun, func_wrapper)
|
||||
|
||||
|
||||
## scan nested namespaces (mapped as python submodules)
|
||||
for nested_namespace in module_namespace.namespaces(allow_empty=True, recursive=False):
|
||||
if nested_namespace.name.startswith('__'):
|
||||
continue
|
||||
nested_module = module.get_submodule(nested_namespace.name)
|
||||
self._scan_namespace_functions(nested_module, nested_namespace)
|
||||
|
||||
nested_module_pygen_func = "register_functions_" + "_".join(nested_module.get_namespace_path())
|
||||
self.pygen_sink.writeln("%s(module.get_submodule(%r), root_module)" %
|
||||
(nested_module_pygen_func, nested_namespace.name))
|
||||
|
||||
self.pygen_sink.writeln("return")
|
||||
self.pygen_sink.unindent()
|
||||
self.pygen_sink.writeln()
|
||||
|
||||
for nested_namespace in module_namespace.namespaces(allow_empty=True, recursive=False):
|
||||
if nested_namespace.name.startswith('__'):
|
||||
continue
|
||||
nested_module = module.get_submodule(nested_namespace.name)
|
||||
|
||||
nested_module_pygen_func = "register_functions_" + "_".join(nested_module.get_namespace_path())
|
||||
self.pygen_sink.writeln("def %s(module, root_module):" % nested_module_pygen_func)
|
||||
self.pygen_sink.indent()
|
||||
|
||||
self._scan_namespace_functions(nested_module, nested_namespace)
|
||||
|
||||
|
||||
def _test():
|
||||
module_parser = ModuleParser('foo', '::')
|
||||
|
||||
+102
-14
@@ -10,37 +10,74 @@ from enum import Enum
|
||||
import utils
|
||||
|
||||
|
||||
class Module(object):
|
||||
class Module(dict):
|
||||
"""
|
||||
A Module object takes care of generating the code for a Python module.
|
||||
|
||||
Module objects can be indexed dictionary style to access contained types. Example::
|
||||
|
||||
>>> from enum import Enum
|
||||
>>> from cppclass import CppClass
|
||||
>>> m = Module("foo", cpp_namespace="foo")
|
||||
>>> subm = m.add_cpp_namespace("subm")
|
||||
>>> c1 = CppClass("Bar")
|
||||
>>> m.add_class(c1)
|
||||
>>> c2 = CppClass("Zbr")
|
||||
>>> subm.add_class(c2)
|
||||
>>> e1 = Enum("En1", ["XX"])
|
||||
>>> m.add_enum(e1)
|
||||
>>> e2 = Enum("En2", ["XX"])
|
||||
>>> subm.add_enum(e2)
|
||||
>>> m["Bar"] is c1
|
||||
True
|
||||
>>> m["foo::Bar"] is c1
|
||||
True
|
||||
>>> m["En1"] is e1
|
||||
True
|
||||
>>> m["foo::En1"] is e1
|
||||
True
|
||||
>>> m["badname"]
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
KeyError: 'badname'
|
||||
>>> m["foo::subm::Zbr"] is c2
|
||||
True
|
||||
>>> m["foo::subm::En2"] is e2
|
||||
True
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, name, parent=None, docstring=None, cpp_namespace=None):
|
||||
"""Constructor
|
||||
name -- module name
|
||||
"""
|
||||
self.name = name
|
||||
@param name: module name
|
||||
@param parent: parent L{module<Module>} (i.e. the one that contains this submodule) or None if this is a root module
|
||||
@param docstring: docstring to use for this module
|
||||
@param cpp_namespace: C++ namespace prefix associated with this module
|
||||
"""
|
||||
super(Module, self).__init__()
|
||||
self.parent = parent
|
||||
self.docstring = docstring
|
||||
self.submodules = []
|
||||
self.enums = []
|
||||
self._forward_declarations_declared = False
|
||||
|
||||
if parent is None:
|
||||
self.prefix = self.name
|
||||
self.cpp_namespace = cpp_namespace
|
||||
if self.parent is None:
|
||||
error_return = 'return;'
|
||||
else:
|
||||
parent.submodules.append(self)
|
||||
self.prefix = parent.prefix + "_" + self.name
|
||||
self.parent.submodules.append(self)
|
||||
error_return = 'return NULL;'
|
||||
|
||||
self.cpp_namespace = cpp_namespace
|
||||
self.prefix = None
|
||||
self.init_function_name = None
|
||||
self._name = None
|
||||
self.name = name
|
||||
|
||||
path = self.get_namespace_path()
|
||||
if path and path[0] == '::':
|
||||
del path[0]
|
||||
self.cpp_namespace_prefix = '::'.join(path)
|
||||
|
||||
self.init_function_name = "init%s" % self.prefix
|
||||
self.declarations = DeclarationsScope()
|
||||
self.functions = {} # name => OverloadedFunction
|
||||
self.classes = []
|
||||
@@ -57,12 +94,36 @@ class Module(object):
|
||||
self.one_time_definitions = parent.one_time_definitions
|
||||
self.includes = parent.includes
|
||||
|
||||
def get_name(self):
|
||||
return self._name
|
||||
|
||||
def set_name(self, name):
|
||||
self._name = name
|
||||
|
||||
if self.parent is None:
|
||||
self.prefix = self.name
|
||||
else:
|
||||
self.prefix = self.parent.prefix + "_" + self.name
|
||||
|
||||
self.init_function_name = "init%s" % self.prefix
|
||||
|
||||
|
||||
name = property(get_name, set_name)
|
||||
|
||||
def get_submodule(self, submodule_name):
|
||||
"get a submodule by its name"
|
||||
for submodule in self.submodules:
|
||||
if submodule.name == submodule_name:
|
||||
return submodule
|
||||
raise ValueError("submodule %s not found" % submodule_name)
|
||||
|
||||
def get_root(self):
|
||||
"returns the root module (even it is self)"
|
||||
root = self
|
||||
while root.parent is not None:
|
||||
root = root.parent
|
||||
return root
|
||||
|
||||
def set_strip_prefix(self, prefix):
|
||||
"""Sets the prefix string to be used when transforming a C
|
||||
function name into the python function name; the given prefix
|
||||
@@ -122,6 +183,23 @@ class Module(object):
|
||||
wrapper.module = self
|
||||
overload.add(wrapper)
|
||||
|
||||
def register_type(self, name, full_name, type_wrapper):
|
||||
"""
|
||||
Register a type wrapper with the module, for easy access in
|
||||
the future. Normally should not be called by the programmer,
|
||||
as it is meant for internal pybindgen use and called automatically.
|
||||
|
||||
@param name: type name without any C++ namespace prefix, or None
|
||||
@param full_name: type name with a C++ namespace prefix, or None
|
||||
@param type_wrapper: the wrapper object for the type (e.g. L{CppClass} or L{Enum})
|
||||
"""
|
||||
module = self
|
||||
if name:
|
||||
module[name] = type_wrapper
|
||||
if full_name:
|
||||
while module is not None:
|
||||
module[full_name] = type_wrapper
|
||||
module = module.parent
|
||||
|
||||
def add_class(self, class_):
|
||||
"""
|
||||
@@ -132,6 +210,7 @@ class Module(object):
|
||||
assert isinstance(class_, CppClass)
|
||||
class_.module = self
|
||||
self.classes.append(class_)
|
||||
self.register_type(class_.name, class_.full_name, class_)
|
||||
|
||||
def add_cpp_namespace(self, name):
|
||||
"""
|
||||
@@ -148,6 +227,7 @@ class Module(object):
|
||||
assert isinstance(enum, Enum)
|
||||
self.enums.append(enum)
|
||||
enum.module = self
|
||||
self.register_type(enum.name, enum.full_name, enum)
|
||||
|
||||
def declare_one_time_definition(self, definition_name):
|
||||
"""
|
||||
@@ -198,14 +278,22 @@ class Module(object):
|
||||
|
||||
def get_namespace_path(self):
|
||||
"""Get the full [root_namespace, namespace, namespace,...] path """
|
||||
if self.cpp_namespace is None:
|
||||
if not self.cpp_namespace:
|
||||
names = []
|
||||
else:
|
||||
names = [self.cpp_namespace]
|
||||
if self.cpp_namespace == '::':
|
||||
names = []
|
||||
else:
|
||||
names = self.cpp_namespace.split('::')
|
||||
if not names[0]:
|
||||
del names[0]
|
||||
parent = self.parent
|
||||
while parent is not None:
|
||||
if parent.cpp_namespace is not None:
|
||||
names.insert(0, parent.cpp_namespace)
|
||||
if parent.cpp_namespace and parent.cpp_namespace != '::':
|
||||
parent_names = parent.cpp_namespace.split('::')
|
||||
if not parent_names[0]:
|
||||
del parent_names[0]
|
||||
names = parent_names + names
|
||||
parent = parent.parent
|
||||
return names
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import sys
|
||||
import gc
|
||||
import os.path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
||||
'..', '..', 'build', 'default', 'examples', 'c-hello'))
|
||||
'..', '..', 'build', 'default', 'tests', 'c-hello'))
|
||||
import hello
|
||||
import unittest
|
||||
|
||||
|
||||
+9
-111
@@ -11,118 +11,17 @@ from pybindgen.gccxmlparser import ModuleParser
|
||||
from pybindgen.function import CustomFunctionWrapper
|
||||
from pybindgen.cppmethod import CustomCppMethodWrapper
|
||||
|
||||
import foomodulegen_common
|
||||
|
||||
|
||||
class PointerHolderTransformation(typehandlers.TypeTransformation):
|
||||
def __init__(self):
|
||||
self.rx = re.compile(r'(?:::)?PointerHolder<(\w+)>')
|
||||
|
||||
def get_untransformed_name(self, name):
|
||||
m = self.rx.match(name)
|
||||
if m is None:
|
||||
return None
|
||||
else:
|
||||
return m.group(1)+'*'
|
||||
|
||||
def create_type_handler(self, type_handler, *args, **kwargs):
|
||||
if issubclass(type_handler, Parameter):
|
||||
kwargs['transfer_ownership'] = False
|
||||
elif issubclass(type_handler, ReturnValue):
|
||||
kwargs['caller_owns_return'] = True
|
||||
else:
|
||||
raise AssertionError
|
||||
handler = type_handler(*args, **kwargs)
|
||||
handler.set_tranformation(self, self.get_untransformed_name(args[0]))
|
||||
return handler
|
||||
|
||||
def untransform(self, type_handler, declarations, code_block, expression):
|
||||
return '(%s).thePointer' % (expression,)
|
||||
|
||||
def transform(self, type_handler, declarations, code_block, expression):
|
||||
assert type_handler.untransformed_ctype[-1] == '*'
|
||||
var = declarations.declare_variable(
|
||||
'PointerHolder<%s>' % type_handler.untransformed_ctype[:-1], 'tmp')
|
||||
return '(%s.thePointer = (%s), %s)' % (var, expression, var)
|
||||
|
||||
transf = PointerHolderTransformation()
|
||||
typehandlers.return_type_matcher.register_transformation(transf)
|
||||
typehandlers.param_type_matcher.register_transformation(transf)
|
||||
del transf
|
||||
|
||||
|
||||
|
||||
def my_module_gen(out_file):
|
||||
out = FileCodeSink(out_file)
|
||||
def my_module_gen():
|
||||
out = FileCodeSink(sys.stdout)
|
||||
pygen_file = open(sys.argv[2], "wt")
|
||||
pybindgen.write_preamble(out)
|
||||
out.writeln("#include \"foo.h\"")
|
||||
module_parser = ModuleParser('foo2', '::')
|
||||
module = module_parser.parse(sys.argv[1:])
|
||||
module = module_parser.parse([sys.argv[1]], includes=['"foo.h"'], pygen_sink=FileCodeSink(pygen_file))
|
||||
pygen_file.close()
|
||||
|
||||
wrapper_body = '''
|
||||
static PyObject *
|
||||
_wrap_foofunction_that_takes_foo_from_string(PyObject * PYBINDGEN_UNUSED(dummy), PyObject *args,
|
||||
PyObject *kwargs, PyObject **return_exception)
|
||||
{
|
||||
PyObject *py_retval;
|
||||
char *datum;
|
||||
const char *keywords[] = {"foo", NULL};
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", (char **) keywords, &datum)) {
|
||||
{
|
||||
PyObject *exc_type, *traceback;
|
||||
PyErr_Fetch(&exc_type, return_exception, &traceback);
|
||||
Py_XDECREF(exc_type);
|
||||
Py_XDECREF(traceback);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
function_that_takes_foo(Foo(datum));
|
||||
py_retval = Py_BuildValue("");
|
||||
return py_retval;
|
||||
}
|
||||
'''
|
||||
module.add_function(CustomFunctionWrapper('function_that_takes_foo',
|
||||
'_wrap_foofunction_that_takes_foo_from_string',
|
||||
wrapper_body))
|
||||
|
||||
|
||||
## test a custom method wrapper
|
||||
Bar, = [cls for cls in module.classes if cls.name == 'Bar']
|
||||
wrapper_body = '''
|
||||
static PyObject *
|
||||
_wrap_PyBar_Hooray_lenx(PyBar *PYBINDGEN_UNUSED(dummy), PyObject *args, PyObject *kwargs,
|
||||
PyObject **return_exception)
|
||||
{
|
||||
PyObject *py_retval;
|
||||
int x;
|
||||
const char *keywords[] = {"x", NULL};
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", (char **) keywords, &x)) {
|
||||
PyObject *exc_type, *traceback;
|
||||
PyErr_Fetch(&exc_type, return_exception, &traceback);
|
||||
Py_XDECREF(exc_type);
|
||||
Py_XDECREF(traceback);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::string retval;
|
||||
retval = Bar::Hooray();
|
||||
py_retval = Py_BuildValue("i", int(retval.size() + x));
|
||||
return py_retval;
|
||||
}
|
||||
'''
|
||||
Bar.add_method(CustomCppMethodWrapper("Hooray", "_wrap_PyBar_Hooray_lenx",
|
||||
wrapper_body,
|
||||
flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"]))
|
||||
|
||||
|
||||
Foo, = [cls for cls in module.classes if cls.name == 'Foo']
|
||||
def Foo_instance_creation_function(dummy_cpp_class, code_block, lvalue,
|
||||
parameters, construct_type_name):
|
||||
code_block.write_code(
|
||||
"%s = new %s(%s);" % (lvalue, construct_type_name, parameters))
|
||||
code_block.write_code("%s->initialize();" % (lvalue,))
|
||||
Foo.set_instance_creation_function(Foo_instance_creation_function)
|
||||
foomodulegen_common.customize_module(module)
|
||||
|
||||
module.generate(out)
|
||||
|
||||
@@ -131,9 +30,8 @@ if __name__ == '__main__':
|
||||
try:
|
||||
import cProfile as profile
|
||||
except ImportError:
|
||||
my_module_gen(sys.stdout)
|
||||
my_module_gen()
|
||||
else:
|
||||
print >> sys.stderr, "** running under profiler"
|
||||
profile.run('my_module_gen(sys.stdout)', 'foomodulegen-auto.pstat')
|
||||
|
||||
profile.run('my_module_gen()', 'foomodulegen-auto.pstat')
|
||||
|
||||
+2
-99
@@ -11,43 +11,7 @@ from pybindgen import (CppMethod, CppConstructor, CppClass, Enum)
|
||||
from pybindgen.function import CustomFunctionWrapper
|
||||
from pybindgen.cppmethod import CustomCppMethodWrapper
|
||||
|
||||
|
||||
|
||||
class PointerHolderTransformation(typehandlers.TypeTransformation):
|
||||
def __init__(self):
|
||||
self.rx = re.compile(r'(?:::)?PointerHolder<(\w+)>')
|
||||
|
||||
def get_untransformed_name(self, name):
|
||||
m = self.rx.match(name)
|
||||
if m is None:
|
||||
return None
|
||||
else:
|
||||
return m.group(1)+'*'
|
||||
|
||||
def create_type_handler(self, type_handler, *args, **kwargs):
|
||||
if issubclass(type_handler, Parameter):
|
||||
kwargs['transfer_ownership'] = False
|
||||
elif issubclass(type_handler, ReturnValue):
|
||||
kwargs['caller_owns_return'] = True
|
||||
else:
|
||||
raise AssertionError
|
||||
handler = type_handler(*args, **kwargs)
|
||||
handler.set_tranformation(self, self.get_untransformed_name(args[0]))
|
||||
return handler
|
||||
|
||||
def untransform(self, type_handler, declarations, code_block, expression):
|
||||
return '(%s).thePointer' % (expression,)
|
||||
|
||||
def transform(self, type_handler, declarations, code_block, expression):
|
||||
assert type_handler.untransformed_ctype[-1] == '*'
|
||||
var = declarations.declare_variable(
|
||||
'PointerHolder<%s>' % type_handler.untransformed_ctype[:-1], 'tmp')
|
||||
return '(%s.thePointer = (%s), %s)' % (var, expression, var)
|
||||
|
||||
transf = PointerHolderTransformation()
|
||||
typehandlers.return_type_matcher.register_transformation(transf)
|
||||
typehandlers.param_type_matcher.register_transformation(transf)
|
||||
del transf
|
||||
import foomodulegen_common
|
||||
|
||||
|
||||
|
||||
@@ -61,13 +25,6 @@ def my_module_gen(out_file):
|
||||
Foo = CppClass('Foo', automatic_type_narrowing=True)
|
||||
mod.add_class(Foo)
|
||||
|
||||
def Foo_instance_creation_function(dummy_cpp_class, code_block, lvalue,
|
||||
parameters, construct_type_name):
|
||||
code_block.write_code(
|
||||
"%s = new %s(%s);" % (lvalue, construct_type_name, parameters))
|
||||
code_block.write_code("%s->initialize();" % (lvalue,))
|
||||
Foo.set_instance_creation_function(Foo_instance_creation_function)
|
||||
|
||||
Foo.add_static_attribute(ReturnValue.new('int'), 'instance_count')
|
||||
Foo.add_constructor(
|
||||
CppConstructor([Parameter.new('std::string', 'datum')]))
|
||||
@@ -425,61 +382,7 @@ int %s::custom_method_added_by_a_hook(int x)
|
||||
pybindgen.settings.error_handler = MyErrorHandler()
|
||||
|
||||
|
||||
## test a custom function wrapper
|
||||
wrapper_body = '''
|
||||
static PyObject *
|
||||
_wrap_foofunction_that_takes_foo_from_string(PyObject * PYBINDGEN_UNUSED(dummy), PyObject *args,
|
||||
PyObject *kwargs, PyObject **return_exception)
|
||||
{
|
||||
PyObject *py_retval;
|
||||
char *datum;
|
||||
const char *keywords[] = {"foo", NULL};
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", (char **) keywords, &datum)) {
|
||||
{
|
||||
PyObject *exc_type, *traceback;
|
||||
PyErr_Fetch(&exc_type, return_exception, &traceback);
|
||||
Py_XDECREF(exc_type);
|
||||
Py_XDECREF(traceback);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
function_that_takes_foo(Foo(datum));
|
||||
py_retval = Py_BuildValue("");
|
||||
return py_retval;
|
||||
}
|
||||
'''
|
||||
mod.add_function(CustomFunctionWrapper('function_that_takes_foo',
|
||||
'_wrap_foofunction_that_takes_foo_from_string',
|
||||
wrapper_body))
|
||||
|
||||
## test a custom method wrapper
|
||||
wrapper_body = '''
|
||||
static PyObject *
|
||||
_wrap_PyBar_Hooray_lenx(PyBar *PYBINDGEN_UNUSED(dummy), PyObject *args, PyObject *kwargs,
|
||||
PyObject **return_exception)
|
||||
{
|
||||
PyObject *py_retval;
|
||||
int x;
|
||||
const char *keywords[] = {"x", NULL};
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", (char **) keywords, &x)) {
|
||||
PyObject *exc_type, *traceback;
|
||||
PyErr_Fetch(&exc_type, return_exception, &traceback);
|
||||
Py_XDECREF(exc_type);
|
||||
Py_XDECREF(traceback);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::string retval;
|
||||
retval = Bar::Hooray();
|
||||
py_retval = Py_BuildValue("i", int(retval.size() + x));
|
||||
return py_retval;
|
||||
}
|
||||
'''
|
||||
Bar.add_method(CustomCppMethodWrapper("Hooray", "_wrap_PyBar_Hooray_lenx",
|
||||
wrapper_body,
|
||||
flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"]))
|
||||
foomodulegen_common.customize_module(mod)
|
||||
|
||||
## ---- finally, generate the whole thing ----
|
||||
mod.generate(FileCodeSink(out_file))
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
import sys
|
||||
|
||||
import pybindgen
|
||||
from pybindgen import (FileCodeSink)
|
||||
|
||||
import foomodulegen_generated
|
||||
import foomodulegen_common
|
||||
|
||||
def my_module_gen():
|
||||
out = FileCodeSink(sys.stdout)
|
||||
root_module = foomodulegen_generated.module_init()
|
||||
|
||||
## this is a very special case when we want to change the name of
|
||||
## the python module to allow parallel testing of the same basic
|
||||
## module 'foo' generated by 3 different code paths; normally
|
||||
## users don't need this.
|
||||
root_module.name = 'foo3'
|
||||
|
||||
foomodulegen_generated.register_types(root_module)
|
||||
foomodulegen_generated.register_methods(root_module)
|
||||
foomodulegen_generated.register_functions(root_module)
|
||||
foomodulegen_common.customize_module(root_module)
|
||||
|
||||
pybindgen.write_preamble(out)
|
||||
root_module.generate(out)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
import cProfile as profile
|
||||
except ImportError:
|
||||
my_module_gen()
|
||||
else:
|
||||
print >> sys.stderr, "** running under profiler"
|
||||
profile.run('my_module_gen()', 'foomodulegen-auto.pstat')
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
import sys
|
||||
import re
|
||||
|
||||
import pybindgen
|
||||
from pybindgen.typehandlers import base as typehandlers
|
||||
from pybindgen import (ReturnValue, Parameter, Module, Function, FileCodeSink)
|
||||
from pybindgen import (CppMethod, CppConstructor, CppClass, Enum)
|
||||
from pybindgen.gccxmlparser import ModuleParser
|
||||
from pybindgen.function import CustomFunctionWrapper
|
||||
from pybindgen.cppmethod import CustomCppMethodWrapper
|
||||
|
||||
|
||||
|
||||
class PointerHolderTransformation(typehandlers.TypeTransformation):
|
||||
def __init__(self):
|
||||
self.rx = re.compile(r'(?:::)?PointerHolder<(\w+)>')
|
||||
|
||||
def get_untransformed_name(self, name):
|
||||
m = self.rx.match(name)
|
||||
if m is None:
|
||||
return None
|
||||
else:
|
||||
return m.group(1)+'*'
|
||||
|
||||
def create_type_handler(self, type_handler, *args, **kwargs):
|
||||
if issubclass(type_handler, Parameter):
|
||||
kwargs['transfer_ownership'] = False
|
||||
elif issubclass(type_handler, ReturnValue):
|
||||
kwargs['caller_owns_return'] = True
|
||||
else:
|
||||
raise AssertionError
|
||||
handler = type_handler(*args, **kwargs)
|
||||
handler.set_tranformation(self, self.get_untransformed_name(args[0]))
|
||||
return handler
|
||||
|
||||
def untransform(self, type_handler, declarations, code_block, expression):
|
||||
return '(%s).thePointer' % (expression,)
|
||||
|
||||
def transform(self, type_handler, declarations, code_block, expression):
|
||||
assert type_handler.untransformed_ctype[-1] == '*'
|
||||
var = declarations.declare_variable(
|
||||
'PointerHolder<%s>' % type_handler.untransformed_ctype[:-1], 'tmp')
|
||||
return '(%s.thePointer = (%s), %s)' % (var, expression, var)
|
||||
|
||||
transf = PointerHolderTransformation()
|
||||
typehandlers.return_type_matcher.register_transformation(transf)
|
||||
typehandlers.param_type_matcher.register_transformation(transf)
|
||||
del transf
|
||||
|
||||
|
||||
|
||||
def customize_module(module):
|
||||
wrapper_body = '''
|
||||
static PyObject *
|
||||
_wrap_foofunction_that_takes_foo_from_string(PyObject * PYBINDGEN_UNUSED(dummy), PyObject *args,
|
||||
PyObject *kwargs, PyObject **return_exception)
|
||||
{
|
||||
PyObject *py_retval;
|
||||
char *datum;
|
||||
const char *keywords[] = {"foo", NULL};
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", (char **) keywords, &datum)) {
|
||||
{
|
||||
PyObject *exc_type, *traceback;
|
||||
PyErr_Fetch(&exc_type, return_exception, &traceback);
|
||||
Py_XDECREF(exc_type);
|
||||
Py_XDECREF(traceback);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
function_that_takes_foo(Foo(datum));
|
||||
py_retval = Py_BuildValue("");
|
||||
return py_retval;
|
||||
}
|
||||
'''
|
||||
module.add_function(CustomFunctionWrapper('function_that_takes_foo',
|
||||
'_wrap_foofunction_that_takes_foo_from_string',
|
||||
wrapper_body))
|
||||
|
||||
|
||||
## test a custom method wrapper
|
||||
Bar, = [cls for cls in module.classes if cls.name == 'Bar']
|
||||
wrapper_body = '''
|
||||
static PyObject *
|
||||
_wrap_PyBar_Hooray_lenx(PyBar *PYBINDGEN_UNUSED(dummy), PyObject *args, PyObject *kwargs,
|
||||
PyObject **return_exception)
|
||||
{
|
||||
PyObject *py_retval;
|
||||
int x;
|
||||
const char *keywords[] = {"x", NULL};
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", (char **) keywords, &x)) {
|
||||
PyObject *exc_type, *traceback;
|
||||
PyErr_Fetch(&exc_type, return_exception, &traceback);
|
||||
Py_XDECREF(exc_type);
|
||||
Py_XDECREF(traceback);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::string retval;
|
||||
retval = Bar::Hooray();
|
||||
py_retval = Py_BuildValue("i", int(retval.size() + x));
|
||||
return py_retval;
|
||||
}
|
||||
'''
|
||||
Bar.add_method(CustomCppMethodWrapper("Hooray", "_wrap_PyBar_Hooray_lenx",
|
||||
wrapper_body,
|
||||
flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"]))
|
||||
|
||||
|
||||
Foo, = [cls for cls in module.classes if cls.name == 'Foo']
|
||||
def Foo_instance_creation_function(dummy_cpp_class, code_block, lvalue,
|
||||
parameters, construct_type_name):
|
||||
code_block.write_code(
|
||||
"%s = new %s(%s);" % (lvalue, construct_type_name, parameters))
|
||||
code_block.write_code("%s->initialize();" % (lvalue,))
|
||||
Foo.set_instance_creation_function(Foo_instance_creation_function)
|
||||
|
||||
+11
-5
@@ -3,12 +3,18 @@ import weakref
|
||||
import gc
|
||||
import os.path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
||||
'..', 'build', 'default', 'examples'))
|
||||
if len(sys.argv) > 1:
|
||||
import foo2 as foo
|
||||
del sys.argv[1]
|
||||
else:
|
||||
'..', 'build', 'default', 'tests'))
|
||||
which = int(sys.argv[1])
|
||||
del sys.argv[1]
|
||||
if which == 1:
|
||||
import foo
|
||||
elif which == 2:
|
||||
import foo2 as foo
|
||||
elif which == 3:
|
||||
import foo3 as foo
|
||||
else:
|
||||
raise AssertionError("bad command line arguments")
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
|
||||
+26
-1
@@ -56,7 +56,9 @@ def build(bld):
|
||||
### Same thing, but using gccxml autoscanning
|
||||
bindgen = bld.create_obj('command-output')
|
||||
bindgen.stdout = 'foomodule2.cc'
|
||||
bindgen.argv = [bindgen.input_file('foomodulegen-auto.py'), bindgen.input_file('foo.h')]
|
||||
bindgen.argv = [bindgen.input_file('foomodulegen-auto.py'),
|
||||
bindgen.input_file('foo.h'),
|
||||
bindgen.output_file('foomodulegen_generated.py')]
|
||||
bindgen.command = bindgen.env['PYTHON']
|
||||
bindgen.command_is_external = True
|
||||
bindgen.prio = 55 # must run after the 'py' objects in ../pybindgen
|
||||
@@ -70,6 +72,29 @@ def build(bld):
|
||||
obj.inst_var = 0 # do not install
|
||||
obj.includes = '.'
|
||||
|
||||
|
||||
### Now using the generated python script
|
||||
bindgen = bld.create_obj('command-output')
|
||||
bindgen.stdout = 'foomodule3.cc'
|
||||
bindgen.argv = [bindgen.input_file('foomodulegen3.py'),
|
||||
bindgen.output_file('foomodule3.cc')]
|
||||
bindgen.command = bindgen.env['PYTHON']
|
||||
bindgen.command_is_external = True
|
||||
bindgen.prio = 56 # run after the previous bindgen
|
||||
|
||||
## yes, this global manipulation of PYTHONPATH is kind of evil :-/
|
||||
## TODO: add WAF command-output support for customising command OS environment
|
||||
os.environ["PYTHONPATH"] = os.pathsep.join([os.environ.get("PYTHONPATH", ''), bindgen.path.abspath(bindgen.env)])
|
||||
|
||||
obj = create_pyext_obj(bld, 'cpp')
|
||||
obj.source = [
|
||||
'foo.cc',
|
||||
'foomodule3.cc'
|
||||
]
|
||||
obj.target = 'foo3'
|
||||
obj.inst_var = 0 # do not install
|
||||
obj.includes = '.'
|
||||
|
||||
## pure C tests
|
||||
bld.add_subdirs('c-hello')
|
||||
|
||||
@@ -123,17 +123,25 @@ def set_options(opt):
|
||||
opt.tool_options('compiler_cc')
|
||||
opt.tool_options('compiler_cxx')
|
||||
|
||||
opt.add_option('--generate-version',
|
||||
help=('Generate a new pybindgen/version.py file from version control'
|
||||
' introspection. Only works from a bzr checkout tree, and is'
|
||||
' meant to be used by pybindgen developers only.'),
|
||||
action="store_true", default=False,
|
||||
dest='generate_version')
|
||||
optgrp = opt.add_option_group("PyBindGen Options")
|
||||
|
||||
opt.add_option('--generate-api-docs',
|
||||
help=('Generate API html documentation, using epydoc.'),
|
||||
action="store_true", default=False,
|
||||
dest='generate_api_docs')
|
||||
if os.path.isdir(".bzr"):
|
||||
optgrp.add_option('--generate-version',
|
||||
help=('Generate a new pybindgen/version.py file from version control'
|
||||
' introspection. Only works from a bzr checkout tree, and is'
|
||||
' meant to be used by pybindgen developers only.'),
|
||||
action="store_true", default=False,
|
||||
dest='generate_version')
|
||||
|
||||
optgrp.add_option('--generate-api-docs',
|
||||
help=('Generate API html documentation, using epydoc.'),
|
||||
action="store_true", default=False,
|
||||
dest='generate_api_docs')
|
||||
|
||||
optgrp.add_option('--examples',
|
||||
help=('Compile the example programs.'),
|
||||
action="store_true", default=False,
|
||||
dest='examples')
|
||||
|
||||
def configure(conf):
|
||||
## Write a pybindgen/version.py file containing the project version
|
||||
@@ -163,12 +171,13 @@ def configure(conf):
|
||||
|
||||
|
||||
def build(bld):
|
||||
if Params.g_options.generate_version:
|
||||
if getattr(Params.g_options, 'generate_version', False):
|
||||
generate_version_py(force=True)
|
||||
|
||||
bld.add_subdirs('pybindgen')
|
||||
bld.add_subdirs('examples')
|
||||
if Params.g_commands['check']:
|
||||
if Params.g_options.examples:
|
||||
bld.add_subdirs('examples')
|
||||
if Params.g_commands['check'] or Params.g_commands['clean']:
|
||||
bld.add_subdirs('tests')
|
||||
|
||||
def shutdown():
|
||||
@@ -179,19 +188,23 @@ def shutdown():
|
||||
|
||||
env = Params.g_build.env()
|
||||
print "Running manual module generation unit tests (module foo)..."
|
||||
retval2 = subprocess.Popen([env['PYTHON'], 'tests/footest.py']).wait()
|
||||
retval2 = subprocess.Popen([env['PYTHON'], 'tests/footest.py', '1']).wait()
|
||||
|
||||
if env['ENABLE_PYGCCXML']:
|
||||
print "Running automatically scanned module generation unit tests (module foo2)..."
|
||||
retval3 = subprocess.Popen([env['PYTHON'], 'tests/footest.py', 'x']).wait()
|
||||
retval3 = subprocess.Popen([env['PYTHON'], 'tests/footest.py', '2']).wait()
|
||||
|
||||
print "Running module generated by automatically generated python script unit tests (module foo3)..."
|
||||
retval3b = subprocess.Popen([env['PYTHON'], 'tests/footest.py', '3']).wait()
|
||||
|
||||
print "Running semi-automatically scanned c-hello module ('hello')..."
|
||||
retval4 = subprocess.Popen([env['PYTHON'], 'tests/c-hello/hellotest.py']).wait()
|
||||
else:
|
||||
print "Skipped automatically scanned module generation unit tests (pygccxml not available)."
|
||||
print "Skipped semi-automatically scanned c-hello module (pygccxml not available)."
|
||||
retval3 = retval4 = 0
|
||||
retval3 = retval3b = retval4 = 0
|
||||
|
||||
if retval1 or retval2 or retval3 or retval4:
|
||||
if retval1 or retval2 or retval3 or retval3b or retval4:
|
||||
raise Params.fatal("Unit test failures")
|
||||
|
||||
if Params.g_options.generate_api_docs:
|
||||
|
||||
Reference in new issue
Block a user