pyplusplus.decl_wrappers.properties

Package pyplusplus :: Package decl_wrappers :: Module properties |

# Source Code for Module pyplusplus.decl_wrappers.properties

      1  # Copyright 2004-2008 Roman Yakovenko.
      2  # Distributed under the Boost Software License, Version 1.0. (See
      3  # accompanying file LICENSE_1_0.txt or copy at
      4  # http://www.boost.org/LICENSE_1_0.txt)
      5
      6  "defines property_t helper class"
      7
      8  import algorithm
      9  from pyplusplus import messages
     10  from pyplusplus import _logging_
     11  from pygccxml import declarations
     12
     13 -class property_t( object ):
     14      """This class describes a "property".
     15
     16      It keeps
     17      """
     18 -    def __init__( self, name, fget, fset=None, doc=None, is_static=False ):
     19          self._name = name
     20          self._fget = fget
     21          self._fset = fset
     22          self._doc = doc
     23          self._is_static = is_static
     24
     25      @property
     26 -    def name( self ):
     27          return self._name
     28
     29      @property
     30 -    def fget( self ):
     31          return self._fget
     32
     33      @property
     34 -    def fset( self ):
     35          return self._fset
     36
     37 -    def _get_doc( self ):
     38          if None is self._doc:
     39              doc = ['get']
     40              if self.fset:
     41                  doc.append( r'\\set' )
     42              doc.append( r' property, built on top of \"%s\"' % self.fget )
     43              if self.fset:
     44                  doc.append( r' and \"%s\"' % self.fset )
     45              self._doc = '"%s"' % ''.join( doc )
     46          return self._doc
     47 -    def _set_doc( self, doc ):
     48          self._doc = doc
     49      doc = property( _get_doc, _set_doc )
     50
     51      @property
     52 -    def is_static( self ):
     53          return self._is_static
     54
     55 -    def __str__( self ):
     56          desc = []
     57          desc.append( 'fget=%s' % declarations.full_name( self.fget ) )
     58          if self.fset:
     59              desc.append( ', ' )
     60              desc.append( 'fset=%s' % declarations.full_name( self.fset ) )
     61          return 'property "%s"[%s]' % ( self.name, ''.join( desc ) )
     62
     63
     64 -class property_recognizer_i(object):
     65 -    def __init__( self ):
     66          object.__init__( self )
     67
     68 -    def create_property( self, fget, fset ):
     69          raise NotImplementedError()
     70
     71 -    def create_read_only_property( sefl, fget ):
     72          raise NotImplementedError()
     73
     74 -    def is_accessor( self, mem_fun ):
     75          if mem_fun.ignore:
     76              return False
     77          if mem_fun.access_type != 'public':
     78              return False
     79          if mem_fun.has_static:
     80              return False #TODO: should be supported
     81          if mem_fun.virtuality == declarations.VIRTUALITY_TYPES.PURE_VIRTUAL:
     82              return False
     83          return True
     84
     85 -    def is_getter( self, mem_fun ):
     86          if mem_fun.arguments:
     87              return False
     88          if declarations.is_void( mem_fun.return_type ):
     89              return False
     90          if not mem_fun.has_const:
     91              return False
     92          if mem_fun.overloads:
     93              return False
     94          return True
     95
     96 -    def is_setter( self, mem_fun ):
     97          if len( mem_fun.arguments ) != 1:
     98              return False
     99          if not declarations.is_void( mem_fun.return_type ):
    100              return False
    101          if mem_fun.has_const:
    102              return False
    103          if mem_fun.overloads:
    104              return False
    105          return True
    106
    107 -    def __get_accessors( self, mem_funs ):
    108          getters = []
    109          setters = []
    110          for mem_fun in mem_funs:
    111              if not self.is_accessor( mem_fun ):
    112                  continue
    113              elif self.is_getter( mem_fun ):
    114                  getters.append( mem_fun )
    115              elif self.is_setter( mem_fun ):
    116                  setters.append( mem_fun )
    117              else:
    118                  continue
    119          return ( getters, setters )
    120
    121 -    def class_accessors( self, cls ):
    122          return self.__get_accessors( cls.mem_funs( recursive=False, allow_empty=True ) )
    123
    124 -    def base_classes( self, cls ):
    125          base_clss = []
    126          for hierarchy_info in cls.recursive_bases:
    127              if hierarchy_info.related_class.ignore: #don't scan excluded classes
    128                  continue
    129              if 'public' != hierarchy_info.access_type: #don't scan non public hierarchy
    130                  continue
    131              base_clss.append( hierarchy_info.related_class )
    132          return base_clss
    133
    134 -    def inherited_accessors( self, cls ):
    135          mem_funs = []
    136          map( lambda base_cls: mem_funs.extend( base_cls.mem_funs( recursive=False, allow_empty=True ) )
    137               , self.base_classes( cls ) )
    138          return self.__get_accessors( mem_funs )
    139
    140
    141 -class name_based_recognizer_t( property_recognizer_i ):
    142 -    def __init__( self ):
    143          property_recognizer_i.__init__( self )
    144
    145 -    def prefixes( self ):
    146          return [  (  'is', 'set' )
    147                  , ( 'get', 'set' )
    148                  , ( 'has', 'set' )
    149                  , (    '', 'set' ) ]
    150
    151 -    def check_prefix( self, name, prefix ):
    152          if not name.startswith( prefix ):
    153              return False
    154          if len( name ) < len( prefix ):
    155              return False
    156          return True
    157
    158 -    def check_name_compatibility( self, gname, sname, gprefix, sprefix ):
    159          if not self.check_prefix( gname, gprefix ):
    160              return False
    161          if not self.check_prefix( sname, sprefix ):
    162              return False
    163          if gname[ len( gprefix ): ] != sname[ len( sprefix ): ]:
    164              return False
    165          return True
    166
    167 -    def make_std_convention( self, gprefix, sprefix ):
    168          convert = lambda x: x + '_'
    169          if gprefix:
    170              gprefix = convert( gprefix )
    171          return ( gprefix, convert( sprefix ) )
    172
    173 -    def make_u_camel_convention( self, gprefix, sprefix ):
    174          convert = lambda x: x[0].upper() + x[1:]
    175          if gprefix:
    176              gprefix = convert( gprefix )
    177          return ( gprefix, convert( sprefix ) )
    178
    179 -    def make_l_camel_convention( self, gprefix, sprefix ):
    180          convert = lambda x: x[0].lower() + x[1:]
    181          if gprefix:
    182              gprefix = convert( gprefix )
    183          return ( gprefix, convert( sprefix ) )
    184
    185 -    def find_out_prefixes( self, gname, sname ):
    186          convention_makers = [
    187              self.make_std_convention #longest first
    188            , self.make_u_camel_convention
    189            , self.make_l_camel_convention ]
    190
    191          for convention_maker in convention_makers:
    192              for g, s in self.prefixes():
    193                  gc, sc = convention_maker( g, s )
    194                  if self.check_name_compatibility( gname, sname, gc, sc ):
    195                      return ( gc, sc )
    196          return None
    197
    198 -    def find_out_ro_prefixes( self, gname ):
    199          convention_makers = [
    200              self.make_std_convention #longest first
    201            , self.make_u_camel_convention
    202            , self.make_l_camel_convention ]
    203
    204          for convention_maker in convention_makers:
    205              for g, unused in self.prefixes():
    206                  if not g:
    207                      continue
    208                  gc, unused = convention_maker( g, 'set' )
    209                  if self.check_prefix( gname, gc ):
    210                      return gc
    211          return ''
    212
    213 -    def check_type_compatibility( self, fget, fset ):
    214          #algorithms allows "const" differences between types
    215          t1 = fget.return_type
    216          t2 = fset.arguments[0].type
    217
    218          if declarations.is_same( t1, t2 ):
    219              return True
    220          elif declarations.is_pointer( t1 ) and declarations.is_pointer( t2 ):
    221              t1 = declarations.remove_cv( declarations.remove_pointer( t1 ) )
    222              t2 = declarations.remove_cv( declarations.remove_pointer( t2 ) )
    223              return declarations.is_same( t1, t2 )
    224          elif declarations.is_reference( t1 ) and declarations.is_reference( t2 ):
    225              t1 = declarations.remove_cv( declarations.remove_reference( t1 ) )
    226              t2 = declarations.remove_cv( declarations.remove_reference( t2 ) )
    227              return declarations.is_same( t1, t2 )
    228          else:
    229              return False
    230
    231 -    def find_out_property_name( self, fget, prefix ):
    232          if fget.name == prefix:
    233              #use class name for property name
    234              return algorithm.create_valid_name( fget.parent.name )
    235          else:
    236              return fget.name[len(prefix):]
    237
    238 -    def create_property( self, fget, fset ):
    239          if not self.check_type_compatibility( fget, fset ):
    240              return None
    241          found = self.find_out_prefixes( fget.name, fset.name )
    242          if not found:
    243              return None
    244          return property_t( self.find_out_property_name( fget, found[0] ), fget, fset )
    245
    246 -    def create_read_only_property( self, fget ):
    247          found = self.find_out_ro_prefixes( fget.name )
    248          if None is found:
    249              return None
    250          else:
    251              return property_t( self.find_out_property_name( fget, found ), fget )
    252
    253 -class properties_finder_t:
    254 -    def __init__( self, cls, recognizer=None, exclude_accessors=False ):
    255          self.cls = cls
    256          if None is recognizer:
    257              recognizer = name_based_recognizer_t()
    258          self.recognizer = recognizer
    259          self.exclude_accessors = exclude_accessors
    260          self.getters, self.setters = recognizer.class_accessors( cls )
    261          self.inherited_getters, self.inherited_setters = recognizer.inherited_accessors( cls )
    262
    263 -    def __report_illegal_property( self, property_ ):
    264          logger = _logging_.loggers.declarations
    265          if not messages.filter_disabled_msgs([messages.W1041], property_.fget.parent.disabled_messages ):
    266              return #user disabled property warning
    267          logger.warn( "%s;%s" % ( property_.fget.parent, messages.W1041 % property_ ) )
    268
    269 -    def __is_legal_property( self, property_ ):
    270          """property is legal if it does not hide other declarations"""
    271          def is_relevant( decl ):
    272              irrelevant_classes = ( declarations.constructor_t
    273                                     , declarations.destructor_t
    274                                     , declarations.typedef_t )
    275
    276              if isinstance( decl, irrelevant_classes ):
    277                  return False
    278              if decl.ignore:
    279                  return False
    280              if decl.alias != property_.name:
    281                  return False
    282              if self.exclude_accessors \
    283                 and ( decl is property_.fget or decl is property_.fset ):
    284                  return False
    285              return True
    286
    287          relevant_decls = []
    288          relevant_classes = [self.cls] + self.recognizer.base_classes( self.cls )
    289          for cls in relevant_classes:
    290              relevant_decls.extend( cls.decls( is_relevant, recursive=False, allow_empty=True ) )
    291          return not bool( relevant_decls )
    292
    293 -    def find_properties( self, getters, setters, used_getters, used_setters ):
    294          properties = []
    295          for fget in getters:
    296              if fget in used_getters:
    297                  continue
    298              for fset in setters:
    299                  if fset in used_setters:
    300                      continue
    301                  property_ = self.recognizer.create_property( fget, fset )
    302                  if property_:
    303                      if self.__is_legal_property( property_ ):
    304                          used_getters.add( fget )
    305                          used_setters.add( fset )
    306                          properties.append( property_ )
    307                          break
    308                      else:
    309                          self.__report_illegal_property( property_ )
    310          return properties
    311
    312 -    def __call__( self ):
    313          used_getters = set()
    314          used_setters = set()
    315          properties = []
    316          #this get, this set
    317          properties.extend(
    318              self.find_properties( self.getters, self.setters, used_getters, used_setters ) )
    319          #this get, base set
    320          properties.extend(
    321              self.find_properties( self.getters, self.inherited_setters, used_getters, used_setters ) )
    322          #base get, this set
    323          properties.extend(
    324              self.find_properties( self.inherited_getters, self.setters, used_getters, used_setters ) )
    325
    326          for fget in self.getters:
    327              if fget in used_getters:
    328                  continue
    329              property_ = self.recognizer.create_read_only_property( fget )
    330              if property_:
    331                  if self.__is_legal_property( property_ ):
    332                      used_getters.add( fget )
    333                      properties.append( property_ )
    334                  else:
    335                      self.__report_illegal_property( property_ )
    336
    337          if self.exclude_accessors:
    338              map( lambda accessor: accessor.exclude(), used_getters )
    339              map( lambda accessor: accessor.exclude(), used_setters )
    340
    341          return properties
    342
    343 -def find_properties( cls, recognizer=None, exclude_accessors=False ):
    344      pf = properties_finder_t( cls, recognizer, exclude_accessors )
    345      properties = pf()
    346      return properties
    347