Package VisionEgg :: Module ParameterTypes |
[frames] | no frames]

# Source Code for Module VisionEgg.ParameterTypes

      1  # The Vision Egg: ParameterTypes
      2  #
      3  # Copyright (C) 2001-2003 Andrew Straw
      4  # Copyright (C) 2005,2008 California Insitute of Technology
      5  #
      6  # URL: <http://www.visionegg.org/>
      7  #
      8  # Distributed under the terms of the GNU Lesser General Public License
      9  # (LGPL). See LICENSE.TXT that came with this file.
     10
     11  """
     12  Type checking for the Vision Egg.
     13
     14  """
     15
     16  import VisionEgg
     17
     18  import types, warnings
     19  import numpy
     20  import numpy.oldnumeric as numpyNumeric
     21
     22  array_types = [numpy.ndarray]
     23  # Allow use of numarray and original Numeric Texture data without requiring either
     24  try:
     25      import numarray
     26      array_types.append( numarray.numarraycore.NumArray )
     27  except ImportError:
     28      pass
     29  try:
     30      import Numeric as orig_Numeric
     31      array_types.append( orig_Numeric.ArrayType )
     32  except ImportError:
     33      pass
     34
     35 -class ParameterTypeDef(object):
     36      """Base class for all parameter type definitions"""
     37 -    def verify(value):
     38          # override this method with type-checking code
     39          raise RuntimeError('must override base class method verify')
     40      verify = staticmethod(verify)
     41
     42 -def get_all_classes_list(klass):
     43      #assert(type(klass) == types.ClassType)
     44      result = [klass]
     45      for base_klass in klass.__bases__:
     46          result.extend(get_all_classes_list(base_klass))
     47      return result
     48
     49 -def is_parameter_type_def(item_type):
     50      if type(item_type) == types.ClassType:
     51          if Sequence in get_all_classes_list(item_type):
     52              raise TypeError("Sequence definition must be an instance (with a contained type).")
     53          else:
     54              return ParameterTypeDef in get_all_classes_list(item_type)
     55      elif isinstance(item_type,ParameterTypeDef):
     56          return True
     57      elif issubclass(item_type,ParameterTypeDef): # for new style classes
     58          return True
     59      elif item_type == types.NoneType:
     60          warnings.warn("types.NoneType will stop being a supported type "+\
     61                        "argument.  Please call "+\
     62                        "VisionEgg.ParameterTypes.get_type(None) to get the "+\
     63                        "supported value",DeprecationWarning,stacklevel=2)
     64          return True
     65      else:
     66          return False
     67
     68 -class AnyOf(ParameterTypeDef):
     69 -    def __init__(self,*item_types):
     70          for item_type in item_types:
     71              if not is_parameter_type_def(item_type):
     72                  raise TypeError("%s is not a valid type definition")
     73          self.item_types = item_types
     74 -    def __str__(self):
     75          return 'AnyOf(%s)'%(' or '.join(map(str,self.item_types)))
     76 -    def verify(self,is_any_of):
     77          for item_type in self.item_types:
     78              if item_type.verify(is_any_of):
     79                  return True
     80          return False
     81 -    def get_item_types(self):
     82          return self.item_types
     83
     84 -class NoneMC(type):
     85      """metaclass for NoneType"""
     86 -    def __str__(self):
     87          return 'None'
     88
     89 -class NoneType(ParameterTypeDef):
     90      __metaclass__ = NoneMC
     91 -    def verify(is_none):
     92          return is_none is None
     93      verify = staticmethod(verify)
     94
     95 -class BooleanMC(type):
     96      """metaclass for Boolean"""
     97 -    def __str__(self):
     98          return 'Boolean'
     99
    100 -class Boolean(ParameterTypeDef):
    101      __metaclass__ = BooleanMC
    102 -    def verify(is_boolean):
    103          if isinstance(is_boolean,(bool,int,numpy.integer)):
    104              return True
    105          else:
    106              return False
    107      verify = staticmethod(verify)
    108
    109 -class CallableMC(type):
    110      """metaclass for Callable"""
    111 -    def __str__(self):
    112          return 'Callable'
    113
    114 -class Callable(ParameterTypeDef):
    115      __metaclass__ = CallableMC
    116 -    def verify(is_callable):
    117          return callable(is_callable)
    118      verify = staticmethod(verify)
    119
    120 -class AnyClassMC(type):
    121      """metaclass for AnyClass"""
    122 -    def __str__(self):
    123          return 'AnyClass'
    124
    125 -class AnyClass(ParameterTypeDef):
    126      """parameter is a class"""
    127      __metaclass__ = AnyClassMC
    128 -    def verify(is_class):
    129          return type(is_class) == types.ClassType
    130      verify = staticmethod(verify)
    131
    132 -class SubClass(ParameterTypeDef):
    133      """parameter is derived from base_class"""
    134 -    def __init__(self,base_class):
    135          if type(base_class) != types.ClassType:
    136              raise TypeError("base_class must be ClassType")
    137          self.base_class = base_class
    138 -    def verify(self,is_class):
    139          if type(self.base_class) != types.ClassType:
    140              return False
    141          return self.base_class in get_all_classes_list(is_class)
    142 -    def __str__(self):
    143          return 'SubClass of %s'%str(self.base_class)
    144
    145 -class Instance(ParameterTypeDef):
    146      """parameter is an instance of class_type"""
    147 -    def __init__(self,class_type):
    148          if type(class_type) not in (types.ClassType, types.TypeType):
    149              raise TypeError("expected a class type")
    150          self.class_type = class_type
    151 -    def __str__(self):
    152          contained_string = str(self.class_type)
    153          return 'Instance of %s'%contained_string
    154 -    def verify(self,is_instance):
    155          return isinstance(is_instance,self.class_type)
    156
    157 -class IntegerMC(type):
    158      """metaclass for Integer"""
    159 -    def __str__(self):
    160          return 'Integer'
    161
    162 -class Integer(ParameterTypeDef):
    163      __metaclass__ = IntegerMC
    164 -    def verify(is_integer):
    165          return isinstance(is_integer,(int,numpy.integer))
    166      verify = staticmethod(verify)
    167
    168 -class UnsignedIntegerMC(IntegerMC):
    169      """metaclass for UnsignedInteger"""
    170 -    def __str__(self):
    171          return 'UnsignedInteger'
    172
    173 -class UnsignedInteger(Integer):
    174      __metaclass__ = UnsignedIntegerMC
    175 -    def verify(is_unsigned_integer):
    176          if not Integer.verify(is_unsigned_integer):
    177              return False
    178          return is_unsigned_integer >= 0
    179      verify = staticmethod(verify)
    180
    181 -class RealMC(type):
    182      """metaclass for Real"""
    183 -    def __str__(self):
    184          return 'Real'
    185
    186 -class Real(ParameterTypeDef):
    187      __metaclass__ = RealMC
    188 -    def verify(is_real):
    189          if isinstance(is_real, (int, float, numpy.floating, numpy.integer)):
    190              return True
    191          elif type(is_real) in array_types:
    192              # scalars can be Numeric arrays
    193              if len(is_real.shape)==0:
    194                  try:
    195                      float(is_real)
    196                      return True
    197                  except:
    198                      return False
    199          return False
    200      verify = staticmethod(verify)
    201
    202 -class Sequence(ParameterTypeDef):
    203      """A tuple, list or Numeric array"""
    204 -    def __init__(self,item_type):
    205          if not is_parameter_type_def(item_type):
    206              raise TypeError("%s is not a valid type definition"%item_type)
    207          self.item_type = item_type
    208 -    def __str__(self):
    209          contained_string = str(self.item_type)
    210          return 'Sequence of %s'%contained_string
    211 -    def verify(self,is_sequence):
    212          try:
    213              len(is_sequence)
    214          except TypeError:
    215              return False
    216          for i in xrange(len(is_sequence)):
    217              if not self.item_type.verify(is_sequence[i]):
    218                  return False
    219          return True
    220
    221 -class Sequence2(Sequence):
    222 -    def __str__(self):
    223          contained_string = str(self.item_type)
    224          return 'Sequence2 of %s'%contained_string
    225 -    def verify(self,is_sequence2):
    226          if not Sequence.verify(self,is_sequence2):
    227              return False
    228          if not len(is_sequence2) == 2:
    229              return False
    230          return True
    231
    232 -class Sequence3(Sequence):
    233 -    def __str__(self):
    234          contained_string = str(self.item_type)
    235          return 'Sequence3 of %s'%contained_string
    236 -    def verify(self,is_sequence3):
    237          if not Sequence.verify(self,is_sequence3):
    238              return False
    239          if not len(is_sequence3) == 3:
    240              return False
    241          return True
    242
    243 -class Sequence4(Sequence):
    244 -    def __str__(self):
    245          contained_string = str(self.item_type)
    246          return 'Sequence4 of %s'%contained_string
    247 -    def verify(self,is_sequence4):
    248          if not Sequence.verify(self,is_sequence4):
    249              return False
    250          if not len(is_sequence4) == 4:
    251              return False
    252          return True
    253
    254 -class Sequence4x4(Sequence4):
    255 -    def __str__(self):
    256          contained_string = str(self.item_type)
    257          return 'Sequence4x4 of %s'%contained_string
    258 -    def verify(self,is_sequence4x4):
    259          try:
    260              len(is_sequence4x4)
    261          except TypeError:
    262              return False
    263          if not len(is_sequence4x4) == 4:
    264              return False
    265          for i in range(4):
    266              if not Sequence4.verify(self,is_sequence4x4[i]):
    267                  return False
    268          return True
    269
    270 -class StringMC(type):
    271      """metaclass for String"""
    272 -    def __str__(self):
    273          return 'String'
    274
    275 -class String(ParameterTypeDef):
    276      __metaclass__ = StringMC
    277 -    def verify(is_string):
    278          if isinstance(is_string,str):
    279              return True
    280          else:
    281              return False
    282      verify = staticmethod(verify)
    283
    284 -class UnicodeMC(type):
    285      """metaclass for Unicode"""
    286 -    def __str__(self):
    287          return 'Unicode'
    288
    289 -class Unicode(ParameterTypeDef):
    290      __metaclass__ = UnicodeMC
    291 -    def verify(is_unicode):
    292          if isinstance(is_unicode,unicode):
    293              return True
    294          else:
    295              return False
    296      verify = staticmethod(verify)
    297
    298 -def get_type(value):
    299      """Take a value and return best guess of ParameterTypeDef it is."""
    300      py_type = type(value)
    301
    302      if isinstance(value,bool):
    303          return Boolean
    304      elif value is None:
    305          return NoneType
    306      elif isinstance(value,(int,numpy.integer)):
    307          if py_type >= 0:
    308              return UnsignedInteger
    309          else:
    310              return Integer
    311      elif isinstance(value,(float,numpy.floating)):
    312          return Real
    313      elif py_type == types.InstanceType:
    314          # hmm, impossible to figure out appropriate class of all possible base classes
    315          return Instance(value.__class__)
    316      elif callable(value):
    317          return Callable
    318      else:
    319          try:
    320              len(value)
    321          except TypeError:
    322              is_sequence = False
    323          else:
    324              is_sequence = True
    325          if is_sequence:
    326              if len(value) == 4:
    327                  # see if it's a 4x4 sequence
    328                  is_sequence4x4 = True
    329                  for i in range(4):
    330                      try:
    331                          len(value[i])
    332                      except TypeError:
    333                          is_sequence4x4 = False
    334                  if is_sequence4x4:
    335                      sequence4x4_type = get_type(value[0][0]) # XXX assume all same types
    336                      return Sequence4x4(sequence4x4_type)
    337              if type(value) == str:
    338                  return String
    339              lcd_type = get_type(value[0]) # lowest common denominator type
    340              for i in range(len(value)):
    341                  this_type = get_type(value[i])
    342                  if lcd_type != this_type:
    343                      # find lowest common denominator type
    344                      if lcd_type.verify( this_type ):
    345                          continue # covered by lowest common denominator
    346                      elif this_type.verify( lcd_type ):
    347                          lcd_type = this_type # new lowest common denominator
    348                      else:
    349                          lcd_type = AnyOf( lcd_type, this_type ) # make new lowest common denominator
    350              sequence_type = lcd_type
    351              if len(value) == 2:
    352                  return Sequence2(sequence_type)
    353              elif len(value) == 3:
    354                  return Sequence3(sequence_type)
    355              elif len(value) == 4:
    356                  return Sequence4(sequence_type)
    357              else:
    358                  return Sequence(sequence_type)
    359      # finally, one last check:
    360      if isinstance(value, object):
    361          # new style class
    362          # hmm, impossible to figure out appropriate class of all possible base classes
    363          return Instance(value.__class__)
    364      else:
    365          raise TypeError("Unable to determine type for '%s'"%value)
    366
    367 -def assert_type(check_type,require_type):
    368      if not is_parameter_type_def(check_type):
    369          raise ValueError("require a ParameterTypeDef as argument (not %s)"%check_type)
    370      if not is_parameter_type_def(require_type):
    371          raise ValueError("require a ParameterTypeDef as argument (not %s)"%require_type)
    372      if check_type == require_type:
    373          return
    374
    375      if check_type in (Integer,UnsignedInteger) and require_type == Boolean:
    376          return # let integers pass as booleans
    377      # XXX doesn't check if Instance is actually instance of proper class
    378      if isinstance(check_type,ParameterTypeDef):
    379          check_class = check_type.__class__
    380      else:
    381          check_class = check_type
    382
    383      if isinstance(require_type,ParameterTypeDef):
    384          if isinstance(require_type,AnyOf):
    385              passed = False
    386              for ok_type in require_type.get_item_types():
    387                  try:
    388                      assert_type(check_type, ok_type )
    389                      return # it's ok
    390                  except:
    391                      pass
    392          else:
    393              require_class = require_type.__class__
    394      else:
    395          require_class = require_type
    396
    397      if require_class in get_all_classes_list(check_class):
    398          return
    399
    400      if issubclass(require_class,Real):
    401          if issubclass(check_class,Boolean):
    402              return
    403          elif issubclass(check_class,Integer):
    404              return
    405
    406      if issubclass(require_class,Integer):
    407          if issubclass(check_class,Boolean):
    408              return
    409
    410      raise TypeError("%s not of type %s"%(check_type,require_type))
    411