merge from the gccxml-to-python branch

This commit is contained in:
Gustavo J. A. M. Carneiro committed 2008-05-07 17:59:27 +01:00
commit e39021291a
11 files changed
+776 -366

No files matched your search

+11 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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))
+38
View 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')
+120
View File
@@ -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
View File
@@ -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
View File
@@ -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')
+30 -17
View File
@@ -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: