Given a Limit object, generate an XML node.

    :param root: The root node of the XML tree being built.
    :param limit: The Limit object to serialize to XML.

def make_limit_node(root, limit):
    """
    Given a Limit object, generate an XML node.

    :param root: The root node of the XML tree being built.
    :param limit: The Limit object to serialize to XML.
    """

    # Build the base limit node
    limit_node = etree.SubElement(root, 'limit',
                                  {'class': limit._limit_full_name})

    # Walk through all the recognized attributes
    for attr in sorted(limit.attrs):
        desc = limit.attrs[attr]
        attr_type = desc.get('type', str)
        value = getattr(limit, attr)

        # Determine the default value, if we have one...
        if 'default' in desc:
            default = (desc['default']() if callable(desc['default']) else
                       desc['default'])

            # Skip attributes that have their default settings
            if value == default:
                continue

        # Set up the attr node
        attr_node = etree.SubElement(limit_node, 'attr', name=attr)

        # Treat lists and dicts specially
        if attr_type == list:
            for val in value:
                val_node = etree.SubElement(attr_node, 'value')
                val_node.text = str(val)
        elif attr_type == dict:
            for key, val in sorted(value.items(), key=lambda x: x[0]):
                val_node = etree.SubElement(attr_node, 'value', key=key)
                val_node.text = str(val)
        else:
            attr_node.text = str(value)