Example #1
0
def outputEnumerations(enumerationSpec, options, pyFile):
    """
    Outputs the enmerations into a Python file.

    Given the portion of the specification definining all
    the enumerations, output options and an output file,
    write out all the necessary enumerations definitions
    and return information on their values for use in
    future parsing.

    Args:
        enumerationSpec (dict): The portion of the
                                specification covering
                                enumerations.
        options (dict):         A dictionary of options to
                                modify output.
        pyFile (file):          A file-like object to which
                                to save the struct code.

    Returns:
        A dictionary of constants to be pulled into the
        environment for parsing structure definitions.
    """
    assert isinstance(enumerationSpec, list)
    assert isinstance(options, dict)
    assert hasattr(pyFile, 'write')
    newLocals = []
    for enumerationName, enumeration in enumerationSpec:
        writeOut(pyFile, '##')
        if 'title' in enumeration:
            writeOut(pyFile, enumeration['title'], '# ')
        else:
            writeOut(pyFile, enumerationName, '# ')
        if 'description' in enumeration:
            writeOut(pyFile, '#')
            writeOutBlock(pyFile, enumeration['description'], '# ')
        writeOut(pyFile, '#')
        varType = enumeration.get('type', None)
        value = None
        for optionName, option in enumeration['options'].items():
            line = []
            if 'description' in option:
                writeOut(pyFile, '    #')
                writeOutBlock(pyFile, option['description'], '    # ')
                writeOut(pyFile, '    #')
            if 'value' in option:
                varType = option.get('type', varType)
                if isStringType(varType):
                    varType = 'str'
                elif isFloatType(varType):
                    varType = 'float'
                elif isBooleanType(varType):
                    varType = 'bool'
                else:
                    varType = 'int'
                if varType is None:
                    if options['verbose']:
                        print('Guessing enumeration type based on value.')
                    varType = str(type(option['value']))[7:-2]
                    if varType == 'str' and (value.startswith('(') and
                                             value.endswith(')')):
                        varType = 'int'
                        if options['verbose']:
                            print('Second-guessing enumeration type.')
                value = option['value']
            else:
                if varType is None:
                    varType = 'int'
                    if options['verbose']:
                        print('Defaulting enumeration type to int.')
                if not isinstance(value, int):
                    value = 0
                else:
                    value += 1
            if varType == str:
                delim = '"'
            else:
                delim = ''
            line.append(optionName)
            line.append(' = {}{}{}'.format(delim, value, delim))
            if 'title' in option:
                line.append(' # {}'.format(option['title']))
            writeOut(pyFile, ''.join(line))
            newLocals.append((optionName, value))
        writeOut(pyFile, '')
        writeOut(pyFile, '')
    return newLocals
Example #2
0
def outputEnumerations(enumerationSpec, options, pyFile):
    """
    Outputs the enmerations into a Python file.

    Given the portion of the specification definining all
    the enumerations, output options and an output file,
    write out all the necessary enumerations definitions
    and return information on their values for use in
    future parsing.

    Args:
        enumerationSpec (dict): The portion of the
                                specification covering
                                enumerations.
        options (dict):         A dictionary of options to
                                modify output.
        pyFile (file):          A file-like object to which
                                to save the struct code.

    Returns:
        A dictionary of constants to be pulled into the
        environment for parsing structure definitions.
    """
    assert isinstance(enumerationSpec, list)
    assert isinstance(options, dict)
    assert hasattr(pyFile, 'write')
    newLocals = []
    for enumerationName, enumeration in enumerationSpec:
        writeOut(pyFile, '##')
        if 'title' in enumeration:
            writeOut(pyFile, enumeration['title'], '# ')
        else:
            writeOut(pyFile, enumerationName, '# ')
        if 'description' in enumeration:
            writeOut(pyFile, '#')
            writeOutBlock(pyFile, enumeration['description'], '# ')
        writeOut(pyFile, '#')
        varType = enumeration.get('type', None)
        value = None
        for optionName, option in enumeration['options'].items():
            line = []
            if 'description' in option:
                writeOut(pyFile, '    #')
                writeOutBlock(pyFile, option['description'], '    # ')
                writeOut(pyFile, '    #')
            if 'value' in option:
                varType = option.get('type', varType)
                if isStringType(varType):
                    varType = 'str'
                elif isFloatType(varType):
                    varType = 'float'
                elif isBooleanType(varType):
                    varType = 'bool'
                else:
                    varType = 'int'
                if varType is None:
                    if options['verbose']:
                        print('Guessing enumeration type based on value.')
                    varType = str(type(option['value']))[7:-2]
                    if varType == 'str' and (value.startswith('(')
                                             and value.endswith(')')):
                        varType = 'int'
                        if options['verbose']:
                            print('Second-guessing enumeration type.')
                value = option['value']
            else:
                if varType is None:
                    varType = 'int'
                    if options['verbose']:
                        print('Defaulting enumeration type to int.')
                if not isinstance(value, int):
                    value = 0
                else:
                    value += 1
            if varType == str:
                delim = '"'
            else:
                delim = ''
            line.append(optionName)
            line.append(' = {}{}{}'.format(delim, value, delim))
            if 'title' in option:
                line.append(' # {}'.format(option['title']))
            writeOut(pyFile, ''.join(line))
            newLocals.append((optionName, value))
        writeOut(pyFile, '')
        writeOut(pyFile, '')
    return newLocals
Example #3
0
def outputPython(specification, options, pyFile):
    """
    Outputs Python struct file.

    Given the specification construct a valid Python struct
    file that describes all the binary packets.

    Args:
        specification (dict): The specification object.
        options (dict):       A dictionary of options to
                              modify output.
        pyFile (file):        A file-like object to which
                              to save the struct code.
    """
    assert isinstance(specification, dict)
    assert isinstance(options, dict)
    assert hasattr(pyFile, 'write')
    packetLengths = {}
    writeOut(pyFile, '#!/usr/bin/env python')
    writeOut(pyFile, '# -*- coding: utf-8 -*-')
    writeOut(pyFile, '"""')
    writeOut(pyFile, specification['title'])
    if 'description' in specification:
        writeOut(pyFile, '')
        writeOutBlock(pyFile, specification['description'])
    for tag in ('version', 'date', 'author', 'documentation', 'metadata'):
        if tag in specification:
            writeOut(pyFile, '')
            writeOut(pyFile, '{}: {}'.format(tag.title(),
                                             specification[tag]))
    writeOut(pyFile, '"""')
    writeOut(pyFile, '')
    writeOut(pyFile, 'from struct import calcsize, pack, unpack_from')
    writeOut(pyFile, 'from zope.interface import directlyProvides, Interface')
    writeOut(pyFile, '')
    writeOut(pyFile, '')
    prefix = '    '

    # Create interfaces for testing and documenting
    extensionlessName = options['pyFilename'].split('.')[0]
    extensionlessName = extensionlessName[0].upper() + extensionlessName[1:]
    writeOut(pyFile, 'class I{}Length(Interface):'.format(extensionlessName))
    writeOut(pyFile, '"""', prefix)
    writeOut(pyFile, 'A binary packet length calculator', prefix)
    writeOut(pyFile, '')
    writeOut(pyFile, 'Interface for an entity that returns the length ' \
             + 'of a binary packet buffer.', prefix)
    writeOut(pyFile, '"""', prefix)
    writeOut(pyFile, 'def __call__():', prefix)
    writeOut(pyFile, '"""Returns the length of the object in bytes."""',
             2 * prefix)
    writeOut(pyFile, '')
    writeOut(pyFile, '')
    writeOut(pyFile, 'class I{}Packer(Interface):'.format(extensionlessName))
    writeOut(pyFile, '"""', prefix)
    writeOut(pyFile, 'A binary data packer', prefix)
    writeOut(pyFile, '')
    writeOut(pyFile, 'Interface for an entity that packs binary data.', prefix)
    writeOut(pyFile, '"""', prefix)
    writeOut(pyFile, 'def __call__(packet):', prefix)
    writeOut(pyFile, '"""Packs a packet dict into a string."""', 2 * prefix)
    writeOut(pyFile, '')
    writeOut(pyFile, '')
    writeOut(pyFile, 'class I{}Unpacker(Interface):'.format(extensionlessName))
    writeOut(pyFile, '"""', prefix)
    writeOut(pyFile, 'A binary data unpacker', prefix)
    writeOut(pyFile, '')
    writeOut(pyFile, 'Interface for an entity that unpacks binary data.',
             prefix)
    writeOut(pyFile, '"""', prefix)
    writeOut(pyFile, 'def __call__(buffer):', prefix)
    writeOut(pyFile, '"""Unpacks a binary string into a dict."""', 2 * prefix)
    writeOut(pyFile, '')
    writeOut(pyFile, '')


    # Parse the enumerations
    newLocals = outputEnumerations(specification['enums'].items(),
                                   options, pyFile)
    # The following is a little ugly but places the enumerations
    # in the current namespace so that they may be referenced
    # when evaluating formats.
    for optionName, value in newLocals:
        if varNameRE.match(optionName) and exprRE.match(str(value)):
            exec '{} = {}'.format(optionName, value)

    # Parse the structure
    for packetName, packet in specification['packets'].items():
        structDefList = []
        structAccretions = {
            'formatList': [],
            'countList': [],
            'varList': [],
            'bitFields': [],
            'titles': [],
            'descriptions': []
        }
        bitFieldCount = populateWorkLists(
            packet, specification,
            structDefList, structAccretions
        )

        # Create the get length function
        writeOut(pyFile, 'def get_{}_len():'.format(packetName))
        writeOut(pyFile, '"""', prefix)
        writeOut(pyFile, "Calculates the size of {}.".format(packetName), prefix)
        writeOut(pyFile, '')
        writeOut(pyFile, "Calculates the total size of the {} structure".format(
                 packetName), prefix)
        writeOut(pyFile, "(including any internal substructures).", prefix)
        writeOut(pyFile, '')
        writeOut(pyFile, 'Returns:', prefix)
        writeOut(pyFile, 'The size of {}.'.format(packetName),
                 2 * prefix)
        # The following section determines how many bytes a packet
        # consists of so we can make good doctests. To do so it
        # evaluates the expressions used for the format descriptions.
        packetLen = 0
        try:
            for structDef in structDefList:
                if structDef['type'] == 'segment':
                    assert structFmtRE.match(structDef['fmt'])
                    packetLen += calcsize(eval(structDef['fmt']))
                elif structDef['type'] == 'substructure':
                    packetLen += packetLengths[structDef['itemType']]
            packetLengths[packetName] = packetLen
            writeOut(pyFile, '')
            writeOut(pyFile, 'Examples:', prefix)
            writeOut(pyFile, '>>> get_{}_len()'.format(packetName), prefix * 2)
            writeOut(pyFile, '{}'.format(packetLen), prefix * 2)
        except KeyError:
            # If we can't evaluate it don't bother with a doctest
            # for this one. This can happen if dependencies get
            # defined after they're used.
            pass
        writeOut(pyFile, '"""', prefix)
        # Create the function itself.
        formatStrList = [structDef['fmt'] for structDef in structDefList
                         if structDef['type'] == 'segment']
        if not formatStrList:
            writeOut(pyFile, 'totalSize = 0', prefix)
        else:
            writeOut(pyFile, 'totalSize = calcsize({})'.format(
                     ') + calcsize('.join(formatStrList)), prefix)
        substructureList = [structDef['itemType'] for structDef in structDefList
                            if structDef['type'] == 'substructure']
        for substruct in substructureList:
            writeOut(pyFile, 'totalSize += get_{}_len()'.format(substruct), prefix)
        writeOut(pyFile, 'return totalSize', prefix)
        writeOut(pyFile, 'directlyProvides(get_{}_len, I{}Length)'.format(
                 packetName, extensionlessName))
        writeOut(pyFile, '')
        writeOut(pyFile, '')

        # Create the pack function
        writeOut(pyFile, 'def pack_{}(packet):'.format(packetName))
        writeOut(pyFile, '"""', prefix)
        writeOut(pyFile, "Packs a {} packet.".format(packetName), prefix)
        if 'description' in packet:
            writeOut(pyFile, '')
            writeOutBlock(pyFile, packet['description'], prefix)
        writeOut(pyFile, '')
        writeOut(pyFile, 'Args:', prefix)
        writeOut(pyFile, 'packet (dict): A dictionary of data to be packed.',
                 2 * prefix)
        writeOut(pyFile, '')
        writeOut(pyFile, 'Returns:', prefix)
        writeOut(pyFile, 'A binary string containing the packed data.',
                 2 * prefix)
        writeOut(pyFile, '"""', prefix)
        writeOut(pyFile, 'assert isinstance(packet, dict)', prefix)
        writeOut(pyFile, 'outList = []', prefix)
        for structDef in structDefList:
            if structDef['type'] == 'segment':
                for fragNum, (bitFieldName, bitFieldNum, bitFieldSize, bitFieldLabel
                              ) in enumerate(reversed(structDef['bitFields'])):
                    if fragNum == 0:
                        writeOut(pyFile, 'bitField{} = {}'.format(
                            bitFieldNum, bitFieldName), prefix)
                    else:
                        writeOut(pyFile, 'bitField{} <<= {}'.format(
                            bitFieldNum, bitFieldSize), prefix)
                        writeOut(pyFile, 'bitField{} |= {}'.format(
                            bitFieldNum, bitFieldName), prefix)
                writeOut(pyFile, 'outList.append(pack({}, {}))'.format(
                    structDef['fmt'], structDef['vars'][1:-1]), prefix)
            elif structDef['type'] == 'substructure':
                writeOut(pyFile, 'outList.append(pack_{}(packet["{}"]))'.format(
                    structDef['itemType'], structDef['itemName']), prefix)
        writeOut(pyFile, 'return "".join(outList)', prefix)
        writeOut(pyFile, 'directlyProvides(pack_{}, I{}Packer)'.format(
                 packetName, extensionlessName))
        writeOut(pyFile, '')
        writeOut(pyFile, '')

        # Create the unpack function
        writeOut(pyFile, 'def unpack_{}(rawData):'.format(packetName))
        writeOut(pyFile, '"""', prefix)
        if 'title' in packet:
            writeOut(pyFile, packet['title'], prefix)
        else:
            writeOut(pyFile, packetName, prefix)
        if 'description' in packet:
            writeOut(pyFile, '')
            writeOutBlock(pyFile, packet['description'], prefix)
        writeOut(pyFile, '')
        writeOut(pyFile, 'Args:', prefix)
        writeOut(pyFile, 'rawData (str): The raw binary data to be unpacked.',
                 2 * prefix)
        writeOut(pyFile, '')
        writeOut(pyFile, 'Returns:', prefix)
        writeOut(pyFile, 'A dictionary of the unpacked data.',
                 2 * prefix)
        # Write out the next bit to a temporary buffer.
        outBufStr = StringIO()
        writeOut(outBufStr, '"""', prefix)
        writeOut(outBufStr, 'assert isinstance(rawData, str)', prefix)
        writeOut(outBufStr, 'packet = {}', prefix)
        writeOut(outBufStr, 'position = 0', prefix)
        for structDef in structDefList:
            line = []
            if structDef['type'] == 'segment':
                line.append('segmentFmt = {}{}'.format(structDef['fmt'], linesep))
                line.append('{}segmentLen = calcsize(segmentFmt){}'.format(prefix, linesep))
                line.append('{}{} = unpack_from(segmentFmt, rawData, position){}'.format(
                            prefix, structDef['vars'], linesep))
                line.append('{}position += segmentLen{}'.format(prefix, linesep))
                for fragNum, (bitFieldName, bitFieldNum, bitFieldSize,
                              bitFieldLabel) in enumerate(structDef['bitFields']):
                    bitFieldMask = hex(int(pow(2, bitFieldSize)) - 1)
                    if isFloatType(bitFieldLabel):
                        bitFieldType = 'float'
                    elif isBooleanType(bitFieldLabel):
                        bitFieldType = 'bool'
                    elif isStringType(bitFieldLabel):
                        bitFieldType = 'str'
                    else:
                        bitFieldType = 'int'
                    line.append("{}{} = {}(bitField{} & {}){}".format(prefix, bitFieldName,
                                bitFieldType, bitFieldNum, bitFieldMask, linesep))
                    if fragNum < len(structDef['bitFields']) - 1:
                        line.append("{}bitField{} >>= {}{}".format(prefix,
                                    bitFieldNum, bitFieldSize, linesep))
                if line[-1].endswith(linesep):
                    line[-1] = line[-1][:-len(linesep)]
            elif structDef['type'] == 'substructure':
                if structDef['description']:
                    writeOut(outBufStr, '')
                    writeOutBlock(outBufStr, structDef['description'], '    # ')
                line.append("packet['{}'] = unpack_{}(rawData[position:]){}".format(
                    structDef['itemName'], structDef['itemType'], linesep))
                line.append("{}position += get_{}_len()".format(
                    prefix, structDef['itemType']))
                if structDef['title']:
                    line.append(' # {}'.format(structDef['title']))
            if line:
                writeOut(outBufStr, ''.join(line), prefix)
        writeOut(outBufStr, 'return packet', prefix)
        writeOut(outBufStr, 'directlyProvides(unpack_{}, I{}Unpacker)'.format(
                 packetName, extensionlessName))
        # Write the temporary buffer to the output file.
        writeOut(pyFile, outBufStr.getvalue())
        outBufStr.close()
        writeOut(pyFile, '')

        # Create the validate function
        writeOut(pyFile, 'def validate_{}(rawData):'.format(packetName))
        writeOut(pyFile, '"""', prefix)
        writeOut(pyFile, "Reads and validates a {} packet.".format(packetName), prefix)
        writeOut(pyFile, '')
        writeOut(pyFile, "Reads a {} structure from raw binary data".format(
                 packetName), prefix)
        writeOut(pyFile, "and validates it.", prefix)
        writeOut(pyFile, '')
        writeOut(pyFile, 'Args:', prefix)
        writeOut(pyFile, 'rawData (str): The raw binary data to be unpacked.',
                 2 * prefix)
        writeOut(pyFile, '')
        writeOut(pyFile, 'Returns:', prefix)
        writeOut(pyFile, 'A structure representing the {} packet.'.format(packetName),
                 2 * prefix)
        writeOut(pyFile, '"""', prefix)
        writeOut(pyFile, 'assert isinstance(rawData, str)', prefix)
        writeOut(pyFile, 'packet = get_{}(rawData)'.format(packetName), prefix)
        writeOut(pyFile, 'return packet', prefix)
        writeOut(pyFile, '')
        writeOut(pyFile, '')

    writeOut(pyFile, 'if __name__ == "__main__":')
    writeOut(pyFile, 'from zope.interface.verify import verifyObject', prefix)
    writeOut(pyFile, 'import doctest', prefix)
    writeOut(pyFile, 'doctest.testmod()', prefix)
Example #4
0
def outputPython(specification, options, pyFile):
    """
    Outputs Python struct file.

    Given the specification construct a valid Python struct
    file that describes all the binary packets.

    Args:
        specification (dict): The specification object.
        options (dict):       A dictionary of options to
                              modify output.
        pyFile (file):        A file-like object to which
                              to save the struct code.
    """
    assert isinstance(specification, dict)
    assert isinstance(options, dict)
    assert hasattr(pyFile, 'write')
    packetLengths = {}
    writeOut(pyFile, '#!/usr/bin/env python')
    writeOut(pyFile, '# -*- coding: utf-8 -*-')
    writeOut(pyFile, '"""')
    writeOut(pyFile, specification['title'])
    if 'description' in specification:
        writeOut(pyFile, '')
        writeOutBlock(pyFile, specification['description'])
    for tag in ('version', 'date', 'author', 'documentation', 'metadata'):
        if tag in specification:
            writeOut(pyFile, '')
            writeOut(pyFile, '{}: {}'.format(tag.title(), specification[tag]))
    writeOut(pyFile, '"""')
    writeOut(pyFile, '')
    writeOut(pyFile, 'from struct import calcsize, pack, unpack_from')
    writeOut(pyFile, 'from zope.interface import directlyProvides, Interface')
    writeOut(pyFile, '')
    writeOut(pyFile, '')
    prefix = '    '

    # Create interfaces for testing and documenting
    extensionlessName = options['pyFilename'].split('.')[0]
    extensionlessName = extensionlessName[0].upper() + extensionlessName[1:]
    writeOut(pyFile, 'class I{}Length(Interface):'.format(extensionlessName))
    writeOut(pyFile, '"""', prefix)
    writeOut(pyFile, 'A binary packet length calculator', prefix)
    writeOut(pyFile, '')
    writeOut(pyFile, 'Interface for an entity that returns the length ' \
             + 'of a binary packet buffer.', prefix)
    writeOut(pyFile, '"""', prefix)
    writeOut(pyFile, 'def __call__():', prefix)
    writeOut(pyFile, '"""Returns the length of the object in bytes."""',
             2 * prefix)
    writeOut(pyFile, '')
    writeOut(pyFile, '')
    writeOut(pyFile, 'class I{}Packer(Interface):'.format(extensionlessName))
    writeOut(pyFile, '"""', prefix)
    writeOut(pyFile, 'A binary data packer', prefix)
    writeOut(pyFile, '')
    writeOut(pyFile, 'Interface for an entity that packs binary data.', prefix)
    writeOut(pyFile, '"""', prefix)
    writeOut(pyFile, 'def __call__(packet):', prefix)
    writeOut(pyFile, '"""Packs a packet dict into a string."""', 2 * prefix)
    writeOut(pyFile, '')
    writeOut(pyFile, '')
    writeOut(pyFile, 'class I{}Unpacker(Interface):'.format(extensionlessName))
    writeOut(pyFile, '"""', prefix)
    writeOut(pyFile, 'A binary data unpacker', prefix)
    writeOut(pyFile, '')
    writeOut(pyFile, 'Interface for an entity that unpacks binary data.',
             prefix)
    writeOut(pyFile, '"""', prefix)
    writeOut(pyFile, 'def __call__(buffer):', prefix)
    writeOut(pyFile, '"""Unpacks a binary string into a dict."""', 2 * prefix)
    writeOut(pyFile, '')
    writeOut(pyFile, '')

    # Parse the enumerations
    newLocals = outputEnumerations(specification['enums'].items(), options,
                                   pyFile)
    # The following is a little ugly but places the enumerations
    # in the current namespace so that they may be referenced
    # when evaluating formats.
    for optionName, value in newLocals:
        if varNameRE.match(optionName) and exprRE.match(str(value)):
            exec '{} = {}'.format(optionName, value)

    # Parse the structure
    for packetName, packet in specification['packets'].items():
        structDefList = []
        structAccretions = {
            'formatList': [],
            'countList': [],
            'varList': [],
            'bitFields': [],
            'titles': [],
            'descriptions': []
        }
        bitFieldCount = populateWorkLists(packet, specification, structDefList,
                                          structAccretions)

        # Create the get length function
        writeOut(pyFile, 'def get_{}_len():'.format(packetName))
        writeOut(pyFile, '"""', prefix)
        writeOut(pyFile, "Calculates the size of {}.".format(packetName),
                 prefix)
        writeOut(pyFile, '')
        writeOut(
            pyFile,
            "Calculates the total size of the {} structure".format(packetName),
            prefix)
        writeOut(pyFile, "(including any internal substructures).", prefix)
        writeOut(pyFile, '')
        writeOut(pyFile, 'Returns:', prefix)
        writeOut(pyFile, 'The size of {}.'.format(packetName), 2 * prefix)
        # The following section determines how many bytes a packet
        # consists of so we can make good doctests. To do so it
        # evaluates the expressions used for the format descriptions.
        packetLen = 0
        try:
            for structDef in structDefList:
                if structDef['type'] == 'segment':
                    assert structFmtRE.match(structDef['fmt'])
                    packetLen += calcsize(eval(structDef['fmt']))
                elif structDef['type'] == 'substructure':
                    packetLen += packetLengths[structDef['itemType']]
            packetLengths[packetName] = packetLen
            writeOut(pyFile, '')
            writeOut(pyFile, 'Examples:', prefix)
            writeOut(pyFile, '>>> get_{}_len()'.format(packetName), prefix * 2)
            writeOut(pyFile, '{}'.format(packetLen), prefix * 2)
        except KeyError:
            # If we can't evaluate it don't bother with a doctest
            # for this one. This can happen if dependencies get
            # defined after they're used.
            pass
        writeOut(pyFile, '"""', prefix)
        # Create the function itself.
        formatStrList = [
            structDef['fmt'] for structDef in structDefList
            if structDef['type'] == 'segment'
        ]
        if not formatStrList:
            writeOut(pyFile, 'totalSize = 0', prefix)
        else:
            writeOut(
                pyFile, 'totalSize = calcsize({})'.format(
                    ') + calcsize('.join(formatStrList)), prefix)
        substructureList = [
            structDef['itemType'] for structDef in structDefList
            if structDef['type'] == 'substructure'
        ]
        for substruct in substructureList:
            writeOut(pyFile, 'totalSize += get_{}_len()'.format(substruct),
                     prefix)
        writeOut(pyFile, 'return totalSize', prefix)
        writeOut(
            pyFile, 'directlyProvides(get_{}_len, I{}Length)'.format(
                packetName, extensionlessName))
        writeOut(pyFile, '')
        writeOut(pyFile, '')

        # Create the pack function
        writeOut(pyFile, 'def pack_{}(packet):'.format(packetName))
        writeOut(pyFile, '"""', prefix)
        writeOut(pyFile, "Packs a {} packet.".format(packetName), prefix)
        if 'description' in packet:
            writeOut(pyFile, '')
            writeOutBlock(pyFile, packet['description'], prefix)
        writeOut(pyFile, '')
        writeOut(pyFile, 'Args:', prefix)
        writeOut(pyFile, 'packet (dict): A dictionary of data to be packed.',
                 2 * prefix)
        writeOut(pyFile, '')
        writeOut(pyFile, 'Returns:', prefix)
        writeOut(pyFile, 'A binary string containing the packed data.',
                 2 * prefix)
        writeOut(pyFile, '"""', prefix)
        writeOut(pyFile, 'assert isinstance(packet, dict)', prefix)
        writeOut(pyFile, 'outList = []', prefix)
        for structDef in structDefList:
            if structDef['type'] == 'segment':
                for fragNum, (bitFieldName, bitFieldNum, bitFieldSize,
                              bitFieldLabel) in enumerate(
                                  reversed(structDef['bitFields'])):
                    if fragNum == 0:
                        writeOut(
                            pyFile,
                            'bitField{} = {}'.format(bitFieldNum,
                                                     bitFieldName), prefix)
                    else:
                        writeOut(
                            pyFile,
                            'bitField{} <<= {}'.format(bitFieldNum,
                                                       bitFieldSize), prefix)
                        writeOut(
                            pyFile,
                            'bitField{} |= {}'.format(bitFieldNum,
                                                      bitFieldName), prefix)
                writeOut(
                    pyFile, 'outList.append(pack({}, {}))'.format(
                        structDef['fmt'], structDef['vars'][1:-1]), prefix)
            elif structDef['type'] == 'substructure':
                writeOut(
                    pyFile, 'outList.append(pack_{}(packet["{}"]))'.format(
                        structDef['itemType'], structDef['itemName']), prefix)
        writeOut(pyFile, 'return "".join(outList)', prefix)
        writeOut(
            pyFile, 'directlyProvides(pack_{}, I{}Packer)'.format(
                packetName, extensionlessName))
        writeOut(pyFile, '')
        writeOut(pyFile, '')

        # Create the unpack function
        writeOut(pyFile, 'def unpack_{}(rawData):'.format(packetName))
        writeOut(pyFile, '"""', prefix)
        if 'title' in packet:
            writeOut(pyFile, packet['title'], prefix)
        else:
            writeOut(pyFile, packetName, prefix)
        if 'description' in packet:
            writeOut(pyFile, '')
            writeOutBlock(pyFile, packet['description'], prefix)
        writeOut(pyFile, '')
        writeOut(pyFile, 'Args:', prefix)
        writeOut(pyFile, 'rawData (str): The raw binary data to be unpacked.',
                 2 * prefix)
        writeOut(pyFile, '')
        writeOut(pyFile, 'Returns:', prefix)
        writeOut(pyFile, 'A dictionary of the unpacked data.', 2 * prefix)
        # Write out the next bit to a temporary buffer.
        outBufStr = StringIO()
        writeOut(outBufStr, '"""', prefix)
        writeOut(outBufStr, 'assert isinstance(rawData, str)', prefix)
        writeOut(outBufStr, 'packet = {}', prefix)
        writeOut(outBufStr, 'position = 0', prefix)
        for structDef in structDefList:
            line = []
            if structDef['type'] == 'segment':
                line.append('segmentFmt = {}{}'.format(structDef['fmt'],
                                                       linesep))
                line.append('{}segmentLen = calcsize(segmentFmt){}'.format(
                    prefix, linesep))
                line.append(
                    '{}{} = unpack_from(segmentFmt, rawData, position){}'.
                    format(prefix, structDef['vars'], linesep))
                line.append('{}position += segmentLen{}'.format(
                    prefix, linesep))
                for fragNum, (bitFieldName, bitFieldNum, bitFieldSize,
                              bitFieldLabel) in enumerate(
                                  structDef['bitFields']):
                    bitFieldMask = hex(int(pow(2, bitFieldSize)) - 1)
                    if isFloatType(bitFieldLabel):
                        bitFieldType = 'float'
                    elif isBooleanType(bitFieldLabel):
                        bitFieldType = 'bool'
                    elif isStringType(bitFieldLabel):
                        bitFieldType = 'str'
                    else:
                        bitFieldType = 'int'
                    line.append("{}{} = {}(bitField{} & {}){}".format(
                        prefix, bitFieldName, bitFieldType, bitFieldNum,
                        bitFieldMask, linesep))
                    if fragNum < len(structDef['bitFields']) - 1:
                        line.append("{}bitField{} >>= {}{}".format(
                            prefix, bitFieldNum, bitFieldSize, linesep))
                if line[-1].endswith(linesep):
                    line[-1] = line[-1][:-len(linesep)]
            elif structDef['type'] == 'substructure':
                if structDef['description']:
                    writeOut(outBufStr, '')
                    writeOutBlock(outBufStr, structDef['description'],
                                  '    # ')
                line.append(
                    "packet['{}'] = unpack_{}(rawData[position:]){}".format(
                        structDef['itemName'], structDef['itemType'], linesep))
                line.append("{}position += get_{}_len()".format(
                    prefix, structDef['itemType']))
                if structDef['title']:
                    line.append(' # {}'.format(structDef['title']))
            if line:
                writeOut(outBufStr, ''.join(line), prefix)
        writeOut(outBufStr, 'return packet', prefix)
        writeOut(
            outBufStr, 'directlyProvides(unpack_{}, I{}Unpacker)'.format(
                packetName, extensionlessName))
        # Write the temporary buffer to the output file.
        writeOut(pyFile, outBufStr.getvalue())
        outBufStr.close()
        writeOut(pyFile, '')

        # Create the validate function
        writeOut(pyFile, 'def validate_{}(rawData):'.format(packetName))
        writeOut(pyFile, '"""', prefix)
        writeOut(pyFile, "Reads and validates a {} packet.".format(packetName),
                 prefix)
        writeOut(pyFile, '')
        writeOut(
            pyFile,
            "Reads a {} structure from raw binary data".format(packetName),
            prefix)
        writeOut(pyFile, "and validates it.", prefix)
        writeOut(pyFile, '')
        writeOut(pyFile, 'Args:', prefix)
        writeOut(pyFile, 'rawData (str): The raw binary data to be unpacked.',
                 2 * prefix)
        writeOut(pyFile, '')
        writeOut(pyFile, 'Returns:', prefix)
        writeOut(pyFile,
                 'A structure representing the {} packet.'.format(packetName),
                 2 * prefix)
        writeOut(pyFile, '"""', prefix)
        writeOut(pyFile, 'assert isinstance(rawData, str)', prefix)
        writeOut(pyFile, 'packet = get_{}(rawData)'.format(packetName), prefix)
        writeOut(pyFile, 'return packet', prefix)
        writeOut(pyFile, '')
        writeOut(pyFile, '')

    writeOut(pyFile, 'if __name__ == "__main__":')
    writeOut(pyFile, 'from zope.interface.verify import verifyObject', prefix)
    writeOut(pyFile, 'import doctest', prefix)
    writeOut(pyFile, 'doctest.testmod()', prefix)