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