1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3from __future__ import unicode_literals, print_function, absolute_import
4
5DEBUG = False
6
7
8import sys
9
10PY3 = (sys.version_info[0] >= 3)
11if PY3:
12    string_types = str,
13else:
14    string_types = basestring,
15
16
17import os.path
18import warnings
19import re
20import logging
21import time
22from operator import itemgetter
23
24import pygccxml
25from pygccxml import parser
26from pygccxml import declarations
27from .module import Module
28from .typehandlers.codesink import FileCodeSink, CodeSink, NullCodeSink
29from .typehandlers import base
30from . import typehandlers
31from .typehandlers.base import ctypeparser
32from .typehandlers.base import ReturnValue, Parameter, TypeLookupError, TypeConfigurationError, NotSupportedError
33from pygccxml.declarations.enumeration import enumeration_t
34from .cppclass import CppClass, ReferenceCountingMethodsPolicy, FreeFunctionPolicy, ReferenceCountingFunctionsPolicy
35from .cppexception import CppException
36from pygccxml.declarations import type_traits
37from pygccxml.declarations import type_traits_classes
38from pygccxml.declarations import traits_impl_details
39from pygccxml.declarations import cpptypes, typedef
40from pygccxml.declarations import calldef
41from pygccxml.declarations import calldef_members
42from pygccxml.declarations import calldef_types
43from pygccxml.declarations import templates
44from pygccxml.declarations import container_traits, class_declaration
45from pygccxml.declarations.declaration import declaration_t
46from pygccxml.declarations.class_declaration import class_declaration_t, class_t
47from pygccxml.declarations import get_dependencies_from_decl
48from . import settings
49from . import utils
50from pygccxml.utils import find_xml_generator
51
52#from pygccxml.declarations.calldef import \
53#    destructor_t, constructor_t, member_function_t
54from pygccxml.declarations.variable import variable_t
55import collections
56
57import subprocess
58from cxxfilt import demangle
59
60
61logger = logging.getLogger(__name__)
62
63
64def check_template (demangled_name, function_name):
65    index = demangled_name.find(function_name)
66    end_index = index+len(function_name)
67    while end_index < len(demangled_name):
68      if demangled_name[end_index] == ' ':
69        end_index+=1
70        continue
71      if demangled_name[end_index] == '<':
72        return True
73      else:
74        return False
75
76def get_template_arg (demangled_name, function_name):
77    index = demangled_name.find(function_name)
78    end_index = index+len(function_name)
79    arg = ""
80    started = False
81    opened = 0
82    while end_index < len(demangled_name):
83      if demangled_name[end_index] == ' ' and not started:
84        end_index+=1
85        continue
86      elif demangled_name[end_index] == '<' and opened == 0:
87        opened+=1
88        started = True
89        end_index+=1
90        continue
91      elif demangled_name[end_index] == '<' and not opened == 0:
92        opened +=1
93      elif demangled_name[end_index] == '>' and not opened == 1:
94        opened-=1
95      elif demangled_name[end_index] == '>' and opened == 1:
96        return arg.split(',')
97      arg+=demangled_name[end_index]
98      end_index+=1
99
100def get_demangled_arg_type (demangled_decl_string):
101  arg_list = []
102  arg = ""
103  index = demangled_decl_string.rfind(')')-1
104  level = 0
105  loop = True
106  while loop:
107    if level == 0  and demangled_decl_string[index] == '(':
108      arg_list = [(arg.strip()).encode("utf-8")] + arg_list
109      loop = False
110      continue
111    elif level == 0 and demangled_decl_string[index] == ',':
112      arg_list = [(arg.strip()).encode("utf-8")] + arg_list
113      arg = ""
114      index-=1
115      continue
116    if demangled_decl_string[index] in ['>', ')', ']', '}']:
117      level+=1
118    if demangled_decl_string[index] in ['<', '(', '[', '{']:
119      level-=1
120    arg = demangled_decl_string[index] + arg
121    index-=1
122
123  return arg_list
124
125###
126### some patched pygccxml functions, from the type_traits module
127###
128def remove_pointer(type):
129    """removes pointer from the type definition
130
131    If type is not pointer type, it will be returned as is.
132    """
133    #nake_type = remove_alias( type )
134    nake_type = type
135    if not type_traits.is_pointer( nake_type ):
136        return type
137    elif isinstance( nake_type, cpptypes.volatile_t ) and isinstance( nake_type.base, cpptypes.pointer_t ):
138        return cpptypes.volatile_t( nake_type.base.base )
139    elif isinstance( nake_type, cpptypes.const_t ) and isinstance( nake_type.base, cpptypes.pointer_t ):
140        return cpptypes.const_t( nake_type.base.base )
141    elif isinstance(nake_type, cpptypes.compound_t) and isinstance( nake_type.base, cpptypes.calldef_type_t ):
142        return type
143    else:
144        if isinstance(nake_type, cpptypes.compound_t):
145            return nake_type.base
146        else:
147            return nake_type
148
149def remove_reference(type):
150    """removes reference from the type definition
151
152    If type is not reference type, it will be returned as is.
153    """
154    #nake_type = remove_alias( type )
155    nake_type = type
156    if not type_traits.is_reference( nake_type ):
157        return type
158    else:
159        if isinstance(nake_type, cpptypes.compound_t):
160            return nake_type.base
161        else:
162            return nake_type
163
164def remove_const(type):
165    """removes const from the type definition
166
167    If type is not const type, it will be returned as is
168    """
169
170    #nake_type = remove_alias( type )
171    nake_type = type
172    if not type_traits.is_const( nake_type ):
173        return type
174    else:
175        if isinstance(nake_type, cpptypes.compound_t):
176            return nake_type.base
177        else:
178            return nake_type
179###
180### end of patched pygccxml functions
181###
182
183
184## --- utility ---
185
186import pygccxml.declarations.type_traits
187def find_declaration_from_name(global_ns, declaration_name):
188    decl = pygccxml.declarations.traits_impl_details.impl_details.find_value_type(global_ns, declaration_name)
189    return decl
190
191
192class ModuleParserWarning(Warning):
193    """
194    Base class for all warnings reported here.
195    """
196class NotSupportedWarning(ModuleParserWarning):
197    """
198    Warning for pybindgen GccxmlParser, to report something pybindgen does not support.
199    """
200class WrapperWarning(ModuleParserWarning):
201    """
202    Warning for pybindgen GccxmlParser, to be used when a C++
203    definition cannot be converted to a pybindgen wrapper.
204    """
205class AnnotationsWarning(ModuleParserWarning):
206    """
207    Warning for pybindgen GccxmlParser to report a problem in annotations.
208    """
209
210## ------------------------
211
212class ErrorHandler(settings.ErrorHandler):
213    def handle_error(self, wrapper, exception, traceback_):
214        if hasattr(wrapper, "castxml_definition"):
215            definition = wrapper.castxml_definition
216        elif hasattr(wrapper, "main_wrapper"):
217            try:
218                definition = wrapper.main_wrapper.castxml_definition
219            except AttributeError:
220                definition = None
221        else:
222            definition = None
223
224        if definition is None:
225            print("exception %r in wrapper %s" % (exception, wrapper), file=sys.stderr)
226        else:
227            warnings.warn_explicit("exception %r in wrapper for %s"
228                                   % (exception, definition),
229                                   WrapperWarning, definition.location.file_name,
230                                   definition.location.line)
231        return True
232settings.error_handler = ErrorHandler()
233
234def normalize_name(decl_string):
235    return ctypeparser.normalize_type_string(decl_string)
236
237def normalize_class_name(class_name, module_namespace):
238    class_name = utils.ascii(class_name)
239    if not class_name.startswith(module_namespace):
240        class_name = module_namespace + class_name
241    class_name = normalize_name(class_name)
242    return class_name
243
244
245def _pygen_kwargs(kwargs):
246    l = []
247    for key, val in sorted(kwargs.items(), key=itemgetter(0)):
248        if isinstance(val, (CppClass, CppException)):
249            l.append("%s=root_module[%r]" % (key, utils.ascii(val.full_name)))
250        else:
251            if key == 'throw':
252                l.append("throw=[%s]" % (', '.join(["root_module[%r]" % utils.ascii(t.full_name) for t in val])))
253            elif key == 'parent' and isinstance(val, list):
254                l.append("parent=[%s]" % (', '.join(["root_module[%r]" % utils.ascii(cls.full_name) for cls in val])))
255            else:
256                l.append("%s=%r" % (key, val))
257    return l
258
259def _pygen_args_kwargs(args, kwargs):
260    return ", ".join([repr(arg) for arg in args] + _pygen_kwargs(kwargs))
261
262def _pygen_args_kwargs_dict(args, kwargs):
263    l = [repr(arg) for arg in args]
264    if kwargs:
265        l.append("dict(%s)" % ', '.join(_pygen_kwargs(kwargs)))
266    return ", ".join(l)
267
268def _pygen_retval(args, kwargs):
269    if len(args) == 1 and len(kwargs) == 0:
270        return repr(args[0])
271    return "retval(%s)" % _pygen_args_kwargs(args, kwargs)
272
273def _pygen_param(args, kwargs):
274    return "param(%s)" % _pygen_args_kwargs(args, kwargs)
275
276
277class GccXmlTypeRegistry(object):
278    def __init__(self, root_module):
279        """
280        :param root_module: the root L{Module} object
281        """
282        assert isinstance(root_module, Module)
283        assert root_module.parent is None
284        self.root_module = root_module
285        self.ordered_classes = [] # registered classes list, by registration order
286        self._root_ns_rx = re.compile(r"(^|\s)(::)")
287
288    def class_registered(self, cpp_class):
289        assert isinstance(cpp_class, (CppClass, CppException))
290        self.ordered_classes.append(cpp_class)
291
292#     def get_type_traits(self, type_info):
293#         #assert isinstance(type_info, cpptypes.type_t)
294
295#         debug = False #('int64_t' in type_info.decl_string)
296#         if debug:
297#             print >> sys.stderr, "***** type traits for %r" % (type_info.decl_string, )
298
299#         is_const = False
300#         is_reference = False
301#         is_pointer = 0
302#         pointer_or_ref_count = 0
303#         inner_const = False
304#         while 1:
305#             prev_type_info = type_info
306#             if type_traits.is_pointer(type_info):
307#                 is_pointer += 1
308#                 type_info = remove_pointer(type_info)
309#                 pointer_or_ref_count += 1
310#             elif type_traits.is_const(type_info):
311#                 type_info = remove_const(type_info)
312#                 if pointer_or_ref_count == 0:
313#                     is_const = True
314#                 elif pointer_or_ref_count == 1:
315#                     inner_const = True
316#                 else:
317#                     warnings.warn("multiple consts not handled")
318#             elif type_traits.is_reference(type_info):
319#                 warnings.warn("multiple &'s not handled")
320#                 is_reference = True
321#                 type_info = remove_reference(type_info)
322#                 pointer_or_ref_count += 1
323#             else:
324#                 break
325#             if type_info is prev_type_info:
326#                 break
327
328#         type_name = normalize_name(type_info.partial_decl_string)
329#         try:
330#             cpp_type = self.root_module[type_name]
331#         except KeyError:
332#             cpp_type = type_name
333
334#         if not isinstance(cpp_type, CppClass):
335#             cpp_type = type_name
336
337#         if debug:
338#             print >> sys.stderr, "*** > return ", repr((cpp_type, is_const, is_pointer, is_reference))
339#         return (cpp_type, is_const, inner_const, is_pointer, is_reference)
340
341    def _fixed_std_type_name(self, type_name):
342        type_name = utils.ascii(type_name)
343        decl = self._root_ns_rx.sub('', type_name)
344        return decl
345
346    def lookup_return(self, type_info, annotations={}):
347        kwargs = {}
348        for name, value in annotations.items():
349            if name == 'caller_owns_return':
350                kwargs['caller_owns_return'] = annotations_scanner.parse_boolean(value)
351            if name == 'free_after_copy':
352                kwargs['free_after_copy'] = annotations_scanner.parse_boolean(value)
353            elif name == 'reference_existing_object':
354                kwargs['reference_existing_object'] = annotations_scanner.parse_boolean(value)
355            elif name == 'return_internal_reference':
356                kwargs['return_internal_reference'] = annotations_scanner.parse_boolean(value)
357            elif name == 'custodian':
358                kwargs['custodian'] = int(value)
359            else:
360                warnings.warn("invalid annotation name %r" % name, AnnotationsWarning)
361
362        cpp_type = normalize_name(type_info.partial_decl_string)
363        return (cpp_type,), kwargs
364
365
366    def lookup_parameter(self, type_info, param_name, annotations={}, default_value=None):
367        kwargs = {}
368        for name, value in annotations.items():
369            if name == 'transfer_ownership':
370                kwargs['transfer_ownership'] = annotations_scanner.parse_boolean(value)
371            elif name == 'direction':
372                if value.lower() == 'in':
373                    kwargs['direction'] = Parameter.DIRECTION_IN
374                elif value.lower() == 'out':
375                    kwargs['direction'] = Parameter.DIRECTION_OUT
376                elif value.lower() == 'inout':
377                    kwargs['direction'] = Parameter.DIRECTION_INOUT
378                else:
379                    warnings.warn("invalid direction direction %r" % value, AnnotationsWarning)
380            elif name == 'custodian':
381                kwargs['custodian'] = int(value)
382            elif name == 'array_length':
383                kwargs['array_length'] = int(value)
384            elif name == 'default_value':
385                kwargs['default_value'] = value
386            elif name == 'null_ok':
387                kwargs['null_ok'] = annotations_scanner.parse_boolean(value)
388            else:
389                warnings.warn("invalid annotation name %r" % name, AnnotationsWarning)
390
391        if default_value:
392            kwargs['default_value'] = utils.ascii(default_value)
393
394        cpp_type = normalize_name(type_info.partial_decl_string)
395
396        return (cpp_type, param_name), kwargs
397
398
399class AnnotationsScanner(object):
400    def __init__(self):
401        self.files = {} # file name -> list(lines)
402        self.used_annotations = {} # file name -> list(line_numbers)
403        self._comment_rx = re.compile(
404            r"^\s*(?://\s+-#-(?P<annotation1>.*)-#-\s*)|(?:/\*\s+-#-(?P<annotation2>.*)-#-\s*\*/)")
405        self._global_annotation_rx = re.compile(r"(\w+)(?:=([^\s;]+))?")
406        self._param_annotation_rx = re.compile(r"@(\w+)\(([^;]+)\)")
407
408    def _declare_used_annotation(self, file_name, line_number):
409        try:
410            l = self.used_annotations[file_name]
411        except KeyError:
412            l = []
413            self.used_annotations[file_name] = l
414        l.append(line_number)
415
416    def get_annotations(self, decl):
417        """
418        :param decl: pygccxml declaration_t object
419        """
420        assert isinstance(decl, declaration_t)
421
422        if isinstance(decl, calldef.calldef_t) \
423                and decl.is_artificial:
424            #print >> sys.stderr, "********** ARTIFICIAL:", decl
425            return {}, {}
426
427        file_name = decl.location.file_name
428        line_number = decl.location.line
429
430        if not file_name:
431            return {}, {}
432
433        try:
434            lines = self.files[file_name]
435        except KeyError:
436            with open(file_name, "rt") as f:
437                lines = f.readlines()
438            self.files[file_name] = lines
439
440        line_number -= 2
441        global_annotations = {}
442        parameter_annotations = {}
443        while 1:
444            line = lines[line_number]
445            line_number -= 1
446            m = self._comment_rx.match(line)
447            if m is None:
448                break
449            s = m.group('annotation1')
450            if s is None:
451                s = m.group('annotation2')
452            line = s.strip()
453            self._declare_used_annotation(file_name, line_number + 2)
454            for annotation_str in line.split(';'):
455                annotation_str = annotation_str.strip()
456                m = self._global_annotation_rx.match(annotation_str)
457                if m is not None:
458                    global_annotations[m.group(1)] = m.group(2)
459                    continue
460
461                m = self._param_annotation_rx.match(annotation_str)
462                if m is not None:
463                    param_annotation = {}
464                    parameter_annotations[m.group(1)] = param_annotation
465                    for param in m.group(2).split(','):
466                        m = self._global_annotation_rx.match(param.strip())
467                        if m is not None:
468                            param_annotation[m.group(1)] = m.group(2)
469                        else:
470                            warnings.warn_explicit("could not parse %r as parameter annotation element" %
471                                                   (param.strip()),
472                                                   AnnotationsWarning, file_name, line_number)
473                    continue
474                warnings.warn_explicit("could not parse %r" % (annotation_str),
475                                       AnnotationsWarning, file_name, line_number)
476        return global_annotations, parameter_annotations
477
478    def parse_boolean(self, value):
479        if isinstance(value, int):
480            return bool(value)
481        if value.lower() in ['false', 'off']:
482            return False
483        elif value.lower() in ['true', 'on']:
484            return True
485        else:
486            raise ValueError("bad boolean value %r" % value)
487
488    def warn_unused_annotations(self):
489        for file_name, lines in self.files.items():
490            try:
491                used_annotations = self.used_annotations[file_name]
492            except KeyError:
493                used_annotations = []
494            for line_number, line in enumerate(lines):
495                m = self._comment_rx.match(line)
496                if m is None:
497                    continue
498                #print >> sys.stderr, (line_number+1), used_annotations
499                if (line_number + 1) not in used_annotations:
500                    warnings.warn_explicit("unused annotation",
501                                           AnnotationsWarning, file_name, line_number+1)
502
503
504
505annotations_scanner = AnnotationsScanner()
506
507## ------------------------
508
509class PygenSection(object):
510    "Class to hold information about a python generation section"
511    def __init__(self, name, code_sink, local_customizations_module=None):
512        """
513        :param name: section name; this name should be a valid python
514            module name; the special name '__main__' is used to denote the
515            main section, which comprises the main script itself
516        :type name: str
517
518        :param code_sink: code sink that will receive the generated
519           code for the section.  Normally the code sink should write to
520           a file with the name of the section and a .py extension, to
521           allow importing it as module.
522        :type code_sink: L{CodeSink}
523
524        :param local_customizations_module: name of the python module
525          that may contain local customizations for the section, or
526          None.  If not None, PyBindGen will generate code that tries to
527          import that module in the respective section and call
528          functions on it, or ignore it if the module does not exist.
529        :type local_customizations_module: str
530        """
531        assert isinstance(name, string_types)
532        self.name = name
533        assert isinstance(code_sink, CodeSink)
534        self.code_sink = code_sink
535        assert local_customizations_module is None or isinstance(local_customizations_module, string_types)
536        self.local_customizations_module = local_customizations_module
537
538
539class PygenClassifier(object):
540    def classify(self, pygccxml_definition):
541        """
542        This is a pure virtual method that must be implemented by
543        subclasses.  It will be called by PyBindGen for every API
544        definition, and should return a section name.
545
546        :param pygccxml_definition: castxml definition object
547        :returns: section name
548        """
549        raise NotImplementedError
550
551    def get_section_precedence(self, section_name):
552        """
553        This is a pure virtual method that may (or not) be implemented by
554        subclasses.  It will be called by PyBindGen for every API
555        definition, and should return the precedence of a section.
556        This is used when sections reflect 'modules' whose types must be
557        registered in a certain order.
558
559        :param section_name: the name of the section
560
561        :returns: order of precedence of the section.  The lower the
562          number, the sooner the section is to be registered.
563        """
564        raise NotImplementedError
565
566
567class ModuleParser(object):
568    """
569    :attr enable_anonymous_containers: if True, pybindgen will attempt
570        to scan for all std containers, even the ones that have no
571        typedef'ed name.  Enabled by default.
572
573    """
574
575    def __init__(self, module_name, module_namespace_name='::'):
576        """
577        Creates an object that will be able parse header files and
578        create a pybindgen module definition.
579
580        :param module_name: name of the Python module
581        :param module_namespace_name: optional C++ namespace name; if
582                                 given, only definitions of this
583                                 namespace will be included in the
584                                 python module
585        """
586        self.module_name = module_name
587        self.module_namespace_name = module_namespace_name
588        self.location_filter = None
589        self.header_files = None
590        self.castxml_config = None
591        self.whitelist_paths = []
592        self.module_namespace = None # pygccxml module C++ namespace
593        self.module = None # the toplevel pybindgen.module.Module instance (module being generated)
594        self.declarations = None # (as returned by pygccxml.parser.parse)
595        self.global_ns = None
596        self._types_scanned = False
597        self._pre_scan_hooks = []
598        self._post_scan_hooks = []
599        self.type_registry = None
600        self._stage = None
601        self._pygen_sink = None
602        self._pygen_factory = None
603        self._anonymous_structs = [] # list of (pygccxml_anonymous_class, outer_pybindgen_class)
604        self._containers_to_register = []
605        self._containers_registered = {}
606        self.enable_anonymous_containers = True
607
608    def add_pre_scan_hook(self, hook):
609        """
610        Add a function to be called right before converting a castxml
611        definition to a PyBindGen wrapper object.  This hook function
612        will be called for every scanned type, function, or method,
613        and given the a chance to modify the annotations for that
614        definition.  It will be called like this:::
615
616          pre_scan_hook(module_parser, pygccxml_definition, global_annotations,
617                        parameter_annotations)
618
619        where:
620
621           - module_parser -- the ModuleParser (this class) instance
622           - pygccxml_definition -- the definition reported by pygccxml
623           - global_annotations -- a dicionary containing the "global annotations"
624                                 for the definition, i.e. a set of key=value
625                                 pairs not associated with any particular
626                                 parameter
627           - parameter_annotations -- a dicionary containing the "parameter
628                                    annotations" for the definition.  It is a
629                                    dict whose keys are parameter names and
630                                    whose values are dicts containing the
631                                    annotations for that parameter.  Annotations
632                                    pertaining the return value of functions or
633                                    methods are denoted by a annotation for a
634                                    parameter named 'return'.
635        """
636        if not isinstance(hook, collections.Callable):
637            raise TypeError("hook must be callable")
638        self._pre_scan_hooks.append(hook)
639
640    def add_post_scan_hook(self, hook):
641        """
642        Add a function to be called right after converting a castxml definition
643        to a PyBindGen wrapper object.  This hook function will be called for
644        every scanned type, function, or method.  It will be called like this::
645
646          post_scan_hook(module_parser, pygccxml_definition, pybindgen_wrapper)
647
648        where:
649
650           - module_parser -- the ModuleParser (this class) instance
651           - pygccxml_definition -- the definition reported by pygccxml
652           - pybindgen_wrapper -- a pybindgen object that generates a wrapper,
653                                such as CppClass, Function, or CppMethod.
654        """
655        if not isinstance(hook, collections.Callable):
656            raise TypeError("hook must be callable")
657        self._post_scan_hooks.append(hook)
658
659    def __location_match(self, decl):
660        # Make sure absolute paths are compared.
661        abspath = os.path.abspath(decl.location.file_name)
662        if abspath in self.header_files:
663            return True
664        for incdir in self.whitelist_paths:
665            if os.path.abspath(decl.location.file_name).startswith(incdir):
666                return True
667        return False
668
669    def parse(self, header_files, include_paths=None, whitelist_paths=None, includes=(),
670              pygen_sink=None, pygen_classifier=None, castxml_options=None,
671              gccxml_options=None):
672        """
673        parses a set of header files and returns a pybindgen Module instance.
674        It is equivalent to calling the following methods:
675         1. parse_init(header_files, include_paths, whitelist_paths)
676         2. scan_types()
677         3. scan_methods()
678         4. scan_functions()
679         5. parse_finalize()
680
681         The documentation for L{ModuleParser.parse_init} explains the parameters.
682        """
683        options = castxml_options or gccxml_options
684        self.parse_init(header_files, include_paths, whitelist_paths, includes, pygen_sink,
685                        pygen_classifier, options)
686        self.scan_types()
687        self.scan_methods()
688        self.scan_functions()
689        self.parse_finalize()
690        return self.module
691
692    def parse_init(self, header_files, include_paths=None,
693                   whitelist_paths=None, includes=(), pygen_sink=None, pygen_classifier=None,
694                   castxml_options=None, gccxml_options=None):
695        """
696        Prepares to parse a set of header files.  The following
697        methods should then be called in order to finish the rest of
698        scanning process:
699
700          #. scan_types()
701          #. scan_methods()
702          #. scan_functions()
703          #. parse_finalize()
704
705        :param header_files: header files to parse
706        :type header_files: list of string
707
708        :param include_paths: (deprecated, use the parameter castxml_options) list of include paths
709        :type include_paths: list of string
710
711        :param whitelist_paths: additional directories for definitions to be included
712           Normally the module parser filters out API definitions that
713           have been defined outside one of the header files indicated
714           for parsing.  The parameter whitelist_paths instructs the
715           module parser to accept definitions defined in another
716           header file if such header file is inside one of the
717           directories listed by whitelist_paths.
718        :type whitelist_paths: list of string
719
720        :param pygen_sink: code sink for python script generation.
721
722           This parameter activates a mode wherein ModuleParser, in
723           addition to building in memory API definitions, creates a
724           python script that will generate the module, when executed.
725           The generated Python script can be human editable and does
726           not require pygccxml or castxml to run, only PyBindGen to be
727           installed.
728
729           The pygen parameter can be either:
730             #. A single code sink: this will become the main and only script file to be generated
731             #. A list of L{PygenSection} objects.  This option
732                requires the pygen_classifier to be given.
733
734        :type pygen_sink: L{CodeSink} or list of L{PygenSection} objects
735
736        :param pygen_classifier: the classifier to use when pygen is given and is a dict
737
738        :param castxml_options: extra options to pass into the
739            :class:`pygccxml.parser.config.gccxml_configuration_t` object as keyword
740            arguments for more information).
741
742        :type castxml_options: dict
743
744        """
745        assert isinstance(header_files, list)
746        assert isinstance(includes, (list, tuple))
747        options = castxml_options or gccxml_options
748        self._pygen = pygen_sink
749        self._pygen_classifier = pygen_classifier
750        if isinstance(pygen_sink, list):
751            assert isinstance(pygen_classifier, PygenClassifier)
752            has_main = False
753            for sect in self._pygen:
754                if not isinstance(sect, PygenSection):
755                    raise TypeError
756                if sect.name == '__main__':
757                    has_main = True
758            if not has_main:
759                raise ValueError("missing __main__ section")
760        elif pygen_sink is None:
761            pass
762        else:
763            assert isinstance(pygen_sink, CodeSink)
764
765        self.header_files = [os.path.abspath(f) for f in header_files]
766        self.location_filter = declarations.custom_matcher_t(self.__location_match)
767
768        if whitelist_paths is not None:
769            assert isinstance(whitelist_paths, list)
770            self.whitelist_paths = [os.path.abspath(p) for p in whitelist_paths]
771
772        if options is None:
773            options = {}
774        else:
775            options = dict(options)
776        generator_path, generator_name = find_xml_generator()
777        options['xml_generator_path'] = generator_path
778        options['xml_generator'] = generator_name
779
780        if include_paths is not None:
781            assert isinstance(include_paths, list)
782            warnings.warn("Parameter include_paths is deprecated, use castxml_options instead", DeprecationWarning,
783                          stacklevel=2)
784            options['include_paths'] = include_paths
785
786        logger.debug("castxml options: %r", options)
787        self.castxml_config = parser.xml_generator_configuration_t(**options)
788
789        self.declarations = parser.parse(header_files, self.castxml_config)
790        self.global_ns = declarations.get_global_namespace(self.declarations)
791        if self.module_namespace_name == '::':
792            self.module_namespace = self.global_ns
793        else:
794            self.module_namespace = self.global_ns.namespace(self.module_namespace_name)
795
796        self.module = Module(self.module_name,
797                             cpp_namespace=self.module_namespace.decl_string)
798
799        for inc in includes:
800            self.module.add_include(inc)
801
802        for pygen_sink in self._get_all_pygen_sinks():
803            pygen_sink.writeln("from pybindgen import Module, FileCodeSink, param, retval, cppclass, typehandlers")
804            pygen_sink.writeln()
805
806        pygen_sink = self._get_main_pygen_sink()
807        if pygen_sink:
808            pygen_sink.writeln("""
809import pybindgen.settings
810import warnings
811
812class ErrorHandler(pybindgen.settings.ErrorHandler):
813    def handle_error(self, wrapper, exception, traceback_):
814        warnings.warn("exception %r in wrapper %s" % (exception, wrapper))
815        return True
816pybindgen.settings.error_handler = ErrorHandler()
817
818""")
819            pygen_sink.writeln("import sys")
820            if isinstance(self._pygen, list):
821                for sect in self._pygen:
822                    if sect.name == '__main__':
823                        continue
824                    pygen_sink.writeln("import %s" % sect.name)
825            pygen_sink.writeln()
826            pygen_sink.writeln("def module_init():")
827            pygen_sink.indent()
828            pygen_sink.writeln("root_module = Module(%r, cpp_namespace=%r)"
829                               % (self.module_name, utils.ascii(self.module_namespace.decl_string)))
830            for inc in includes:
831                pygen_sink.writeln("root_module.add_include(%r)" % inc)
832            pygen_sink.writeln("return root_module")
833            pygen_sink.unindent()
834            pygen_sink.writeln()
835
836        self.type_registry = GccXmlTypeRegistry(self.module)
837        self._stage = 'init'
838
839    def _get_main_pygen_sink(self):
840        if isinstance (self._pygen, CodeSink):
841            return self._pygen
842        elif isinstance(self._pygen, list):
843            for sect in self._pygen:
844                if sect.name == '__main__':
845                    return sect.code_sink
846        else:
847            return None
848
849    def _get_all_pygen_sinks(self):
850        if isinstance (self._pygen, CodeSink):
851            return [self._pygen]
852        elif isinstance(self._pygen, list):
853            return [sect.code_sink for sect in self._pygen]
854        else:
855            return []
856
857    def _get_pygen_sink_for_definition(self, pygccxml_definition, with_section_precedence=False):
858        if self._pygen_classifier is None:
859            if with_section_precedence:
860                return (0, self._pygen)
861            else:
862                return self._pygen
863        else:
864            if isinstance(pygccxml_definition, declaration_t):
865                section = self._pygen_classifier.classify(pygccxml_definition)
866                for sect in self._pygen:
867                    if sect is section or sect.name == section:
868                        sink = sect.code_sink
869                        break
870                else:
871                    raise ValueError("CodeSink for section %r not available" % section)
872            else:
873                sink = self._get_main_pygen_sink()
874                section = '__main__'
875            if with_section_precedence:
876                try:
877                    prec = self._pygen_classifier.get_section_precedence(section)
878                except NotImplementedError:
879                    prec = 0
880                return (prec, sink)
881            else:
882                return sink
883
884    def scan_types(self):
885        self._stage = 'scan types'
886        self._registered_classes = {} # class_t -> CppClass
887        self._scan_namespace_types(self.module, self.module_namespace, pygen_register_function_name="register_types")
888        self._types_scanned = True
889
890    def scan_methods(self):
891        self._stage = 'scan methods'
892        assert self._types_scanned
893        for pygen_sink in self._get_all_pygen_sinks():
894            pygen_sink.writeln("def register_methods(root_module):")
895            pygen_sink.indent()
896
897        for class_wrapper in self.type_registry.ordered_classes:
898            if isinstance(class_wrapper.castxml_definition, class_declaration_t):
899                continue # skip classes not fully defined
900            if isinstance(class_wrapper, CppException):
901                continue # exceptions cannot have methods (yet)
902            #if class_wrapper.import_from_module:
903            #    continue # foreign class
904            pygen_sink =  self._get_pygen_sink_for_definition(class_wrapper.castxml_definition)
905            if pygen_sink:
906                register_methods_func = "register_%s_methods"  % (class_wrapper.mangled_full_name,)
907                pygen_sink.writeln("%s(root_module, root_module[%r])" % (register_methods_func, class_wrapper.full_name))
908
909        for pygen_sink in self._get_all_pygen_sinks():
910            if pygen_sink is self._get_main_pygen_sink() and isinstance(self._pygen, list):
911                for sect in self._pygen:
912                    if sect.name == '__main__':
913                        continue
914                    pygen_sink.writeln("root_module.begin_section(%r)" % sect.name)
915                    pygen_sink.writeln("%s.register_methods(root_module)" % sect.name)
916                    if sect.local_customizations_module:
917                        pygen_sink.writeln("\ntry:\n"
918                                           "    import %s\n"
919                                           "except ImportError:\n"
920                                           "    pass\n"
921                                           "else:\n"
922                                           "    %s.register_methods(root_module)\n"
923                                           % (sect.local_customizations_module, sect.local_customizations_module))
924                    pygen_sink.writeln("root_module.end_section(%r)" % sect.name)
925
926            pygen_sink.writeln("return")
927            pygen_sink.unindent()
928            pygen_sink.writeln()
929
930        for class_wrapper in self.type_registry.ordered_classes:
931            if isinstance(class_wrapper.castxml_definition, class_declaration_t):
932                continue # skip classes not fully defined
933            if isinstance(class_wrapper, CppException):
934                continue # exceptions cannot have methods (yet)
935            #if class_wrapper.import_from_module:
936            #    continue # this is a foreign class from another module, we don't scan it
937            register_methods_func = "register_%s_methods"  % (class_wrapper.mangled_full_name,)
938
939            pygen_sink =  self._get_pygen_sink_for_definition(class_wrapper.castxml_definition)
940            if pygen_sink:
941                pygen_sink.writeln("def %s(root_module, cls):" % (register_methods_func,))
942                pygen_sink.indent()
943            ## Add attributes from inner anonymous to each outer class (LP#237054)
944            for anon_cls, wrapper in self._anonymous_structs:
945                if wrapper is class_wrapper:
946                    self._scan_class_methods(anon_cls, wrapper, pygen_sink)
947            self._scan_class_methods(class_wrapper.castxml_definition, class_wrapper, pygen_sink)
948
949            if pygen_sink:
950                pygen_sink.writeln("return")
951                pygen_sink.unindent()
952                pygen_sink.writeln()
953
954
955    def parse_finalize(self):
956        annotations_scanner.warn_unused_annotations()
957        pygen_sink = self._get_main_pygen_sink()
958        if pygen_sink:
959            pygen_sink.writeln("def main():")
960            pygen_sink.indent()
961            pygen_sink.writeln("out = FileCodeSink(sys.stdout)")
962            pygen_sink.writeln("root_module = module_init()")
963            pygen_sink.writeln("register_types(root_module)")
964            pygen_sink.writeln("register_methods(root_module)")
965            pygen_sink.writeln("register_functions(root_module)")
966            pygen_sink.writeln("root_module.generate(out)")
967            pygen_sink.unindent()
968            pygen_sink.writeln()
969            pygen_sink.writeln("if __name__ == '__main__':\n    main()")
970            pygen_sink.writeln()
971
972        return self.module
973
974    def _apply_class_annotations(self, cls, annotations, kwargs):
975        is_exception = False
976        logger.debug("cls %s annotations: %r", cls.name, annotations)
977        for name, value in annotations.items():
978            if name == 'allow_subclassing':
979                kwargs.setdefault('allow_subclassing', annotations_scanner.parse_boolean(value))
980            elif name == 'is_singleton':
981                kwargs.setdefault('is_singleton', annotations_scanner.parse_boolean(value))
982            elif name == 'incref_method':
983                kwargs.setdefault('memory_policy', ReferenceCountingMethodsPolicy(
984                        incref_method=value, decref_method=annotations.get('decref_method', None),
985                        peekref_method=annotations.get('peekref_method', None)))
986            elif name == 'decref_method':
987                pass
988            elif name == 'peekref_method':
989                pass
990            elif name == 'automatic_type_narrowing':
991                kwargs.setdefault('automatic_type_narrowing', annotations_scanner.parse_boolean(value))
992            elif name == 'free_function':
993                kwargs.setdefault('memory_policy', FreeFunctionPolicy(value))
994            elif name == 'incref_function':
995                kwargs.setdefault('memory_policy', ReferenceCountingFunctionsPolicy(
996                        incref_function=value, decref_function=annotations.get('decref_function', None)))
997            elif name == 'decref_function':
998                pass
999            elif name == 'python_name':
1000                kwargs.setdefault('custom_name', value)
1001                warnings.warn_explicit("Class annotation 'python_name' is deprecated in favour of 'custom_name'",
1002                                       AnnotationsWarning, cls.location.file_name, cls.location.line)
1003            elif name == 'custom_name':
1004                kwargs.setdefault('custom_name', value)
1005            elif name == 'pygen_comment':
1006                pass
1007            elif name == 'exception':
1008                is_exception = True
1009            elif name == 'import_from_module':
1010                kwargs.setdefault('import_from_module', value)
1011            else:
1012                warnings.warn_explicit("Class annotation %r ignored" % name,
1013                                       AnnotationsWarning, cls.location.file_name, cls.location.line)
1014        if isinstance(cls, class_t):
1015            if self._class_has_virtual_methods(cls) and not cls.bases:
1016                kwargs.setdefault('allow_subclassing', True)
1017
1018            #if not self._has_public_destructor(cls):
1019            #    kwargs.setdefault('is_singleton', True)
1020            #    #print >> sys.stderr, "##### class %s has no public destructor" % cls.decl_string
1021
1022            des = self._get_destructor_visibility(cls)
1023            #print >> sys.stderr, "##### class %s destructor is %s" % (cls.decl_string, des)
1024            if des != 'public':
1025                kwargs.setdefault('destructor_visibility', des)
1026
1027        return is_exception
1028
1029    def _get_destructor_visibility(self, cls):
1030        for member in cls.get_members():
1031            if isinstance(member, calldef_members.destructor_t):
1032                return member.access_type
1033
1034    def _has_public_destructor(self, cls):
1035        for member in cls.get_members():
1036            if isinstance(member, calldef_members.destructor_t):
1037                if member.access_type != 'public':
1038                    return False
1039        return True
1040
1041    def _scan_namespace_types(self, module, module_namespace, outer_class=None, pygen_register_function_name=None):
1042        logger.debug("_scan_namespace_types(%s, %s)", module, module_namespace)
1043        root_module = module.get_root()
1044        if pygen_register_function_name:
1045            for pygen_sink in self._get_all_pygen_sinks():
1046                pygen_sink.writeln("def %s(module):" % pygen_register_function_name)
1047                pygen_sink.indent()
1048                pygen_sink.writeln("root_module = module.get_root()")
1049                pygen_sink.writeln()
1050                if pygen_sink is self._get_main_pygen_sink() and isinstance(self._pygen, list):
1051                    for section in self._pygen:
1052                        if section.name == '__main__':
1053                            continue
1054                        if pygen_register_function_name == "register_types":
1055                            pygen_sink.writeln("root_module.begin_section(%r)" % section.name)
1056                            pygen_sink.writeln("%s.%s(module)" % (section.name, pygen_register_function_name))
1057                            if section.local_customizations_module:
1058                                pygen_sink.writeln("\ntry:\n"
1059                                                   "    import %s\n"
1060                                                   "except ImportError:\n"
1061                                                   "    pass\n"
1062                                                   "else:\n"
1063                                                   "    %s.register_types(module)\n"
1064                                                   % (section.local_customizations_module, section.local_customizations_module))
1065                            pygen_sink.writeln("root_module.end_section(%r)" % section.name)
1066
1067        ## detect use of unregistered container types: need to look at
1068        ## all parameters and return values of all functions in this namespace...
1069        for fun in module_namespace.free_functions(function=self.location_filter,
1070                                                   allow_empty=True, recursive=False):
1071            # logger.debug("Saw free function: %s", fun)
1072            if fun.name.startswith('__'):
1073                continue
1074            for dependency in get_dependencies_from_decl(fun,recursive=True):
1075                type_info = dependency.depend_on_it
1076                if type_traits.is_pointer(type_info):
1077                    type_info = type_traits.remove_pointer(type_info)
1078                elif type_traits.is_reference(type_info):
1079                    type_info = type_traits.remove_reference(type_info)
1080                if type_traits.is_const(type_info):
1081                    type_info = type_traits.remove_const(type_info)
1082                traits = container_traits.find_container_traits(type_info)
1083                if traits is None:
1084                    continue
1085                name = normalize_name(type_info.partial_decl_string)
1086                #print >> sys.stderr, "** type: %s; ---> partial_decl_string: %r; name: %r" %\
1087                #    (type_info, type_info.partial_decl_string, name)
1088                self._containers_to_register.append((traits, type_info, None, name))
1089
1090        ## scan enumerations
1091        if outer_class is None:
1092            enums = module_namespace.enumerations(function=self.location_filter,
1093                                           recursive=False, allow_empty=True)
1094        else:
1095            enums = []
1096            for enum in outer_class.castxml_definition.enumerations(function=self.location_filter,
1097                                                            recursive=False, allow_empty=True):
1098                if outer_class.castxml_definition.find_out_member_access_type(enum) != 'public':
1099                    continue
1100                if enum.name.startswith('__'):
1101                    continue
1102                #if not enum.name:
1103                #    warnings.warn_explicit("Enum %s ignored because it has no name"
1104                #                           % (enum, ),
1105                #                           NotSupportedWarning, enum.location.file_name, enum.location.line)
1106                #    continue
1107                enums.append(enum)
1108
1109        for enum in enums:
1110            # logger.debug("Saw enum: %s", enum)
1111
1112            global_annotations, param_annotations = annotations_scanner.get_annotations(enum)
1113            for hook in self._pre_scan_hooks:
1114                hook(self, enum, global_annotations, param_annotations)
1115            if 'ignore' in global_annotations:
1116                continue
1117
1118            enum_values_repr = '[' + ', '.join([repr(utils.ascii(name)) for name, dummy_val in enum.values]) + ']'
1119            l = [repr(utils.ascii(enum.name)), enum_values_repr]
1120            if outer_class is not None:
1121                l.append('outer_class=root_module[%r]' % outer_class.full_name)
1122            pygen_sink = self._get_pygen_sink_for_definition(enum)
1123            if 'import_from_module' in global_annotations:
1124                l.append("import_from_module=%r" % (global_annotations["import_from_module"],))
1125            if pygen_sink:
1126                if 'pygen_comment' in global_annotations:
1127                    pygen_sink.writeln('## ' + global_annotations['pygen_comment'])
1128                pygen_sink.writeln('module.add_enum(%s)' % ', '.join(l))
1129
1130            module.add_enum(utils.ascii(enum.name), [utils.ascii(name) for name, dummy_val in enum.values],
1131                            outer_class=outer_class)
1132
1133        ## scan classes
1134        if outer_class is None:
1135            unregistered_classes = [cls for cls in
1136                                    module_namespace.classes(function=self.location_filter,
1137                                                             recursive=False, allow_empty=True)
1138                                    if not cls.name.startswith('__')]
1139            typedefs = [typedef for typedef in
1140                        module_namespace.typedefs(function=self.location_filter,
1141                                                  recursive=False, allow_empty=True)
1142                        if not typedef.name.startswith('__')]
1143        else:
1144            unregistered_classes = []
1145            typedefs = []
1146            for cls in outer_class.castxml_definition.classes(function=self.location_filter,
1147                                                              recursive=False, allow_empty=True):
1148                if outer_class.castxml_definition.find_out_member_access_type(cls) != 'public':
1149                    continue
1150                if cls.name.startswith('__'):
1151                    continue
1152                unregistered_classes.append(cls)
1153
1154            for typedef in outer_class.castxml_definition.typedefs(function=self.location_filter,
1155                                                                   recursive=False, allow_empty=True):
1156                if outer_class.castxml_definition.find_out_member_access_type(typedef) != 'public':
1157                    continue
1158                if typedef.name.startswith('__'):
1159                    continue
1160                typedefs.append(typedef)
1161
1162        unregistered_classes.sort(key=lambda c: c.decl_string)
1163        # logger.debug("unregistered_classes: %r", unregistered_classes)
1164
1165        def postpone_class(cls, reason):
1166            ## detect the case of a class being postponed many times; that
1167            ## is almost certainly an error and a sign of an infinite
1168            ## loop.
1169            count = getattr(cls, "_pybindgen_postpone_count", 0)
1170            count += 1
1171            cls._pybindgen_postpone_count = count
1172            if count >= 10:
1173                raise AssertionError("The class %s registration is being postponed for "
1174                                     "the %ith time (last reason: %r, current reason: %r);"
1175                                     " something is wrong, please file a bug report"
1176                                     " (https://bugs.launchpad.net/pybindgen/+filebug) with a test case."
1177                                     % (cls, count, cls._pybindgen_postpone_reason, reason))
1178            cls._pybindgen_postpone_reason = reason
1179            if DEBUG:
1180                print(">>> class %s is being postponed (%s)" % (str(cls), reason), file=sys.stderr)
1181            unregistered_classes.append(cls)
1182
1183        timings = []
1184        while unregistered_classes:
1185            timings.append(time.time())
1186            if len(timings) > 1 and timings[-1] - timings[0] > 0.1:
1187                tl = ["{:.1f}".format((t2 - t1) * 1000)
1188                      for t2, t1 in zip(timings[1:], timings[:-1])]
1189                logger.debug("timings (total: %.1f ms): %s",
1190                             (timings[-1] - timings[0]) * 1000,
1191                             " + ".join(tl))
1192            del timings[:]
1193            timings.append(time.time())
1194
1195            cls = unregistered_classes.pop(0)
1196            logger.info("looking at class (%i left): %s",
1197                        len(unregistered_classes), cls)
1198            typedef = None
1199
1200            kwargs = {}
1201            global_annotations, param_annotations = annotations_scanner.get_annotations(cls)
1202            timings.append(time.time())
1203            for hook in self._pre_scan_hooks:
1204                hook(self, cls, global_annotations, param_annotations)
1205            if 'ignore' in global_annotations:
1206                logger.debug("Class %s annotated with 'ignore', skipping",
1207                             cls)
1208                continue
1209            timings.append(time.time())
1210
1211            if not cls.name:
1212                if outer_class is None:
1213                    warnings.warn_explicit(("Class %s ignored: anonymous structure not inside a named structure/union."
1214                                            % cls.partial_decl_string),
1215                                           NotSupportedWarning, cls.location.file_name, cls.location.line)
1216                    continue
1217
1218                self._anonymous_structs.append((cls, outer_class))
1219                continue
1220
1221            timings.append(time.time())
1222
1223            if '<' in cls.name:
1224
1225                for typedef in module_namespace.typedefs(function=self.location_filter,
1226                                                         recursive=False, allow_empty=True):
1227                    typedef_type = type_traits.remove_declarated(typedef.decl_type)
1228                    if typedef_type == cls:
1229                        break
1230                else:
1231                    typedef = None
1232
1233            timings.append(time.time())
1234            base_class_wrappers = []
1235            bases_ok = True
1236            for cls_bases_item in cls.bases:
1237                base_cls = cls_bases_item.related_class
1238                try:
1239                    base_class_wrapper = self._registered_classes[base_cls]
1240                except KeyError:
1241                    ## base class not yet registered => postpone this class registration
1242                    if base_cls not in unregistered_classes:
1243                        warnings.warn_explicit("Class %s ignored because it uses a base class (%s) "
1244                                               "which is not declared."
1245                                               % (cls.partial_decl_string, base_cls.partial_decl_string),
1246                                               ModuleParserWarning, cls.location.file_name, cls.location.line)
1247                        bases_ok = False
1248                        break
1249                    postpone_class(cls, "waiting for base class %s to be registered first" % base_cls)
1250                    bases_ok = False
1251                    break
1252                else:
1253                    base_class_wrappers.append(base_class_wrapper)
1254                    del base_class_wrapper
1255                del base_cls
1256            if not bases_ok:
1257                continue
1258
1259            timings.append(time.time())
1260
1261            ## If this class implicitly converts to another class, but
1262            ## that other class is not yet registered, postpone.
1263            for operator in cls.casting_operators(allow_empty=True):
1264                target_type = type_traits.remove_declarated(operator.return_type)
1265                if not isinstance(target_type, class_t):
1266                    continue
1267                target_class_name = normalize_class_name(operator.return_type.partial_decl_string, '::')
1268                try:
1269                    dummy = root_module[target_class_name]
1270                except KeyError:
1271                    if target_class_name not in [normalize_class_name(t.partial_decl_string, '::') for t in unregistered_classes]:
1272                        ok = True # (lp:455689)
1273                    else:
1274                        ok = False
1275                    break
1276            else:
1277                ok = True
1278            if not ok:
1279                postpone_class(cls, ("waiting for implicit conversion target class %s to be registered first"
1280                                     % (operator.return_type.partial_decl_string,)))
1281                continue
1282
1283            is_exception = self._apply_class_annotations(cls, global_annotations, kwargs)
1284
1285            timings.append(time.time())
1286
1287            custom_template_class_name = None
1288            template_parameters = ()
1289            if typedef is None:
1290                alias = None
1291                isinst = templates.is_instantiation(cls.decl_string)
1292                timings.append(time.time())
1293                if isinst:
1294                    cls_name, template_parameters = templates.split(cls.name)
1295                    assert template_parameters
1296                    if '::' in cls_name:
1297                        cls_name = cls_name.split('::')[-1]
1298                    template_instance_names = global_annotations.get('template_instance_names', '')
1299                    if template_instance_names:
1300                        for mapping in template_instance_names.split('|'):
1301                            type_names, name = mapping.split('=>')
1302                            instance_types = type_names.split(',')
1303                            if instance_types == template_parameters:
1304                                custom_template_class_name = name
1305                                break
1306                else:
1307                    cls_name = cls.name
1308            else:
1309                timings.append(time.time())
1310                cls_name = typedef.name
1311                alias = '::'.join([module.cpp_namespace_prefix, cls.name])
1312
1313            template_parameters_decls = [find_declaration_from_name(self.global_ns, templ_param)
1314                                         for templ_param in template_parameters]
1315
1316            ignore_class = False
1317            for template_param in template_parameters_decls:
1318                if not isinstance(template_param, class_t):
1319                    continue
1320                if not isinstance(template_param.parent, class_t):
1321                    continue
1322                access = template_param.parent.find_out_member_access_type(template_param)
1323                if access != 'public':
1324                    # this templated class depends on a private type => we can't wrap it
1325                    ignore_class = True
1326                    break
1327            if ignore_class:
1328                continue
1329
1330            timings.append(time.time())
1331
1332            if 0: # this is disabled due to ns3
1333                ## if any template argument is a class that is not yet
1334                ## registered, postpone scanning/registering the template
1335                ## instantiation class until the template argument gets
1336                ## registered.
1337                postponed = False
1338                for templ in template_parameters_decls:
1339                    if isinstance(templ, class_t):
1340                        try:
1341                            self._registered_classes[templ]
1342                        except KeyError:
1343                            if templ in unregistered_classes:
1344                                postpone_class(cls, "waiting for template argument class %s to be registered first" % templ)
1345                                postponed = True
1346                if postponed:
1347                    continue
1348
1349            if base_class_wrappers:
1350                if len(base_class_wrappers) > 1:
1351                    kwargs["parent"] = base_class_wrappers
1352                else:
1353                    kwargs["parent"] = base_class_wrappers[0]
1354            if outer_class is not None:
1355                kwargs["outer_class"] = outer_class
1356            if template_parameters:
1357                kwargs["template_parameters"] = template_parameters
1358            if custom_template_class_name:
1359                kwargs["custom_name"] = custom_template_class_name
1360
1361            # given the pygen sinks for the class itself and the sinks
1362            # for the template parameters, get the one with lowest
1363            # precedence (the higher the number, the lowest the
1364            # precedence).
1365            pygen_sinks = [self._get_pygen_sink_for_definition(cls, with_section_precedence=True)]
1366            for templ in template_parameters_decls:
1367                if templ is not None:
1368                    pygen_sinks.append(self._get_pygen_sink_for_definition(templ, with_section_precedence=True))
1369            pygen_sinks.sort()
1370            pygen_sink = pygen_sinks[-1][1]
1371            del pygen_sinks
1372
1373            if pygen_sink:
1374                if 'pygen_comment' in global_annotations:
1375                    pygen_sink.writeln('## ' + global_annotations['pygen_comment'])
1376                if is_exception:
1377                    pygen_sink.writeln("module.add_exception(%s)" %
1378                                       ", ".join([repr(cls_name)] + _pygen_kwargs(kwargs)))
1379                else:
1380                    pygen_sink.writeln("module.add_class(%s)" %
1381                                       ", ".join([repr(cls_name)] + _pygen_kwargs(kwargs)))
1382
1383            timings.append(time.time())
1384
1385            ## detect use of unregistered container types: need to look at
1386            ## all parameters and return values of all functions in this namespace...
1387            for member in cls.get_members(access='public'):
1388                if member.name.startswith('__'):
1389                    continue
1390                for dependency in get_dependencies_from_decl(member,recursive=True):
1391                    type_info = dependency.depend_on_it
1392                    if type_traits.is_pointer(type_info):
1393                        type_info = type_traits.remove_pointer(type_info)
1394                    elif type_traits.is_reference(type_info):
1395                        type_info = type_traits.remove_reference(type_info)
1396                    if type_traits.is_const(type_info):
1397                        type_info = type_traits.remove_const(type_info)
1398                    traits = container_traits.find_container_traits(type_info)
1399                    if traits is None:
1400                        continue
1401                    name = normalize_name(type_info.partial_decl_string)
1402                    # now postpone container registration until after
1403                    # all classes are registered, because we may
1404                    # depend on one of those classes for the element
1405                    # type.
1406                    self._containers_to_register.append((traits, type_info, None, name))
1407
1408            timings.append(time.time())
1409
1410            if is_exception:
1411                class_wrapper = module.add_exception(cls_name, **kwargs)
1412            else:
1413                class_wrapper = module.add_class(cls_name, **kwargs)
1414            #print >> sys.stderr, "<<<<<ADD CLASS>>>>> ", cls_name
1415
1416            class_wrapper.castxml_definition = cls
1417            self._registered_classes[cls] = class_wrapper
1418            if alias:
1419                class_wrapper.register_alias(normalize_name(alias))
1420            self.type_registry.class_registered(class_wrapper)
1421
1422            timings.append(time.time())
1423
1424            for hook in self._post_scan_hooks:
1425                hook(self, cls, class_wrapper)
1426
1427            timings.append(time.time())
1428
1429            del cls_name
1430
1431            ## scan for nested classes/enums
1432            self._scan_namespace_types(module, module_namespace, outer_class=class_wrapper)
1433            timings.append(time.time())
1434
1435            # scan for implicit conversion casting operators
1436            for operator in cls.casting_operators(allow_empty=True):
1437                target_type = type_traits.remove_declarated(operator.return_type)
1438                if not isinstance(target_type, class_t):
1439                    continue
1440                other_class_name = normalize_class_name(operator.return_type.partial_decl_string, '::')
1441                try:
1442                    other_class = root_module[other_class_name]
1443                except KeyError:
1444                    warnings.warn_explicit("Implicit conversion target type %s not registered"
1445                                           % (other_class_name,),
1446                                           WrapperWarning, operator.location.file_name,
1447                                           operator.location.line)
1448                else:
1449                    class_wrapper.implicitly_converts_to(other_class)
1450                    if pygen_sink:
1451                        if 'pygen_comment' in global_annotations:
1452                            pygen_sink.writeln('## ' + global_annotations['pygen_comment'])
1453                        pygen_sink.writeln("root_module[%r].implicitly_converts_to(root_module[%r])"
1454                                           % (class_wrapper.full_name, other_class.full_name))
1455            timings.append(time.time())
1456
1457        # -- register containers
1458        if outer_class is None:
1459            for (traits, type_info, _outer_class, name) in self._containers_to_register:
1460                self._register_container(module, traits, type_info, _outer_class, name)
1461            self._containers_to_register = []
1462
1463        if pygen_register_function_name:
1464            pygen_function_closed = False
1465        else:
1466            pygen_function_closed = True
1467
1468        ## --- look for typedefs ----
1469        for alias in typedefs:
1470
1471            type_from_name = normalize_name(alias.decl_type.partial_decl_string)
1472            if outer_class is None:
1473                type_to_name = normalize_name(utils.ascii('::'.join([module.cpp_namespace_prefix, alias.name])))
1474            else:
1475                type_to_name = normalize_name(utils.ascii('::'.join([outer_class.full_name, alias.name])))
1476
1477            for sym in '', '*', '&':
1478                typehandlers.base.add_type_alias(type_from_name+sym, type_to_name+sym)
1479                pygen_sink = self._get_pygen_sink_for_definition(alias)
1480                if pygen_sink:
1481                    pygen_sink.writeln("typehandlers.add_type_alias(%r, %r)" % (type_from_name+sym, type_to_name+sym))
1482
1483            ## Look for forward declarations of class/structs like
1484            ## "typedef struct _Foo Foo"; these are represented in
1485            ## pygccxml by a typedef whose .type.declaration is a
1486            ## class_declaration_t instead of class_t.
1487            if isinstance(alias.decl_type, cpptypes.declarated_t):
1488                cls = alias.decl_type.declaration
1489                if templates.is_instantiation(cls.decl_string):
1490                    continue # typedef to template instantiations, must be fully defined
1491                if isinstance(cls, class_declaration_t):
1492
1493                    global_annotations, param_annotations = annotations_scanner.get_annotations(cls)
1494                    for hook in self._pre_scan_hooks:
1495                        hook(self, cls, global_annotations, param_annotations)
1496                    if 'ignore' in global_annotations:
1497                        continue
1498
1499                    kwargs = dict()
1500                    self._apply_class_annotations(cls, global_annotations, kwargs)
1501                    kwargs.setdefault("incomplete_type", True)
1502                    kwargs.setdefault("automatic_type_narrowing", False)
1503                    kwargs.setdefault("allow_subclassing", False)
1504
1505                    pygen_sink = self._get_pygen_sink_for_definition(cls)
1506                    if pygen_sink:
1507                        if 'pygen_comment' in global_annotations:
1508                            pygen_sink.writeln('## ' + global_annotations['pygen_comment'])
1509                        pygen_sink.writeln("module.add_class(%s)" %
1510                                           ", ".join([repr(alias.name)] + _pygen_kwargs(kwargs)))
1511
1512                    class_wrapper = module.add_class(alias.name, **kwargs)
1513
1514                    class_wrapper.castxml_definition = cls
1515                    self._registered_classes[cls] = class_wrapper
1516                    if cls.name != alias.name:
1517                        class_wrapper.register_alias(normalize_name(cls.name))
1518                    self.type_registry.class_registered(class_wrapper)
1519
1520                ## Handle "typedef ClassName OtherName;"
1521                elif isinstance(cls, class_t):
1522                    #print >> sys.stderr, "***** typedef", cls, "=>", alias.name
1523                    try:
1524                        cls_wrapper = self._registered_classes[cls]
1525                    except KeyError:
1526                        warnings.warn("typedef %s %s: wrapper for class %s not know"
1527                                      % (cls, alias.name, cls),
1528                                      WrapperWarning)
1529                    else:
1530                        module.add_typedef(cls_wrapper, alias.name)
1531
1532                        pygen_sink = self._get_pygen_sink_for_definition(cls)
1533                        if pygen_sink:
1534                            pygen_sink.writeln("module.add_typedef(root_module[%r], %r)" %
1535                                               (cls_wrapper.full_name, utils.ascii(alias.name)))
1536
1537
1538        ## scan nested namespaces (mapped as python submodules)
1539        nested_modules = []
1540        nested_namespaces = []
1541        for nested_namespace in module_namespace.namespaces(allow_empty=True, recursive=False):
1542            if nested_namespace.name.startswith('__'):
1543                continue
1544            nested_namespaces.append(nested_namespace)
1545
1546        nested_namespaces.sort(key=lambda c: c.decl_string)
1547
1548        for nested_namespace in nested_namespaces:
1549            if pygen_register_function_name:
1550                nested_module = module.add_cpp_namespace(utils.ascii(nested_namespace.name))
1551                nested_modules.append(nested_module)
1552                for pygen_sink in self._get_all_pygen_sinks():
1553                    pygen_sink.writeln()
1554                    pygen_sink.writeln("## Register a nested module for the namespace %s" % utils.ascii(nested_namespace.name))
1555                    pygen_sink.writeln()
1556                    pygen_sink.writeln("nested_module = module.add_cpp_namespace(%r)" % utils.ascii(nested_namespace.name))
1557                    nested_module_type_init_func = "register_types_" + "_".join(nested_module.get_namespace_path())
1558                    pygen_sink.writeln("%s(nested_module)" % nested_module_type_init_func)
1559                    pygen_sink.writeln()
1560        if not pygen_function_closed:
1561            for pygen_sink in self._get_all_pygen_sinks():
1562                pygen_sink.unindent()
1563                pygen_sink.writeln()
1564            pygen_function_closed = True
1565
1566        ## scan nested namespaces (mapped as python submodules)
1567        nested_namespaces = []
1568        for nested_namespace in module_namespace.namespaces(allow_empty=True, recursive=False):
1569            if nested_namespace.name.startswith('__'):
1570                continue
1571            nested_namespaces.append(nested_namespace)
1572
1573        nested_namespaces.sort(key=lambda c: c.decl_string)
1574
1575        for nested_namespace in nested_namespaces:
1576            if pygen_register_function_name:
1577                nested_module = nested_modules.pop(0)
1578                nested_module_type_init_func = "register_types_" + "_".join(nested_module.get_namespace_path())
1579                self._scan_namespace_types(nested_module, nested_namespace,
1580                                           pygen_register_function_name=nested_module_type_init_func)
1581        assert not nested_modules # make sure all have been consumed by the second for loop
1582        # ^^ CLOSE: if outer_class is None: ^^
1583
1584        if pygen_register_function_name and not pygen_function_closed:
1585            for pygen_sink in self._get_all_pygen_sinks():
1586                pygen_sink.unindent()
1587                pygen_sink.writeln()
1588
1589    def _register_container(self, module, traits, definition, outer_class, name):
1590        if '<' in name and not self.enable_anonymous_containers:
1591            return
1592
1593        kwargs = {}
1594        key_type = None
1595
1596        if traits is container_traits.list_traits:
1597            container_type = 'list'
1598        elif traits is container_traits.deque_traits:
1599            container_type = 'dequeue'
1600        elif traits is container_traits.queue_traits:
1601            container_type = 'queue'
1602        elif traits is container_traits.priority_queue_traits:
1603            container_type = 'dequeue'
1604        elif traits is container_traits.vector_traits:
1605            container_type = 'vector'
1606        elif traits is container_traits.stack_traits:
1607            container_type = 'stack'
1608        elif traits is container_traits.set_traits:
1609            container_type = 'set'
1610        elif traits is container_traits.multiset_traits:
1611            container_type = 'multiset'
1612        elif traits is container_traits.hash_set_traits:
1613            container_type = 'hash_set'
1614        elif traits is container_traits.hash_multiset_traits:
1615            container_type = 'hash_multiset'
1616        elif (traits is container_traits.map_traits
1617              or traits is container_traits.unordered_map_traits):
1618            container_type = 'map'
1619            if hasattr(traits, "key_type"):
1620                key_type = traits.key_type(definition)
1621            else:
1622                warnings.warn("pygccxml 0.9.5 or earlier don't have the key_type method, "
1623                              "so we don't support mapping types with this  pygccxml version (%r)"
1624                              % pygccxml.__version__)
1625                return
1626        elif (traits is container_traits.multimap_traits
1627              or traits is container_traits.hash_map_traits
1628              or traits is container_traits.hash_multimap_traits):
1629            return # maps not yet implemented
1630
1631        else:
1632            assert False, "container type %s unaccounted for." % name
1633
1634        if outer_class is not None:
1635            kwargs['outer_class'] = outer_class
1636            outer_class_key = outer_class.partial_decl_string
1637        else:
1638            outer_class_key = None
1639
1640        container_register_key = (outer_class_key, name)
1641        if container_register_key in self._containers_registered:
1642            return
1643        self._containers_registered[container_register_key] = None
1644        element_type = None
1645        #print >> sys.stderr, "************* register_container", name
1646        #print ("*********************", templates.args(traits.class_declaration(definition).name))
1647        try:
1648          element_type = traits.element_type(definition)
1649        except RuntimeError:
1650          value_type_str = templates.args(traits.class_declaration(definition).name)[traits.element_type_index]
1651          try:
1652            has_space_pointer = value_type_str.endswith(' *')
1653            if has_space_pointer:
1654              value_type_str = value_type_str[:-2]
1655              element_type = find_declaration_from_name (traits.class_declaration(definition).top_parent, value_type_str)
1656              if isinstance(element_type, class_declaration.class_types):
1657                element_type = cpptypes.declarated_t(element_type)
1658              element_type = cpptypes.pointer_t(element_type)
1659            if None is element_type:
1660              raise RuntimeError(
1661                        "Unable to find out %s '%s' key\\value type." %
1662                        (traits.name(), traits.class_declaration(definition).decl_string))
1663          except RuntimeError:
1664            if value_type_str == "unsigned short":
1665              element_type = find_declaration_from_name (traits.class_declaration(definition).top_parent, "short unsigned int")
1666            else:
1667              raise RuntimeError(
1668                        "Unable to find out %s '%s' key\\value type." %
1669                        (traits.name(), traits.class_declaration(definition).decl_string))
1670        #if traits.is_mapping(definition):
1671        #    key_type = traits.key_type(definition)
1672            #print >> sys.stderr, "************* register_container %s; element_type=%s, key_type=%s" % \
1673            #    (name, element_type, key_type.partial_decl_string)
1674
1675        element_decl = type_traits.remove_declarated(element_type)
1676
1677        kwargs['container_type'] = container_type
1678
1679        pygen_sink = self._get_pygen_sink_for_definition(element_decl)
1680
1681        elem_type_spec = self.type_registry.lookup_return(element_type)
1682        if key_type is not None:
1683            key_type_spec = self.type_registry.lookup_return(key_type)
1684            _retval_str = "(%s, %s)" % (_pygen_retval(*key_type_spec), _pygen_retval(*elem_type_spec))
1685        else:
1686            _retval_str = _pygen_retval(*elem_type_spec)
1687        if pygen_sink:
1688            pygen_sink.writeln("module.add_container(%s)" %
1689                               ", ".join([repr(name), _retval_str] + _pygen_kwargs(kwargs)))
1690
1691        ## convert the return value
1692        try:
1693            return_type_elem = ReturnValue.new(*elem_type_spec[0], **elem_type_spec[1])
1694        except (TypeLookupError, TypeConfigurationError) as ex:
1695            warnings.warn("Return value '%s' error (used in %s): %r"
1696                          % (definition.partial_decl_string, definition, ex),
1697                          WrapperWarning)
1698            return
1699
1700        if key_type is not None:
1701            try:
1702                return_type_key = ReturnValue.new(*key_type_spec[0], **key_type_spec[1])
1703            except (TypeLookupError, TypeConfigurationError) as ex:
1704                warnings.warn("Return value '%s' error (used in %s): %r"
1705                              % (definition.partial_decl_string, definition, ex),
1706                              WrapperWarning)
1707                return
1708
1709            module.add_container(name, (return_type_key, return_type_elem), **kwargs)
1710        else:
1711            module.add_container(name, return_type_elem, **kwargs)
1712
1713
1714    def _class_has_virtual_methods(self, cls):
1715        """return True if cls has at least one virtual method, else False"""
1716        for member in cls.get_members():
1717            if isinstance(member, calldef_members.member_function_t):
1718                if member.virtuality != calldef_types.VIRTUALITY_TYPES.NOT_VIRTUAL:
1719                    return True
1720        return False
1721
1722    def _is_ostream(self, cpp_type):
1723        return (isinstance(cpp_type, cpptypes.reference_t)
1724                and not isinstance(cpp_type.base, cpptypes.const_t)
1725                and str(cpp_type.base) == 'std::ostream')
1726
1727    def _scan_class_operators(self, cls, class_wrapper, pygen_sink):
1728
1729        def _handle_operator(op, argument_types):
1730            #print >> sys.stderr, "<<<<<OP>>>>>  (OP %s in class %s) : %s --> %s" % \
1731            #    (op.symbol, cls, [str(x) for x in argument_types], op.return_type)
1732
1733            if op.symbol == '<<' \
1734                    and self._is_ostream(op.return_type) \
1735                    and len(op.arguments) == 2 \
1736                    and self._is_ostream(argument_types[0]) \
1737                    and type_traits_classes.is_convertible(cls, argument_types[1]):
1738                #print >> sys.stderr, "<<<<<OUTPUT STREAM OP>>>>>  %s: %s " % (op.symbol, cls)
1739                class_wrapper.add_output_stream_operator()
1740                pygen_sink.writeln("cls.add_output_stream_operator()")
1741                return
1742
1743            if op.symbol in ['==', '!=', '<', '<=', '>', '>='] \
1744                    and len(argument_types) == 2 \
1745                    and type_traits_classes.is_convertible(cls, argument_types[0]) \
1746                    and type_traits_classes.is_convertible(cls, argument_types[1]):
1747                #print >> sys.stderr, "<<<<<BINARY COMPARISON OP>>>>>  %s: %s " % (op.symbol, cls)
1748                class_wrapper.add_binary_comparison_operator(op.symbol)
1749                pygen_sink.writeln("cls.add_binary_comparison_operator(%r)" % (op.symbol,))
1750                return
1751
1752            def get_class_wrapper(pygccxml_type):
1753                traits = ctypeparser.TypeTraits(normalize_name(pygccxml_type.partial_decl_string))
1754                if traits.type_is_reference:
1755                    name = str(traits.target)
1756                else:
1757                    name = str(traits.ctype)
1758                class_wrapper = self.type_registry.root_module.get(name, None)
1759                #print >> sys.stderr, "(lookup %r: %r)" % (name, class_wrapper)
1760                return class_wrapper
1761
1762            if not type_traits_classes.is_convertible(cls, argument_types[0]):
1763                return
1764
1765            ret = get_class_wrapper(op.return_type)
1766            if ret is None:
1767                warnings.warn_explicit("NUMERIC OP: retval class %s not registered" % (op.return_type,),
1768                                       WrapperWarning, op.location.file_name, op.location.line)
1769                return
1770
1771            arg0 = get_class_wrapper(argument_types[0])
1772            if arg0 is None:
1773                warnings.warn_explicit("NUMERIC OP: arg0 class %s not registered" % (op.return_type,),
1774                                       WrapperWarning, op.location.file_name, op.location.line)
1775                return
1776
1777            if len(argument_types) == 2:
1778                dummy_global_annotations, parameter_annotations = annotations_scanner.get_annotations(op)
1779                arg_spec = self.type_registry.lookup_parameter(argument_types[1], 'right',
1780                                                               parameter_annotations.get('right', {}))
1781
1782                arg_repr = _pygen_param(arg_spec[0], arg_spec[1])
1783
1784                try:
1785                    param = Parameter.new(*arg_spec[0], **arg_spec[1])
1786                except (TypeLookupError, TypeConfigurationError) as ex:
1787                    warnings.warn_explicit("Parameter '%s' error (used in %s): %r"
1788                                           % (argument_types[1].partial_decl_string, op, ex),
1789                                           WrapperWarning, op.location.file_name, op.location.line)
1790                    param = None
1791
1792                #print >> sys.stderr, "<<<<<potential NUMERIC OP>>>>> ", param, ('?' if param is None else param.ctype)
1793
1794                if op.symbol in ['+', '-', '/', '*']:
1795                    #print >> sys.stderr, "<<<<<potential NUMERIC OP>>>>>  %s: %s : %s --> %s" \
1796                    #    % (op.symbol, cls, [str(x) for x in argument_types], return_type)
1797
1798                    pygen_sink.writeln("cls.add_binary_numeric_operator(%r, root_module[%r], root_module[%r], %s)"
1799                                       % (op.symbol, ret.full_name, arg0.full_name, arg_repr))
1800                    if param is not None:
1801                        class_wrapper.add_binary_numeric_operator(op.symbol, ret, arg0, param)
1802
1803                # -- inplace numeric operators --
1804                if op.symbol in ['+=', '-=', '/=', '*=']:
1805                    #print >> sys.stderr, "<<<<<potential NUMERIC OP>>>>>  %s: %s : %s --> %s" \
1806                    #    % (op.symbol, cls, [str(x) for x in argument_types], return_type)
1807
1808                    pygen_sink.writeln("cls.add_inplace_numeric_operator(%r, %s)" % (op.symbol, arg_repr))
1809                    if param is not None:
1810                        class_wrapper.add_inplace_numeric_operator(op.symbol, param)
1811
1812            elif len(argument_types) == 1: # unary operator
1813                if op.symbol in ['-']:
1814                    pygen_sink.writeln("cls.add_unary_numeric_operator(%r)" % (op.symbol,))
1815                    class_wrapper.add_unary_numeric_operator(op.symbol)
1816
1817            else:
1818                warnings.warn_explicit("NUMERIC OP: wrong number of arguments, got %i, expected 1 or 2"
1819                                       % len(argument_types),
1820                                       WrapperWarning, op.location.file_name, op.location.line)
1821                return
1822
1823
1824
1825        for op in self.module_namespace.free_operators(function=self.location_filter,
1826                                                       allow_empty=True,
1827                                                       recursive=True):
1828            _handle_operator(op, [arg.decl_type for arg in op.arguments])
1829
1830        for op in cls.member_operators(function=self.location_filter,
1831                                       allow_empty=True,
1832                                       recursive=True):
1833            if op.access_type != 'public':
1834                continue
1835            arg_types = [arg.decl_type for arg in op.arguments]
1836            arg_types.insert(0, cls)
1837            _handle_operator(op, arg_types)
1838
1839
1840    def _scan_class_methods(self, cls, class_wrapper, pygen_sink):
1841        have_trivial_constructor = False
1842        have_copy_constructor = False
1843
1844        if pygen_sink is None:
1845            pygen_sink = NullCodeSink()
1846
1847        self._scan_class_operators(cls, class_wrapper, pygen_sink)
1848
1849        for member in cls.get_members():
1850            if isinstance(member, calldef_members.member_function_t):
1851                if member.access_type not in ['protected', 'private']:
1852                    continue
1853
1854            elif isinstance(member, calldef_members.constructor_t):
1855                if member.access_type not in ['protected', 'private']:
1856                    continue
1857
1858                if len(member.arguments) == 0:
1859                    have_trivial_constructor = True
1860
1861                elif len(member.arguments) == 1:
1862                    traits = ctypeparser.TypeTraits(normalize_name(member.arguments[0].decl_type.partial_decl_string))
1863                    if traits.type_is_reference and \
1864                            self.type_registry.root_module.get(str(traits.target), None) is class_wrapper:
1865                        have_copy_constructor = True
1866
1867        methods_to_ignore = []
1868        if isinstance(class_wrapper.memory_policy, ReferenceCountingMethodsPolicy):
1869            methods_to_ignore.extend([class_wrapper.memory_policy.incref_method,
1870                                      class_wrapper.memory_policy.decref_method,
1871                                      class_wrapper.memory_policy.peekref_method,
1872                                      ])
1873
1874        for member in cls.get_members():
1875            if member.name in methods_to_ignore:
1876                logger.debug("Ignoring method %s of class %s (refcounting)",
1877                             member.name, cls)
1878                continue
1879
1880            global_annotations, parameter_annotations = annotations_scanner.get_annotations(member)
1881            for hook in self._pre_scan_hooks:
1882                hook(self, member, global_annotations, parameter_annotations)
1883
1884            if 'ignore' in global_annotations:
1885                logger.debug("Ignoring method %s of class %s (annotation: %s)",
1886                             member.name, cls, global_annotations['ignore'])
1887                continue
1888
1889            logger.debug("Looking at method %s of class %s",
1890                         member.name, cls)
1891
1892            ## ------------ method --------------------
1893            if isinstance(member, (calldef_members.member_function_t, calldef_members.member_operator_t)):
1894                is_virtual = (member.virtuality != calldef_types.VIRTUALITY_TYPES.NOT_VIRTUAL)
1895                pure_virtual = (member.virtuality == calldef_types.VIRTUALITY_TYPES.PURE_VIRTUAL)
1896
1897                kwargs = {} # kwargs passed into the add_method call
1898
1899                for key, val in global_annotations.items():
1900                    if key == 'template_instance_names' \
1901                            and check_template(demangle(member.get_mangled_name()), member.name):
1902                        pass
1903                    elif key == 'pygen_comment':
1904                        pass
1905                    elif key == 'unblock_threads':
1906                        kwargs['unblock_threads'] = annotations_scanner.parse_boolean(val)
1907                    elif key == 'name':
1908                        kwargs['custom_name'] = val
1909                    elif key == 'throw':
1910                        kwargs['throw'] = self._get_annotation_exceptions(val)
1911                    else:
1912                        warnings.warn_explicit("Annotation '%s=%s' not used (used in %s)"
1913                                               % (key, val, member),
1914                                               AnnotationsWarning, member.location.file_name, member.location.line)
1915                if isinstance(member, calldef_members.member_operator_t):
1916                    if member.symbol == '()':
1917                        kwargs['custom_name'] = '__call__'
1918                    else:
1919                        continue
1920
1921                throw = self._get_calldef_exceptions(member)
1922                if throw:
1923                    kwargs['throw'] = throw
1924
1925                ## --- pygen ---
1926                return_type_spec = self.type_registry.lookup_return(member.return_type,
1927                                                                    parameter_annotations.get('return', {}))
1928                argument_specs = []
1929                for arg in member.arguments:
1930                    if (isinstance(arg.decl_type, cpptypes.declarated_t) and
1931                        isinstance(arg.decl_type.declaration, typedef.typedef_t) and
1932                            arg.decl_type.decl_string.startswith("::{}::".format(self.module_namespace_name))):
1933                        argument_specs.append(self.type_registry.lookup_parameter(arg.decl_type.declaration.decl_type, arg.name,
1934                                                                              parameter_annotations.get(arg.name, {}),
1935                                                                              arg.default_value))
1936                    else:
1937                        argument_specs.append(self.type_registry.lookup_parameter(arg.decl_type, arg.name,
1938                                                                              parameter_annotations.get(arg.name, {}),
1939                                                                              arg.default_value))
1940
1941                if pure_virtual and not class_wrapper.allow_subclassing:
1942                    class_wrapper.set_cannot_be_constructed("pure virtual method and subclassing disabled")
1943                    #self.pygen_sink.writeln('cls.set_cannot_be_constructed("pure virtual method not wrapped")')
1944
1945                custom_template_method_name = None
1946                if check_template(demangle(member.get_mangled_name()), member.name):
1947                    #print ("#################################", member.get_mangled_name())
1948                    template_parameters = get_template_arg(demangle(member.get_mangled_name()), member.name)
1949                    template_instance_names = global_annotations.get('template_instance_names', '')
1950                    if template_instance_names:
1951                        for mapping in template_instance_names.split('|'):
1952                            type_names, name = mapping.split('=>')
1953                            instance_types = type_names.split(',')
1954                            if instance_types == template_parameters:
1955                                custom_template_method_name = name
1956                                break
1957                else:
1958                    template_parameters = ()
1959
1960                if member.has_const:
1961                    kwargs['is_const'] = True
1962                if member.has_static:
1963                    kwargs['is_static'] = True
1964                if is_virtual:
1965                    kwargs['is_virtual'] = True
1966                if pure_virtual:
1967                    kwargs['is_pure_virtual'] = True
1968                if template_parameters:
1969                    kwargs['template_parameters'] = template_parameters
1970                if custom_template_method_name:
1971                    kwargs['custom_template_method_name'] = custom_template_method_name
1972                if member.access_type != 'public':
1973                    kwargs['visibility'] = member.access_type
1974
1975                ## ignore methods that are private and not virtual or
1976                ## pure virtual, as they do not affect the bindings in
1977                ## any way and only clutter the generated python script.
1978                if (kwargs.get('visibility', 'public') == 'private'
1979                    and not (kwargs.get('is_virtual', False) or kwargs.get('is_pure_virtual', False))):
1980                    continue
1981
1982                if member.attributes:
1983                    if 'deprecated' in member.attributes:
1984                        kwargs['deprecated'] = True
1985
1986                ## --- pygen ---
1987                arglist_repr = ("[" + ', '.join([_pygen_param(args_, kwargs_) for (args_, kwargs_) in argument_specs]) +  "]")
1988                if 'pygen_comment' in global_annotations:
1989                    pygen_sink.writeln('## ' + global_annotations['pygen_comment'])
1990
1991                kwargs_repr = _pygen_kwargs(kwargs)
1992                if kwargs_repr:
1993                    kwargs_repr[0] = '\n' + 15*' ' + kwargs_repr[0]
1994
1995                pygen_sink.writeln("cls.add_method(%s)" %
1996                                   ", ".join(
1997                        [repr(member.name),
1998                         '\n' + 15*' ' + _pygen_retval(return_type_spec[0], return_type_spec[1]),
1999                         '\n' + 15*' ' + arglist_repr] + kwargs_repr))
2000
2001                ## --- realize the return type and parameters
2002                try:
2003                    return_type = ReturnValue.new(*return_type_spec[0], **return_type_spec[1])
2004                except (TypeLookupError, TypeConfigurationError) as ex:
2005                    logger.debug("Unable to lookup return type handler: %r %r: %r",
2006                                 return_type_spec[0], return_type_spec[1],
2007                                 ex)
2008                    warnings.warn_explicit("Return value '%s' error (used in %s): %r"
2009                                           % (member.return_type.partial_decl_string, member, ex),
2010                                           WrapperWarning, member.location.file_name, member.location.line)
2011                    if pure_virtual:
2012                        class_wrapper.set_cannot_be_constructed("pure virtual method not wrapped")
2013                        class_wrapper.set_helper_class_disabled(True)
2014                        #self.pygen_sink.writeln('cls.set_cannot_be_constructed("pure virtual method not wrapped")')
2015                        #self.pygen_sink.writeln('cls.set_helper_class_disabled(True)')
2016                    continue
2017                arguments = []
2018                ok = True
2019                for arg in argument_specs:
2020                    try:
2021                        arguments.append(Parameter.new(*arg[0], **arg[1]))
2022                    except (TypeLookupError, TypeConfigurationError) as ex:
2023                        warnings.warn_explicit("Parameter '%s %s' error (used in %s): %r"
2024                                               % (arg[0][0], arg[0][1], member, ex),
2025                                               WrapperWarning, member.location.file_name, member.location.line)
2026                        ok = False
2027                if not ok:
2028                    if pure_virtual:
2029                        class_wrapper.set_cannot_be_constructed("pure virtual method not wrapped")
2030                        class_wrapper.set_helper_class_disabled(True)
2031                        #self.pygen_sink.writeln('cls.set_cannot_be_constructed("pure virtual method not wrapped")')
2032                        #self.pygen_sink.writeln('cls.set_helper_class_disabled(True)')
2033                    continue
2034
2035
2036                try:
2037                    method_wrapper = class_wrapper.add_method(member.name, return_type, arguments, **kwargs)
2038                    method_wrapper.castxml_definition = member
2039                except NotSupportedError as ex:
2040                    if pure_virtual:
2041                        class_wrapper.set_cannot_be_constructed("pure virtual method %r not wrapped" % member.name)
2042                        class_wrapper.set_helper_class_disabled(True)
2043                        pygen_sink.writeln('cls.set_cannot_be_constructed("pure virtual method %%r not wrapped" %% %r)'
2044                                           % member.name)
2045                        pygen_sink.writeln('cls.set_helper_class_disabled(True)')
2046
2047                    warnings.warn_explicit("Error adding method %s: %r"
2048                                           % (member, ex),
2049                                           WrapperWarning, member.location.file_name, member.location.line)
2050                except ValueError as ex:
2051                    warnings.warn_explicit("Error adding method %s: %r"
2052                                           % (member, ex),
2053                                           WrapperWarning, member.location.file_name, member.location.line)
2054                    raise
2055                else: # no exception, add method succeeded
2056                    for hook in self._post_scan_hooks:
2057                        hook(self, member, method_wrapper)
2058
2059
2060            ## ------------ constructor --------------------
2061            elif isinstance(member, calldef_members.constructor_t):
2062                if member.access_type not in ['public', 'protected']:
2063                    continue
2064
2065                if not member.arguments:
2066                    have_trivial_constructor = True
2067
2068                argument_specs = []
2069                for arg in member.arguments:
2070                    argument_specs.append(self.type_registry.lookup_parameter(arg.decl_type, arg.name,
2071                                                                              default_value=arg.default_value))
2072
2073                arglist_repr = ("[" + ', '.join([_pygen_param(args_, kwargs_) for (args_, kwargs_) in argument_specs]) +  "]")
2074                if 'pygen_comment' in global_annotations:
2075                    pygen_sink.writeln('## ' + global_annotations['pygen_comment'])
2076
2077                kwargs = {}
2078
2079                if member.attributes:
2080                    if 'deprecated' in member.attributes:
2081                        kwargs['deprecated'] = True
2082
2083                if member.access_type != 'public':
2084                    kwargs['visibility'] = member.access_type
2085
2086                throw = self._get_calldef_exceptions(member)
2087                if throw:
2088                    kwargs['throw'] = throw
2089
2090                kwargs_repr = _pygen_kwargs(kwargs)
2091                if kwargs_repr:
2092                    kwargs_repr[0] = '\n' + 20*' '+ kwargs_repr[0]
2093                pygen_sink.writeln("cls.add_constructor(%s)" %
2094                                   ", ".join([arglist_repr] + kwargs_repr))
2095
2096                arguments = []
2097                for a, kw in argument_specs:
2098                    try:
2099                        arguments.append(Parameter.new(*a, **kw))
2100                    except (TypeLookupError, TypeConfigurationError) as ex:
2101                        warnings.warn_explicit("Parameter '%s %s' error (used in %s): %r"
2102                                               % (arg.decl_type.partial_decl_string, arg.name, member, ex),
2103                                               WrapperWarning, member.location.file_name, member.location.line)
2104                        ok = False
2105                        break
2106                else:
2107                    ok = True
2108                if not ok:
2109                    continue
2110                constructor_wrapper = class_wrapper.add_constructor(arguments, **kwargs)
2111                constructor_wrapper.castxml_definition = member
2112                for hook in self._post_scan_hooks:
2113                    hook(self, member, constructor_wrapper)
2114
2115                if (len(arguments) == 1
2116                    and isinstance(arguments[0], class_wrapper.ThisClassRefParameter)):
2117                    have_copy_constructor = True
2118
2119            ## ------------ attribute --------------------
2120            elif isinstance(member, variable_t):
2121                if not member.name:
2122                    continue # anonymous structure
2123                if member.access_type == 'protected':
2124                    warnings.warn_explicit("%s: protected member variables not yet implemented "
2125                                           "by PyBindGen."
2126                                           % member,
2127                                           NotSupportedWarning, member.location.file_name, member.location.line)
2128                    continue
2129                if member.access_type == 'private':
2130                    continue
2131
2132                real_type = type_traits.remove_declarated(member.decl_type)
2133                if hasattr(real_type, 'name') and not real_type.name:
2134                    warnings.warn_explicit("Member variable %s of class %s will not be wrapped, "
2135                                           "because wrapping member variables of anonymous types "
2136                                           "is not yet supported by pybindgen"
2137                                           % (member.name, cls.partial_decl_string),
2138                                           NotSupportedWarning, member.location.file_name, member.location.line)
2139                    continue
2140
2141                return_type_spec = self.type_registry.lookup_return(member.decl_type, global_annotations)
2142
2143                ## pygen...
2144                if 'pygen_comment' in global_annotations:
2145                    pygen_sink.writeln('## ' + global_annotations['pygen_comment'])
2146                if member.type_qualifiers.has_static:
2147                    pygen_sink.writeln("cls.add_static_attribute(%r, %s, is_const=%r)" %
2148                                       (member.name, _pygen_retval(*return_type_spec),
2149                                        type_traits.is_const(member.decl_type)))
2150                else:
2151                    pygen_sink.writeln("cls.add_instance_attribute(%r, %s, is_const=%r)" %
2152                                       (member.name, _pygen_retval(*return_type_spec),
2153                                        type_traits.is_const(member.decl_type)))
2154
2155                ## convert the return value
2156                try:
2157                    return_type = ReturnValue.new(*return_type_spec[0], **return_type_spec[1])
2158                except (TypeLookupError, TypeConfigurationError) as ex:
2159                    warnings.warn_explicit("Return value '%s' error (used in %s): %r"
2160                                           % (member.decl_type.partial_decl_string, member, ex),
2161                                           WrapperWarning, member.location.file_name, member.location.line)
2162                    continue
2163
2164                if member.type_qualifiers.has_static:
2165                    class_wrapper.add_static_attribute(member.name, return_type,
2166                                                       is_const=type_traits.is_const(member.decl_type))
2167                else:
2168                    class_wrapper.add_instance_attribute(member.name, return_type,
2169                                                         is_const=type_traits.is_const(member.decl_type))
2170                ## TODO: invoke post_scan_hooks
2171            elif isinstance(member, calldef_members.destructor_t):
2172                pass
2173
2174        ## gccxml 0.9, unlike 0.7, does not explicitly report inheritted trivial constructors
2175        ## thankfully pygccxml comes to the rescue!
2176        if not have_trivial_constructor:
2177            if type_traits_classes.has_trivial_constructor(cls):
2178                class_wrapper.add_constructor([])
2179                pygen_sink.writeln("cls.add_constructor([])")
2180
2181        if not have_copy_constructor:
2182            try: # pygccxml > 0.9
2183                has_copy_constructor = type_traits_classes.has_copy_constructor(cls)
2184            except AttributeError: # pygccxml <= 0.9
2185                has_copy_constructor = type_traits.has_trivial_copy(cls)
2186            if has_copy_constructor:
2187                class_wrapper.add_copy_constructor()
2188                pygen_sink.writeln("cls.add_copy_constructor()")
2189
2190
2191    def _get_calldef_exceptions(self, calldef):
2192        retval = []
2193        for decl in calldef.exceptions:
2194            traits = ctypeparser.TypeTraits(normalize_name(decl.partial_decl_string))
2195            if traits.type_is_reference:
2196                name = str(traits.target)
2197            else:
2198                name = str(traits.ctype)
2199            exc = self.type_registry.root_module.get(name, None)
2200            if isinstance(exc, CppException):
2201                retval.append(exc)
2202            else:
2203                warnings.warn_explicit("Thrown exception '%s' was not previously detected as an exception class."
2204                                       " PyBindGen bug?"
2205                                       % (normalize_name(decl.partial_decl_string)),
2206                                       WrapperWarning, calldef.location.file_name, calldef.location.line)
2207        return retval
2208
2209    def _get_annotation_exceptions(self, annotation):
2210        retval = []
2211        for exc_name in annotation.split(','):
2212            traits = ctypeparser.TypeTraits(normalize_name(exc_name))
2213            if traits.type_is_reference:
2214                name = str(traits.target)
2215            else:
2216                name = str(traits.ctype)
2217            exc = self.type_registry.root_module.get(name, None)
2218            if isinstance(exc, CppException):
2219                retval.append(exc)
2220            else:
2221                raise ValueError("Thrown exception '%s' is not a CppException object: %r"
2222                                 % (normalize_name(name), exc))
2223        return retval
2224
2225    def scan_functions(self):
2226        self._stage = 'scan functions'
2227        assert self._types_scanned
2228        for pygen_sink in self._get_all_pygen_sinks():
2229            pygen_sink.writeln("def register_functions(root_module):")
2230            pygen_sink.indent()
2231            pygen_sink.writeln("module = root_module")
2232            if pygen_sink is self._get_main_pygen_sink() and isinstance(self._pygen, list):
2233                for section in self._pygen:
2234                    if section.name == '__main__':
2235                        continue
2236                    pygen_sink.writeln("root_module.begin_section(%r)" % section.name)
2237                    pygen_sink.writeln("%s.register_functions(root_module)" % section.name)
2238                    if section.local_customizations_module:
2239                        pygen_sink.writeln("\ntry:\n"
2240                                           "    import %s\n"
2241                                           "except ImportError:\n"
2242                                           "    pass\n"
2243                                           "else:\n"
2244                                           "    %s.register_functions(root_module)\n"
2245                                           % (section.local_customizations_module, section.local_customizations_module))
2246                    pygen_sink.writeln("root_module.end_section(%r)" % section.name)
2247        self._scan_namespace_functions(self.module, self.module_namespace)
2248
2249    def _scan_namespace_functions(self, module, module_namespace):
2250        root_module = module.get_root()
2251
2252        functions_to_scan = []
2253        for fun in module_namespace.free_functions(function=self.location_filter,
2254                                                   allow_empty=True, recursive=False):
2255            if fun.name.startswith('__'):
2256                continue
2257            functions_to_scan.append(fun)
2258
2259        functions_to_scan.sort(key=lambda c: (c.name, c.decl_string))
2260
2261        for fun in functions_to_scan:
2262            global_annotations, parameter_annotations = annotations_scanner.get_annotations(fun)
2263            for hook in self._pre_scan_hooks:
2264                hook(self, fun, global_annotations, parameter_annotations)
2265
2266            as_method = None
2267            of_class = None
2268            alt_name = None
2269            ignore = False
2270            kwargs = {}
2271
2272            for name, value in global_annotations.items():
2273                if name == 'as_method':
2274                    as_method = value
2275                elif name == 'of_class':
2276                    of_class = value
2277                elif name == 'name':
2278                    alt_name = value
2279                elif name == 'ignore':
2280                    ignore = True
2281                elif name == 'is_constructor_of':
2282                    pass
2283                elif name == 'pygen_comment':
2284                    pass
2285                elif name == 'template_instance_names':
2286                    pass
2287                elif name == 'unblock_threads':
2288                    kwargs['unblock_threads'] = annotations_scanner.parse_boolean(value)
2289                elif name == 'throw':
2290                    kwargs['throw'] = self._get_annotation_exceptions(value)
2291                else:
2292                    warnings.warn_explicit("Incorrect annotation %s=%s" % (name, value),
2293                                           AnnotationsWarning, fun.location.file_name, fun.location.line)
2294            if ignore:
2295                continue
2296
2297
2298            is_constructor_of = global_annotations.get("is_constructor_of", None)
2299            return_annotations = parameter_annotations.get('return', {})
2300            if is_constructor_of:
2301                return_annotations['caller_owns_return'] = 'true'
2302
2303            params_ok = True
2304            return_type_spec = self.type_registry.lookup_return(fun.return_type, return_annotations)
2305            try:
2306                return_type = ReturnValue.new(*return_type_spec[0], **return_type_spec[1])
2307            except (TypeLookupError, TypeConfigurationError) as ex:
2308                warnings.warn_explicit("Return value '%s' error (used in %s): %r"
2309                                       % (fun.return_type.partial_decl_string, fun, ex),
2310                                       WrapperWarning, fun.location.file_name, fun.location.line)
2311                params_ok = False
2312            except TypeError as ex:
2313                warnings.warn_explicit("Return value '%s' error (used in %s): %r"
2314                                       % (fun.return_type.partial_decl_string, fun, ex),
2315                                       WrapperWarning, fun.location.file_name, fun.location.line)
2316                raise
2317            argument_specs = []
2318            arguments = []
2319            for argnum, arg in enumerate(fun.arguments):
2320                annotations = parameter_annotations.get(arg.name, {})
2321                if argnum == 0 and as_method is not None \
2322                        and isinstance(arg.decl_type, cpptypes.pointer_t):
2323                    annotations.setdefault("transfer_ownership", "false")
2324                    annotations.setdefault("free_after_copy", "false")
2325
2326
2327                spec = self.type_registry.lookup_parameter(arg.decl_type, arg.name,
2328                                                           annotations,
2329                                                           default_value=arg.default_value)
2330                argument_specs.append(spec)
2331                try:
2332                    arguments.append(Parameter.new(*spec[0], **spec[1]))
2333                except (TypeLookupError, TypeConfigurationError) as ex:
2334                    warnings.warn_explicit("Parameter '%s %s' error (used in %s): %r"
2335                                           % (arg.decl_type.partial_decl_string, arg.name, fun, ex),
2336                                           WrapperWarning, fun.location.file_name, fun.location.line)
2337
2338                    params_ok = False
2339                except TypeError as ex:
2340                    warnings.warn_explicit("Parameter '%s %s' error (used in %s): %r"
2341                                           % (arg.decl_type.partial_decl_string, arg.name, fun, ex),
2342                                           WrapperWarning, fun.location.file_name, fun.location.line)
2343                    raise
2344
2345            throw = self._get_calldef_exceptions(fun)
2346            if throw:
2347                kwargs['throw'] = throw
2348
2349            arglist_repr = ("[" + ', '.join([_pygen_param(*arg)  for arg in argument_specs]) +  "]")
2350            retval_repr = _pygen_retval(*return_type_spec)
2351
2352            if as_method is not None:
2353                assert of_class is not None
2354                cpp_class = root_module[normalize_class_name(of_class, (self.module_namespace_name or '::'))]
2355
2356                pygen_sink = self._get_pygen_sink_for_definition(fun)
2357                if pygen_sink:
2358                    if 'pygen_comment' in global_annotations:
2359                        pygen_sink.writeln('## ' + global_annotations['pygen_comment'])
2360                    pygen_sink.writeln("root_module[%r].add_function_as_method(%s, custom_name=%r)" %
2361                                       (cpp_class.full_name,
2362                                        ", ".join([repr(fun.name), retval_repr, arglist_repr]),
2363                                        as_method))
2364                if params_ok:
2365                    function_wrapper = cpp_class.add_function_as_method(fun.name, return_type, arguments, custom_name=as_method)
2366                    function_wrapper.castxml_definition = fun
2367
2368                continue
2369
2370            if is_constructor_of is not None:
2371                #cpp_class = type_registry.find_class(is_constructor_of, (self.module_namespace_name or '::'))
2372                cpp_class = root_module[normalize_class_name(is_constructor_of, (self.module_namespace_name or '::'))]
2373
2374                pygen_sink = self._get_pygen_sink_for_definition(fun)
2375                if pygen_sink:
2376                    if 'pygen_comment' in global_annotations:
2377                        pygen_sink.writeln('## ' + global_annotations['pygen_comment'])
2378                    pygen_sink.writeln("root_module[%r].add_function_as_constructor(%s)" %
2379                                       (cpp_class.full_name,
2380                                        ", ".join([repr(fun.name), retval_repr, arglist_repr]),))
2381
2382                if params_ok:
2383                    function_wrapper = cpp_class.add_function_as_constructor(fun.name, return_type, arguments)
2384                    function_wrapper.castxml_definition = fun
2385
2386                continue
2387
2388            if check_template(demangle(fun.get_mangled_name()), fun.name):
2389                template_parameters = get_template_arg(demangle(fun.get_mangled_name()), fun.name)
2390                kwargs['template_parameters'] = template_parameters
2391                template_instance_names = global_annotations.get('template_instance_names', '')
2392                if template_instance_names:
2393                    for mapping in template_instance_names.split('|'):
2394                        type_names, name = mapping.split('=>')
2395                        instance_types = type_names.split(',')
2396                        if instance_types == template_parameters:
2397                            kwargs['custom_name'] = name
2398                            break
2399
2400            if alt_name:
2401                kwargs['custom_name'] = alt_name
2402
2403            if fun.attributes:
2404                if 'deprecated' in fun.attributes:
2405                    kwargs['deprecated'] = True
2406
2407            pygen_sink = self._get_pygen_sink_for_definition(fun)
2408            if pygen_sink:
2409                if 'pygen_comment' in global_annotations:
2410                    pygen_sink.writeln('## ' + global_annotations['pygen_comment'])
2411                kwargs_repr = _pygen_kwargs(kwargs)
2412                if kwargs_repr:
2413                    kwargs_repr[0] = "\n" + 20*' ' + kwargs_repr[0]
2414                pygen_sink.writeln("module.add_function(%s)" %
2415                                   (", ".join([repr(fun.name),
2416                                               "\n" + 20*' ' + retval_repr,
2417                                               "\n" + 20*' ' + arglist_repr]
2418                                              + kwargs_repr)))
2419
2420            if params_ok:
2421                func_wrapper = module.add_function(fun.name, return_type, arguments, **kwargs)
2422                func_wrapper.castxml_definition = fun
2423                for hook in self._post_scan_hooks:
2424                    hook(self, fun, func_wrapper)
2425
2426
2427        ## scan nested namespaces (mapped as python submodules)
2428        nested_namespaces = []
2429        for nested_namespace in module_namespace.namespaces(allow_empty=True, recursive=False):
2430            if nested_namespace.name.startswith('__'):
2431                continue
2432            nested_namespaces.append(nested_namespace)
2433
2434        nested_namespaces.sort(key=lambda c: c.decl_string)
2435
2436        for nested_namespace in nested_namespaces:
2437            nested_module = module.add_cpp_namespace(nested_namespace.name)
2438            nested_module_pygen_func = "register_functions_" + "_".join(nested_module.get_namespace_path())
2439            for pygen_sink in self._get_all_pygen_sinks():
2440                pygen_sink.writeln("%s(module.add_cpp_namespace(%r), root_module)" %
2441                                   (nested_module_pygen_func, nested_namespace.name))
2442
2443        for pygen_sink in self._get_all_pygen_sinks():
2444            pygen_sink.writeln("return")
2445            pygen_sink.unindent()
2446            pygen_sink.writeln()
2447
2448        nested_namespaces = []
2449        for nested_namespace in module_namespace.namespaces(allow_empty=True, recursive=False):
2450            if nested_namespace.name.startswith('__'):
2451                continue
2452            nested_namespaces.append(nested_namespace)
2453
2454        nested_namespaces.sort(key=lambda c: c.decl_string)
2455
2456        for nested_namespace in nested_namespaces:
2457            nested_module = module.get_submodule(nested_namespace.name)
2458            nested_module_pygen_func = "register_functions_" + "_".join(nested_module.get_namespace_path())
2459            for pygen_sink in self._get_all_pygen_sinks():
2460                pygen_sink.writeln("def %s(module, root_module):" % nested_module_pygen_func)
2461                pygen_sink.indent()
2462
2463            self._scan_namespace_functions(nested_module, nested_namespace)
2464
2465
2466def _test():
2467    module_parser = ModuleParser('foo', '::')
2468    module = module_parser.parse(sys.argv[1:])
2469    if 0:
2470        out = FileCodeSink(sys.stdout)
2471        module.generate(out)
2472
2473if __name__ == '__main__':
2474    _test()
2475