code: def make_parser_and_packer(streamer, message_dict, message_post_unpacks):
      
      message_parsers = dict((k, _make_parser(streamer, v)) for k, v in message_dict.items())
  
      def parse_from_data(message_name, data):
          message_stream = io.BytesIO(data)
          parser = message_parsers.get(message_name)
          if parser is None:
              raise KeyError("unknown message: %s" % message_name)
          d = parser(message_stream)
          post_unpack = message_post_unpacks.get(message_name)
          if post_unpack:
              d = post_unpack(d, message_stream)
          return d
  
      def pack_from_data(message_name, **kwargs):
          the_struct = message_dict[message_name]
          if not the_struct:
              return b''
          f = io.BytesIO()
          the_fields = the_struct.split(" ")
          pairs = [t.split(":") for t in the_fields]
          for name, type in pairs:
              if type[0] == '[':
                  streamer.stream_struct("I", f, len(kwargs[name]))
                  for v in kwargs[name]:
                      if not isinstance(v, (tuple, list)):
                          v = [v]
                      streamer.stream_struct(type[1:-1], f, *v)
              else:
                  streamer.stream_struct(type, f, kwargs[name])
          return f.getvalue()
  
      return parse_from_data, pack_from_data
docs: Create a parser and a packer for a peer's network messages.
  
      streamer:
          used in conjunction with the message_dict. The message_dict turns a message into
          a string specifying the fields, and this dictionary specifies how to pack or unpack
          fields to or from bytes
      message_dict:
          a dictionary specifying how to pack or unpack the various messages like "version"
      message_post_unpacks:
          a dictionary specifying functions to call to postprocess message to, for example
          extract submessages, like in "alert"