Package pyplusplus :: Package module_creator :: Module dependencies_manager |

# Source Code for Module pyplusplus.module_creator.dependencies_manager

      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 class, which informs user about used, but unexposed declarations"""
      7
      8  import os
      9  from pyplusplus import utils
     10  from pyplusplus import messages
     11  from pygccxml import declarations
     12  from pyplusplus import decl_wrappers
     13
     14 -class duplicated_names_reporter_t(object):
     15 -    def __init__( self, decls, value_getter, msg ):
     16          self.decls = decls
     17          self.get_value = value_getter
     18          self.msg = msg
     19
     20 -    def __select( self ):
     21          duplicated = {}
     22          for decl in self.decls:
     23              value = self.get_value( decl )
     24              if not duplicated.has_key( value ):
     25                  duplicated[ value ] = set()
     26              duplicated[ value ].add( decl )
     27          result = duplicated.copy()
     28          for value, buggy_decls in duplicated.items():
     29              if 1 == len( buggy_decls ):
     30                  del result[ value ]
     31          return result
     32
     33 -    def __report_single( self, control_decl, duplicated, logger ):
     34          value = self.get_value( control_decl )
     35          if value not in duplicated:
     36              return
     37          buggy_decls = duplicated[value].copy()
     38          buggy_decls.remove( control_decl )
     39          warning = self.msg % ( value, os.linesep.join( map( str, buggy_decls ) ) )
     40          logger.warn( "%s;%s" % ( str( control_decl ), warning ) )
     41
     42 -    def report( self, logger ):
     43          duplicated = self.__select()
     44          for decl in self.decls:
     45              self.__report_single( decl, duplicated, logger )
     46
     47  duplicated_aliases_reporter \
     48      = lambda decls: duplicated_names_reporter_t( decls, lambda d: d.alias, messages.W1047 )
     49
     50  duplicated_wrapper_aliases_reporter \
     51      = lambda decls: duplicated_names_reporter_t( decls, lambda d: d.wrapper_alias, messages.W1065 )
     52
     53
     54 -class manager_t( object ):
     55 -    def __init__( self, logger ):
     56          object.__init__( self )
     57          self.__exported_decls = []
     58          self.__logger = logger
     59
     60 -    def add_exported( self, decl ):
     61          self.__exported_decls.append( decl )
     62
     63 -    def __is_std_decl( self, decl ):
     64          #Every class under std should be exported by Boost.Python and\\or Py++
     65          #Also this is not the case right now, I prefer to hide the warnings
     66          dpath = declarations.declaration_path( decl )
     67          if len( dpath ) < 3:
     68              return False
     69          if dpath[1] != 'std':
     70              return False
     71          if decl.name.startswith( 'pair<' ):
     72              #special case
     73              return False
     74          return True
     75
     76 -    def __build_dependencies( self, decl ):
     77          if self.__is_std_decl( decl ):
     78              #TODO add element_type to the list of dependencies
     79              return [] #std declarations should be exported by Py++!
     80          if decl.already_exposed:
     81              return []
     82          dependencies = decl.i_depend_on_them(recursive=False)
     83
     84          if isinstance( decl, declarations.class_t ):
     85              dependencies = filter( lambda d: d.access_type != declarations.ACCESS_TYPES.PRIVATE
     86                                     , dependencies )
     87
     88          return dependencies
     89
     90 -    def __has_unexposed_dependency( self, exported_ids, depend_on_decl, dependency ):
     91          sptr_traits = declarations.smart_pointer_traits
     92
     93          if None is depend_on_decl:
     94              return
     95
     96          if self.__is_std_decl( depend_on_decl ):
     97              return
     98
     99          if sptr_traits.is_smart_pointer( depend_on_decl ):
    100              try:
    101                  value_type = sptr_traits.value_type( depend_on_decl )
    102                  return self.__has_unexposed_dependency( exported_ids, value_type, dependency )
    103              except RuntimeError:
    104                  pass
    105
    106          if isinstance( depend_on_decl, decl_wrappers.decl_wrapper_t ):
    107              if depend_on_decl.already_exposed:
    108                  return
    109              if isinstance( depend_on_decl, declarations.class_types ):
    110                  if depend_on_decl.opaque:
    111                      return
    112                  if dependency.hint == "base class":
    113                      return #base class for some class don't have to be exported
    114              if isinstance( depend_on_decl, declarations.variable_t ):
    115                  if not decl.expose_value:
    116                      return
    117
    118          if isinstance( dependency.decl, declarations.variable_t ):
    119              #the only dependency of the variable is its type
    120              if not dependency.decl.expose_value:
    121                  return
    122
    123          if dependency.hint == "return type":
    124              #in this case we don't check, the return type but the function
    125              if isinstance( dependency.decl, declarations.calldef_t ):
    126                  if dependency.decl.return_type and dependency.decl.call_policies \
    127                     and decl_wrappers.is_return_opaque_pointer_policy( dependency.decl.call_policies ):
    128                     return
    129
    130          return id( depend_on_decl ) not in exported_ids
    131
    132 -    def __find_out_used_but_not_exported( self ):
    133          used_not_exported = []
    134          exported_ids = set( map( lambda d: id( d ), self.__exported_decls ) )
    135          for decl in self.__exported_decls:
    136              for dependency in self.__build_dependencies( decl ):
    137                  depend_on_decl = dependency.find_out_depend_on_declaration()
    138                  if self.__has_unexposed_dependency( exported_ids, depend_on_decl, dependency ):
    139                      if messages.filter_disabled_msgs([messages.W1040], depend_on_decl.disabled_messages ):
    140                          #need to report dependency errors
    141                          used_not_exported.append(dependency)
    142          return used_not_exported
    143
    144 -    def __group_by_unexposed( self, dependencies ):
    145          groups = {}
    146          for dependency in dependencies:
    147              depend_on_decl = dependency.find_out_depend_on_declaration()
    148              if not groups.has_key( id( depend_on_decl ) ):
    149                  groups[ id( depend_on_decl ) ] = []
    150              groups[ id( depend_on_decl ) ].append( dependency )
    151          return groups
    152
    153 -    def __create_dependencies_msg( self, dependencies ):
    154          depend_on_decl = dependencies[0].find_out_depend_on_declaration()
    155          decls = []
    156          for dependency in dependencies:
    157              decls.append( os.linesep + ' ' + str( dependency.declaration ) )
    158          return "%s;%s" % ( depend_on_decl, messages.W1040 % ''.join( decls ) )
    159
    160 -    def __report_duplicated_aliases( self ):
    161          decls = filter( lambda decl: isinstance( decl, declarations.class_types ) \
    162                                       and isinstance( decl.parent, declarations.namespace_t )
    163                          , self.__exported_decls )
    164
    165          dar = duplicated_aliases_reporter( decls )
    166          dar.report( self.__logger )
    167
    168          classes = filter( lambda c: isinstance( c, declarations.class_t ), decls )
    169          query = lambda decl: isinstance( decl, declarations.class_types ) \
    170                               and decl.ignore == False \
    171                               and decl._already_exposed == False
    172
    173          for cls in classes:
    174              internal_decls = cls.decls( query, recursive=False, allow_empty=True)
    175              dar = duplicated_aliases_reporter( internal_decls )
    176              dar.report( self.__logger )
    177
    178 -    def __report_duplicated_wrapper_aliases( self ):
    179          decls = filter( lambda decl: isinstance( decl, declarations.class_t ) \
    180                                       and isinstance( decl.parent, declarations.namespace_t )
    181                          , self.__exported_decls )
    182
    183          dwar = duplicated_wrapper_aliases_reporter( decls )
    184          dwar.report( self.__logger )
    185
    186          query = lambda decl: decl.ignore == False and decl._already_exposed == False
    187
    188          for cls in decls:
    189              internal_decls = cls.classes( query, recursive=False, allow_empty=True)
    190              dwar = duplicated_wrapper_aliases_reporter( internal_decls )
    191              dwar.report( self.__logger )
    192
    193 -    def inform_user( self ):
    194          used_not_exported_decls = self.__find_out_used_but_not_exported()
    195          groups = self.__group_by_unexposed( used_not_exported_decls )
    196          for group in groups.itervalues():
    197              self.__logger.warn( self.__create_dependencies_msg( group ) )
    198          self.__report_duplicated_aliases()
    199          self.__report_duplicated_wrapper_aliases()
    200