diff --git a/pybindgen/__init__.py b/pybindgen/__init__.py index cadff26..91e8eb2 100644 --- a/pybindgen/__init__.py +++ b/pybindgen/__init__.py @@ -72,7 +72,7 @@ from function import Function from typehandlers.codesink import CodeSink, FileCodeSink from cppclass import CppMethod, CppClass, CppConstructor from enum import Enum -from utils import write_preamble +from utils import write_preamble, param, retval import version diff --git a/pybindgen/cppclass.py b/pybindgen/cppclass.py index 565a13b..09213d0 100644 --- a/pybindgen/cppclass.py +++ b/pybindgen/cppclass.py @@ -12,7 +12,8 @@ from typehandlers.codesink import NullCodeSink, MemoryCodeSink from cppmethod import CppMethod, CppConstructor, CppNoConstructor, CppFunctionAsConstructor, \ CppOverloadedMethod, CppOverloadedConstructor, \ - CppVirtualMethodParentCaller, CppVirtualMethodProxy, CustomCppMethodWrapper + CppVirtualMethodParentCaller, CppVirtualMethodProxy, CustomCppMethodWrapper, \ + CppDummyMethod from cppattribute import (CppInstanceAttributeGetter, CppInstanceAttributeSetter, CppStaticAttributeGetter, CppStaticAttributeSetter, @@ -396,6 +397,7 @@ class CppClass(object): self.is_singleton = is_singleton self.full_name = None # full name with C++ namespaces attached and template parameters self.methods = {} # name => OverloadedMethod + self._dummy_methods = [] # methods that have parameter/retval binding problems self.nonpublic_methods = [] self.constructors = [] # (name, wrapper) pairs self.slots = dict() @@ -564,7 +566,7 @@ class CppClass(object): self._have_pure_virtual_methods = False for pos, cls in enumerate(mro_reversed): - for method in cls.get_all_methods(): + for method in list(cls.get_all_methods()) + cls._dummy_methods: if not isinstance(method, CppMethod): continue @@ -574,7 +576,7 @@ class CppClass(object): ## this pure virtual method. implemented = False for child_cls in mro_reversed[pos+1:]: - for child_method in child_cls.get_all_methods(): + for child_method in list(child_cls.get_all_methods()) + child_cls._dummy_methods: if not isinstance(child_method, CppMethod): continue if not child_method.is_virtual: @@ -1010,7 +1012,17 @@ public: ## else: - meth = CppMethod(*args, **kwargs) + try: + meth = CppMethod(*args, **kwargs) + except utils.SkipWrapper: + if kwargs.get('is_virtual', False): + ## if the method was supposed to be virtual, this + ## is a very important fact that needs to be + ## recorded in the class, even if the method is + ## not wrapped. + dummy = CppDummyMethod(*args, **kwargs) + self._dummy_methods.append(dummy) + return None self._add_method_obj(meth) return meth @@ -1020,7 +1032,10 @@ public: L{Function.__init__} for information on accepted parameters. TODO: explain the implicit first function parameter """ - meth = function.Function(*args, **kwargs) + try: + meth = function.Function(*args, **kwargs) + except utils.SkipWrapper: + return None self._add_method_obj(meth) return meth @@ -1028,7 +1043,10 @@ public: """ Adds a custom method wrapper. See L{CustomCppMethodWrapper} for more information. """ - meth = CustomCppMethodWrapper(*args, **kwargs) + try: + meth = CustomCppMethodWrapper(*args, **kwargs) + except utils.SkipWrapper: + return None self._add_method_obj(meth) return meth @@ -1072,7 +1090,10 @@ public: constructor.module = self.module ## else: - constructor = CppConstructor(*args, **kwargs) + try: + constructor = CppConstructor(*args, **kwargs) + except utils.SkipWrapper: + return None self._add_constructor_obj(constructor) return constructor @@ -1081,7 +1102,10 @@ public: Wrap a function that behaves as a constructor to the class. See the documentation for L{CppFunctionAsConstructor.__init__} for information on accepted parameters. """ - constructor = CppFunctionAsConstructor(*args, **kwargs) + try: + constructor = CppFunctionAsConstructor(*args, **kwargs) + except utils.SkipWrapper: + return None self._add_constructor_obj(constructor) return constructor @@ -1098,6 +1122,11 @@ public: DeprecationWarning, stacklevel=2) value_type, name = name, value_type + try: + value_type = utils.eval_retval(value_type, None) + except utils.SkipWrapper: + return + assert isinstance(value_type, ReturnValue) getter = CppStaticAttributeGetter(value_type, self, name) if is_const: @@ -1122,6 +1151,11 @@ public: DeprecationWarning, stacklevel=2) value_type, name = name, value_type + try: + value_type = utils.eval_retval(value_type, None) + except utils.SkipWrapper: + return + assert isinstance(value_type, ReturnValue) getter_wrapper = CppInstanceAttributeGetter(value_type, self, name, getter=getter) if is_const: diff --git a/pybindgen/cppmethod.py b/pybindgen/cppmethod.py index ce50f9c..00b42b0 100644 --- a/pybindgen/cppmethod.py +++ b/pybindgen/cppmethod.py @@ -7,12 +7,12 @@ from copy import copy from typehandlers.base import ForwardWrapperBase, ReverseWrapperBase, \ join_ctype_and_name, CodeGenerationError, ReturnValue +from typehandlers.base import ReturnValue, Parameter from typehandlers import codesink import overloading import settings import utils - class CppMethod(ForwardWrapperBase): """ Class that generates a wrapper to a C++ class method @@ -72,6 +72,10 @@ class CppMethod(ForwardWrapperBase): if unblock_threads is None: unblock_threads = settings.unblock_threads + + return_value = utils.eval_retval(return_value, self) + parameters = [utils.eval_param(param, self) for param in parameters] + super(CppMethod, self).__init__( return_value, parameters, "return NULL;", "return NULL;", @@ -261,6 +265,58 @@ class CppOverloadedMethod(overloading.OverloadedWrapper): ERROR_RETURN = 'return NULL;' + +class DummyReturnValue(ReturnValue): + CTYPES = [] + """ + A 'dummy' return value object used for modelling methods that have + incomplete or incorrect parameters or return values. + """ + def __init__(self, arg): + """ + Accepts either a ReturnValue object or a tuple as sole + parameter. In case it's a tuple, it is assumed to be a retval + spec (*args, **kwargs). + """ + if isinstance(arg, ReturnValue): + super(DummyReturnValue, self).__init__(arg.ctype) + else: + args, kwargs = utils.parse_retval_spec(arg) + super(DummyReturnValue, self).__init__(*args, **kwargs) + + +class DummyParameter(Parameter): + CTYPES = [] + DIRECTIONS = [Parameter.DIRECTION_IN, Parameter.DIRECTION_OUT, Parameter.DIRECTION_INOUT] + """ + A 'dummy' parameter object used for modelling methods that have + incomplete or incorrect parameters or return values. + """ + def __init__(self, arg): + """ + Accepts either a Parameter object or a tuple as sole + parameter. In case it's a tuple, it is assumed to be a retval + spec (*args, **kwargs). + """ + if isinstance(arg, ReturnValue): + super(DummyParameter, self).__init__(arg.ctype) + else: + args, kwargs = utils.parse_param_spec(arg) + super(DummyParameter, self).__init__(*args, **kwargs) + + +class CppDummyMethod(CppMethod): + """ + A 'dummy' method; cannot be generated due to incomple or incorrect + parameters, but is added to the class to model the missing method. + """ + + def __init__(self, method_name, return_value, parameters, *args, **kwargs): + return_value = DummyReturnValue(return_value) + parameters = [DummyParameter(p) for p in parameters] + super(CppDummyMethod, self).__init__(method_name, return_value, parameters, *args, **kwargs) + + class CppConstructor(ForwardWrapperBase): """ Class that generates a wrapper to a C++ class constructor. Such @@ -273,6 +329,9 @@ class CppConstructor(ForwardWrapperBase): """ if unblock_threads is None: unblock_threads = settings.unblock_threads + + parameters = [utils.eval_param(param, self) for param in parameters] + super(CppConstructor, self).__init__( None, parameters, "return -1;", "return -1;", @@ -414,6 +473,9 @@ class CppFunctionAsConstructor(CppConstructor): """ if unblock_threads is None: unblock_threads = settings.unblock_threads + + parameters = [utils.eval_param(param, self) for param in parameters] + super(CppFunctionAsConstructor, self).__init__(parameters) self.c_function_name = c_function_name self.function_return_value = return_value diff --git a/pybindgen/function.py b/pybindgen/function.py index 98fc79b..b7c69fe 100644 --- a/pybindgen/function.py +++ b/pybindgen/function.py @@ -38,6 +38,9 @@ class Function(ForwardWrapperBase): if return_value is None: return_value = ReturnValue.new('void') + return_value = utils.eval_retval(return_value, self) + parameters = [utils.eval_param(param, self) for param in parameters] + super(Function, self).__init__( return_value, parameters, parse_error_return="return NULL;", diff --git a/pybindgen/gccxmlparser.py b/pybindgen/gccxmlparser.py index d2f549d..1368a09 100644 --- a/pybindgen/gccxmlparser.py +++ b/pybindgen/gccxmlparser.py @@ -82,6 +82,22 @@ def _pygen_kwargs(kwargs): l.append("%s=%r" % (key, val)) return l +def _pygen_args_kwargs(args, kwargs): + return ", ".join([repr(arg) for arg in args] + _pygen_kwargs(kwargs)) + +def _pygen_args_kwargs_dict(args, kwargs): + l = [repr(arg) for arg in args] + if kwargs: + l.append("dict(%s)" % ', '.join(_pygen_kwargs(kwargs))) + return ", ".join(l) + +def _pygen_retval(args, kwargs): + if len(args) == 1 and len(kwargs) == 0: + return repr(args[0]) + return "retval(%s)" % _pygen_args_kwargs(args, kwargs) + +def _pygen_param(args, kwargs): + return "param(%s)" % _pygen_args_kwargs(args, kwargs) class GccXmlTypeRegistry(object): @@ -181,21 +197,11 @@ class GccXmlTypeRegistry(object): name = normalize_name(type_info.decl_string) else: 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 + return (name,), kwargs ## class value if not is_pointer and not is_reference: - 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 + return (cpp_class.full_name,), kwargs ## pointer to class if is_pointer and not is_reference: @@ -203,30 +209,11 @@ class GccXmlTypeRegistry(object): ## a pointer to const object "usually" means caller_owns_return=False ## some guessing going on here, though.. kwargs['caller_owns_return'] = False - if ALWAYS_USE_PARAMETER_NEW: - retval = ReturnValue.new(cpp_class.full_name+'*', **kwargs) - retval._pygen_repr = ("ReturnValue.new(%s)" - % (", ".join([repr(cpp_class.full_name + '*')] + _pygen_kwargs(kwargs)))) - else: - retval = cpp_class.ThisClassPtrReturn(type_info.decl_string, **kwargs) - retval._pygen_repr = ("root_module[%r].ThisClassPtrReturn(%s)" - % (cpp_class.full_name, - ", ".join([repr(type_info.decl_string)] + _pygen_kwargs(kwargs)))) - - return retval + return (cpp_class.full_name+'*',), kwargs ## reference of class if not is_pointer and is_reference: - 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 + return (cpp_class.full_name+'&',), kwargs assert 0, "this line should not be reached" @@ -265,62 +252,24 @@ class GccXmlTypeRegistry(object): else: 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 + return (type_name, param_name), kwargs assert isinstance(cpp_class, CppClass)#, cpp_class.full_name ## class value if not is_pointer and not is_reference: - 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 + return (cpp_class.full_name, param_name), kwargs ## 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) - 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 + return (cpp_class.full_name+'*', param_name), kwargs ## 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 - raise + return (cpp_class.full_name+'&', param_name), kwargs assert 0, "this line should not be reached" @@ -562,10 +511,10 @@ class ModuleParser(object): for inc in includes: self.module.add_include(inc) self.pygen_sink.writeln("import sys") - self.pygen_sink.writeln("from pybindgen import Module, FileCodeSink, write_preamble") - self.pygen_sink.writeln("from pybindgen.cppclass import CppClass") - self.pygen_sink.writeln("from pybindgen.cppmethod import CppMethod, CppConstructor, CppNoConstructor") - self.pygen_sink.writeln("from pybindgen import ReturnValue, Parameter, Function, Enum") + self.pygen_sink.writeln("from pybindgen import Module, FileCodeSink, write_preamble, param, retval") + #self.pygen_sink.writeln("from pybindgen.cppclass import CppClass") + #self.pygen_sink.writeln("from pybindgen.cppmethod import CppMethod, CppConstructor, CppNoConstructor") + #self.pygen_sink.writeln("from pybindgen import ReturnValue, Parameter, Function, Enum") self.pygen_sink.writeln() self.pygen_sink.writeln("def module_init():") self.pygen_sink.indent() @@ -690,15 +639,15 @@ class ModuleParser(object): enums.append(enum) for enum in enums: - module.add_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(%s)' % ', '.join(l)) + module.add_enum(enum.name, [name for name, dummy_val in enum.values], + outer_class=outer_class) + registered_classes = {} # class_t -> CppClass ## Look for forward declarations of class/structs like @@ -742,10 +691,11 @@ class ModuleParser(object): kwargs.setdefault("automatic_type_narrowing", False) kwargs.setdefault("allow_subclassing", False) - class_wrapper = module.add_class(alias.name, **kwargs) self.pygen_sink.writeln("module.add_class(%s)" % ", ".join([repr(alias.name)] + _pygen_kwargs(kwargs))) + class_wrapper = module.add_class(alias.name, **kwargs) + class_wrapper.gccxml_definition = cls registered_classes[cls] = class_wrapper if cls.name != alias.name: @@ -881,11 +831,11 @@ class ModuleParser(object): kwargs["template_parameters"] = template_parameters if custom_template_class_name: kwargs["custom_template_class_name"] = custom_template_class_name - class_wrapper = module.add_class(cls_name, **kwargs) self.pygen_sink.writeln("module.add_class(%s)" % ", ".join([repr(cls_name)] + _pygen_kwargs(kwargs))) + class_wrapper = module.add_class(cls_name, **kwargs) class_wrapper.gccxml_definition = cls registered_classes[cls] = class_wrapper if alias: @@ -1015,44 +965,21 @@ class ModuleParser(object): warnings.warn_explicit("Annotation '%s=%s' not used (used in %s)" % (key, val, member), Warning, member.location.file_name, member.location.line) - - try: - 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), - Warning, member.location.file_name, member.location.line) - 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 + + ## --- pygen --- + return_type_spec = self.type_registry.lookup_return(member.return_type, + parameter_annotations.get('return', {})) + argument_specs = [] for arg in member.arguments: - try: - 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), - Warning, member.location.file_name, member.location.line) - ok = False - if not ok: - 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 + argument_specs.append(self.type_registry.lookup_parameter(arg.type, arg.name, + parameter_annotations.get(arg.name, {}), + arg.default_value)) + + 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")') + #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): @@ -1091,18 +1018,49 @@ class ModuleParser(object): and not (kwargs.get('is_virtual', False) or kwargs.get('is_pure_virtual', False))): continue - def _pygen_method(): - arglist_repr = ("[" + ', '.join([arg._pygen_repr for arg in arguments]) + "]") - self.pygen_sink.writeln("cls.add_method(%s)" % - ", ".join( - [repr(member.name), return_type._pygen_repr, arglist_repr] - + _pygen_kwargs(kwargs))) + ## --- pygen --- + arglist_repr = ("[" + ', '.join([_pygen_param(args_, kwargs_) for (args_, kwargs_) in argument_specs]) + "]") + self.pygen_sink.writeln("cls.add_method(%s)" % + ", ".join( + [repr(member.name), _pygen_retval(return_type_spec[0], return_type_spec[1]), arglist_repr] + + _pygen_kwargs(kwargs))) + + ## --- realize the return type and parameters + try: + return_type = ReturnValue.new(*return_type_spec[0], **return_type_spec[1]) + except (TypeLookupError, TypeConfigurationError), ex: + warnings.warn_explicit("Return value '%s' error (used in %s): %r" + % (member.return_type.decl_string, member, ex), + Warning, member.location.file_name, member.location.line) + if pure_virtual: + class_wrapper.set_cannot_be_constructed("pure virtual method not wrapped") + class_wrapper.set_helper_class_disabled(True) + #self.pygen_sink.writeln('cls.set_cannot_be_constructed("pure virtual method not wrapped")') + #self.pygen_sink.writeln('cls.set_helper_class_disabled(True)') + continue + arguments = [] + ok = True + for arg in argument_specs: + try: + arguments.append(Parameter.new(*arg[0], **arg[1])) + except (TypeLookupError, TypeConfigurationError), ex: + warnings.warn_explicit("Parameter '%s %s' error (used in %s): %r" + % (arg[0][0], arg[0][1], member, ex), + Warning, member.location.file_name, member.location.line) + ok = False + if not ok: + 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 + try: method_wrapper = class_wrapper.add_method(member.name, return_type, arguments, **kwargs) method_wrapper.gccxml_definition = member except NotSupportedError, ex: - _pygen_method() if pure_virtual: class_wrapper.set_cannot_be_constructed("pure virtual method %r not wrapped" % member.name) class_wrapper.set_helper_class_disabled(True) @@ -1119,7 +1077,6 @@ class ModuleParser(object): Warning, member.location.file_name, member.location.line) raise else: # no exception, add method succeeded - _pygen_method() for hook in self._post_scan_hooks: hook(self, member, method_wrapper) @@ -1132,11 +1089,20 @@ class ModuleParser(object): if not member.arguments: have_trivial_constructor = True - arguments = [] + argument_specs = [] for arg in member.arguments: + argument_specs.append(self.type_registry.lookup_parameter(arg.type, arg.name, + default_value=arg.default_value)) + + arglist_repr = ("[" + ', '.join([_pygen_param(args_, kwargs_) for (args_, kwargs_) in argument_specs]) + "]") + self.pygen_sink.writeln("cls.add_constructor(%s)" % + ", ".join([arglist_repr, "visibility=%r" % member.access_type])) + + + arguments = [] + for a, kw in argument_specs: try: - arguments.append(self.type_registry.lookup_parameter(arg.type, arg.name, - default_value=arg.default_value)) + arguments.append(Parameter.new(*a, **kw)) except (TypeLookupError, TypeConfigurationError), ex: warnings.warn_explicit("Parameter '%s %s' error (used in %s): %r" % (arg.type.decl_string, arg.name, member, ex), @@ -1156,10 +1122,6 @@ 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(%s)" % - ", ".join([arglist_repr, "visibility=%r" % member.access_type])) - ## ------------ attribute -------------------- elif isinstance(member, variable_t): if member.access_type == 'protected': @@ -1171,23 +1133,33 @@ class ModuleParser(object): if member.access_type == 'private': continue + return_type_spec = self.type_registry.lookup_return(member.type) + + ## pygen... + if member.type_qualifiers.has_static: + self.pygen_sink.writeln("cls.add_static_attribute(%r, %s, is_const=%r)" % + (member.name, _pygen_retval(*return_type_spec), + type_traits.is_const(member.type))) + else: + self.pygen_sink.writeln("cls.add_instance_attribute(%r, %s, is_const=%r)" % + (member.name, _pygen_retval(*return_type_spec), + type_traits.is_const(member.type))) + + ## convert the return value try: - return_type = self.type_registry.lookup_return(member.type) + return_type = ReturnValue.new(*return_type_spec[0], **return_type_spec[1]) except (TypeLookupError, TypeConfigurationError), ex: warnings.warn_explicit("Return value '%s' error (used in %s): %r" % (member.type.decl_string, member, ex), Warning, member.location.file_name, member.location.line) continue + if member.type_qualifiers.has_static: class_wrapper.add_static_attribute(member.name, return_type, is_const=type_traits.is_const(member.type)) - self.pygen_sink.writeln("cls.add_static_attribute(%r, %s, is_const=%r)" % - (member.name, return_type._pygen_repr, type_traits.is_const(member.type))) else: class_wrapper.add_instance_attribute(member.name, return_type, is_const=type_traits.is_const(member.type)) - self.pygen_sink.writeln("cls.add_instance_attribute(%r, %s, is_const=%r)" % - (member.name, return_type._pygen_repr, type_traits.is_const(member.type))) ## TODO: invoke post_scan_hooks elif isinstance(member, calldef.destructor_t): pass @@ -1257,72 +1229,76 @@ class ModuleParser(object): return_annotations = parameter_annotations.get('return', {}) if is_constructor_of: return_annotations['caller_owns_return'] = 'true' + + params_ok = True + return_type_spec = self.type_registry.lookup_return(fun.return_type, return_annotations) try: - return_type = self.type_registry.lookup_return(fun.return_type, return_annotations) + return_type = ReturnValue.new(*return_type_spec[0], **return_type_spec[1]) except (TypeLookupError, TypeConfigurationError), ex: warnings.warn_explicit("Return value '%s' error (used in %s): %r" % (fun.return_type.decl_string, fun, ex), Warning, fun.location.file_name, fun.location.line) - continue + params_ok = False except TypeError, ex: warnings.warn_explicit("Return value '%s' error (used in %s): %r" % (fun.return_type.decl_string, fun, ex), Warning, fun.location.file_name, fun.location.line) raise + argument_specs = [] arguments = [] for argnum, arg in enumerate(fun.arguments): annotations = parameter_annotations.get(arg.name, {}) if argnum == 0 and as_method is not None \ and isinstance(arg.type, cpptypes.pointer_t): annotations.setdefault("transfer_ownership", "false") + + spec = self.type_registry.lookup_parameter(arg.type, arg.name, + annotations, + default_value=arg.default_value) + argument_specs.append(spec) try: - arguments.append(self.type_registry.lookup_parameter(arg.type, arg.name, - annotations, - default_value=arg.default_value)) + arguments.append(Parameter.new(*spec[0], **spec[1])) except (TypeLookupError, TypeConfigurationError), ex: warnings.warn_explicit("Parameter '%s %s' error (used in %s): %r" % (arg.type.decl_string, arg.name, fun, ex), Warning, fun.location.file_name, fun.location.line) - ok = False - break + params_ok = False except TypeError, ex: warnings.warn_explicit("Parameter '%s %s' error (used in %s): %r" % (arg.type.decl_string, arg.name, fun, ex), Warning, fun.location.file_name, fun.location.line) raise - else: - ok = True - if not ok: - continue - arglist_repr = ("[" + ', '.join([arg._pygen_repr for arg in arguments]) + "]") + arglist_repr = ("[" + ', '.join([_pygen_param(*arg) for arg in argument_specs]) + "]") + retval_repr = _pygen_retval(*return_type_spec) 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 = root_module[normalize_class_name(of_class, (self.module_namespace_name or '::'))] - function_wrapper = cpp_class.add_function_as_method(fun.name, return_type, arguments, custom_name=as_method) - function_wrapper.gccxml_definition = fun - self.pygen_sink.writeln("root_module[%r].add_function_as_method(%s, custom_name=%r)" % (cpp_class.full_name, - ", ".join([repr(fun.name), return_type._pygen_repr, arglist_repr]), + ", ".join([repr(fun.name), retval_repr, arglist_repr]), as_method)) + if params_ok: + function_wrapper = cpp_class.add_function_as_method(fun.name, return_type, arguments, custom_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 = root_module[normalize_class_name(is_constructor_of, (self.module_namespace_name or '::'))] - function_wrapper = cpp_class.add_function_as_constructor(fun.name, return_type, arguments) - - function_wrapper.gccxml_definition = fun self.pygen_sink.writeln("root_module[%r].add_function_as_constructor(%s)" % (cpp_class.full_name, - ", ".join([repr(fun.name), return_type._pygen_repr, arglist_repr]),)) + ", ".join([repr(fun.name), retval_repr, arglist_repr]),)) + + if params_ok: + function_wrapper = cpp_class.add_function_as_constructor(fun.name, return_type, arguments) + function_wrapper.gccxml_definition = fun + continue @@ -1334,14 +1310,14 @@ class ModuleParser(object): if alt_name: kwargs['custom_name'] = alt_name - func_wrapper = module.add_function(fun.name, return_type, arguments, **kwargs) - func_wrapper.gccxml_definition = fun - self.pygen_sink.writeln("module.add_function(%s)" % - (", ".join([repr(fun.name), return_type._pygen_repr, arglist_repr] + _pygen_kwargs(kwargs)))) + (", ".join([repr(fun.name), retval_repr, arglist_repr] + _pygen_kwargs(kwargs)))) - for hook in self._post_scan_hooks: - hook(self, fun, func_wrapper) + if params_ok: + func_wrapper = module.add_function(fun.name, return_type, arguments, **kwargs) + func_wrapper.gccxml_definition = fun + for hook in self._post_scan_hooks: + hook(self, fun, func_wrapper) ## scan nested namespaces (mapped as python submodules) diff --git a/pybindgen/module.py b/pybindgen/module.py index 4c2b277..2dcbeba 100644 --- a/pybindgen/module.py +++ b/pybindgen/module.py @@ -234,7 +234,10 @@ class ModuleBase(dict): assert len(args) == 1 assert len(kwargs) == 0 else: - func = Function(*args, **kwargs) + try: + func = Function(*args, **kwargs) + except utils.SkipWrapper: + return None self._add_function_obj(func) return func @@ -243,7 +246,10 @@ class ModuleBase(dict): Add a function, using custom wrapper code, to the module/namespace. See the documentation for L{CustomFunctionWrapper.__init__} for information on accepted parameters. """ - func = CustomFunctionWrapper(*args, **kwargs) + try: + func = CustomFunctionWrapper(*args, **kwargs) + except utils.SkipWrapper: + return None self._add_function_obj(func) return func diff --git a/pybindgen/utils.py b/pybindgen/utils.py index a26aee0..1743a9a 100644 --- a/pybindgen/utils.py +++ b/pybindgen/utils.py @@ -1,6 +1,7 @@ import sys from typehandlers.codesink import CodeSink -from typehandlers.base import TypeConfigurationError, CodeGenerationError, NotSupportedError +from typehandlers.base import TypeLookupError, TypeConfigurationError, CodeGenerationError, NotSupportedError, \ + Parameter, ReturnValue import version import settings @@ -129,3 +130,73 @@ def ascii(value): else: raise TypeError("value must be str or ascii string contained in a unicode object") + +def param(*args, **kwargs): + """ + Simplified syntax for representing a parameter with delayed lookup. + + Parameters are the same as L{Parameter.new}. + """ + return (args + (kwargs,)) + + +def retval(*args, **kwargs): + """ + Simplified syntax for representing a return value with delayed lookup. + + Parameters are the same as L{ReturnValue.new}. + """ + return (args + (kwargs,)) + + +def parse_param_spec(param_spec): + if isinstance(param_spec, tuple): + assert len(param_spec) >= 2 + if isinstance(param_spec[-1], dict): + kwargs = param_spec[-1] + args = param_spec[:-1] + else: + kwargs = dict() + args = param_spec + else: + raise TypeError("Could not parse `%r' as a Parameter" % param_spec) + return args, kwargs + + +def parse_retval_spec(retval_spec): + if isinstance(retval_spec, tuple): + assert len(retval_spec) >= 1 + if isinstance(retval_spec[-1], dict): + kwargs = retval_spec[-1] + args = retval_spec[:-1] + else: + kwargs = dict() + args = retval_spec + elif isinstance(retval_spec, str): + kwargs = dict() + args = (retval_spec,) + else: + raise TypeError("Could not parse `%r' as a ReturnValue" % retval_spec) + return args, kwargs + + +def eval_param(param_value, wrapper=None): + if isinstance(param_value, Parameter): + return param_value + else: + args, kwargs = parse_param_spec(param_value) + return call_with_error_handling(Parameter.new, args, kwargs, wrapper, + exceptions_to_handle=(TypeConfigurationError, + NotSupportedError, + TypeLookupError)) + + +def eval_retval(retval_value, wrapper=None): + if isinstance(retval_value, ReturnValue): + return retval_value + else: + args, kwargs = parse_retval_spec(retval_value) + return call_with_error_handling(ReturnValue.new, args, kwargs, wrapper, + exceptions_to_handle=(TypeConfigurationError, + NotSupportedError, + TypeLookupError)) diff --git a/tests/foomodulegen3.py b/tests/foomodulegen3.py index bcbe4c1..59073a2 100644 --- a/tests/foomodulegen3.py +++ b/tests/foomodulegen3.py @@ -1,13 +1,25 @@ #! /usr/bin/env python import sys +import warnings import pybindgen +import pybindgen.settings from pybindgen import (FileCodeSink) import foomodulegen_generated import foomodulegen_common + + +class ErrorHandler(pybindgen.settings.ErrorHandler): + def handle_error(self, wrapper, exception, dummy_traceback_): + warnings.warn("exception >>> %r in wrapper %s" % (exception, wrapper)) + return True +pybindgen.settings.error_handler = ErrorHandler() + + + def my_module_gen(): out = FileCodeSink(sys.stdout) root_module = foomodulegen_generated.module_init() diff --git a/tests/test.py b/tests/test.py index 4d51de8..ed44d40 100644 --- a/tests/test.py +++ b/tests/test.py @@ -1,6 +1,6 @@ import pybindgen.typehandlers.base as typehandlers import pybindgen.typehandlers.codesink as codesink -from pybindgen import module, cppclass, overloading +from pybindgen import module, cppclass, overloading, utils import unittest @@ -68,6 +68,7 @@ if __name__ == '__main__': module, cppclass, overloading, + utils ]: suite.addTest(doctest.DocTestSuite(mod))