"Fossies" - the Fresh Open Source Software Archive

Member "muscle/python3/message.py" (28 Nov 2019, 24364 Bytes) of package /linux/privat/muscle7.52.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "message.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 7.51_vs_7.52.

    1 """
    2 Python implementation of the MUSCLE Message class.
    3 Messages Flatten()'d by this class produce data buffers that are compatible with the MUSCLE standard.
    4 """
    5 
    6 __author__    = "Jeremy Friesner (jaf@meyersound.com)"
    7 __version__   = "$Revision: 1.18 $"
    8 __date__      = "$Date: 2005/01/01 01:03:21 $"
    9 __copyright__ = "Copyright (c) 2000-2013 Meyer Sound Laboratories Inc"
   10 __license__   = "See enclosed LICENSE.TXT file"
   11 
   12 import struct
   13 import types
   14 import array
   15 import sys
   16 import io
   17 
   18 # Standard MUSCLE field type-constants
   19 B_ANY_TYPE     = 1095653716 # 'ANYT',  // wild card
   20 B_BOOL_TYPE    = 1112493900 # 'BOOL',
   21 B_DOUBLE_TYPE  = 1145195589 # 'DBLE',
   22 B_FLOAT_TYPE   = 1179406164 # 'FLOT',
   23 B_INT64_TYPE   = 1280069191 # 'LLNG',
   24 B_INT32_TYPE   = 1280265799 # 'LONG',
   25 B_INT16_TYPE   = 1397248596 # 'SHRT',
   26 B_INT8_TYPE    = 1113150533 # 'BYTE',
   27 B_MESSAGE_TYPE = 1297303367 # 'MSGG',
   28 B_POINTER_TYPE = 1347310674 # 'PNTR',
   29 B_POINT_TYPE   = 1112559188 # 'BPNT',
   30 B_RECT_TYPE    = 1380270932 # 'RECT',
   31 B_STRING_TYPE  = 1129534546 # 'CSTR',
   32 B_OBJECT_TYPE  = 1330664530 # 'OPTR',  // used for flattened objects
   33 B_RAW_TYPE     = 1380013908 # 'RAWT',  // used for raw byte arrays
   34 
   35 CURRENT_PROTOCOL_VERSION = 1347235888 # 'PM00' -- our magic number
   36 
   37 _dataNeedsSwap = (sys.byteorder != 'little')  # MUSCLE's serialized-bytes-protocol expected little-endian data
   38 
   39 def GetHumanReadableTypeString(t):
   40    """Given a B_*_TYPE value, returns a human-readable string showing its bytes"""
   41    ret = ""
   42    for dummy in range(4):
   43       nextByte = t%256
   44       ret = chr(nextByte) + ret
   45       t = t//256
   46    return ret
   47 
   48 class Message:
   49    """Python implementation of the MUSCLE Message data-storage class.
   50 
   51    This class can hold various types of data, and be flattened out into a
   52    buffer of bytes that is compatible with the MUSCLE flattened-data standard.
   53    """
   54    def __init__(self, what=0):
   55       self.what   = what
   56       self.__fields = {}
   57 
   58    def Clear(self):
   59       """Removes all fields from this Message and sets the what code to zero"""
   60       self.what = 0
   61       self.__fields = {}
   62 
   63    def GetFieldNames(self):
   64       """Returns the list of field names currently in this Message"""
   65       return list(self.__fields.keys())
   66 
   67    def HasFieldName(self, fieldName, fieldTypeCode=B_ANY_TYPE):
   68       """Returns 1 if the given fieldName exists and is of the given type, or 0 otherwise."""
   69       if self.GetFieldContents(fieldName, fieldTypeCode) is not None:
   70          return 1
   71       else:
   72          return 0
   73 
   74    def PutFieldContents(self, fieldName, fieldTypeCode, fieldContents):
   75       """Adds (or replaces) a new field into this Message.
   76          
   77       (fieldName) should be a string representing the unique name of the field.
   78       (fieldTypeCode) should be a numeric type code for the field (usually B_*_TYPE).
   79       (fieldContents) should be the field's contents (either an item or a list or array of items)
   80       Returns None.
   81       """
   82       if isinstance(fieldContents, (list, array.array)):
   83          self.__fields[fieldName] = (fieldTypeCode, fieldContents)
   84       else:
   85          self.__fields[fieldName] = (fieldTypeCode, [fieldContents])
   86 
   87    def GetFieldContents(self, fieldName, fieldTypeCode=B_ANY_TYPE, defaultValue=None):
   88       """Returns the contents of an existing field in this message (if any).
   89 
   90       (fieldName) should be a string representing the unique name of the field.
   91       (fieldTypeCode) may specify the only type code we want.  If not specified,
   92                       the default value, B_ANY_TYPE, means that any field found
   93                       under the given field name will be returned, regardless of type.
   94       (defaultValue) is the value that should be returned if the requested data is
   95                      not available.  Default value of this parameter is None.
   96       Returns the contents of the field, or None if the field doesn't exist or
   97       is of the wrong type.
   98       """
   99       if fieldName in self.__fields:
  100          ret = self.__fields[fieldName]
  101          if fieldTypeCode != B_ANY_TYPE and fieldTypeCode != ret[0]:
  102             return defaultValue # oops!  typecode mismatch
  103          else: 
  104             return ret[1]       # return just the data portion
  105       else:
  106          return defaultValue
  107       
  108    def GetFieldItem(self, fieldName, fieldTypeCode=B_ANY_TYPE, index=0):
  109       """Returns the (index)th item of an existing field in this message (if any).
  110 
  111       (fieldName) should be a string representing the unique name of the field.
  112       (fieldTypeCode) may specify the only type code we want.  If not specified,
  113                       the default value, B_ANY_TYPE, means that any field found
  114                       under the given field name will be returned, regardless of type.
  115       (index) specifies which item from the given field's list we want.  
  116               Defaults to zero (the first item in the list).
  117       Returns the (index)th item of the given field's contents, or None if 
  118       the field doesn't exist or is of the wrong type, or if the index isn't
  119       a valid one for that list.
  120       """
  121       ret = self.GetFieldContents(fieldName, fieldTypeCode)
  122       if ret is not None:
  123          num = len(ret)
  124          if index < -num or index >= num:
  125             ret = None
  126          else:
  127             ret = ret[index]
  128       return ret
  129 
  130    def GetFieldType(self, fieldName):
  131       """Returns the type code of the given field, or None if the field doesn't exist.
  132 
  133       (fieldName) should be a string representing the unique name of the field.
  134       """
  135       if fieldName in self.__fields:
  136          return self.__fields[fieldName][0]
  137       else:
  138          return None
  139 
  140    def PrintToStream(self, maxRecurseLevel = 2147483648, linePrefix = ""):
  141       """Dumps our state to stdout.  Handy for debugging purposes"""
  142       print(self.ToString(maxRecurseLevel, linePrefix))
  143 
  144    def __str__(self): 
  145       return self.ToString()
  146 
  147    def ToString(self, maxRecurseLevel = 2147483648, linePrefix = ""):
  148       """Returns our state as a string, up to the specified recursion level."""
  149       s = "%sMessage: what=%i FlattenedSize=%i\n" % (linePrefix, self.what, self.FlattenedSize())
  150       for x in list(self.__fields.keys()):
  151          ft = self.GetFieldType(x)
  152          s = s + "%s [ %s : %s : %s ]\n" % (linePrefix, x, GetHumanReadableTypeString(ft), self.GetFieldContents(x))
  153          if (ft == B_MESSAGE_TYPE) and (maxRecurseLevel > 0):
  154             subMsgs = self.GetMessages(x)
  155             subPrefix = linePrefix + "   "
  156             for sm in subMsgs:
  157                s = s + sm.ToString(maxRecurseLevel-1, subPrefix)
  158       return s
  159 
  160    def FlattenedSize(self):
  161       """Returns the number of bytes that this Message would take up if flattened"""
  162 
  163       # Header is 12 bytes: protocol_version(4) + num_entries(4) + what(4)
  164       ret = 3*4  
  165 
  166       # Now calculate the space for each field
  167       fields = self.GetFieldNames()
  168       for fieldName in fields:
  169          ret += 4+len(fieldName)+1+4+4  # namelen(4), name(n), NUL(1), type(4), fieldBytes(4)
  170          fieldContents = self.GetFieldContents(fieldName)
  171          fieldType     = self.GetFieldType(fieldName)
  172          ret += self.GetFieldContentsLength(fieldType, fieldContents)
  173       return ret
  174 
  175    def Unflatten(self, inFile):
  176       """Reads in the new contents of this Message from the given file object."""
  177       global _dataNeedsSwap
  178       self.Clear()
  179       protocolVersion, self.what, numFields = struct.unpack("<3L", inFile.read(3*4))
  180       if protocolVersion != CURRENT_PROTOCOL_VERSION:
  181          raise IOError("Bad flattened-Message Protocol version ").with_traceback(protocolVersion)
  182       for dummy in range(numFields):
  183          fieldName = inFile.read(struct.unpack("<L", inFile.read(4))[0]-1).decode()
  184          inFile.read(1)  # throw away the NUL terminator byte, we don't need it
  185          fieldTypeCode, fieldDataLength = struct.unpack("<2L", inFile.read(2*4))
  186          if fieldTypeCode == B_BOOL_TYPE:
  187             fieldContents = array.array('b')
  188             fieldContents.fromstring(inFile.read(fieldDataLength))
  189          elif fieldTypeCode == B_DOUBLE_TYPE:
  190             fieldContents = array.array('d')
  191             fieldContents.fromstring(inFile.read(fieldDataLength))
  192          elif fieldTypeCode == B_FLOAT_TYPE:
  193             fieldContents = array.array('f')
  194             fieldContents.fromstring(inFile.read(fieldDataLength))
  195          elif fieldTypeCode == B_INT32_TYPE: 
  196             fieldContents = array.array('i')
  197             fieldContents.fromstring(inFile.read(fieldDataLength))
  198          elif fieldTypeCode == B_INT16_TYPE:
  199             fieldContents = array.array('h')
  200             fieldContents.fromstring(inFile.read(fieldDataLength))
  201          elif fieldTypeCode == B_INT8_TYPE:
  202             fieldContents = array.array('b')
  203             fieldContents.fromstring(inFile.read(fieldDataLength))
  204          elif fieldTypeCode == B_INT64_TYPE:
  205             fieldContents = array.array('q')
  206             fieldContents.fromstring(inFile.read(fieldDataLength))
  207          elif fieldTypeCode == B_MESSAGE_TYPE:
  208             fieldContents = []
  209             while fieldDataLength > 0:
  210                subMessageLength = struct.unpack("<L", inFile.read(4))[0]
  211                subMsg = Message()
  212                subMsg.Unflatten(inFile)
  213                fieldContents.append(subMsg)
  214                fieldDataLength = fieldDataLength-(subMessageLength+4)
  215          elif fieldTypeCode == B_POINT_TYPE:
  216             fieldContents = []
  217             for dummy in range(fieldDataLength//8):
  218                fieldContents.append(struct.unpack("<2f", inFile.read(8)))
  219          elif fieldTypeCode == B_RECT_TYPE:
  220             fieldContents = []
  221             for dummy in range(fieldDataLength//16):
  222                fieldContents.append(struct.unpack("<4f", inFile.read(16)))
  223          elif fieldTypeCode == B_STRING_TYPE:
  224             fieldContents = []
  225             numItems = struct.unpack("<L", inFile.read(4))[0]
  226             for dummy in range(numItems):
  227                fieldContents.append(inFile.read(struct.unpack("<L", inFile.read(4))[0]-1).decode())
  228                inFile.read(1)  # throw away the NUL byte, we don't need it
  229          else:
  230             fieldContents = []
  231             numItems = struct.unpack("<L", inFile.read(4))[0]
  232             for dummy in range(numItems):
  233                fieldContents.append(inFile.read(struct.unpack("<L", inFile.read(4))[0]))
  234 
  235          if _dataNeedsSwap and isinstance(fieldContents, array.array):
  236             fieldContents.byteswap()
  237 
  238          self.PutFieldContents(fieldName, fieldTypeCode, fieldContents)
  239           
  240    def Flatten(self, outFile):
  241       """Writes the state of this Message out to the given file object, in the standard platform-neutral flattened represenation."""
  242       global _dataNeedsSwap
  243       outFile.write(struct.pack("<3L", CURRENT_PROTOCOL_VERSION, self.what, len(self.__fields)))
  244       for fieldName in list(self.__fields.keys()):
  245          outFile.write(struct.pack("<L", len(fieldName)+1))
  246          outFile.write(fieldName.encode())
  247          outFile.write(bytes([0]))
  248          fieldContents = self.GetFieldContents(fieldName)
  249          fieldType = self.GetFieldType(fieldName)
  250          outFile.write(struct.pack("<2L", fieldType, self.GetFieldContentsLength(fieldType, fieldContents)))
  251 
  252          # Convert to array form and byte swap, if necessary
  253          isFieldContentsArray = isinstance(fieldContents, array.array)
  254          if _dataNeedsSwap or (not isFieldContentsArray):
  255             wasFieldContentsArray = isFieldContentsArray
  256             isFieldContentsArray = True
  257             if fieldType == B_BOOL_TYPE:
  258                fieldContents = array.array('b', fieldContents)
  259             elif fieldType == B_DOUBLE_TYPE:
  260                fieldContents = array.array('d', fieldContents)
  261             elif fieldType == B_FLOAT_TYPE:
  262                fieldContents = array.array('f', fieldContents)
  263             elif fieldType == B_INT32_TYPE:
  264                fieldContents = array.array('i', fieldContents)
  265             elif (fieldType == B_INT64_TYPE):
  266                fieldContents = array.array('q', fieldContents)
  267             elif fieldType == B_INT16_TYPE:
  268                fieldContents = array.array('h', fieldContents)
  269             elif fieldType == B_INT8_TYPE:
  270                fieldContents = array.array('b', fieldContents)
  271             else:
  272                isFieldContentsArray = wasFieldContentsArray  # roll back!
  273 
  274             if _dataNeedsSwap and isFieldContentsArray:
  275                fieldContents.byteswap()
  276 
  277          # Add the actual data for this field's contents
  278          if isFieldContentsArray:
  279             if fieldType == B_BOOL_TYPE or fieldType == B_DOUBLE_TYPE or fieldType == B_FLOAT_TYPE or fieldType == B_INT32_TYPE or fieldType == B_INT16_TYPE or fieldType == B_INT8_TYPE or fieldType == B_INT64_TYPE:
  280                outFile.write(bytes(fieldContents))
  281             else:
  282                raise ValueError("Array fieldContents found for non-Array type in field ").with_traceback(fieldName)
  283          else:
  284             if (fieldType == B_INT64_TYPE):
  285                for fieldItem in fieldContents:
  286                   outFile.write(struct.pack("<q", fieldItem))
  287             elif fieldType == B_MESSAGE_TYPE:
  288                for fieldItem in fieldContents:
  289                   outFile.write(struct.pack("<L", fieldItem.FlattenedSize())) 
  290                   fieldItem.Flatten(outFile)
  291             elif fieldType == B_POINT_TYPE:
  292                for fieldItem in fieldContents:
  293                   outFile.write(struct.pack("<2f", fieldItem[0], fieldItem[1]))
  294             elif fieldType == B_RECT_TYPE:
  295                for fieldItem in fieldContents:
  296                   outFile.write(struct.pack("<4f", fieldItem[0], fieldItem[1], fieldItem[2], fieldItem[3]))
  297             elif fieldType == B_STRING_TYPE:
  298                outFile.write(struct.pack("<L", len(fieldContents)))
  299                for fieldItem in fieldContents:
  300                   outFile.write(struct.pack("<L", len(fieldItem)+1))
  301                   outFile.write(fieldItem.encode())
  302                   outFile.write(bytes([0]))
  303             else:
  304                outFile.write(struct.pack("<L", len(fieldContents)))
  305                for fieldItem in fieldContents:
  306                   outFile.write(struct.pack("<L", len(fieldItem))) 
  307                   outFile.write(fieldItem)
  308    
  309    def GetFlattenedBuffer(self):
  310       """Convenience method:  returns a binary buffer that is the platform-neutral flattened representation of this Message."""
  311       f = io.BytesIO()
  312       self.Flatten(f)
  313       return f.getvalue()
  314 
  315    def SetFromFlattenedBuffer(self, buf):
  316       """Convenience method:  sets the state of this Message from the specified binary buffer."""
  317       self.Unflatten(io.BytesIO(buf))
  318 
  319    def GetFieldContentsLength(self, fieldType, fieldContents):
  320       """Returns the number of bytes it will take to flatten a field's contents.  Used internally."""
  321       ret = 0
  322       itemCount = len(fieldContents)
  323       if fieldType == B_BOOL_TYPE:
  324          ret += itemCount*1
  325       elif fieldType == B_DOUBLE_TYPE:
  326          ret += itemCount*8
  327       elif fieldType == B_FLOAT_TYPE:
  328          ret += itemCount*4
  329       elif fieldType == B_INT64_TYPE:
  330          ret += itemCount*8
  331       elif fieldType == B_INT32_TYPE:
  332          ret += itemCount*4
  333       elif fieldType == B_INT16_TYPE:
  334          ret += itemCount*2
  335       elif fieldType == B_INT8_TYPE:
  336          ret += itemCount*1
  337       elif fieldType == B_MESSAGE_TYPE:
  338          ret += itemCount*4  # item length fields (4 each) (no num_items field for this type!)
  339          for item in fieldContents:
  340             ret += item.FlattenedSize()
  341       elif fieldType == B_POINT_TYPE:
  342          ret += itemCount*8   # 2 floats per point
  343       elif fieldType == B_RECT_TYPE:
  344          ret += itemCount*16  # 4 floats per rect
  345       elif fieldType == B_STRING_TYPE:
  346          ret += 4  # num_strings(4)
  347          for item in fieldContents:
  348             ret += 4+len(item)+1  # string_length(4), string(n), NUL(1)
  349       else:
  350          ret += 4  # num_bufs(4)
  351          for item in fieldContents:
  352             ret += 4+len(item)
  353       return ret
  354 
  355    def PutString(self, fieldName, fieldContents):
  356       """Convenience method; identical to PutFieldContents(fieldName, B_STRING_TYPE, fieldContents)"""
  357       return self.PutFieldContents(fieldName, B_STRING_TYPE, fieldContents)
  358 
  359    def PutInt8(self, fieldName, fieldContents):
  360       """Convenience method; identical to PutFieldContents(fieldName, B_INT8_TYPE, fieldContents)"""
  361       return self.PutFieldContents(fieldName, B_INT8_TYPE, fieldContents)
  362 
  363    def PutInt16(self, fieldName, fieldContents):
  364       """Convenience method; identical to PutFieldContents(fieldName, B_INT16_TYPE, fieldContents)"""
  365       return self.PutFieldContents(fieldName, B_INT16_TYPE, fieldContents)
  366 
  367    def PutInt32(self, fieldName, fieldContents):
  368       """Convenience method; identical to PutFieldContents(fieldName, B_INT32_TYPE, fieldContents)"""
  369       return self.PutFieldContents(fieldName, B_INT32_TYPE, fieldContents)
  370 
  371    def PutInt64(self, fieldName, fieldContents):
  372       """Convenience method; identical to PutFieldContents(fieldName, B_INT64_TYPE, fieldContents)"""
  373       return self.PutFieldContents(fieldName, B_INT64_TYPE, fieldContents)
  374 
  375    def PutBool(self, fieldName, fieldContents):
  376       """Convenience method; identical to PutFieldContents(fieldName, B_BOOL_TYPE, fieldContents)"""
  377       return self.PutFieldContents(fieldName, B_BOOL_TYPE, fieldContents)
  378 
  379    def PutFloat(self, fieldName, fieldContents):
  380       """Convenience method; identical to PutFieldContents(fieldName, B_FLOAT_TYPE, fieldContents)"""
  381       return self.PutFieldContents(fieldName, B_FLOAT_TYPE, fieldContents)
  382 
  383    def PutDouble(self, fieldName, fieldContents):
  384       """Convenience method; identical to PutFieldContents(fieldName, B_DOUBLE_TYPE, fieldContents)"""
  385       return self.PutFieldContents(fieldName, B_DOUBLE_TYPE, fieldContents)
  386 
  387    def PutMessage(self, fieldName, fieldContents):
  388       """Convenience method; identical to PutFieldContents(fieldName, B_MESSAGE_TYPE, fieldContents)"""
  389       return self.PutFieldContents(fieldName, B_MESSAGE_TYPE, fieldContents)
  390 
  391    def PutPoint(self, fieldName, fieldContents):
  392       """Convenience method; identical to PutFieldContents(fieldName, B_POINT_TYPE, fieldContents)"""
  393       return self.PutFieldContents(fieldName, B_POINT_TYPE, fieldContents)
  394 
  395    def PutRect(self, fieldName, fieldContents):
  396       """Convenience method; identical to PutFieldContents(fieldName, B_RECT_TYPE, fieldContents)"""
  397       return self.PutFieldContents(fieldName, B_RECT_TYPE, fieldContents)
  398 
  399    def RemoveName(self, fieldName):
  400       """Removes the given field from the Message, if it exists."""
  401       if fieldName in self.__fields:
  402          del self.__fields[fieldName]
  403 
  404    def GetStrings(self, fieldName):
  405       """Convenience method; returns a list of all strings under the given name, or [] if there are none."""
  406       return self.GetFieldContents(fieldName, B_STRING_TYPE, [])
  407 
  408    def GetInt8s(self, fieldName):
  409       """Convenience method; returns a list of all int8s under the given name, or [] if there are none."""
  410       return self.GetFieldContents(fieldName, B_INT8_TYPE, [])
  411 
  412    def GetInt16s(self, fieldName):
  413       """Convenience method; returns a list of all int16s under the given name, or [] if there are none."""
  414       return self.GetFieldContents(fieldName, B_INT16_TYPE, [])
  415 
  416    def GetInt32s(self, fieldName):
  417       """Convenience method; returns a list of all int32s under the given name, or [] if there are none."""
  418       return self.GetFieldContents(fieldName, B_INT32_TYPE, [])
  419 
  420    def GetInt64s(self, fieldName):
  421       """Convenience method; returns a list of all int64s under the given name, or [] if there are none."""
  422       return self.GetFieldContents(fieldName, B_INT64_TYPE, [])
  423 
  424    def GetBools(self, fieldName):
  425       """Convenience method; returns a list of all bools under the given name, or [] if there are none."""
  426       return self.GetFieldContents(fieldName, B_BOOL_TYPE, [])
  427 
  428    def GetFloats(self, fieldName):
  429       """Convenience method; returns a list of all floats under the given name, or [] if there are none."""
  430       return self.GetFieldContents(fieldName, B_FLOAT_TYPE, [])
  431 
  432    def GetDoubles(self, fieldName):
  433       """Convenience method; returns a list of all doubles under the given name, or [] if there are none."""
  434       return self.GetFieldContents(fieldName, B_DOUBLE_TYPE, [])
  435 
  436    def GetMessages(self, fieldName):
  437       """Convenience method; returns a list of all messages under the given name, or [] if there are none."""
  438       return self.GetFieldContents(fieldName, B_MESSAGE_TYPE, [])
  439 
  440    def GetPoints(self, fieldName):
  441       """Convenience method; returns a list of all points under the given name, or [] if there are none."""
  442       return self.GetFieldContents(fieldName, B_POINT_TYPE, [])
  443 
  444    def GetRects(self, fieldName):
  445       """Convenience method; returns a list of all rects under the given name, or [] if there are none."""
  446       return self.GetFieldContents(fieldName, B_RECT_TYPE, [])
  447 
  448    def GetString(self, fieldName, index=0):
  449       """Convenience method; returns the (index)th String item under (fieldName), or None."""
  450       return self.GetFieldItem(fieldName, B_STRING_TYPE, index)
  451 
  452    def GetInt8(self, fieldName, index=0):
  453       """Convenience method; returns the (index)th Int8 item under (fieldName), or None."""
  454       return self.GetFieldItem(fieldName, B_INT8_TYPE, index)
  455 
  456    def GetInt16(self, fieldName, index=0):
  457       """Convenience method; returns the (index)th Int16 item under (fieldName), or None."""
  458       return self.GetFieldItem(fieldName, B_INT16_TYPE, index)
  459 
  460    def GetInt32(self, fieldName, index=0):
  461       """Convenience method; returns the (index)th Int32 item under (fieldName), or None."""
  462       return self.GetFieldItem(fieldName, B_INT32_TYPE, index)
  463 
  464    def GetInt64(self, fieldName, index=0):
  465       """Convenience method; returns the (index)th Int64 item under (fieldName), or None."""
  466       return self.GetFieldItem(fieldName, B_INT64_TYPE, index)
  467 
  468    def GetBool(self, fieldName, index=0):
  469       """Convenience method; returns the (index)th Bool item under (fieldName), or None."""
  470       return self.GetFieldItem(fieldName, B_BOOL_TYPE, index)
  471 
  472    def GetFloat(self, fieldName, index=0):
  473       """Convenience method; returns the (index)th Float item under (fieldName), or None."""
  474       return self.GetFieldItem(fieldName, B_FLOAT_TYPE, index)
  475 
  476    def GetDouble(self, fieldName, index=0):
  477       """Convenience method; returns the (index)th Double item under (fieldName), or None."""
  478       return self.GetFieldItem(fieldName, B_DOUBLE_TYPE, index)
  479 
  480    def GetMessage(self, fieldName, index=0):
  481       """Convenience method; returns the (index)th Message item under (fieldName), or None."""
  482       return self.GetFieldItem(fieldName, B_MESSAGE_TYPE, index)
  483 
  484    def GetPoint(self, fieldName, index=0):
  485       """Convenience method; returns the (index)th Point item under (fieldName), or None."""
  486       return self.GetFieldItem(fieldName, B_POINT_TYPE, index)
  487 
  488    def GetRect(self, fieldName, index=0):
  489       """Convenience method; returns the (index)th Rect item under (fieldName), or None."""
  490       return self.GetFieldItem(fieldName, B_RECT_TYPE, index)
  491         
  492 
  493 # --------------------------------------------------------------------------------------------
  494 
  495 # Silly little test stub.  Just writes a flattened Message out to a file, then reads in back in.
  496 if __name__ == "__main__":
  497    tm = Message(666)
  498    tm.PutBool("bool", [True,False])
  499    tm.PutInt8("int8", [8,9,10])
  500    tm.PutInt16("int16", [16,18,19])
  501    tm.PutInt32("int32", [32,31,30])
  502    tm.PutInt64("int64", [64,63,62, -20, -25])
  503    tm.PutString("string", ["stringme!", "strungme!", "strongme!"])
  504    tm.PutFloat("float", [3.14159, 6.141, 9.999, 2.1, 4])
  505    tm.PutDouble("double", [2.7172, 3.4, 5.6, -1.0])
  506    tm.PutPoint("point", [(6.5, 7.5), (9,10), (11,15)])
  507    tm.PutRect("rect", [(9.1,10,11,12.5), (1,2,3,4), (2,3,4,5)])
  508    tm.PutFieldContents("data", 555, ["testing...", "stuff", "out"])
  509    subMsg = Message(777)
  510    subMsg.PutString("hola", "senor")
  511    tm.PutMessage("submsg", subMsg)
  512 
  513    tm.PrintToStream()
  514    outFile = open('test.msg', 'wb')
  515    tm.Flatten(outFile)
  516    outFile.close()
  517 
  518    print("Unflattening...")
  519    inFile = open('test.msg', 'rb')
  520    m2 = Message()
  521    m2.Unflatten(inFile)
  522    inFile.close()
  523    m2.PrintToStream()