text: '''
  Dynamic structures
  
  Build classes from a description of their fields
  '''
  
  from collections import namedtuple
  import struct
  import inspect
  
  # An object description is a (ordered) list of field descriptions
  # TODO: describe valid types
  # TODO: add enums
  # TODO: add printing hook, default to str()
  
  ApiStructField = namedtuple('ApiStructField', 'name type len')
  
  # obj = [ { 'name': 'field_name', 'type': [int, array, boolean], 'len': l }, ]
  
  def parse_field(field, data, index = 0):
      s = index
      e = index + field.len
      if field.type is 'array':
          desc = '{0}B'.format(field.len)
          val = list(struct.unpack(desc, data[s:e]))
      elif field.type is 'int' and field.len is 1:
          val = struct.unpack('B', data[s])[0]
      elif field.type is 'int' and field.len is 2:
          val = struct.unpack('!H', data[s:e])[0]
      elif field.type is 'int' and field.len is 4:
          val = struct.unpack('!L', data[s:e])[0]
      elif field.type is 'boolean':
          val = ord(data[s]) == 1
          pass
      elif inspect.isclass(field.type):
          val = parse_obj(field.type, data, index)
      else:
          raise Exception('unknown field type: ' + field.type)
      return (e, val)
  
  def parse_obj(obj_type, data, index = 0):
      'Parse an object given a class and a data string'
      obj_vals = {}
      if len(data[index:]) < obj_type.serialized_length:
          raise IndexError('expected at least %s bytes of input' % obj_type.serialized_length)
      for f in obj_type.fields:
          (index, val) = parse_field(f, data, index)
          obj_vals[f.name] = val
      return obj_type(**obj_vals)
  
  def to_string(self):
      'Generate a pretty printed string'
      out = ''
      for f in self.fields:
          out += '%16s: %s\n' % (f.name, str(self.__getattribute__(f.name)))
      return out
  
  def serialize(self):
      'Serialize the structure in its binary API format'
      resp = ''
      for f in self.fields:
          val = self.__getattribute__(f.name)
          if f.type is 'array':
              desc = '{0}B'.format(f.len)
              resp += struct.pack(desc, *val)
          elif f.type is 'int' and f.len is 1:
              resp += struct.pack('B', val)
          elif f.type is 'int' and f.len is 2:
              resp += struct.pack('!H', val)
          elif f.type is 'int' and f.len is 4:
              resp += struct.pack('!L', val)
          elif f.type is 'boolean':
              resp += struct.pack('B', val)
          elif inspect.isclass(f.type):
              resp += val.serialize()
          else:
              raise Exception('unknown field type: ' + f.type)
      return resp
  
  
  
  class GenObjectFactory(object):
      'Generic Factory for creating record objects from a list of fields'
      def __init__(self):
          self.object_map = {}
  
      def add_object(self, obj_id, obj_type):
          self.object_map[obj_id] = obj_type
          
      def parse(self, obj_id, data, index = 0):
          if obj_id in self.object_map:
              return parse_obj(self.object_map[obj_id], data, index)
          else:
              return None
      
      # note: fields is an array because we need to preserve field ordering
      def synthesize(self, obj_name, fields, obj_id = None):
          'Create an API object class from its list of fields'
          field_names = ' '.join([f.name for f in fields])
          obj_type = namedtuple(obj_name, field_names)
          # add the object to the list of things we can parse
          if obj_id:
              self.add_object(obj_id, obj_type)
          # add some useful stuff to the object
          obj_type.serialized_length = sum(f.len for f in fields)
          obj_type.fields = fields
          obj_type.__str__ = to_string
          obj_type.serialize = serialize
          return obj_type
  
  