From 954fc286913b2f1fa08d4e1bea44a3f8e0a52189 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Wed, 30 Apr 2008 17:41:01 +0100 Subject: [PATCH 01/29] Add Module support for easy accessing classes and enums by name --- pybindgen/module.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/pybindgen/module.py b/pybindgen/module.py index 312f3e5..3e450fd 100644 --- a/pybindgen/module.py +++ b/pybindgen/module.py @@ -10,15 +10,39 @@ 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") + >>> c1 = CppClass("Bar") + >>> m.add_class(c1) + >>> e1 = Enum("En1", ["XX"]) + >>> m.add_enum(e1) + >>> 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 "", line 1, in + KeyError: 'badname' + """ def __init__(self, name, parent=None, docstring=None, cpp_namespace=None): """Constructor name -- module name """ + super(Module, self).__init__() self.name = name self.parent = parent self.docstring = docstring @@ -132,6 +156,8 @@ class Module(object): assert isinstance(class_, CppClass) class_.module = self self.classes.append(class_) + self[class_.name] = class_ + self[class_.full_name] = class_ def add_cpp_namespace(self, name): """ @@ -148,6 +174,8 @@ class Module(object): assert isinstance(enum, Enum) self.enums.append(enum) enum.module = self + self[enum.name] = enum + self[enum.full_name] = enum def declare_one_time_definition(self, definition_name): """ From 16ccaea5f40dcd19c4c8236d78a88898deb59ef1 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Wed, 30 Apr 2008 17:41:43 +0100 Subject: [PATCH 02/29] clean files even when they are not to be compiled --- wscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wscript b/wscript index e7df956..7602936 100644 --- a/wscript +++ b/wscript @@ -168,7 +168,7 @@ def build(bld): bld.add_subdirs('pybindgen') bld.add_subdirs('examples') - if Params.g_commands['check']: + if Params.g_commands['check'] or Params.g_commands['clean']: bld.add_subdirs('tests') def shutdown(): From 01a70bdbe3ebe76ea0523fa03e4a43a03cb62b6a Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Wed, 30 Apr 2008 18:37:27 +0100 Subject: [PATCH 03/29] Allow also accessing enums/classes of nested modules from the main module --- pybindgen/module.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/pybindgen/module.py b/pybindgen/module.py index 3e450fd..5c84d49 100644 --- a/pybindgen/module.py +++ b/pybindgen/module.py @@ -19,10 +19,15 @@ class Module(dict): >>> 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 @@ -35,6 +40,10 @@ class Module(dict): Traceback (most recent call last): File "", line 1, in KeyError: 'badname' + >>> m["foo::subm::Zbr"] is c2 + True + >>> m["foo::subm::En2"] is e2 + True """ @@ -156,8 +165,11 @@ class Module(dict): assert isinstance(class_, CppClass) class_.module = self self.classes.append(class_) + module = self self[class_.name] = class_ - self[class_.full_name] = class_ + while module is not None: + module[class_.full_name] = class_ + module = module.parent def add_cpp_namespace(self, name): """ @@ -175,7 +187,10 @@ class Module(dict): self.enums.append(enum) enum.module = self self[enum.name] = enum - self[enum.full_name] = enum + module = self + while module is not None: + module[enum.full_name] = enum + module = module.parent def declare_one_time_definition(self, definition_name): """ From 673e704c6f9e905790f7d90f0e912e211a012f6f Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Thu, 1 May 2008 17:40:44 +0100 Subject: [PATCH 04/29] Fix Module.cpp_namespace_prefix so that it never starts with :: --- pybindgen/module.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/pybindgen/module.py b/pybindgen/module.py index 5c84d49..66c16c1 100644 --- a/pybindgen/module.py +++ b/pybindgen/module.py @@ -48,8 +48,11 @@ class Module(dict): """ def __init__(self, name, parent=None, docstring=None, cpp_namespace=None): - """Constructor - name -- module name + """ + @param name: module name + @param parent: parent L{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.name = name @@ -91,11 +94,19 @@ class Module(dict): self.includes = parent.includes 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 @@ -241,14 +252,22 @@ class Module(dict): 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 From 48946bca98c53bb7f0f84da52252d7aebfbc91b6 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Thu, 1 May 2008 19:45:10 +0100 Subject: [PATCH 05/29] refactoring --- pybindgen/module.py | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/pybindgen/module.py b/pybindgen/module.py index 66c16c1..92c4fd7 100644 --- a/pybindgen/module.py +++ b/pybindgen/module.py @@ -166,6 +166,23 @@ class Module(dict): 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_): """ @@ -176,11 +193,7 @@ class Module(dict): assert isinstance(class_, CppClass) class_.module = self self.classes.append(class_) - module = self - self[class_.name] = class_ - while module is not None: - module[class_.full_name] = class_ - module = module.parent + self.register_type(class_.name, class_.full_name, class_) def add_cpp_namespace(self, name): """ @@ -197,11 +210,7 @@ class Module(dict): assert isinstance(enum, Enum) self.enums.append(enum) enum.module = self - self[enum.name] = enum - module = self - while module is not None: - module[enum.full_name] = enum - module = module.parent + self.register_type(enum.name, enum.full_name, enum) def declare_one_time_definition(self, definition_name): """ From 3732021ead87126a0e87aad145b77683092de4f2 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Thu, 1 May 2008 19:45:50 +0100 Subject: [PATCH 06/29] register class aliases with the module type registry as well --- pybindgen/cppclass.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pybindgen/cppclass.py b/pybindgen/cppclass.py index 6f94544..60349f3 100644 --- a/pybindgen/cppclass.py +++ b/pybindgen/cppclass.py @@ -766,6 +766,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) From 0148bbf3fe0038fe20798fa9db00aff19157e012 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Thu, 1 May 2008 19:56:51 +0100 Subject: [PATCH 07/29] Fix the built test modules path (waf check was testing installed modules!) --- tests/c-hello/hellotest.py | 2 +- tests/footest.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/c-hello/hellotest.py b/tests/c-hello/hellotest.py index d6d8721..e071117 100644 --- a/tests/c-hello/hellotest.py +++ b/tests/c-hello/hellotest.py @@ -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 diff --git a/tests/footest.py b/tests/footest.py index d7a78ce..d15c8be 100644 --- a/tests/footest.py +++ b/tests/footest.py @@ -3,7 +3,7 @@ import weakref import gc import os.path sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), - '..', 'build', 'default', 'examples')) + '..', 'build', 'default', 'tests')) if len(sys.argv) > 1: import foo2 as foo del sys.argv[1] From 756bf4b74904c1d729b5bf0d15bccd83f45a1f32 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Fri, 2 May 2008 00:01:03 +0100 Subject: [PATCH 08/29] First small steps to generate python scripts.. for now only classes at the root namespace.. --- pybindgen/gccxmlparser.py | 186 ++++++++++++++++++++++++++----------- tests/foomodulegen-auto.py | 12 ++- tests/wscript | 4 +- 3 files changed, 140 insertions(+), 62 deletions(-) diff --git a/pybindgen/gccxmlparser.py b/pybindgen/gccxmlparser.py index 6d51ce4..e3f63db 100644 --- a/pybindgen/gccxmlparser.py +++ b/pybindgen/gccxmlparser.py @@ -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 @@ -64,35 +64,27 @@ 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 + 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 +98,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: @@ -226,6 +218,7 @@ class GccXmlTypeRegistry(object): return Parameter.new(normalize_name(type_info.decl_string), param_name, **kwargs) else: return Parameter.new(self._fixed_std_type_name(type_info), param_name, **kwargs) + assert isinstance(cpp_class, CppClass)#, cpp_class.full_name if not is_pointer and not is_reference: return cpp_class.ThisClassParameter(type_info.decl_string, param_name, **kwargs) if is_pointer and not is_reference: @@ -248,8 +241,6 @@ class GccXmlTypeRegistry(object): raise assert 0, "this line should not be reached" -type_registry = GccXmlTypeRegistry() - class AnnotationsScanner(object): def __init__(self): @@ -371,6 +362,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 +424,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, 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 +434,14 @@ 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, 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, 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 +452,11 @@ class ModuleParser(object): 3. parse_finalize() """ assert isinstance(header_files, list) + 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,14 +475,30 @@ 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) + self.pygen_sink.writeln("import sys") + self.pygen_sink.writeln("from pybindgen import CppClass, Module, FileCodeSink, write_preamble") + 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)) + 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.pygen_sink.writeln("def register_types(module):") + self.pygen_sink.indent() + self.pygen_sink.writeln("root_module = module.get_root()") self._scan_namespace_types(self.module, self.module_namespace) self._types_scanned = True + self.pygen_sink.unindent() + self.pygen_sink.writeln() def scan_methods(self): assert self._types_scanned - for class_wrapper in type_registry.ordered_classes: + for class_wrapper in self.type_registry.ordered_classes: if isinstance(class_wrapper.gccxml_definition, class_declaration_t): continue # skip classes not fully defined self._scan_class_methods(class_wrapper.gccxml_definition, class_wrapper) @@ -497,6 +509,19 @@ class ModuleParser(object): 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("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 +557,18 @@ class ModuleParser(object): if not self._class_has_public_destructor(cls): kwargs.setdefault('is_singleton', True) + def _pygen_kwargs(self, 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 def _scan_namespace_types(self, module, module_namespace, outer_class=None): + root_module = module.get_root() + ## scan enumerations if outer_class is None: enums = module_namespace.enums(function=self.location_filter, @@ -599,11 +634,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)] + self._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 +662,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 +719,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 +731,15 @@ class ModuleParser(object): if not isinstance(target_type, class_t): continue try: - type_registry.find_class(operator.return_type.decl_string, '::') + 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 +767,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)] + self._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,7 +799,8 @@ 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) if outer_class is None: @@ -775,7 +845,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 +878,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), @@ -822,9 +892,9 @@ class ModuleParser(object): 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)) except (TypeLookupError, TypeConfigurationError), ex: warnings.warn_explicit("Parameter '%s %s' error (used in %s): %r" % (arg.type.decl_string, arg.name, member, ex), @@ -891,8 +961,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), @@ -925,7 +995,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), @@ -958,6 +1028,7 @@ class ModuleParser(object): 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 +1067,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 +1085,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: @@ -1036,13 +1107,16 @@ class ModuleParser(object): 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 '::')) + #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 diff --git a/tests/foomodulegen-auto.py b/tests/foomodulegen-auto.py index 55a28d0..47ea31d 100644 --- a/tests/foomodulegen-auto.py +++ b/tests/foomodulegen-auto.py @@ -51,12 +51,14 @@ 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]], pygen_sink=FileCodeSink(pygen_file)) + pygen_file.close() wrapper_body = ''' static PyObject * @@ -131,9 +133,9 @@ 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') diff --git a/tests/wscript b/tests/wscript index 372f47a..beb00b3 100644 --- a/tests/wscript +++ b/tests/wscript @@ -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 From e5422909e3dbdea42676ca7a6ef0b4cfffee3f69 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Fri, 2 May 2008 13:20:59 +0100 Subject: [PATCH 09/29] make pylint a little happier --- pybindgen/gccxmlparser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pybindgen/gccxmlparser.py b/pybindgen/gccxmlparser.py index e3f63db..b251983 100644 --- a/pybindgen/gccxmlparser.py +++ b/pybindgen/gccxmlparser.py @@ -731,7 +731,7 @@ class ModuleParser(object): if not isinstance(target_type, class_t): continue try: - root_module[normalize_class_name(operator.return_type.decl_string, '::')] + dummy = root_module[normalize_class_name(operator.return_type.decl_string, '::')] except KeyError: ok = False break From 73dfc384c6ef48fd0ad252395eb67b25ea7859d5 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Fri, 2 May 2008 16:45:18 +0100 Subject: [PATCH 10/29] pygen: register types of nested modules --- pybindgen/gccxmlparser.py | 53 ++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/pybindgen/gccxmlparser.py b/pybindgen/gccxmlparser.py index b251983..7b265c7 100644 --- a/pybindgen/gccxmlparser.py +++ b/pybindgen/gccxmlparser.py @@ -488,13 +488,8 @@ class ModuleParser(object): self.type_registry = GccXmlTypeRegistry(self.module) def scan_types(self): - self.pygen_sink.writeln("def register_types(module):") - self.pygen_sink.indent() - self.pygen_sink.writeln("root_module = module.get_root()") - 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 - self.pygen_sink.unindent() - self.pygen_sink.writeln() def scan_methods(self): assert self._types_scanned @@ -566,7 +561,7 @@ class ModuleParser(object): l.append("%s=%r" % (key, val)) return l - def _scan_namespace_types(self, module, module_namespace, outer_class=None): + def _scan_namespace_types(self, module, module_namespace, outer_class=None, pygen_register_function_name=None): root_module = module.get_root() ## scan enumerations @@ -678,6 +673,12 @@ class ModuleParser(object): cls._pybindgen_postpone_reason = reason unregistered_classes.append(cls) + 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() + while unregistered_classes: cls = unregistered_classes.pop(0) typedef = None @@ -803,13 +804,47 @@ class ModuleParser(object): other_class = root_module[normalize_class_name(operator.return_type.decl_string, '::')] class_wrapper.implicitly_converts_to(other_class) + 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""" From 80e0c2897b54ff60314ac1d4f62963a01aff2adb Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Sun, 4 May 2008 01:33:03 +0100 Subject: [PATCH 11/29] Add a CppClass mangled_full_name attribute. --- pybindgen/cppclass.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pybindgen/cppclass.py b/pybindgen/cppclass.py index 60349f3..e7b042a 100644 --- a/pybindgen/cppclass.py +++ b/pybindgen/cppclass.py @@ -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 From ccdbceac9f46552ef4c833b4b4e68bf76af35f6c Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Sun, 4 May 2008 01:33:33 +0100 Subject: [PATCH 12/29] python generation now takes care of class methods, constructors, and member variables; missing: functions --- pybindgen/gccxmlparser.py | 189 ++++++++++++++++++++++++++++---------- 1 file changed, 138 insertions(+), 51 deletions(-) diff --git a/pybindgen/gccxmlparser.py b/pybindgen/gccxmlparser.py index 7b265c7..8d9b301 100644 --- a/pybindgen/gccxmlparser.py +++ b/pybindgen/gccxmlparser.py @@ -71,6 +71,17 @@ def normalize_class_name(class_name, module_namespace): 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, root_module): """ @@ -165,25 +176,36 @@ 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 if not is_pointer and not is_reference: - return cpp_class.ThisClassReturn(type_info.decl_string) + 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 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) - 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) + 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 + 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 + if not is_pointer and is_reference: - return cpp_class.ThisClassRefReturn(type_info.decl_string, **kwargs) + 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): @@ -207,35 +229,54 @@ 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 if not is_pointer and not is_reference: - return cpp_class.ThisClassParameter(type_info.decl_string, param_name, **kwargs) + 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 + 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) - 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 + if not is_pointer and is_reference: try: - return cpp_class.ThisClassRefParameter(type_info.decl_string, param_name, **kwargs) + 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 @@ -424,7 +465,7 @@ class ModuleParser(object): return True return False - def parse(self, header_files, include_paths=None, whitelist_paths=None, pygen_sink=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: @@ -434,14 +475,15 @@ class ModuleParser(object): 4. scan_functions() 5. parse_finalize() """ - self.parse_init(header_files, include_paths, whitelist_paths, pygen_sink) + 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, pygen_sink=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 @@ -452,6 +494,7 @@ 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) @@ -475,13 +518,20 @@ 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 CppClass, Module, FileCodeSink, write_preamble") + 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") 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() @@ -493,10 +543,29 @@ class ModuleParser(object): def scan_methods(self): assert self._types_scanned + 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 @@ -552,15 +621,6 @@ class ModuleParser(object): if not self._class_has_public_destructor(cls): kwargs.setdefault('is_singleton', True) - def _pygen_kwargs(self, 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 - def _scan_namespace_types(self, module, module_namespace, outer_class=None, pygen_register_function_name=None): root_module = module.get_root() @@ -594,7 +654,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): @@ -632,7 +692,7 @@ class ModuleParser(object): class_wrapper = CppClass(alias.name, **kwargs) self.pygen_sink.writeln("module.add_class(CppClass(%s))" % - ", ".join([repr(alias.name)] + self._pygen_kwargs(kwargs))) + ", ".join([repr(alias.name)] + _pygen_kwargs(kwargs))) class_wrapper.gccxml_definition = cls module.add_class(class_wrapper) @@ -779,7 +839,7 @@ class ModuleParser(object): class_wrapper = CppClass(cls_name, **kwargs) self.pygen_sink.writeln("module.add_class(CppClass(%s))" % - ", ".join([repr(cls_name)] + self._pygen_kwargs(kwargs))) + ", ".join([repr(cls_name)] + _pygen_kwargs(kwargs))) class_wrapper.gccxml_definition = cls module.add_class(class_wrapper) @@ -930,6 +990,7 @@ class ModuleParser(object): 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), @@ -958,14 +1019,23 @@ 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 + + method_wrapper = CppMethod(return_type, member.name, arguments, **kwargs) method_wrapper.gccxml_definition = member try: class_wrapper.add_method(method_wrapper) @@ -984,6 +1054,12 @@ class ModuleParser(object): else: for hook in self._post_scan_hooks: hook(self, member, method_wrapper) + + 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))) ## ------------ constructor -------------------- elif isinstance(member, calldef.constructor_t): @@ -1018,6 +1094,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': @@ -1039,9 +1119,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 @@ -1140,6 +1224,8 @@ 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 '::')) @@ -1154,6 +1240,7 @@ class ModuleParser(object): 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 continue From 8eae263419ae40e2fb5aaadb475fac3016c8f496 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Sun, 4 May 2008 01:34:57 +0100 Subject: [PATCH 13/29] Integrate generation of python scripts into unit testing framework. Naturally not all tests pass because work is not yet complete. --- tests/foomodulegen-auto.py | 3 +-- tests/footest.py | 14 ++++++++++---- tests/wscript | 19 +++++++++++++++++++ wscript | 10 +++++++--- 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/tests/foomodulegen-auto.py b/tests/foomodulegen-auto.py index 47ea31d..0f1922d 100644 --- a/tests/foomodulegen-auto.py +++ b/tests/foomodulegen-auto.py @@ -55,9 +55,8 @@ 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]], pygen_sink=FileCodeSink(pygen_file)) + module = module_parser.parse([sys.argv[1]], includes=['"foo.h"'], pygen_sink=FileCodeSink(pygen_file)) pygen_file.close() wrapper_body = ''' diff --git a/tests/footest.py b/tests/footest.py index d15c8be..3568723 100644 --- a/tests/footest.py +++ b/tests/footest.py @@ -4,11 +4,17 @@ import gc import os.path sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'build', 'default', 'tests')) -if len(sys.argv) > 1: - import foo2 as foo - del sys.argv[1] -else: +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 diff --git a/tests/wscript b/tests/wscript index beb00b3..7572ac6 100644 --- a/tests/wscript +++ b/tests/wscript @@ -72,6 +72,25 @@ 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('foomodulegen-generated.py'), + bindgen.output_file('foomodule3.cc')] + bindgen.command = bindgen.env['PYTHON'] + bindgen.command_is_external = True + bindgen.prio = 56 # run after the previous bindgen + + 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') diff --git a/wscript b/wscript index 7602936..43d3e41 100644 --- a/wscript +++ b/wscript @@ -179,11 +179,15 @@ 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: @@ -191,7 +195,7 @@ def shutdown(): print "Skipped semi-automatically scanned c-hello module (pygccxml not available)." retval3 = 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: From 0431ead72019ed9fad716c500836dcba71376eba Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Sun, 4 May 2008 16:24:56 +0100 Subject: [PATCH 14/29] stupid bug --- wscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wscript b/wscript index 43d3e41..99cd19b 100644 --- a/wscript +++ b/wscript @@ -193,7 +193,7 @@ def shutdown(): 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 retval3b or retval4: raise Params.fatal("Unit test failures") From a538c34f30f5608be07b0e8dde7b07c4f7dd73f3 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Sun, 4 May 2008 16:25:42 +0100 Subject: [PATCH 15/29] Factor out the common portions of foomodulegen and foomodulegen-auto into a common python module --- tests/foomodulegen-auto.py | 107 +------------------------------ tests/foomodulegen.py | 101 +---------------------------- tests/foomodulegen_common.py | 120 +++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+), 204 deletions(-) create mode 100644 tests/foomodulegen_common.py diff --git a/tests/foomodulegen-auto.py b/tests/foomodulegen-auto.py index 0f1922d..0a9d660 100644 --- a/tests/foomodulegen-auto.py +++ b/tests/foomodulegen-auto.py @@ -11,45 +11,7 @@ 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 - - +import foomodulegen_common def my_module_gen(): out = FileCodeSink(sys.stdout) @@ -59,71 +21,7 @@ def my_module_gen(): 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) @@ -137,4 +35,3 @@ if __name__ == '__main__': print >> sys.stderr, "** running under profiler" profile.run('my_module_gen()', 'foomodulegen-auto.pstat') - diff --git a/tests/foomodulegen.py b/tests/foomodulegen.py index c8dfa05..0a81cd3 100755 --- a/tests/foomodulegen.py +++ b/tests/foomodulegen.py @@ -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)) diff --git a/tests/foomodulegen_common.py b/tests/foomodulegen_common.py new file mode 100644 index 0000000..d1a2207 --- /dev/null +++ b/tests/foomodulegen_common.py @@ -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) + From 36fdd04aa22d7513ecdfec8707b3dd00b7f6ff98 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Sun, 4 May 2008 17:36:06 +0100 Subject: [PATCH 16/29] missing register_methods() call --- pybindgen/gccxmlparser.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pybindgen/gccxmlparser.py b/pybindgen/gccxmlparser.py index 8d9b301..219f018 100644 --- a/pybindgen/gccxmlparser.py +++ b/pybindgen/gccxmlparser.py @@ -579,6 +579,7 @@ class ModuleParser(object): 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("write_preamble(out)") self.pygen_sink.writeln("root_module.generate(out)") self.pygen_sink.unindent() From 29fa6466bcd3e890aa16ced310d1a703f9b099c4 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Sun, 4 May 2008 17:38:54 +0100 Subject: [PATCH 17/29] Allow changing module name after it is created --- pybindgen/module.py | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/pybindgen/module.py b/pybindgen/module.py index 92c4fd7..b42aa71 100644 --- a/pybindgen/module.py +++ b/pybindgen/module.py @@ -55,28 +55,29 @@ class Module(dict): @param cpp_namespace: C++ namespace prefix associated with this module """ super(Module, self).__init__() - self.name = name 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 = [] @@ -93,6 +94,22 @@ class Module(dict): 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: From a7ea12220c2a04d4be6ae85522abcb0ccf68e4e1 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Sun, 4 May 2008 17:39:16 +0100 Subject: [PATCH 18/29] Fix foo3 module build system integration --- tests/wscript | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/wscript b/tests/wscript index 7572ac6..048ccb2 100644 --- a/tests/wscript +++ b/tests/wscript @@ -58,7 +58,7 @@ def build(bld): bindgen.stdout = 'foomodule2.cc' bindgen.argv = [bindgen.input_file('foomodulegen-auto.py'), bindgen.input_file('foo.h'), - bindgen.output_file('foomodulegen-generated.py')] + 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 @@ -76,12 +76,16 @@ def build(bld): ### Now using the generated python script bindgen = bld.create_obj('command-output') bindgen.stdout = 'foomodule3.cc' - bindgen.argv = [bindgen.input_file('foomodulegen-generated.py'), + 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', From f400ed60b29b5e5e6ca3e154c13b6b921298c669 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Sun, 4 May 2008 17:58:45 +0100 Subject: [PATCH 19/29] generate python code when abstract classes are detected --- pybindgen/gccxmlparser.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/pybindgen/gccxmlparser.py b/pybindgen/gccxmlparser.py index 219f018..6b4fea3 100644 --- a/pybindgen/gccxmlparser.py +++ b/pybindgen/gccxmlparser.py @@ -983,6 +983,8 @@ 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 @@ -1001,10 +1003,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): @@ -1038,12 +1043,25 @@ class ModuleParser(object): 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) @@ -1052,15 +1070,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) - 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))) ## ------------ constructor -------------------- elif isinstance(member, calldef.constructor_t): From 8beec32b811650122c89a4e78779dcc71f9a13a6 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Sun, 4 May 2008 18:45:36 +0100 Subject: [PATCH 20/29] 'pygen' plain functions --- pybindgen/gccxmlparser.py | 57 ++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/pybindgen/gccxmlparser.py b/pybindgen/gccxmlparser.py index 6b4fea3..1244906 100644 --- a/pybindgen/gccxmlparser.py +++ b/pybindgen/gccxmlparser.py @@ -524,7 +524,7 @@ class ModuleParser(object): 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") + self.pygen_sink.writeln("from pybindgen import ReturnValue, Parameter, Function") self.pygen_sink.writeln() self.pygen_sink.writeln("def module_init():") self.pygen_sink.indent() @@ -567,10 +567,6 @@ class ModuleParser(object): 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() @@ -580,6 +576,7 @@ class ModuleParser(object): 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() @@ -1160,9 +1157,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('__'): @@ -1259,25 +1264,53 @@ class ModuleParser(object): function_wrapper.gccxml_definition = fun 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) + + arglist_repr = ("[" + ', '.join([arg._pygen_repr for arg in arguments]) + "]") + 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', '::') From d04c71ccad951846ae4e1a14bdfd3f44ff4817cd Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Sun, 4 May 2008 19:19:55 +0100 Subject: [PATCH 21/29] pygen enums --- pybindgen/gccxmlparser.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/pybindgen/gccxmlparser.py b/pybindgen/gccxmlparser.py index 1244906..aabbf5f 100644 --- a/pybindgen/gccxmlparser.py +++ b/pybindgen/gccxmlparser.py @@ -524,7 +524,7 @@ class ModuleParser(object): 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") + 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() @@ -622,6 +622,13 @@ class ModuleParser(object): 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() + + ## scan enumerations if outer_class is None: enums = module_namespace.enums(function=self.location_filter, @@ -645,6 +652,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 @@ -731,12 +744,6 @@ class ModuleParser(object): cls._pybindgen_postpone_reason = reason unregistered_classes.append(cls) - 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() - while unregistered_classes: cls = unregistered_classes.pop(0) typedef = None From fec977ed1680a5b3e8b800c95ab3757640dc0fcf Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Sun, 4 May 2008 20:03:24 +0100 Subject: [PATCH 22/29] pygen function-as-method and function-as-constructor --- pybindgen/gccxmlparser.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/pybindgen/gccxmlparser.py b/pybindgen/gccxmlparser.py index aabbf5f..ca9e4a7 100644 --- a/pybindgen/gccxmlparser.py +++ b/pybindgen/gccxmlparser.py @@ -1251,7 +1251,7 @@ class ModuleParser(object): if not ok: continue - #arglist_repr = ("[" + ', '.join([arg._pygen_repr for arg in arguments]) + "]") + arglist_repr = ("[" + ', '.join([arg._pygen_repr for arg in arguments]) + "]") if as_method is not None: assert of_class is not None @@ -1261,7 +1261,14 @@ class ModuleParser(object): function_wrapper = Function(return_type, fun.name, arguments) cpp_class.add_method(function_wrapper, name=as_method) 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 '::'))] @@ -1269,6 +1276,11 @@ class ModuleParser(object): 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 = {} @@ -1280,7 +1292,6 @@ class ModuleParser(object): func_wrapper.gccxml_definition = fun module.add_function(func_wrapper, name=alt_name) - arglist_repr = ("[" + ', '.join([arg._pygen_repr for arg in arguments]) + "]") if alt_name is None: _pygen_altname_arg = '' else: From 4ab5868cfff5ee76259ef5eeaa574d58988ead01 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Sun, 4 May 2008 21:23:46 +0100 Subject: [PATCH 23/29] Always use Parameter.new and ReturnValue.new, to make the generated python code simpler --- pybindgen/gccxmlparser.py | 93 ++++++++++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 26 deletions(-) diff --git a/pybindgen/gccxmlparser.py b/pybindgen/gccxmlparser.py index ca9e4a7..fbc33a4 100644 --- a/pybindgen/gccxmlparser.py +++ b/pybindgen/gccxmlparser.py @@ -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): @@ -184,26 +186,46 @@ class GccXmlTypeRegistry(object): % (", ".join([repr(name)] + _pygen_kwargs(kwargs)))) return retval + ## class value if not is_pointer and not is_reference: - retval = cpp_class.ThisClassReturn(type_info.decl_string) - retval._pygen_repr = ("root_module[%r].ThisClassReturn(%r)" % (cpp_class.full_name, type_info.decl_string)) + 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.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 - 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)))) + 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: - 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)))) + 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" @@ -250,32 +272,51 @@ class GccXmlTypeRegistry(object): return retval assert isinstance(cpp_class, CppClass)#, cpp_class.full_name + + ## class value if not is_pointer and not is_reference: - 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)))) + 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) - 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 - - if not is_pointer and is_reference: - try: - retval = cpp_class.ThisClassRefParameter(type_info.decl_string, param_name, **kwargs) - retval._pygen_repr = ("root_module[%r].ThisClassRefParameter(%s)" + 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.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: + 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 From abd50a82fa95e913b82ac67f8e453e1bea1610b6 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Sun, 4 May 2008 22:34:40 +0100 Subject: [PATCH 24/29] 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. --- pybindgen/gccxmlparser.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pybindgen/gccxmlparser.py b/pybindgen/gccxmlparser.py index fbc33a4..fc08fd9 100644 --- a/pybindgen/gccxmlparser.py +++ b/pybindgen/gccxmlparser.py @@ -1086,6 +1086,13 @@ class ModuleParser(object): 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 From 44b5b52182083360ba1bc02092d6453e13f347af Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Wed, 7 May 2008 11:30:40 +0100 Subject: [PATCH 25/29] missing file :-/ --- tests/foomodulegen3.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/foomodulegen3.py diff --git a/tests/foomodulegen3.py b/tests/foomodulegen3.py new file mode 100644 index 0000000..bcbe4c1 --- /dev/null +++ b/tests/foomodulegen3.py @@ -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') + From fea45edd18fd0b824bb768453d5a3b79ae53097c Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Wed, 7 May 2008 11:48:48 +0100 Subject: [PATCH 26/29] Add --examples option (only compiles examples if this option is given); add a PyBindGen option group, for prettier help output. --- wscript | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/wscript b/wscript index 99cd19b..60c4098 100644 --- a/wscript +++ b/wscript @@ -123,17 +123,24 @@ 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') + 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 @@ -167,7 +174,8 @@ def build(bld): generate_version_py(force=True) bld.add_subdirs('pybindgen') - bld.add_subdirs('examples') + if Params.g_options.examples: + bld.add_subdirs('examples') if Params.g_commands['check'] or Params.g_commands['clean']: bld.add_subdirs('tests') From 44b569a5097ee62ff6474127889e8ad16f1dd76d Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Wed, 7 May 2008 11:56:08 +0100 Subject: [PATCH 27/29] Hide the --generate-version option if not running from a bzr branch --- wscript | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/wscript b/wscript index 60c4098..c3ea821 100644 --- a/wscript +++ b/wscript @@ -125,12 +125,13 @@ def set_options(opt): optgrp = opt.add_option_group("PyBindGen Options") - 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') + 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.'), @@ -170,7 +171,7 @@ 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') From 21374694f747a050a1f088ca53ca7b0d6073fce9 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Wed, 7 May 2008 16:27:28 +0100 Subject: [PATCH 28/29] pygen implicit constructor --- pybindgen/gccxmlparser.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pybindgen/gccxmlparser.py b/pybindgen/gccxmlparser.py index fc08fd9..2c41a02 100644 --- a/pybindgen/gccxmlparser.py +++ b/pybindgen/gccxmlparser.py @@ -1202,6 +1202,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) From b1641fd3e7ab71b87004c6a034cb17d40d515b6c Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Wed, 7 May 2008 17:57:44 +0100 Subject: [PATCH 29/29] pygen implicit conversions --- pybindgen/gccxmlparser.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pybindgen/gccxmlparser.py b/pybindgen/gccxmlparser.py index 2c41a02..69775f1 100644 --- a/pybindgen/gccxmlparser.py +++ b/pybindgen/gccxmlparser.py @@ -909,6 +909,8 @@ class ModuleParser(object): #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: