diff --git a/doc/apiref.rst b/doc/apiref.rst index d8dfa5e..1fd4dc1 100644 --- a/doc/apiref.rst +++ b/doc/apiref.rst @@ -18,10 +18,10 @@ Higher layers cppexception container - gccxmlparser + castxmlparser settings - - + + Lower layers ----------------------- .. toctree:: diff --git a/doc/gccxmlparser.rst b/doc/castxmlparser.rst similarity index 64% rename from doc/gccxmlparser.rst rename to doc/castxmlparser.rst index c7488df..144239d 100644 --- a/doc/gccxmlparser.rst +++ b/doc/castxmlparser.rst @@ -1,10 +1,10 @@ ========================================================== -gccxmlparser: scan header files to extract API definitions +castxmlparser: scan header files to extract API definitions ========================================================== -.. automodule:: pybindgen.gccxmlparser +.. automodule:: pybindgen.castxmlparser :members: :undoc-members: :show-inheritance: diff --git a/doc/tutorial.rst b/doc/tutorial.rst index 84db010..8fa0ea9 100644 --- a/doc/tutorial.rst +++ b/doc/tutorial.rst @@ -37,7 +37,7 @@ and, then, will proceed to show how to use the automatic binding generator. Supported Python versions ========================= -PyBindGen officially supports Python versions 2.6, 2.7, and >= 3.3[*] (tested in 3.3 and 3.4). +PyBindGen officially supports Python versions 2.6, 2.7, and >= 3.3 (tested in 3.3 and 3.4). PyBindGen does not support Python versions 3.0, 3.1, and 3.2. @@ -45,10 +45,6 @@ Note that C files generated by PyBindGen transparently support multiple Python versions. In particular, the generated code can build in either Python 2.x or Python 3.x. -[*] The automatic header file scanning feature of PyBindGen (submodule -`pybindgen.gccxmlparser`) does not work in any Python 3.x version, -since the pygccxml package has not been ported to Python 3. - Work flows @@ -77,7 +73,7 @@ are several ways that PyBindGen can be used: A simple example ================ -The best way to get a feel for what PyBindGen looks like is to go through a +The best way to get a feel for what PyBindGen looks like is to go through a simple example. Code generation script @@ -90,7 +86,7 @@ declared in a header my-module.h: void MyModuleDoAction (void); -What we want to do is call this C function from python and be able to write +What we want to do is call this C function from python and be able to write python code such as:: import MyModule @@ -188,13 +184,13 @@ should be self-explanatory:: [...] $ export PYTHONPATH=build/lib.linux-x86_64-2.7/ $ python - Python 2.7.5+ (default, Sep 19 2013, 13:48:49) + Python 2.7.5+ (default, Sep 19 2013, 13:48:49) [GCC 4.8.1] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import mymodule >>> mymodule.MyModuleDoAction () You called MyModuleDoAction ! - >>> + >>> Wrapping types by value @@ -217,8 +213,8 @@ argument to add_function specifies that our function returns a value of type 'int' and the third argument specifies that our function takes as a single argument an 'int' of name 'value':: - mod.add_function('MyModuleDoAction', - pybindgen.retval ('int'), + mod.add_function('MyModuleDoAction', + pybindgen.retval ('int'), [pybindgen.param ('int', 'v1'), pybindgen.param ('int', 'v2')]) @@ -241,7 +237,7 @@ using positional arguments. Of course, the above example could be rewritten to the more compact and readable:: from pybindgen import * - mod.add_function('MyModuleDoAction', retval ('int'), + mod.add_function('MyModuleDoAction', retval ('int'), [param ('int', 'v1'), param ('int', 'v2')]) @@ -459,11 +455,11 @@ no special challenge. Let's look at an example: namespace Outer { void Do (void); - class MyClass + class MyClass {}; namespace Inner { void Do (void); - class MyClass + class MyClass {}; } // namespace Inner } // namespace Outer @@ -502,7 +498,7 @@ Memory management for pointer types Until then, we have shown how to pass back and forth data through C/C++ APIs only by value but, a large fraction of real-world APIs use raw pointers -(and, in the case of C++, smart pointers) as arguments or return values +(and, in the case of C++, smart pointers) as arguments or return values of functions/methods. Rather than try to explain the detail of every option offered by PyBindGen @@ -572,14 +568,14 @@ disappears. A reference-counted object -------------------------- -A nice way to avoid some of the ambiguities of the above-mentioned API bindings is to +A nice way to avoid some of the ambiguities of the above-mentioned API bindings is to use reference-counted C or C++ objects which must provide a pair of functions or methods to increase or decrease the reference count of the object. For example, a classic C++ reference-counted class: .. code-block:: c++ - class MyClass + class MyClass { public: void Ref (void); @@ -597,9 +593,9 @@ To wrap this class, we first need to declare our class:: from pybindgen import cppclass [...] - mod.add_class('MyClass', memory_policy=cppclass.ReferenceCountingMethodsPolicy( - incref_method='Ref', - decref_method='Unref', + mod.add_class('MyClass', memory_policy=cppclass.ReferenceCountingMethodsPolicy( + incref_method='Ref', + decref_method='Unref', peekref_method='PeekRef')) The above allows PyBindGen to maintain and track the reference count @@ -621,7 +617,7 @@ method. If transfer_ownership=True it would mean that the caller would keep the passed in reference to itself, and if the caller wants to keep the reference it must call the incref method first. -A more interesting case is that of returning such a reference counted +A more interesting case is that of returning such a reference counted object from a function: .. code-block:: c++ @@ -749,7 +745,7 @@ scan the API definitions directly from the header files:: import pybindgen from pybindgen import FileCodeSink - from pybindgen.gccxmlparser import ModuleParser + from pybindgen.castxmlparser import ModuleParser def my_module_gen(): module_parser = ModuleParser('a1', '::') @@ -781,7 +777,7 @@ PyBindGen based Python script:: import sys from pybindgen import FileCodeSink - from pybindgen.gccxmlparser import ModuleParser + from pybindgen.castxmlparser import ModuleParser def my_module_gen(): module_parser = ModuleParser('a2', '::') diff --git a/pybindgen/gccxmlparser.py b/pybindgen/gccxmlparser.py deleted file mode 100644 index 893437e..0000000 --- a/pybindgen/gccxmlparser.py +++ /dev/null @@ -1,2303 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -from __future__ import unicode_literals, print_function, absolute_import - -DEBUG = False - - -import sys - -PY3 = (sys.version_info[0] >= 3) -if PY3: - string_types = str, -else: - string_types = basestring, - - -import os.path -import warnings -import re -import pygccxml -from operator import itemgetter - -from pygccxml import parser -from pygccxml import declarations -from .module import Module -from .typehandlers.codesink import FileCodeSink, CodeSink, NullCodeSink -from .typehandlers import base -from . import typehandlers -from .typehandlers.base import ctypeparser -from .typehandlers.base import ReturnValue, Parameter, TypeLookupError, TypeConfigurationError, NotSupportedError -from pygccxml.declarations.enumeration import enumeration_t -from .cppclass import CppClass, ReferenceCountingMethodsPolicy, FreeFunctionPolicy, ReferenceCountingFunctionsPolicy -from .cppexception import CppException -from pygccxml.declarations import type_traits -from pygccxml.declarations import cpptypes -from pygccxml.declarations import calldef -from pygccxml.declarations import templates -from pygccxml.declarations import container_traits -from pygccxml.declarations.declaration import declaration_t -from pygccxml.declarations.class_declaration import class_declaration_t, class_t -from . import settings -from . import utils - -#from pygccxml.declarations.calldef import \ -# destructor_t, constructor_t, member_function_t -from pygccxml.declarations.variable import variable_t -import collections - - -### -### some patched pygccxml functions, from the type_traits module -### -def remove_pointer(type): - """removes pointer from the type definition - - If type is not pointer type, it will be returned as is. - """ - #nake_type = remove_alias( type ) - nake_type = type - if not type_traits.is_pointer( nake_type ): - return type - elif isinstance( nake_type, cpptypes.volatile_t ) and isinstance( nake_type.base, cpptypes.pointer_t ): - return cpptypes.volatile_t( nake_type.base.base ) - elif isinstance( nake_type, cpptypes.const_t ) and isinstance( nake_type.base, cpptypes.pointer_t ): - return cpptypes.const_t( nake_type.base.base ) - elif isinstance(nake_type, cpptypes.compound_t) and isinstance( nake_type.base, cpptypes.calldef_type_t ): - return type - else: - if isinstance(nake_type, cpptypes.compound_t): - return nake_type.base - else: - return nake_type - -def remove_reference(type): - """removes reference from the type definition - - If type is not reference type, it will be returned as is. - """ - #nake_type = remove_alias( type ) - nake_type = type - if not type_traits.is_reference( nake_type ): - return type - else: - if isinstance(nake_type, cpptypes.compound_t): - return nake_type.base - else: - return nake_type - -def remove_const(type): - """removes const from the type definition - - If type is not const type, it will be returned as is - """ - - #nake_type = remove_alias( type ) - nake_type = type - if not type_traits.is_const( nake_type ): - return type - else: - if isinstance(nake_type, cpptypes.compound_t): - return nake_type.base - else: - return nake_type -### -### end of patched pygccxml functions -### - - -## --- utility --- - -import pygccxml.declarations.type_traits -def find_declaration_from_name(global_ns, declaration_name): - decl = pygccxml.declarations.type_traits.impl_details.find_value_type(global_ns, declaration_name) - return decl - - -class ModuleParserWarning(Warning): - """ - Base class for all warnings reported here. - """ -class NotSupportedWarning(ModuleParserWarning): - """ - Warning for pybindgen GccxmlParser, to report something pybindgen does not support. - """ -class WrapperWarning(ModuleParserWarning): - """ - Warning for pybindgen GccxmlParser, to be used when a C++ - definition cannot be converted to a pybindgen wrapper. - """ -class AnnotationsWarning(ModuleParserWarning): - """ - Warning for pybindgen GccxmlParser to report a problem in annotations. - """ - -## ------------------------ - -class ErrorHandler(settings.ErrorHandler): - def handle_error(self, wrapper, exception, traceback_): - if hasattr(wrapper, "gccxml_definition"): - definition = wrapper.gccxml_definition - elif hasattr(wrapper, "main_wrapper"): - try: - definition = wrapper.main_wrapper.gccxml_definition - except AttributeError: - definition = None - else: - definition = None - - if definition is None: - print("exception %r in wrapper %s" % (exception, wrapper), file=sys.stderr) - else: - warnings.warn_explicit("exception %r in wrapper for %s" - % (exception, definition), - WrapperWarning, definition.location.file_name, - definition.location.line) - return True -settings.error_handler = ErrorHandler() - -def normalize_name(decl_string): - return ctypeparser.normalize_type_string(decl_string) - -def normalize_class_name(class_name, module_namespace): - class_name = utils.ascii(class_name) - if not class_name.startswith(module_namespace): - class_name = module_namespace + class_name - class_name = normalize_name(class_name) - return class_name - - -def _pygen_kwargs(kwargs): - l = [] - for key, val in sorted(kwargs.items(), key=itemgetter(0)): - if isinstance(val, (CppClass, CppException)): - l.append("%s=root_module[%r]" % (key, utils.ascii(val.full_name))) - else: - if key == 'throw': - l.append("throw=[%s]" % (', '.join(["root_module[%r]" % utils.ascii(t.full_name) for t in val]))) - elif key == 'parent' and isinstance(val, list): - l.append("parent=[%s]" % (', '.join(["root_module[%r]" % utils.ascii(cls.full_name) for cls in val]))) - else: - 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): - def __init__(self, root_module): - """ - :param 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 class_registered(self, cpp_class): - assert isinstance(cpp_class, (CppClass, CppException)) - self.ordered_classes.append(cpp_class) - -# def get_type_traits(self, type_info): -# #assert isinstance(type_info, cpptypes.type_t) - -# debug = False #('int64_t' in type_info.decl_string) -# if debug: -# print >> sys.stderr, "***** type traits for %r" % (type_info.decl_string, ) - -# is_const = False -# is_reference = False -# is_pointer = 0 -# pointer_or_ref_count = 0 -# inner_const = False -# while 1: -# prev_type_info = type_info -# if type_traits.is_pointer(type_info): -# is_pointer += 1 -# type_info = remove_pointer(type_info) -# pointer_or_ref_count += 1 -# elif type_traits.is_const(type_info): -# type_info = remove_const(type_info) -# if pointer_or_ref_count == 0: -# is_const = True -# elif pointer_or_ref_count == 1: -# inner_const = True -# else: -# warnings.warn("multiple consts not handled") -# elif type_traits.is_reference(type_info): -# warnings.warn("multiple &'s not handled") -# is_reference = True -# type_info = remove_reference(type_info) -# pointer_or_ref_count += 1 -# else: -# break -# if type_info is prev_type_info: -# break - -# type_name = normalize_name(type_info.partial_decl_string) -# try: -# cpp_type = self.root_module[type_name] -# except KeyError: -# cpp_type = type_name - -# if not isinstance(cpp_type, CppClass): -# cpp_type = type_name - -# if debug: -# print >> sys.stderr, "*** > return ", repr((cpp_type, is_const, is_pointer, is_reference)) -# return (cpp_type, is_const, inner_const, is_pointer, is_reference) - - def _fixed_std_type_name(self, type_name): - type_name = utils.ascii(type_name) - decl = self._root_ns_rx.sub('', type_name) - return decl - - def lookup_return(self, type_info, annotations={}): - kwargs = {} - for name, value in annotations.items(): - if name == 'caller_owns_return': - kwargs['caller_owns_return'] = annotations_scanner.parse_boolean(value) - elif name == 'reference_existing_object': - kwargs['reference_existing_object'] = annotations_scanner.parse_boolean(value) - elif name == 'return_internal_reference': - kwargs['return_internal_reference'] = annotations_scanner.parse_boolean(value) - elif name == 'custodian': - kwargs['custodian'] = int(value) - else: - warnings.warn("invalid annotation name %r" % name, AnnotationsWarning) - - cpp_type = normalize_name(type_info.partial_decl_string) - return (cpp_type,), kwargs - - - def lookup_parameter(self, type_info, param_name, annotations={}, default_value=None): - kwargs = {} - for name, value in annotations.items(): - if name == 'transfer_ownership': - kwargs['transfer_ownership'] = annotations_scanner.parse_boolean(value) - elif name == 'direction': - if value.lower() == 'in': - kwargs['direction'] = Parameter.DIRECTION_IN - elif value.lower() == 'out': - kwargs['direction'] = Parameter.DIRECTION_OUT - elif value.lower() == 'inout': - kwargs['direction'] = Parameter.DIRECTION_INOUT - else: - warnings.warn("invalid direction direction %r" % value, AnnotationsWarning) - elif name == 'custodian': - kwargs['custodian'] = int(value) - elif name == 'array_length': - kwargs['array_length'] = int(value) - elif name == 'default_value': - kwargs['default_value'] = value - elif name == 'null_ok': - kwargs['null_ok'] = annotations_scanner.parse_boolean(value) - else: - warnings.warn("invalid annotation name %r" % name, AnnotationsWarning) - - if default_value: - kwargs['default_value'] = utils.ascii(default_value) - - cpp_type = normalize_name(type_info.partial_decl_string) - - return (cpp_type, param_name), kwargs - - -class AnnotationsScanner(object): - def __init__(self): - self.files = {} # file name -> list(lines) - self.used_annotations = {} # file name -> list(line_numbers) - self._comment_rx = re.compile( - r"^\s*(?://\s+-#-(?P.*)-#-\s*)|(?:/\*\s+-#-(?P.*)-#-\s*\*/)") - self._global_annotation_rx = re.compile(r"(\w+)(?:=([^\s;]+))?") - self._param_annotation_rx = re.compile(r"@(\w+)\(([^;]+)\)") - - def _declare_used_annotation(self, file_name, line_number): - try: - l = self.used_annotations[file_name] - except KeyError: - l = [] - self.used_annotations[file_name] = l - l.append(line_number) - - def get_annotations(self, decl): - """ - :param decl: pygccxml declaration_t object - """ - assert isinstance(decl, declaration_t) - - if isinstance(decl, calldef.calldef_t) \ - and decl.is_artificial: - #print >> sys.stderr, "********** ARTIFICIAL:", decl - return {}, {} - - file_name = decl.location.file_name - line_number = decl.location.line - - try: - lines = self.files[file_name] - except KeyError: - with open(file_name, "rt") as f: - lines = f.readlines() - self.files[file_name] = lines - - line_number -= 2 - global_annotations = {} - parameter_annotations = {} - while 1: - line = lines[line_number] - line_number -= 1 - m = self._comment_rx.match(line) - if m is None: - break - s = m.group('annotation1') - if s is None: - s = m.group('annotation2') - line = s.strip() - self._declare_used_annotation(file_name, line_number + 2) - for annotation_str in line.split(';'): - annotation_str = annotation_str.strip() - m = self._global_annotation_rx.match(annotation_str) - if m is not None: - global_annotations[m.group(1)] = m.group(2) - continue - - m = self._param_annotation_rx.match(annotation_str) - if m is not None: - param_annotation = {} - parameter_annotations[m.group(1)] = param_annotation - for param in m.group(2).split(','): - m = self._global_annotation_rx.match(param.strip()) - if m is not None: - param_annotation[m.group(1)] = m.group(2) - else: - warnings.warn_explicit("could not parse %r as parameter annotation element" % - (param.strip()), - AnnotationsWarning, file_name, line_number) - continue - warnings.warn_explicit("could not parse %r" % (annotation_str), - AnnotationsWarning, file_name, line_number) - return global_annotations, parameter_annotations - - def parse_boolean(self, value): - if isinstance(value, int): - return bool(value) - if value.lower() in ['false', 'off']: - return False - elif value.lower() in ['true', 'on']: - return True - else: - raise ValueError("bad boolean value %r" % value) - - def warn_unused_annotations(self): - for file_name, lines in self.files.items(): - try: - used_annotations = self.used_annotations[file_name] - except KeyError: - used_annotations = [] - for line_number, line in enumerate(lines): - m = self._comment_rx.match(line) - if m is None: - continue - #print >> sys.stderr, (line_number+1), used_annotations - if (line_number + 1) not in used_annotations: - warnings.warn_explicit("unused annotation", - AnnotationsWarning, file_name, line_number+1) - - - -annotations_scanner = AnnotationsScanner() - -## ------------------------ - -class PygenSection(object): - "Class to hold information about a python generation section" - def __init__(self, name, code_sink, local_customizations_module=None): - """ - :param name: section name; this name should be a valid python - module name; the special name '__main__' is used to denote the - main section, which comprises the main script itself - :type name: str - - :param code_sink: code sink that will receive the generated - code for the section. Normally the code sink should write to - a file with the name of the section and a .py extension, to - allow importing it as module. - :type code_sink: L{CodeSink} - - :param local_customizations_module: name of the python module - that may contain local customizations for the section, or - None. If not None, PyBindGen will generate code that tries to - import that module in the respective section and call - functions on it, or ignore it if the module does not exist. - :type local_customizations_module: str - """ - assert isinstance(name, string_types) - self.name = name - assert isinstance(code_sink, CodeSink) - self.code_sink = code_sink - assert local_customizations_module is None or isinstance(local_customizations_module, string_types) - self.local_customizations_module = local_customizations_module - - -class PygenClassifier(object): - def classify(self, pygccxml_definition): - """ - This is a pure virtual method that must be implemented by - subclasses. It will be called by PyBindGen for every API - definition, and should return a section name. - - :param pygccxml_definition: gccxml definition object - :returns: section name - """ - raise NotImplementedError - - def get_section_precedence(self, section_name): - """ - This is a pure virtual method that may (or not) be implemented by - subclasses. It will be called by PyBindGen for every API - definition, and should return the precedence of a section. - This is used when sections reflect 'modules' whose types must be - registered in a certain order. - - :param section_name: the name of the section - - :returns: order of precedence of the section. The lower the - number, the sooner the section is to be registered. - """ - raise NotImplementedError - - -class ModuleParser(object): - """ - :attr enable_anonymous_containers: if True, pybindgen will attempt - to scan for all std containers, even the ones that have no - typedef'ed name. Enabled by default. - - """ - - def __init__(self, module_name, module_namespace_name='::'): - """ - Creates an object that will be able parse header files and - create a pybindgen module definition. - - :param module_name: name of the Python module - :param module_namespace_name: optional C++ namespace name; if - given, only definitions of this - namespace will be included in the - python module - """ - self.module_name = module_name - self.module_namespace_name = module_namespace_name - self.location_filter = None - self.header_files = None - self.gccxml_config = None - self.whitelist_paths = [] - self.module_namespace = None # pygccxml module C++ namespace - self.module = None # the toplevel pybindgen.module.Module instance (module being generated) - self.declarations = None # (as returned by pygccxml.parser.parse) - self.global_ns = None - self._types_scanned = False - self._pre_scan_hooks = [] - self._post_scan_hooks = [] - self.type_registry = None - self._stage = None - self._pygen_sink = None - self._pygen_factory = None - self._anonymous_structs = [] # list of (pygccxml_anonymous_class, outer_pybindgen_class) - self._containers_to_register = [] - self._containers_registered = {} - self.enable_anonymous_containers = True - - def add_pre_scan_hook(self, hook): - """ - Add a function to be called right before converting a gccxml - definition to a PyBindGen wrapper object. This hook function - will be called for every scanned type, function, or method, - and given the a chance to modify the annotations for that - definition. It will be called like this::: - - pre_scan_hook(module_parser, pygccxml_definition, global_annotations, - parameter_annotations) - - where: - - - module_parser -- the ModuleParser (this class) instance - - pygccxml_definition -- the definition reported by pygccxml - - global_annotations -- a dicionary containing the "global annotations" - for the definition, i.e. a set of key=value - pairs not associated with any particular - parameter - - parameter_annotations -- a dicionary containing the "parameter - annotations" for the definition. It is a - dict whose keys are parameter names and - whose values are dicts containing the - annotations for that parameter. Annotations - pertaining the return value of functions or - methods are denoted by a annotation for a - parameter named 'return'. - """ - if not isinstance(hook, collections.Callable): - raise TypeError("hook must be callable") - self._pre_scan_hooks.append(hook) - - def add_post_scan_hook(self, hook): - """ - Add a function to be called right after converting a gccxml definition - to a PyBindGen wrapper object. This hook function will be called for - every scanned type, function, or method. It will be called like this:: - - post_scan_hook(module_parser, pygccxml_definition, pybindgen_wrapper) - - where: - - - module_parser -- the ModuleParser (this class) instance - - pygccxml_definition -- the definition reported by pygccxml - - pybindgen_wrapper -- a pybindgen object that generates a wrapper, - such as CppClass, Function, or CppMethod. - """ - if not isinstance(hook, collections.Callable): - raise TypeError("hook must be callable") - self._post_scan_hooks.append(hook) - - def __location_match(self, decl): - if decl.location.file_name in self.header_files: - return True - for incdir in self.whitelist_paths: - if os.path.abspath(decl.location.file_name).startswith(incdir): - return True - return False - - def parse(self, header_files, include_paths=None, whitelist_paths=None, includes=(), - pygen_sink=None, pygen_classifier=None, gccxml_options=None): - """ - parses a set of header files and returns a pybindgen Module instance. - It is equivalent to calling the following methods: - 1. parse_init(header_files, include_paths, whitelist_paths) - 2. scan_types() - 3. scan_methods() - 4. scan_functions() - 5. parse_finalize() - - The documentation for L{ModuleParser.parse_init} explains the parameters. - """ - self.parse_init(header_files, include_paths, whitelist_paths, includes, pygen_sink, - pygen_classifier, gccxml_options) - 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, includes=(), pygen_sink=None, pygen_classifier=None, - gccxml_options=None): - """ - Prepares to parse a set of header files. The following - methods should then be called in order to finish the rest of - scanning process: - - #. scan_types() - #. scan_methods() - #. scan_functions() - #. parse_finalize() - - :param header_files: header files to parse - :type header_files: list of string - - :param include_paths: (deprecated, use the parameter gccxml_options) list of include paths - :type include_paths: list of string - - :param whitelist_paths: additional directories for definitions to be included - Normally the module parser filters out API definitions that - have been defined outside one of the header files indicated - for parsing. The parameter whitelist_paths instructs the - module parser to accept definitions defined in another - header file if such header file is inside one of the - directories listed by whitelist_paths. - :type whitelist_paths: list of string - - :param pygen_sink: code sink for python script generation. - - This parameter activates a mode wherein ModuleParser, in - addition to building in memory API definitions, creates a - python script that will generate the module, when executed. - The generated Python script can be human editable and does - not require pygccxml or gccxml to run, only PyBindGen to be - installed. - - The pygen parameter can be either: - #. A single code sink: this will become the main and only script file to be generated - #. A list of L{PygenSection} objects. This option - requires the pygen_classifier to be given. - - :type pygen_sink: L{CodeSink} or list of L{PygenSection} objects - - :param pygen_classifier: the classifier to use when pygen is given and is a dict - - :param gccxml_options: extra options to pass into the - :class:`pygccxml.parser.config.gccxml_configuration_t` object as keyword - arguments for more information). - - :type gccxml_options: dict - - """ - assert isinstance(header_files, list) - assert isinstance(includes, (list, tuple)) - self._pygen = pygen_sink - self._pygen_classifier = pygen_classifier - if isinstance(pygen_sink, list): - assert isinstance(pygen_classifier, PygenClassifier) - has_main = False - for sect in self._pygen: - if not isinstance(sect, PygenSection): - raise TypeError - if sect.name == '__main__': - has_main = True - if not has_main: - raise ValueError("missing __main__ section") - elif pygen_sink is None: - pass - else: - assert 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 whitelist_paths is not None: - assert isinstance(whitelist_paths, list) - self.whitelist_paths = [os.path.abspath(p) for p in whitelist_paths] - - if gccxml_options is None: - gccxml_options = {} - - if include_paths is not None: - assert isinstance(include_paths, list) - warnings.warn("Parameter include_paths is deprecated, use gccxml_options instead", DeprecationWarning, - stacklevel=2) - self.gccxml_config = parser.gccxml_configuration_t(include_paths=include_paths, **gccxml_options) - else: - self.gccxml_config = parser.gccxml_configuration_t(**gccxml_options) - - self.declarations = parser.parse(header_files, self.gccxml_config) - self.global_ns = declarations.get_global_namespace(self.declarations) - if self.module_namespace_name == '::': - self.module_namespace = self.global_ns - else: - self.module_namespace = self.global_ns.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) - - for pygen_sink in self._get_all_pygen_sinks(): - pygen_sink.writeln("from pybindgen import Module, FileCodeSink, param, retval, cppclass, typehandlers") - pygen_sink.writeln() - - pygen_sink = self._get_main_pygen_sink() - if pygen_sink: - pygen_sink.writeln(""" -import pybindgen.settings -import warnings - -class ErrorHandler(pybindgen.settings.ErrorHandler): - def handle_error(self, wrapper, exception, traceback_): - warnings.warn("exception %r in wrapper %s" % (exception, wrapper)) - return True -pybindgen.settings.error_handler = ErrorHandler() - -""") - pygen_sink.writeln("import sys") - if isinstance(self._pygen, list): - for sect in self._pygen: - if sect.name == '__main__': - continue - pygen_sink.writeln("import %s" % sect.name) - pygen_sink.writeln() - pygen_sink.writeln("def module_init():") - pygen_sink.indent() - pygen_sink.writeln("root_module = Module(%r, cpp_namespace=%r)" - % (self.module_name, utils.ascii(self.module_namespace.decl_string))) - for inc in includes: - pygen_sink.writeln("root_module.add_include(%r)" % inc) - pygen_sink.writeln("return root_module") - pygen_sink.unindent() - pygen_sink.writeln() - - self.type_registry = GccXmlTypeRegistry(self.module) - self._stage = 'init' - - def _get_main_pygen_sink(self): - if isinstance (self._pygen, CodeSink): - return self._pygen - elif isinstance(self._pygen, list): - for sect in self._pygen: - if sect.name == '__main__': - return sect.code_sink - else: - return None - - def _get_all_pygen_sinks(self): - if isinstance (self._pygen, CodeSink): - return [self._pygen] - elif isinstance(self._pygen, list): - return [sect.code_sink for sect in self._pygen] - else: - return [] - - def _get_pygen_sink_for_definition(self, pygccxml_definition, with_section_precedence=False): - if self._pygen_classifier is None: - if with_section_precedence: - return (0, self._pygen) - else: - return self._pygen - else: - if isinstance(pygccxml_definition, declaration_t): - section = self._pygen_classifier.classify(pygccxml_definition) - for sect in self._pygen: - if sect is section or sect.name == section: - sink = sect.code_sink - break - else: - raise ValueError("CodeSink for section %r not available" % section) - else: - sink = self._get_main_pygen_sink() - section = '__main__' - if with_section_precedence: - try: - prec = self._pygen_classifier.get_section_precedence(section) - except NotImplementedError: - prec = 0 - return (prec, sink) - else: - return sink - - def scan_types(self): - self._stage = 'scan types' - self._registered_classes = {} # class_t -> CppClass - self._scan_namespace_types(self.module, self.module_namespace, pygen_register_function_name="register_types") - self._types_scanned = True - - def scan_methods(self): - self._stage = 'scan methods' - assert self._types_scanned - for pygen_sink in self._get_all_pygen_sinks(): - pygen_sink.writeln("def register_methods(root_module):") - 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 - if isinstance(class_wrapper, CppException): - continue # exceptions cannot have methods (yet) - #if class_wrapper.import_from_module: - # continue # foreign class - pygen_sink = self._get_pygen_sink_for_definition(class_wrapper.gccxml_definition) - if pygen_sink: - register_methods_func = "register_%s_methods" % (class_wrapper.mangled_full_name,) - pygen_sink.writeln("%s(root_module, root_module[%r])" % (register_methods_func, class_wrapper.full_name)) - - for pygen_sink in self._get_all_pygen_sinks(): - if pygen_sink is self._get_main_pygen_sink() and isinstance(self._pygen, list): - for sect in self._pygen: - if sect.name == '__main__': - continue - pygen_sink.writeln("root_module.begin_section(%r)" % sect.name) - pygen_sink.writeln("%s.register_methods(root_module)" % sect.name) - if sect.local_customizations_module: - pygen_sink.writeln("\ntry:\n" - " import %s\n" - "except ImportError:\n" - " pass\n" - "else:\n" - " %s.register_methods(root_module)\n" - % (sect.local_customizations_module, sect.local_customizations_module)) - pygen_sink.writeln("root_module.end_section(%r)" % sect.name) - - pygen_sink.writeln("return") - pygen_sink.unindent() - 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 - if isinstance(class_wrapper, CppException): - continue # exceptions cannot have methods (yet) - #if class_wrapper.import_from_module: - # continue # this is a foreign class from another module, we don't scan it - register_methods_func = "register_%s_methods" % (class_wrapper.mangled_full_name,) - - pygen_sink = self._get_pygen_sink_for_definition(class_wrapper.gccxml_definition) - if pygen_sink: - pygen_sink.writeln("def %s(root_module, cls):" % (register_methods_func,)) - pygen_sink.indent() - ## Add attributes from inner anonymous to each outer class (LP#237054) - for anon_cls, wrapper in self._anonymous_structs: - if wrapper is class_wrapper: - self._scan_class_methods(anon_cls, wrapper, pygen_sink) - self._scan_class_methods(class_wrapper.gccxml_definition, class_wrapper, pygen_sink) - - if pygen_sink: - pygen_sink.writeln("return") - pygen_sink.unindent() - pygen_sink.writeln() - - - def parse_finalize(self): - annotations_scanner.warn_unused_annotations() - pygen_sink = self._get_main_pygen_sink() - if pygen_sink: - pygen_sink.writeln("def main():") - pygen_sink.indent() - pygen_sink.writeln("out = FileCodeSink(sys.stdout)") - pygen_sink.writeln("root_module = module_init()") - pygen_sink.writeln("register_types(root_module)") - pygen_sink.writeln("register_methods(root_module)") - pygen_sink.writeln("register_functions(root_module)") - pygen_sink.writeln("root_module.generate(out)") - pygen_sink.unindent() - pygen_sink.writeln() - pygen_sink.writeln("if __name__ == '__main__':\n main()") - pygen_sink.writeln() - - return self.module - - def _apply_class_annotations(self, cls, annotations, kwargs): - is_exception = False - for name, value in annotations.items(): - if name == 'allow_subclassing': - kwargs.setdefault('allow_subclassing', annotations_scanner.parse_boolean(value)) - elif name == 'is_singleton': - kwargs.setdefault('is_singleton', annotations_scanner.parse_boolean(value)) - elif name == 'incref_method': - kwargs.setdefault('memory_policy', ReferenceCountingMethodsPolicy( - incref_method=value, decref_method=annotations.get('decref_method', None), - peekref_method=annotations.get('peekref_method', None))) - elif name == 'decref_method': - pass - elif name == 'peekref_method': - pass - elif name == 'automatic_type_narrowing': - kwargs.setdefault('automatic_type_narrowing', annotations_scanner.parse_boolean(value)) - elif name == 'free_function': - kwargs.setdefault('memory_policy', FreeFunctionPolicy(value)) - elif name == 'incref_function': - kwargs.setdefault('memory_policy', ReferenceCountingFunctionsPolicy( - incref_function=value, decref_function=annotations.get('decref_function', None))) - elif name == 'decref_function': - pass - elif name == 'python_name': - kwargs.setdefault('custom_name', value) - warnings.warn_explicit("Class annotation 'python_name' is deprecated in favour of 'custom_name'", - AnnotationsWarning, cls.location.file_name, cls.location.line) - elif name == 'custom_name': - kwargs.setdefault('custom_name', value) - elif name == 'pygen_comment': - pass - elif name == 'exception': - is_exception = True - elif name == 'import_from_module': - kwargs.setdefault('import_from_module', value) - else: - warnings.warn_explicit("Class annotation %r ignored" % name, - AnnotationsWarning, cls.location.file_name, cls.location.line) - if isinstance(cls, class_t): - if self._class_has_virtual_methods(cls) and not cls.bases: - kwargs.setdefault('allow_subclassing', True) - - #if not self._has_public_destructor(cls): - # kwargs.setdefault('is_singleton', True) - # #print >> sys.stderr, "##### class %s has no public destructor" % cls.decl_string - - des = self._get_destructor_visibility(cls) - #print >> sys.stderr, "##### class %s destructor is %s" % (cls.decl_string, des) - if des != 'public': - kwargs.setdefault('destructor_visibility', des) - - return is_exception - - def _get_destructor_visibility(self, cls): - for member in cls.get_members(): - if isinstance(member, calldef.destructor_t): - return member.access_type - - def _has_public_destructor(self, cls): - for member in cls.get_members(): - if isinstance(member, calldef.destructor_t): - if member.access_type != 'public': - return False - return True - - def _scan_namespace_types(self, module, module_namespace, outer_class=None, pygen_register_function_name=None): - root_module = module.get_root() - - if pygen_register_function_name: - for pygen_sink in self._get_all_pygen_sinks(): - pygen_sink.writeln("def %s(module):" % pygen_register_function_name) - pygen_sink.indent() - pygen_sink.writeln("root_module = module.get_root()") - pygen_sink.writeln() - if pygen_sink is self._get_main_pygen_sink() and isinstance(self._pygen, list): - for section in self._pygen: - if section.name == '__main__': - continue - if pygen_register_function_name == "register_types": - pygen_sink.writeln("root_module.begin_section(%r)" % section.name) - pygen_sink.writeln("%s.%s(module)" % (section.name, pygen_register_function_name)) - if section.local_customizations_module: - pygen_sink.writeln("\ntry:\n" - " import %s\n" - "except ImportError:\n" - " pass\n" - "else:\n" - " %s.register_types(module)\n" - % (section.local_customizations_module, section.local_customizations_module)) - pygen_sink.writeln("root_module.end_section(%r)" % section.name) - - ## detect use of unregistered container types: need to look at - ## all parameters and return values of all functions in this namespace... - for fun in module_namespace.free_functions(function=self.location_filter, - allow_empty=True, recursive=False): - if fun.name.startswith('__'): - continue - for dependency in fun.i_depend_on_them(recursive=True): - type_info = dependency.depend_on_it - if type_traits.is_pointer(type_info): - type_info = type_traits.remove_pointer(type_info) - elif type_traits.is_reference(type_info): - type_info = type_traits.remove_reference(type_info) - if type_traits.is_const(type_info): - type_info = type_traits.remove_const(type_info) - traits = container_traits.find_container_traits(type_info) - if traits is None: - continue - name = normalize_name(type_info.partial_decl_string) - #print >> sys.stderr, "** type: %s; ---> partial_decl_string: %r; name: %r" %\ - # (type_info, type_info.partial_decl_string, name) - self._containers_to_register.append((traits, type_info, None, name)) - - ## scan enumerations - if outer_class is None: - enums = module_namespace.enums(function=self.location_filter, - recursive=False, allow_empty=True) - else: - enums = [] - for enum in outer_class.gccxml_definition.enums(function=self.location_filter, - recursive=False, allow_empty=True): - if outer_class.gccxml_definition.find_out_member_access_type(enum) != 'public': - continue - if enum.name.startswith('__'): - continue - #if not enum.name: - # warnings.warn_explicit("Enum %s ignored because it has no name" - # % (enum, ), - # NotSupportedWarning, enum.location.file_name, enum.location.line) - # continue - enums.append(enum) - - for enum in enums: - - global_annotations, param_annotations = annotations_scanner.get_annotations(enum) - for hook in self._pre_scan_hooks: - hook(self, enum, global_annotations, param_annotations) - if 'ignore' in global_annotations: - continue - - enum_values_repr = '[' + ', '.join([repr(utils.ascii(name)) for name, dummy_val in enum.values]) + ']' - l = [repr(utils.ascii(enum.name)), enum_values_repr] - if outer_class is not None: - l.append('outer_class=root_module[%r]' % outer_class.full_name) - pygen_sink = self._get_pygen_sink_for_definition(enum) - if 'import_from_module' in global_annotations: - l.append("import_from_module=%r" % (global_annotations["import_from_module"],)) - if pygen_sink: - if 'pygen_comment' in global_annotations: - pygen_sink.writeln('## ' + global_annotations['pygen_comment']) - pygen_sink.writeln('module.add_enum(%s)' % ', '.join(l)) - - module.add_enum(utils.ascii(enum.name), [utils.ascii(name) for name, dummy_val in enum.values], - outer_class=outer_class) - - ## scan classes - if outer_class is None: - unregistered_classes = [cls for cls in - module_namespace.classes(function=self.location_filter, - recursive=False, allow_empty=True) - if not cls.name.startswith('__')] - typedefs = [typedef for typedef in - module_namespace.typedefs(function=self.location_filter, - recursive=False, allow_empty=True) - if not typedef.name.startswith('__')] - else: - unregistered_classes = [] - typedefs = [] - for cls in outer_class.gccxml_definition.classes(function=self.location_filter, - recursive=False, allow_empty=True): - if outer_class.gccxml_definition.find_out_member_access_type(cls) != 'public': - continue - if cls.name.startswith('__'): - continue - unregistered_classes.append(cls) - - for typedef in outer_class.gccxml_definition.typedefs(function=self.location_filter, - recursive=False, allow_empty=True): - if outer_class.gccxml_definition.find_out_member_access_type(typedef) != 'public': - continue - if typedef.name.startswith('__'): - continue - typedefs.append(typedef) - - unregistered_classes.sort(key=lambda c: c.decl_string) - - 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 >= 10: - 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, count, cls._pybindgen_postpone_reason, reason)) - cls._pybindgen_postpone_reason = reason - if DEBUG: - print(">>> class %s is being postponed (%s)" % (str(cls), reason), file=sys.stderr) - unregistered_classes.append(cls) - - while unregistered_classes: - cls = unregistered_classes.pop(0) - if DEBUG: - print(">>> looking at class ", str(cls), file=sys.stderr) - typedef = None - - kwargs = {} - global_annotations, param_annotations = annotations_scanner.get_annotations(cls) - for hook in self._pre_scan_hooks: - hook(self, cls, global_annotations, param_annotations) - if 'ignore' in global_annotations: - continue - - if not cls.name: - if outer_class is None: - warnings.warn_explicit(("Class %s ignored: anonymous structure not inside a named structure/union." - % cls.partial_decl_string), - NotSupportedWarning, cls.location.file_name, cls.location.line) - continue - - self._anonymous_structs.append((cls, outer_class)) - continue - - - if '<' in cls.name: - - for typedef in module_namespace.typedefs(function=self.location_filter, - recursive=False, allow_empty=True): - typedef_type = type_traits.remove_declarated(typedef.type) - if typedef_type == cls: - break - else: - typedef = None - - base_class_wrappers = [] - bases_ok = True - for cls_bases_item in cls.bases: - base_cls = cls_bases_item.related_class - try: - base_class_wrapper = self._registered_classes[base_cls] - except KeyError: - ## base class not yet registered => postpone this class registration - if base_cls not in unregistered_classes: - warnings.warn_explicit("Class %s ignored because it uses a base class (%s) " - "which is not declared." - % (cls.partial_decl_string, base_cls.partial_decl_string), - ModuleParserWarning, cls.location.file_name, cls.location.line) - bases_ok = False - break - postpone_class(cls, "waiting for base class %s to be registered first" % base_cls) - bases_ok = False - break - else: - base_class_wrappers.append(base_class_wrapper) - del base_class_wrapper - del base_cls - if not bases_ok: - continue - - ## If this class implicitly converts to another class, but - ## that other class is not yet registered, postpone. - for operator in cls.casting_operators(allow_empty=True): - target_type = type_traits.remove_declarated(operator.return_type) - if not isinstance(target_type, class_t): - continue - target_class_name = normalize_class_name(operator.return_type.partial_decl_string, '::') - try: - dummy = root_module[target_class_name] - except KeyError: - if target_class_name not in [normalize_class_name(t.partial_decl_string, '::') for t in unregistered_classes]: - ok = True # (lp:455689) - else: - ok = False - break - else: - ok = True - if not ok: - postpone_class(cls, ("waiting for implicit conversion target class %s to be registered first" - % (operator.return_type.partial_decl_string,))) - continue - - is_exception = self._apply_class_annotations(cls, global_annotations, kwargs) - - custom_template_class_name = None - template_parameters = () - if typedef is None: - alias = None - if templates.is_instantiation(cls.decl_string): - cls_name, template_parameters = templates.split(cls.name) - assert template_parameters - if '::' in cls_name: - cls_name = cls_name.split('::')[-1] - template_instance_names = global_annotations.get('template_instance_names', '') - if template_instance_names: - for mapping in template_instance_names.split('|'): - type_names, name = mapping.split('=>') - instance_types = type_names.split(',') - if instance_types == template_parameters: - custom_template_class_name = name - break - else: - cls_name = cls.name - else: - cls_name = typedef.name - alias = '::'.join([module.cpp_namespace_prefix, cls.name]) - - template_parameters_decls = [find_declaration_from_name(self.global_ns, templ_param) - for templ_param in template_parameters] - - ignore_class = False - for template_param in template_parameters_decls: - if not isinstance(template_param, class_t): - continue - if not isinstance(template_param.parent, class_t): - continue - access = template_param.parent.find_out_member_access_type(template_param) - if access != 'public': - # this templated class depends on a private type => we can't wrap it - ignore_class = True - break - if ignore_class: - continue - - if 0: # this is disabled due to ns3 - ## if any template argument is a class that is not yet - ## registered, postpone scanning/registering the template - ## instantiation class until the template argument gets - ## registered. - postponed = False - for templ in template_parameters_decls: - if isinstance(templ, class_t): - try: - self._registered_classes[templ] - except KeyError: - if templ in unregistered_classes: - postpone_class(cls, "waiting for template argument class %s to be registered first" % templ) - postponed = True - if postponed: - continue - - if base_class_wrappers: - if len(base_class_wrappers) > 1: - kwargs["parent"] = base_class_wrappers - else: - kwargs["parent"] = base_class_wrappers[0] - 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_name"] = custom_template_class_name - - # given the pygen sinks for the class itself and the sinks - # for the template parameters, get the one with lowest - # precedence (the higher the number, the lowest the - # precedence). - pygen_sinks = [self._get_pygen_sink_for_definition(cls, with_section_precedence=True)] - for templ in template_parameters_decls: - if templ is not None: - pygen_sinks.append(self._get_pygen_sink_for_definition(templ, with_section_precedence=True)) - pygen_sinks.sort() - pygen_sink = pygen_sinks[-1][1] - del pygen_sinks - - if pygen_sink: - if 'pygen_comment' in global_annotations: - pygen_sink.writeln('## ' + global_annotations['pygen_comment']) - if is_exception: - pygen_sink.writeln("module.add_exception(%s)" % - ", ".join([repr(cls_name)] + _pygen_kwargs(kwargs))) - else: - pygen_sink.writeln("module.add_class(%s)" % - ", ".join([repr(cls_name)] + _pygen_kwargs(kwargs))) - - ## detect use of unregistered container types: need to look at - ## all parameters and return values of all functions in this namespace... - for member in cls.get_members(access='public'): - if member.name.startswith('__'): - continue - for dependency in member.i_depend_on_them(recursive=True): - type_info = dependency.depend_on_it - if type_traits.is_pointer(type_info): - type_info = type_traits.remove_pointer(type_info) - elif type_traits.is_reference(type_info): - type_info = type_traits.remove_reference(type_info) - if type_traits.is_const(type_info): - type_info = type_traits.remove_const(type_info) - traits = container_traits.find_container_traits(type_info) - if traits is None: - continue - name = normalize_name(type_info.partial_decl_string) - # now postpone container registration until after - # all classes are registered, because we may - # depend on one of those classes for the element - # type. - self._containers_to_register.append((traits, type_info, None, name)) - - if is_exception: - class_wrapper = module.add_exception(cls_name, **kwargs) - else: - class_wrapper = module.add_class(cls_name, **kwargs) - #print >> sys.stderr, "<<<<>>>> ", cls_name - - class_wrapper.gccxml_definition = cls - self._registered_classes[cls] = class_wrapper - 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) - - del cls_name - - ## scan for nested classes/enums - self._scan_namespace_types(module, module_namespace, outer_class=class_wrapper) - - # scan for implicit conversion casting operators - for operator in cls.casting_operators(allow_empty=True): - target_type = type_traits.remove_declarated(operator.return_type) - if not isinstance(target_type, class_t): - continue - other_class_name = normalize_class_name(operator.return_type.partial_decl_string, '::') - try: - other_class = root_module[other_class_name] - except KeyError: - warnings.warn_explicit("Implicit conversion target type %s not registered" - % (other_class_name,), - WrapperWarning, operator.location.file_name, - operator.location.line) - else: - class_wrapper.implicitly_converts_to(other_class) - if pygen_sink: - if 'pygen_comment' in global_annotations: - pygen_sink.writeln('## ' + global_annotations['pygen_comment']) - pygen_sink.writeln("root_module[%r].implicitly_converts_to(root_module[%r])" - % (class_wrapper.full_name, other_class.full_name)) - - # -- register containers - if outer_class is None: - for (traits, type_info, _outer_class, name) in self._containers_to_register: - self._register_container(module, traits, type_info, _outer_class, name) - self._containers_to_register = [] - - if pygen_register_function_name: - pygen_function_closed = False - else: - pygen_function_closed = True - - if outer_class is None: - - ## --- look for typedefs ---- - for alias in module_namespace.typedefs(function=self.location_filter, - recursive=False, allow_empty=True): - - type_from_name = normalize_name(str(alias.type)) - type_to_name = normalize_name(utils.ascii('::'.join([module.cpp_namespace_prefix, alias.name]))) - - for sym in '', '*', '&': - typehandlers.base.add_type_alias(type_from_name+sym, type_to_name+sym) - pygen_sink = self._get_pygen_sink_for_definition(alias) - if pygen_sink: - pygen_sink.writeln("typehandlers.add_type_alias(%r, %r)" % (type_from_name+sym, type_to_name+sym)) - - ## Look for forward declarations of class/structs like - ## "typedef struct _Foo Foo"; these are represented in - ## pygccxml by a typedef whose .type.declaration is a - ## class_declaration_t instead of class_t. - if isinstance(alias.type, cpptypes.declarated_t): - cls = alias.type.declaration - if templates.is_instantiation(cls.decl_string): - continue # typedef to template instantiations, must be fully defined - if isinstance(cls, class_declaration_t): - - global_annotations, param_annotations = annotations_scanner.get_annotations(cls) - for hook in self._pre_scan_hooks: - hook(self, cls, global_annotations, param_annotations) - if 'ignore' in global_annotations: - continue - - kwargs = dict() - self._apply_class_annotations(cls, global_annotations, kwargs) - kwargs.setdefault("incomplete_type", True) - kwargs.setdefault("automatic_type_narrowing", False) - kwargs.setdefault("allow_subclassing", False) - - pygen_sink = self._get_pygen_sink_for_definition(cls) - if pygen_sink: - if 'pygen_comment' in global_annotations: - pygen_sink.writeln('## ' + global_annotations['pygen_comment']) - 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 - self._registered_classes[cls] = class_wrapper - if cls.name != alias.name: - class_wrapper.register_alias(normalize_name(cls.name)) - self.type_registry.class_registered(class_wrapper) - - ## Handle "typedef ClassName OtherName;" - elif isinstance(cls, class_t): - #print >> sys.stderr, "***** typedef", cls, "=>", alias.name - try: - cls_wrapper = self._registered_classes[cls] - except KeyError: - warnings.warn("typedef %s %s: wrapper for class %s not know" - % (cls, alias.name, cls), - WrapperWarning) - else: - module.add_typedef(cls_wrapper, alias.name) - - pygen_sink = self._get_pygen_sink_for_definition(cls) - if pygen_sink: - pygen_sink.writeln("module.add_typedef(root_module[%r], %r)" % - (cls_wrapper.full_name, utils.ascii(alias.name))) - - - ## scan nested namespaces (mapped as python submodules) - nested_modules = [] - nested_namespaces = [] - for nested_namespace in module_namespace.namespaces(allow_empty=True, recursive=False): - if nested_namespace.name.startswith('__'): - continue - nested_namespaces.append(nested_namespace) - - nested_namespaces.sort(key=lambda c: c.decl_string) - - for nested_namespace in nested_namespaces: - if pygen_register_function_name: - nested_module = module.add_cpp_namespace(utils.ascii(nested_namespace.name)) - nested_modules.append(nested_module) - for pygen_sink in self._get_all_pygen_sinks(): - pygen_sink.writeln() - pygen_sink.writeln("## Register a nested module for the namespace %s" % utils.ascii(nested_namespace.name)) - pygen_sink.writeln() - pygen_sink.writeln("nested_module = module.add_cpp_namespace(%r)" % utils.ascii(nested_namespace.name)) - nested_module_type_init_func = "register_types_" + "_".join(nested_module.get_namespace_path()) - pygen_sink.writeln("%s(nested_module)" % nested_module_type_init_func) - pygen_sink.writeln() - if not pygen_function_closed: - for pygen_sink in self._get_all_pygen_sinks(): - pygen_sink.unindent() - pygen_sink.writeln() - pygen_function_closed = True - - ## scan nested namespaces (mapped as python submodules) - nested_namespaces = [] - for nested_namespace in module_namespace.namespaces(allow_empty=True, recursive=False): - if nested_namespace.name.startswith('__'): - continue - nested_namespaces.append(nested_namespace) - - nested_namespaces.sort(key=lambda c: c.decl_string) - - for nested_namespace in nested_namespaces: - 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 - # ^^ CLOSE: if outer_class is None: ^^ - - if pygen_register_function_name and not pygen_function_closed: - for pygen_sink in self._get_all_pygen_sinks(): - pygen_sink.unindent() - pygen_sink.writeln() - - def _register_container(self, module, traits, definition, outer_class, name): - if '<' in name and not self.enable_anonymous_containers: - return - - kwargs = {} - key_type = None - - if traits is container_traits.list_traits: - container_type = 'list' - elif traits is container_traits.deque_traits: - container_type = 'dequeue' - elif traits is container_traits.queue_traits: - container_type = 'queue' - elif traits is container_traits.priority_queue_traits: - container_type = 'dequeue' - elif traits is container_traits.vector_traits: - container_type = 'vector' - elif traits is container_traits.stack_traits: - container_type = 'stack' - elif traits is container_traits.set_traits: - container_type = 'set' - elif traits is container_traits.multiset_traits: - container_type = 'multiset' - elif traits is container_traits.hash_set_traits: - container_type = 'hash_set' - elif traits is container_traits.hash_multiset_traits: - container_type = 'hash_multiset' - elif traits is container_traits.map_traits: - container_type = 'map' - if hasattr(traits, "key_type"): - key_type = traits.key_type(definition) - else: - warnings.warn("pygccxml 0.9.5 or earlier don't have the key_type method, " - "so we don't support mapping types with this pygccxml version (%r)" - % pygccxml.__version__) - return - - elif (traits is container_traits.map_traits - or traits is container_traits.multimap_traits - or traits is container_traits.hash_map_traits - or traits is container_traits.hash_multimap_traits): - return # maps not yet implemented - - else: - assert False, "container type %s unaccounted for." % name - - if outer_class is not None: - kwargs['outer_class'] = outer_class - outer_class_key = outer_class.partial_decl_string - else: - outer_class_key = None - - container_register_key = (outer_class_key, name) - if container_register_key in self._containers_registered: - return - self._containers_registered[container_register_key] = None - - #print >> sys.stderr, "************* register_container", name - - element_type = traits.element_type(definition) - #if traits.is_mapping(definition): - # key_type = traits.key_type(definition) - #print >> sys.stderr, "************* register_container %s; element_type=%s, key_type=%s" % \ - # (name, element_type, key_type.partial_decl_string) - - element_decl = type_traits.remove_declarated(element_type) - - kwargs['container_type'] = container_type - - pygen_sink = self._get_pygen_sink_for_definition(element_decl) - - elem_type_spec = self.type_registry.lookup_return(element_type) - if key_type is not None: - key_type_spec = self.type_registry.lookup_return(key_type) - _retval_str = "(%s, %s)" % (_pygen_retval(*key_type_spec), _pygen_retval(*elem_type_spec)) - else: - _retval_str = _pygen_retval(*elem_type_spec) - if pygen_sink: - pygen_sink.writeln("module.add_container(%s)" % - ", ".join([repr(name), _retval_str] + _pygen_kwargs(kwargs))) - - ## convert the return value - try: - return_type_elem = ReturnValue.new(*elem_type_spec[0], **elem_type_spec[1]) - except (TypeLookupError, TypeConfigurationError) as ex: - warnings.warn("Return value '%s' error (used in %s): %r" - % (definition.partial_decl_string, definition, ex), - WrapperWarning) - return - - if key_type is not None: - try: - return_type_key = ReturnValue.new(*key_type_spec[0], **key_type_spec[1]) - except (TypeLookupError, TypeConfigurationError) as ex: - warnings.warn("Return value '%s' error (used in %s): %r" - % (definition.partial_decl_string, definition, ex), - WrapperWarning) - return - - module.add_container(name, (return_type_key, return_type_elem), **kwargs) - else: - module.add_container(name, return_type_elem, **kwargs) - - - def _class_has_virtual_methods(self, cls): - """return True if cls has at least one virtual method, else False""" - for member in cls.get_members(): - if isinstance(member, calldef.member_function_t): - if member.virtuality != calldef.VIRTUALITY_TYPES.NOT_VIRTUAL: - return True - return False - - def _is_ostream(self, cpp_type): - return (isinstance(cpp_type, cpptypes.reference_t) - and not isinstance(cpp_type.base, cpptypes.const_t) - and str(cpp_type.base) == 'std::ostream') - - def _scan_class_operators(self, cls, class_wrapper, pygen_sink): - - def _handle_operator(op, argument_types): - #print >> sys.stderr, "<<<<>>>> (OP %s in class %s) : %s --> %s" % \ - # (op.symbol, cls, [str(x) for x in argument_types], op.return_type) - - if op.symbol == '<<' \ - and self._is_ostream(op.return_type) \ - and len(op.arguments) == 2 \ - and self._is_ostream(argument_types[0]) \ - and type_traits.is_convertible(cls, argument_types[1]): - #print >> sys.stderr, "<<<<>>>> %s: %s " % (op.symbol, cls) - class_wrapper.add_output_stream_operator() - pygen_sink.writeln("cls.add_output_stream_operator()") - return - - if op.symbol in ['==', '!=', '<', '<=', '>', '>='] \ - and len(argument_types) == 2 \ - and type_traits.is_convertible(cls, argument_types[0]) \ - and type_traits.is_convertible(cls, argument_types[1]): - #print >> sys.stderr, "<<<<>>>> %s: %s " % (op.symbol, cls) - class_wrapper.add_binary_comparison_operator(op.symbol) - pygen_sink.writeln("cls.add_binary_comparison_operator(%r)" % (op.symbol,)) - return - - def get_class_wrapper(pygccxml_type): - traits = ctypeparser.TypeTraits(normalize_name(pygccxml_type.partial_decl_string)) - if traits.type_is_reference: - name = str(traits.target) - else: - name = str(traits.ctype) - class_wrapper = self.type_registry.root_module.get(name, None) - #print >> sys.stderr, "(lookup %r: %r)" % (name, class_wrapper) - return class_wrapper - - if not type_traits.is_convertible(cls, argument_types[0]): - return - - ret = get_class_wrapper(op.return_type) - if ret is None: - warnings.warn_explicit("NUMERIC OP: retval class %s not registered" % (op.return_type,), - WrapperWarning, op.location.file_name, op.location.line) - return - - arg0 = get_class_wrapper(argument_types[0]) - if arg0 is None: - warnings.warn_explicit("NUMERIC OP: arg0 class %s not registered" % (op.return_type,), - WrapperWarning, op.location.file_name, op.location.line) - return - - if len(argument_types) == 2: - dummy_global_annotations, parameter_annotations = annotations_scanner.get_annotations(op) - arg_spec = self.type_registry.lookup_parameter(argument_types[1], 'right', - parameter_annotations.get('right', {})) - - arg_repr = _pygen_param(arg_spec[0], arg_spec[1]) - - try: - param = Parameter.new(*arg_spec[0], **arg_spec[1]) - except (TypeLookupError, TypeConfigurationError) as ex: - warnings.warn_explicit("Parameter '%s' error (used in %s): %r" - % (argument_types[1].partial_decl_string, op, ex), - WrapperWarning, op.location.file_name, op.location.line) - param = None - - #print >> sys.stderr, "<<<<>>>> ", param, ('?' if param is None else param.ctype) - - if op.symbol in ['+', '-', '/', '*']: - #print >> sys.stderr, "<<<<>>>> %s: %s : %s --> %s" \ - # % (op.symbol, cls, [str(x) for x in argument_types], return_type) - - pygen_sink.writeln("cls.add_binary_numeric_operator(%r, root_module[%r], root_module[%r], %s)" - % (op.symbol, ret.full_name, arg0.full_name, arg_repr)) - if param is not None: - class_wrapper.add_binary_numeric_operator(op.symbol, ret, arg0, param) - - # -- inplace numeric operators -- - if op.symbol in ['+=', '-=', '/=', '*=']: - #print >> sys.stderr, "<<<<>>>> %s: %s : %s --> %s" \ - # % (op.symbol, cls, [str(x) for x in argument_types], return_type) - - pygen_sink.writeln("cls.add_inplace_numeric_operator(%r, %s)" % (op.symbol, arg_repr)) - if param is not None: - class_wrapper.add_inplace_numeric_operator(op.symbol, param) - - elif len(argument_types) == 1: # unary operator - if op.symbol in ['-']: - pygen_sink.writeln("cls.add_unary_numeric_operator(%r)" % (op.symbol,)) - class_wrapper.add_unary_numeric_operator(op.symbol) - - else: - warnings.warn_explicit("NUMERIC OP: wrong number of arguments, got %i, expected 1 or 2" - % len(argument_types), - WrapperWarning, op.location.file_name, op.location.line) - return - - - - for op in self.module_namespace.free_operators(function=self.location_filter, - allow_empty=True, - recursive=True): - _handle_operator(op, [arg.type for arg in op.arguments]) - - for op in cls.member_operators(function=self.location_filter, - allow_empty=True, - recursive=True): - if op.access_type != 'public': - continue - arg_types = [arg.type for arg in op.arguments] - arg_types.insert(0, cls) - _handle_operator(op, arg_types) - - - def _scan_class_methods(self, cls, class_wrapper, pygen_sink): - have_trivial_constructor = False - have_copy_constructor = False - - if pygen_sink is None: - pygen_sink = NullCodeSink() - - self._scan_class_operators(cls, class_wrapper, pygen_sink) - - for member in cls.get_members(): - if isinstance(member, calldef.member_function_t): - if member.access_type not in ['protected', 'private']: - continue - - elif isinstance(member, calldef.constructor_t): - if member.access_type not in ['protected', 'private']: - continue - - if len(member.arguments) == 0: - have_trivial_constructor = True - - elif len(member.arguments) == 1: - traits = ctypeparser.TypeTraits(normalize_name(member.arguments[0].type.partial_decl_string)) - if traits.type_is_reference and \ - self.type_registry.root_module.get(str(traits.target), None) is class_wrapper: - have_copy_constructor = True - - methods_to_ignore = [] - if isinstance(class_wrapper.memory_policy, ReferenceCountingMethodsPolicy): - methods_to_ignore.extend([class_wrapper.memory_policy.incref_method, - class_wrapper.memory_policy.decref_method, - class_wrapper.memory_policy.peekref_method, - ]) - - for member in cls.get_members(): - if member.name in methods_to_ignore: - continue - - global_annotations, parameter_annotations = annotations_scanner.get_annotations(member) - for hook in self._pre_scan_hooks: - hook(self, member, global_annotations, parameter_annotations) - - if 'ignore' in global_annotations: - continue - - ## ------------ method -------------------- - if isinstance(member, (calldef.member_function_t, calldef.member_operator_t)): - is_virtual = (member.virtuality != calldef.VIRTUALITY_TYPES.NOT_VIRTUAL) - pure_virtual = (member.virtuality == calldef.VIRTUALITY_TYPES.PURE_VIRTUAL) - - kwargs = {} # kwargs passed into the add_method call - - for key, val in global_annotations.items(): - if key == 'template_instance_names' \ - and templates.is_instantiation(member.demangled_name): - pass - elif key == 'pygen_comment': - pass - elif key == 'unblock_threads': - kwargs['unblock_threads'] = annotations_scanner.parse_boolean(val) - elif key == 'name': - kwargs['custom_name'] = val - elif key == 'throw': - kwargs['throw'] = self._get_annotation_exceptions(val) - else: - warnings.warn_explicit("Annotation '%s=%s' not used (used in %s)" - % (key, val, member), - AnnotationsWarning, member.location.file_name, member.location.line) - if isinstance(member, calldef.member_operator_t): - if member.symbol == '()': - kwargs['custom_name'] = '__call__' - else: - continue - - throw = self._get_calldef_exceptions(member) - if throw: - kwargs['throw'] = throw - - ## --- pygen --- - return_type_spec = self.type_registry.lookup_return(member.return_type, - parameter_annotations.get('return', {})) - argument_specs = [] - for arg in member.arguments: - 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")') - - custom_template_method_name = None - if templates.is_instantiation(member.demangled_name): - template_parameters = templates.args(member.demangled_name) - template_instance_names = global_annotations.get('template_instance_names', '') - if template_instance_names: - for mapping in template_instance_names.split('|'): - type_names, name = mapping.split('=>') - instance_types = type_names.split(',') - if instance_types == template_parameters: - custom_template_method_name = name - break - else: - template_parameters = () - - if member.has_const: - kwargs['is_const'] = True - if member.has_static: - kwargs['is_static'] = True - if is_virtual: - kwargs['is_virtual'] = True - if pure_virtual: - kwargs['is_pure_virtual'] = True - if template_parameters: - kwargs['template_parameters'] = template_parameters - if custom_template_method_name: - kwargs['custom_template_method_name'] = custom_template_method_name - if member.access_type != 'public': - kwargs['visibility'] = member.access_type - - ## ignore methods that are private and not virtual or - ## pure virtual, as they do not affect the bindings in - ## any way and only clutter the generated python script. - if (kwargs.get('visibility', 'public') == 'private' - and not (kwargs.get('is_virtual', False) or kwargs.get('is_pure_virtual', False))): - continue - - if member.attributes: - if 'deprecated' in member.attributes: - kwargs['deprecated'] = True - - ## --- pygen --- - arglist_repr = ("[" + ', '.join([_pygen_param(args_, kwargs_) for (args_, kwargs_) in argument_specs]) + "]") - if 'pygen_comment' in global_annotations: - pygen_sink.writeln('## ' + global_annotations['pygen_comment']) - - kwargs_repr = _pygen_kwargs(kwargs) - if kwargs_repr: - kwargs_repr[0] = '\n' + 15*' ' + kwargs_repr[0] - - pygen_sink.writeln("cls.add_method(%s)" % - ", ".join( - [repr(member.name), - '\n' + 15*' ' + _pygen_retval(return_type_spec[0], return_type_spec[1]), - '\n' + 15*' ' + arglist_repr] + kwargs_repr)) - - ## --- realize the return type and parameters - try: - return_type = ReturnValue.new(*return_type_spec[0], **return_type_spec[1]) - except (TypeLookupError, TypeConfigurationError) as ex: - warnings.warn_explicit("Return value '%s' error (used in %s): %r" - % (member.return_type.partial_decl_string, member, ex), - WrapperWarning, 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) as ex: - warnings.warn_explicit("Parameter '%s %s' error (used in %s): %r" - % (arg[0][0], arg[0][1], member, ex), - WrapperWarning, 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 as ex: - if pure_virtual: - class_wrapper.set_cannot_be_constructed("pure virtual method %r not wrapped" % member.name) - class_wrapper.set_helper_class_disabled(True) - pygen_sink.writeln('cls.set_cannot_be_constructed("pure virtual method %%r not wrapped" %% %r)' - % member.name) - pygen_sink.writeln('cls.set_helper_class_disabled(True)') - - warnings.warn_explicit("Error adding method %s: %r" - % (member, ex), - WrapperWarning, member.location.file_name, member.location.line) - except ValueError as ex: - warnings.warn_explicit("Error adding method %s: %r" - % (member, ex), - WrapperWarning, member.location.file_name, member.location.line) - raise - else: # no exception, add method succeeded - for hook in self._post_scan_hooks: - hook(self, member, method_wrapper) - - - ## ------------ constructor -------------------- - elif isinstance(member, calldef.constructor_t): - if member.access_type not in ['public', 'protected']: - continue - - if not member.arguments: - have_trivial_constructor = True - - 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]) + "]") - if 'pygen_comment' in global_annotations: - pygen_sink.writeln('## ' + global_annotations['pygen_comment']) - - kwargs = {} - - if member.attributes: - if 'deprecated' in member.attributes: - kwargs['deprecated'] = True - - if member.access_type != 'public': - kwargs['visibility'] = member.access_type - - throw = self._get_calldef_exceptions(member) - if throw: - kwargs['throw'] = throw - - kwargs_repr = _pygen_kwargs(kwargs) - if kwargs_repr: - kwargs_repr[0] = '\n' + 20*' '+ kwargs_repr[0] - pygen_sink.writeln("cls.add_constructor(%s)" % - ", ".join([arglist_repr] + kwargs_repr)) - - arguments = [] - for a, kw in argument_specs: - try: - arguments.append(Parameter.new(*a, **kw)) - except (TypeLookupError, TypeConfigurationError) as ex: - warnings.warn_explicit("Parameter '%s %s' error (used in %s): %r" - % (arg.type.partial_decl_string, arg.name, member, ex), - WrapperWarning, member.location.file_name, member.location.line) - ok = False - break - else: - ok = True - if not ok: - continue - constructor_wrapper = class_wrapper.add_constructor(arguments, **kwargs) - constructor_wrapper.gccxml_definition = member - for hook in self._post_scan_hooks: - hook(self, member, constructor_wrapper) - - if (len(arguments) == 1 - and isinstance(arguments[0], class_wrapper.ThisClassRefParameter)): - have_copy_constructor = True - - ## ------------ attribute -------------------- - elif isinstance(member, variable_t): - if not member.name: - continue # anonymous structure - if member.access_type == 'protected': - warnings.warn_explicit("%s: protected member variables not yet implemented " - "by PyBindGen." - % member, - NotSupportedWarning, member.location.file_name, member.location.line) - continue - if member.access_type == 'private': - continue - - real_type = type_traits.remove_declarated(member.type) - if hasattr(real_type, 'name') and not real_type.name: - warnings.warn_explicit("Member variable %s of class %s will not be wrapped, " - "because wrapping member variables of anonymous types " - "is not yet supported by pybindgen" - % (member.name, cls.partial_decl_string), - NotSupportedWarning, member.location.file_name, member.location.line) - continue - - return_type_spec = self.type_registry.lookup_return(member.type, global_annotations) - - ## pygen... - if 'pygen_comment' in global_annotations: - pygen_sink.writeln('## ' + global_annotations['pygen_comment']) - if member.type_qualifiers.has_static: - 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: - 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 = ReturnValue.new(*return_type_spec[0], **return_type_spec[1]) - except (TypeLookupError, TypeConfigurationError) as ex: - warnings.warn_explicit("Return value '%s' error (used in %s): %r" - % (member.type.partial_decl_string, member, ex), - WrapperWarning, 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)) - else: - class_wrapper.add_instance_attribute(member.name, return_type, - is_const=type_traits.is_const(member.type)) - ## TODO: invoke post_scan_hooks - elif isinstance(member, calldef.destructor_t): - pass - - ## gccxml 0.9, unlike 0.7, does not explicitly report inheritted trivial constructors - ## thankfully pygccxml comes to the rescue! - if not have_trivial_constructor: - if type_traits.has_trivial_constructor(cls): - class_wrapper.add_constructor([]) - pygen_sink.writeln("cls.add_constructor([])") - - if not have_copy_constructor: - try: # pygccxml > 0.9 - has_copy_constructor = type_traits.has_copy_constructor(cls) - except AttributeError: # pygccxml <= 0.9 - has_copy_constructor = type_traits.has_trivial_copy(cls) - if has_copy_constructor: - class_wrapper.add_copy_constructor() - pygen_sink.writeln("cls.add_copy_constructor()") - - - def _get_calldef_exceptions(self, calldef): - retval = [] - for decl in calldef.exceptions: - traits = ctypeparser.TypeTraits(normalize_name(decl.partial_decl_string)) - if traits.type_is_reference: - name = str(traits.target) - else: - name = str(traits.ctype) - exc = self.type_registry.root_module.get(name, None) - if isinstance(exc, CppException): - retval.append(exc) - else: - warnings.warn_explicit("Thrown exception '%s' was not previously detected as an exception class." - " PyBindGen bug?" - % (normalize_name(decl.partial_decl_string)), - WrapperWarning, calldef.location.file_name, calldef.location.line) - return retval - - def _get_annotation_exceptions(self, annotation): - retval = [] - for exc_name in annotation.split(','): - traits = ctypeparser.TypeTraits(normalize_name(exc_name)) - if traits.type_is_reference: - name = str(traits.target) - else: - name = str(traits.ctype) - exc = self.type_registry.root_module.get(name, None) - if isinstance(exc, CppException): - retval.append(exc) - else: - raise ValueError("Thrown exception '%s' is not a CppException object: %r" - % (normalize_name(name), exc)) - return retval - - def scan_functions(self): - self._stage = 'scan functions' - assert self._types_scanned - for pygen_sink in self._get_all_pygen_sinks(): - pygen_sink.writeln("def register_functions(root_module):") - pygen_sink.indent() - pygen_sink.writeln("module = root_module") - if pygen_sink is self._get_main_pygen_sink() and isinstance(self._pygen, list): - for section in self._pygen: - if section.name == '__main__': - continue - pygen_sink.writeln("root_module.begin_section(%r)" % section.name) - pygen_sink.writeln("%s.register_functions(root_module)" % section.name) - if section.local_customizations_module: - pygen_sink.writeln("\ntry:\n" - " import %s\n" - "except ImportError:\n" - " pass\n" - "else:\n" - " %s.register_functions(root_module)\n" - % (section.local_customizations_module, section.local_customizations_module)) - pygen_sink.writeln("root_module.end_section(%r)" % section.name) - self._scan_namespace_functions(self.module, self.module_namespace) - - def _scan_namespace_functions(self, module, module_namespace): - root_module = module.get_root() - - functions_to_scan = [] - for fun in module_namespace.free_functions(function=self.location_filter, - allow_empty=True, recursive=False): - if fun.name.startswith('__'): - continue - functions_to_scan.append(fun) - - functions_to_scan.sort(key=lambda c: (c.name, c.decl_string)) - - for fun in functions_to_scan: - global_annotations, parameter_annotations = annotations_scanner.get_annotations(fun) - for hook in self._pre_scan_hooks: - hook(self, fun, global_annotations, parameter_annotations) - - as_method = None - of_class = None - alt_name = None - ignore = False - kwargs = {} - - for name, value in global_annotations.items(): - if name == 'as_method': - as_method = value - elif name == 'of_class': - of_class = value - elif name == 'name': - alt_name = value - elif name == 'ignore': - ignore = True - elif name == 'is_constructor_of': - pass - elif name == 'pygen_comment': - pass - elif name == 'template_instance_names': - pass - elif name == 'unblock_threads': - kwargs['unblock_threads'] = annotations_scanner.parse_boolean(value) - elif name == 'throw': - kwargs['throw'] = self._get_annotation_exceptions(value) - else: - warnings.warn_explicit("Incorrect annotation %s=%s" % (name, value), - AnnotationsWarning, fun.location.file_name, fun.location.line) - if ignore: - continue - - - is_constructor_of = global_annotations.get("is_constructor_of", None) - 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 = ReturnValue.new(*return_type_spec[0], **return_type_spec[1]) - except (TypeLookupError, TypeConfigurationError) as ex: - warnings.warn_explicit("Return value '%s' error (used in %s): %r" - % (fun.return_type.partial_decl_string, fun, ex), - WrapperWarning, fun.location.file_name, fun.location.line) - params_ok = False - except TypeError as ex: - warnings.warn_explicit("Return value '%s' error (used in %s): %r" - % (fun.return_type.partial_decl_string, fun, ex), - WrapperWarning, 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(Parameter.new(*spec[0], **spec[1])) - except (TypeLookupError, TypeConfigurationError) as ex: - warnings.warn_explicit("Parameter '%s %s' error (used in %s): %r" - % (arg.type.partial_decl_string, arg.name, fun, ex), - WrapperWarning, fun.location.file_name, fun.location.line) - - params_ok = False - except TypeError as ex: - warnings.warn_explicit("Parameter '%s %s' error (used in %s): %r" - % (arg.type.partial_decl_string, arg.name, fun, ex), - WrapperWarning, fun.location.file_name, fun.location.line) - raise - - throw = self._get_calldef_exceptions(fun) - if throw: - kwargs['throw'] = throw - - 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 = root_module[normalize_class_name(of_class, (self.module_namespace_name or '::'))] - - pygen_sink = self._get_pygen_sink_for_definition(fun) - if pygen_sink: - if 'pygen_comment' in global_annotations: - pygen_sink.writeln('## ' + global_annotations['pygen_comment']) - pygen_sink.writeln("root_module[%r].add_function_as_method(%s, custom_name=%r)" % - (cpp_class.full_name, - ", ".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 '::'))] - - pygen_sink = self._get_pygen_sink_for_definition(fun) - if pygen_sink: - if 'pygen_comment' in global_annotations: - pygen_sink.writeln('## ' + global_annotations['pygen_comment']) - pygen_sink.writeln("root_module[%r].add_function_as_constructor(%s)" % - (cpp_class.full_name, - ", ".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 - - if templates.is_instantiation(fun.demangled_name): - template_parameters = templates.args(fun.demangled_name) - kwargs['template_parameters'] = template_parameters - template_instance_names = global_annotations.get('template_instance_names', '') - if template_instance_names: - for mapping in template_instance_names.split('|'): - type_names, name = mapping.split('=>') - instance_types = type_names.split(',') - if instance_types == template_parameters: - kwargs['custom_name'] = name - break - - if alt_name: - kwargs['custom_name'] = alt_name - - if fun.attributes: - if 'deprecated' in fun.attributes: - kwargs['deprecated'] = True - - pygen_sink = self._get_pygen_sink_for_definition(fun) - if pygen_sink: - if 'pygen_comment' in global_annotations: - pygen_sink.writeln('## ' + global_annotations['pygen_comment']) - kwargs_repr = _pygen_kwargs(kwargs) - if kwargs_repr: - kwargs_repr[0] = "\n" + 20*' ' + kwargs_repr[0] - pygen_sink.writeln("module.add_function(%s)" % - (", ".join([repr(fun.name), - "\n" + 20*' ' + retval_repr, - "\n" + 20*' ' + arglist_repr] - + kwargs_repr))) - - 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) - nested_namespaces = [] - for nested_namespace in module_namespace.namespaces(allow_empty=True, recursive=False): - if nested_namespace.name.startswith('__'): - continue - nested_namespaces.append(nested_namespace) - - nested_namespaces.sort(key=lambda c: c.decl_string) - - for nested_namespace in nested_namespaces: - nested_module = module.get_submodule(nested_namespace.name) - nested_module_pygen_func = "register_functions_" + "_".join(nested_module.get_namespace_path()) - for pygen_sink in self._get_all_pygen_sinks(): - pygen_sink.writeln("%s(module.get_submodule(%r), root_module)" % - (nested_module_pygen_func, nested_namespace.name)) - - for pygen_sink in self._get_all_pygen_sinks(): - pygen_sink.writeln("return") - pygen_sink.unindent() - pygen_sink.writeln() - - nested_namespaces = [] - for nested_namespace in module_namespace.namespaces(allow_empty=True, recursive=False): - if nested_namespace.name.startswith('__'): - continue - nested_namespaces.append(nested_namespace) - - nested_namespaces.sort(key=lambda c: c.decl_string) - - for nested_namespace in nested_namespaces: - nested_module = module.get_submodule(nested_namespace.name) - nested_module_pygen_func = "register_functions_" + "_".join(nested_module.get_namespace_path()) - for pygen_sink in self._get_all_pygen_sinks(): - pygen_sink.writeln("def %s(module, root_module):" % nested_module_pygen_func) - pygen_sink.indent() - - self._scan_namespace_functions(nested_module, nested_namespace) - - -def _test(): - module_parser = ModuleParser('foo', '::') - module = module_parser.parse(sys.argv[1:]) - if 0: - out = FileCodeSink(sys.stdout) - module.generate(out) - -if __name__ == '__main__': - _test() diff --git a/tests/c-hello/hellomodulegen.py b/tests/c-hello/hellomodulegen.py index c39fffb..137761f 100755 --- a/tests/c-hello/hellomodulegen.py +++ b/tests/c-hello/hellomodulegen.py @@ -5,6 +5,7 @@ import re import pybindgen from pybindgen import FileCodeSink +from pybindgen.castxmlparser import ModuleParser constructor_rx = re.compile("hello_foo_new(_.*)?") @@ -40,11 +41,6 @@ def pre_scan_hook(dummy_module_parser, def my_module_gen(out_file, pygccxml_mode): - if pygccxml_mode == 'castxml': - from pybindgen.castxmlparser import ModuleParser - else: - from pybindgen.gccxmlparser import ModuleParser - out = FileCodeSink(out_file) #pybindgen.write_preamble(out) out.writeln("#include \"hello.h\"") diff --git a/tests/foo.h b/tests/foo.h index fe3f0d5..947b451 100644 --- a/tests/foo.h +++ b/tests/foo.h @@ -652,7 +652,7 @@ public: return new AbstractBaseClassImpl; } - // This method will be scanned by gccxmlparser and generate an error + // This method will be scanned by castxmlparser and generate an error // that is only detected in code generation time. // -#- @return(caller_owns_return=false) -#- static AbstractBaseClass* get_abstract_base_class_ptr2 () @@ -676,7 +676,7 @@ inline AbstractBaseClass* get_abstract_base_class_ptr1 () return AbstractBaseClassImpl::get_abstract_base_class_ptr1 (); } -// This function will be scanned by gccxmlparser and generate an error +// This function will be scanned by castxmlparser and generate an error // that is only detected in code generation time. // -#- @return(caller_owns_return=false) -#- inline AbstractBaseClass* get_abstract_base_class_ptr2 () diff --git a/tests/foomodulegen-auto-split.py b/tests/foomodulegen-auto-split.py index ae4e830..af4e73e 100644 --- a/tests/foomodulegen-auto-split.py +++ b/tests/foomodulegen-auto-split.py @@ -10,6 +10,7 @@ from pybindgen import (ReturnValue, Parameter, Module, Function, FileCodeSink) from pybindgen import (CppMethod, CppConstructor, CppClass, Enum) from pybindgen.function import CustomFunctionWrapper from pybindgen.cppmethod import CustomCppMethodWrapper +from pybindgen.castxmlparser import ModuleParser, PygenClassifier, PygenSection import foomodulegen_common @@ -17,10 +18,6 @@ import foomodulegen_common def my_module_gen(): - if sys.argv[6] == 'castxml': - from pybindgen.castxmlparser import ModuleParser, PygenClassifier, PygenSection - else: - from pybindgen.gccxmlparser import ModuleParser, PygenClassifier, PygenSection class MyPygenClassifier(PygenClassifier): def classify(self, pygccxml_definition): diff --git a/tests/foomodulegen-auto.py b/tests/foomodulegen-auto.py index a15d53f..d608fd7 100644 --- a/tests/foomodulegen-auto.py +++ b/tests/foomodulegen-auto.py @@ -11,16 +11,13 @@ from pybindgen import (ReturnValue, Parameter, Module, Function, FileCodeSink) from pybindgen import (CppMethod, CppConstructor, CppClass, Enum) from pybindgen.function import CustomFunctionWrapper from pybindgen.cppmethod import CustomCppMethodWrapper +from pybindgen.castxmlparser import ModuleParser import foomodulegen_common def my_module_gen(): pygccxml_mode = sys.argv[4] - if pygccxml_mode == 'castxml': - from pybindgen.castxmlparser import ModuleParser - else: - from pybindgen.gccxmlparser import ModuleParser out = FileCodeSink(sys.stdout) pygen_file = open(sys.argv[3], "wt")