1   
  2   
  3   
  4   
  5   
  6  """defines class that configure class definition and class declaration exposing""" 
  7   
  8  import os 
  9  import user_text 
 10  import properties 
 11  import decl_wrapper 
 12  import scopedef_wrapper 
 13  from pyplusplus import messages 
 14  from pygccxml import declarations 
 15  import indexing_suite1 as isuite1 
 16  import indexing_suite2 as isuite2 
 17   
 18  ACCESS_TYPES = declarations.ACCESS_TYPES 
 19  VIRTUALITY_TYPES = declarations.VIRTUALITY_TYPES 
 27   
 28   
 29  always_expose_using_scope_documentation = \ 
 30  """boolean, configures how Py++ should generate code for class. 
 31  Py can generate code using IDL like syntax: 
 32   
 33      class_< ... >( ... ) 
 34          .def( ... ); 
 35   
 36  Or it can generate code using more complex form: 
 37   
 38      typedef bp::class_< my_class > my_class_exposer_t; 
 39      my_class_exposer_t my_class_exposer = my_class_exposer_t( "my_class" ); 
 40      boost::python::scope my_class_scope( my_class_exposer ); 
 41      my_class_exposer.def( ... ); 
 42   
 43  Also, the second way is much longer, it solves few problems: 
 44   
 45      - you can not expose enums and internal classes defined within the class using first method 
 46      - you will get much better compilation errors 
 47      - the code looks like regular C++ code after all :-) 
 48   
 49  By default, this property is set to False. Also, Py++ knows pretty well 
 50  when it have to ignore this property and generate right code 
 51  """ 
 54      """defines few properties that are common to 
 55      L{class declaration<pygccxml.declarations.class_declaration_t>} and 
 56      L{definition<pygccxml.declarations.class_t>} classes 
 57      """ 
 59          object.__init__( self ) 
 60          self._always_expose_using_scope = None 
 61          self._indexing_suite = None 
 62          self._equality_comparable = None 
 63          self._less_than_comparable = None 
 64          self._isuite_version = 1 
 65          self._opaque = False 
  66   
 68          return self._isuite_version 
  70          assert version in ( 1, 2 ) 
 71          if self._isuite_version != version: 
 72              self._isuite_version = version 
 73              self._indexing_suite = None 
  74      indexing_suite_version = property( _get_indexing_suite_version, _set_indexing_suite_version 
 75                                         , doc="indexing suite version") 
 76   
 77      @property 
 79          """reference to indexing suite configuration class. 
 80   
 81          If the class is not STD container, this property will contain None" 
 82          """ 
 83          if self._indexing_suite is None: 
 84              if self.container_traits: 
 85                  if self._isuite_version == 1: 
 86                      self._indexing_suite = isuite1.indexing_suite1_t( self ) 
 87                  else: 
 88                      self._indexing_suite = isuite2.indexing_suite2_t( self ) 
 89          return self._indexing_suite 
  90   
 97   
108   
110          self._always_expose_using_scope = value 
 111      always_expose_using_scope = property( _get_always_expose_using_scope, _set_always_expose_using_scope 
112                                            , doc="please see L{class_wrapper.always_expose_using_scope_documentation} variable for documentation."  ) 
113   
115          if None is self._equality_comparable: 
116              self._equality_comparable = declarations.has_public_equal( self ) 
117          return self._equality_comparable 
 118   
120          self._equality_comparable = value 
 121   
122      equality_comparable = property( _get_equality_comparable, _set_equality_comparable 
123                                      , doc="indicates existence of public operator=" \ 
124                                           +"Default value is calculated, based on information presented in the declarations tree" ) 
125   
127          if None is self._less_than_comparable: 
128              self._less_than_comparable = declarations.has_public_less( self ) 
129          return self._less_than_comparable 
 130   
132          self._less_than_comparable = value 
 133   
134      less_than_comparable = property( _get_less_than_comparable, _set_less_than_comparable 
135                                       , doc="indicates existence of public operator<. " \ 
136                                            +"Default value is calculated, based on information presented in the declarations tree" ) 
137   
140   
144   
145      opaque = property( _get_opaque, _set_opaque 
146                        , doc="If True, Py++ will treat return types and arguments T* as opaque types." \ 
147                              +"Thus it will be able to generate code, that uses " \ 
148                              +" BOOST_PYTHON_OPAQUE_SPECIALIZED_TYPE_ID macro in a right places." ) 
149   
150      @property 
152          return self.alias + '_exposer' 
  153   
154   
155 -class class_declaration_t( class_common_details_t 
156                             , decl_wrapper.decl_wrapper_t 
157                             , declarations.class_declaration_t ): 
 158 -    def __init__(self, *arguments, **keywords): 
 163 -class class_t( class_common_details_t 
164                 , scopedef_wrapper.scopedef_t 
165                 , declarations.class_t): 
 166   
171   
172      FAKE_CONSTRUCTOR_TYPES = ( declarations.member_function_t, declarations.free_function_t ) 
173      FAKE_CONSTRUCTOR_TYPE_NAMES = 'member and free functions' 
174   
175 -    def __init__(self, *arguments, **keywords): 
 176          class_common_details_t.__init__( self ) 
177          declarations.class_t.__init__(self, *arguments, **keywords ) 
178          scopedef_wrapper.scopedef_t.__init__( self ) 
179   
180          self._redefine_operators = False 
181          self._held_type = None 
182          self._noncopyable = None 
183          self._wrapper_alias = None 
184          self._registration_code_head = [] 
185          self._registration_code_tail = [] 
186          self._declaration_code = [] 
187          self._wrapper_code = [] 
188          self._destructor_code = [] 
189          self._exception_translation_code = None 
190          self._properties = [] 
191          self._redefined_funcs = None 
192          self._require_self_reference  = False 
193          self._exposed_class_type = self.EXPOSED_CLASS_TYPE.DECLARED 
194          self._expose_this = None 
195          self._expose_sizeof = None 
196          self._fake_constructors = [] 
197          self._no_init = None 
 198   
199      @property 
201          """list of fake constructors""" 
202          return self._fake_constructors 
 203   
205          """f - reference to a calldef_t object or list of them 
206   
207          boost::python::make_constructor allows to register a C++ function, as a 
208          class constructor. 
209          """ 
210          if isinstance( f, declarations.calldef_t ): 
211              self._fake_constructors.add( f ) 
212          else: 
213              self._fake_constructors.extend( f ) 
 214   
216          return self._redefine_operators 
 218          self._redefine_operators = new_value 
 219      redefine_operators = property( _get_redefine_operators, _set_redefine_operators 
220                                     , doc="tells Py++ to redefine operators from base class in this class, False by default") 
221   
223          return self._exposed_class_type 
 227      exposed_class_type = property( _get_exposed_class_type, _set_exposed_class_type 
228                            , doc="set this value to CLASS_TYPE.WRAPPER, if you need to transfer ownership of" \ 
229                                  "polymorphic class" ) 
230   
232          return self._held_type 
 235      held_type = property( _get_held_type, _set_held_type 
236                            , doc="string, this property tells Py++ what HeldType this class has" \ 
237                                 +"Default value is calculated, based on information presented in exposed declarations" ) 
238   
245      noncopyable = property( _get_noncopyable, _set_noncopyable 
246                              , doc="True if the class is noncopyable, False otherwies" \ 
247                                   +"Default value is calculated, based on information presented in the declarations tree" ) 
248   
250          if None is self._wrapper_alias: 
251              self._wrapper_alias = self._generate_valid_name(self.partial_name) + "_wrapper" 
252          return self._wrapper_alias 
 254          self._wrapper_alias = walias 
 255      wrapper_alias = property( _get_wrapper_alias, _set_wrapper_alias 
256                                , doc="class-wrapper name") 
257   
258      @property 
260          """ 
261          List of strings, that contains valid C++ code, that will be added to 
262          the class declaration section 
263          """ 
264          return self._declaration_code 
 265   
266      @property 
268          """ 
269          List of strings, that contains valid C++ code, that will be added to 
270          the head of the class registration section 
271          """ 
272          return self._registration_code_head 
 273   
274      @property 
276          """ 
277          List of strings, that contains valid C++ code, that will be added to 
278          the tail of the class registration section 
279          """ 
280          return self._registration_code_tail 
 281   
282      @property 
289   
290      @property 
292          """ 
293          List of strings, that contains valid C++ code, that will be added to 
294          the class wrapper. 
295          """ 
296          return self._wrapper_code 
 297   
299          c = self.find_trivial_constructor() 
300          if c: 
301              return c.body 
302          else: 
303              return ''         
 305          c = self.find_trivial_constructor() 
306          if c: 
307              c.body = body 
 308      null_constructor_body = property( _get_null_constructor_body, _set_null_constructor_body 
309                                        , doc="null constructor code, that will be added as is to the null constructor of class-wrapper" ) 
310   
312          c = self.find_copy_constructor() 
313          if c: 
314              return c.body 
315          else: 
316              return ''         
 317   
319          c = self.find_copy_constructor() 
320          if c: 
321              c.body = body 
 322      copy_constructor_body = property( _get_copy_constructor_body, _set_copy_constructor_body 
323                                        , doc="copy constructor code, that will be added as is to the copy constructor of class-wrapper") 
324   
325      @property 
327          """list of code to be added to wrapper destructor""" 
328          return self._destructor_code 
 329   
331          """adds code to the class-wrapper destructor""" 
332          self._destructor_code.append( code ) 
 333   
334      @property 
336          """exception argument name for translate exception function 
337   
338          If you don't understand what this argument is, please take a look on 
339          Boost.Python documentation: http://www.boost.org/libs/python/doc/v2/exception_translator.html 
340          """ 
341          return 'exc' 
 342   
344          return self._exception_translation_code 
 346          self._exception_translation_code = code 
 347      exception_translation_code = property( _get_exception_translation_code, _set_exception_translation_code 
348                                             , doc="C++ exception to Python exception translation code" \ 
349                                                  +"\nExample: PyErr_SetString(PyExc_RuntimeError, exc.what()); " \ 
350                                                  +"\nPy++ will generate the rest of the code." \ 
351                                                  +"\nPay attention: the exception variable name is exc." ) 
352   
354          """registers exception translation to string 
355   
356          @param python_exception_type: Python exception type, for example PyExc_RuntimeError 
357          @type python_exception_type: str 
358   
359          @param to_string: C++ expression that extracts information from exception. 
360                            The type of expression should be char*. 
361          @type to_string: str 
362          """ 
363           
364           
365           
366           
367          code = "PyErr_SetString( %(exception_type)s, %(to_string)s ); " \ 
368                 % { 'exception_type' : python_exception_type, 'to_string' : to_string } 
369          self.exception_translation_code = code 
 370   
374   
388   
389       
390      add_code = add_registration_code 
391   
395   
396 -    def set_constructors_body( self, body ): 
 397          """Sets the body for all constructors""" 
398          constrs = self.constructors(allow_empty=True, recursive=False) 
399          if constrs: 
400              constrs.body = body 
401          self.null_constructor_body = body 
402          self.copy_constructor_body = body 
 403   
421   
450           
451           
452          members = filter( is_exportable, members ) 
453          sorted_members = members 
454          if sort: 
455              sorted_members = sort( members ) 
456          return sorted_members 
 457   
458      @property 
460          """list of properties""" 
461          return self._properties 
 462   
464          """adds new property to the class 
465   
466          @param name: name of the property 
467          @type name: str 
468   
469          @param fget: reference to the class member function 
470          @param fset: reference to the class member function, could be None 
471          @param doc: documentation string 
472          """ 
473          self._properties.append( properties.property_t( name, fget, fset, doc ) ) 
 474   
475 -    def add_properties( self, recognizer=None, exclude_accessors=False ): 
 478   
482   
484          """returns list of member functions that should be defined in class wrapper 
485   
486          It comes useful in 3 tier hierarchy: 
487          struct base{ 
488              virtual void do_nothing() = 0; 
489          }; 
490   
491          struct derived{ 
492              virtual void do_something() = 0; 
493          }; 
494   
495          struct concrete{ 
496              virtual void do_nothing(){} 
497              virtual void do_something(){} 
498          }; 
499   
500          derived_wrapper should define do_nothing function, otherwise the generated 
501          code will not compile 
502          """ 
503   
504          if isinstance( self._redefined_funcs, list ): 
505              return self._redefined_funcs 
506   
507          all_included = declarations.custom_matcher_t( lambda decl: decl.ignore == False and decl.exportable ) 
508          all_protected = declarations.access_type_matcher_t( 'protected' ) & all_included 
509          all_pure_virtual = declarations.virtuality_type_matcher_t( VIRTUALITY_TYPES.PURE_VIRTUAL ) 
510          all_virtual = declarations.virtuality_type_matcher_t( VIRTUALITY_TYPES.VIRTUAL ) \ 
511                        & ( declarations.access_type_matcher_t( 'public' ) \ 
512                            | declarations.access_type_matcher_t( 'protected' )) 
513          all_not_pure_virtual = ~all_pure_virtual 
514   
515          query = all_protected | all_pure_virtual 
516          mf_query = query | all_virtual 
517          relevant_opers = declarations.custom_matcher_t( lambda decl: decl.symbol in ('()', '[]') ) 
518          funcs = [] 
519          defined_funcs = [] 
520   
521          for base in self.recursive_bases: 
522              if base.access == ACCESS_TYPES.PRIVATE: 
523                  continue 
524              base_cls = base.related_class 
525   
526              funcs.extend( base_cls.member_functions( mf_query, recursive=False, allow_empty=True ) ) 
527              funcs.extend( base_cls.member_operators( relevant_opers & query, recursive=False, allow_empty=True ) ) 
528   
529              defined_funcs.extend( base_cls.member_functions( all_not_pure_virtual, recursive=False, allow_empty=True ) ) 
530              defined_funcs.extend( base_cls.member_operators( all_not_pure_virtual & relevant_opers, recursive=False, allow_empty=True ) ) 
531   
532          not_reimplemented_funcs = set() 
533          is_same_function = declarations.is_same_function 
534          for f in funcs: 
535              cls_fs = self.calldefs( name=f.name, recursive=False, allow_empty=True ) 
536              for cls_f in cls_fs: 
537                  if is_same_function( f, cls_f ): 
538                      break 
539              else: 
540                   
541                  for f_impl in not_reimplemented_funcs: 
542                      if is_same_function( f, f_impl ): 
543                          if declarations.is_base_and_derived( f_impl.parent, f.parent ): 
544                               
545                              not_reimplemented_funcs.remove( f_impl ) 
546                              not_reimplemented_funcs.add( f ) 
547                          break 
548                  else: 
549                       
550                      if f.virtuality != VIRTUALITY_TYPES.PURE_VIRTUAL: 
551                          not_reimplemented_funcs.add( f ) 
552                      else: 
553                          for f_defined in defined_funcs: 
554                              if is_same_function( f, f_defined ): 
555                                  break 
556                          else: 
557                              not_reimplemented_funcs.add( f ) 
558          functions = filter( lambda f: ( False == f.ignore and True == f.exportable ) 
559                                        or all_pure_virtual( f ) 
560                              , list( not_reimplemented_funcs ) ) 
561   
562   
563           
564           
565           
566   
567          def buggy_bpl_filter( f ): 
568              if f.parent is self: 
569                  return False 
570              if f.access_type != ACCESS_TYPES.PUBLIC: 
571                  return False 
572              if f.virtuality != VIRTUALITY_TYPES.NOT_VIRTUAL: 
573                  return False 
574               
575              this_funs = self.decls( name=f.name 
576                                      , decl_type=declarations.calldef_t 
577                                      , recursive=False 
578                                      , allow_empty=True ) 
579              for this_f in this_funs: 
580                  if is_same_function( this_f, f ): 
581                       
582                      return False 
583              else: 
584                  return True 
 585   
586          tmp = {}  
587          for redefined_f in functions: 
588               
589              for rfo in redefined_f.overloads: 
590                  if id(rfo) in tmp: 
591                      continue 
592                  if buggy_bpl_filter( rfo ): 
593                      tmp[ id(rfo) ] = rfo 
594          functions.extend( tmp.values() ) 
595   
596          functions.sort( cmp=lambda f1, f2: cmp( ( f1.name, f1.location.as_tuple() ) 
597                                                  , ( f2.name, f2.location.as_tuple() ) ) ) 
598   
599          self._redefined_funcs = functions 
600          return self._redefined_funcs 
601   
640   
652   
662           
663          if self.member_operators( is_assign, allow_empty=True, recursive=False ): 
664              return impl_details.GUESS_VALUES.ALWAYS_TRUE 
665          return super(class_t, self).guess_always_expose_using_scope_value() 
666   
668          return self._require_self_reference 
 671      require_self_reference = property( _get_require_self_reference, _set_require_self_reference 
672                       , doc="boolean, if True the first argument to the constructor will be reference to self object" ) 
673   
675          return self._expose_this 
 677          self._expose_this = new_value 
 678      expose_this = property( _get_expose_this, _set_expose_this 
679                             , doc="boolean, if True an object address( this pointer ) will be exposed to Python as integer.") 
680   
682          return self._expose_sizeof 
 684          self._expose_sizeof = new_value 
 685      expose_sizeof = property( _get_expose_sizeof, _set_expose_sizeof 
686                                , doc="boolean, if True the sizeof(obj) will be exposed to Python as integer.") 
687   
688      @property 
690          """returns True, if during exposing this class, new scope will be created 
691   
692          For example, anonymous structs will be exposed in a parent scope. 
693          """ 
694          if not self.name: 
695              return False 
696          elif self.class_type == declarations.CLASS_TYPES.UNION: 
697              return False 
698          else: 
699              return True 
 700   
731          self._no_init = value 
 732           
733      no_init = property( _get_no_init, _set_no_init 
734                          , doc="If True, class will be registered with 'boost::python::no_init'" ) 
735