def make_limit_node(root, limit):
    limit_node = etree.SubElement(root, 'limit',
                                  {'class': limit._limit_full_name})
    for attr in sorted(limit.attrs):
        desc = limit.attrs[attr]
        attr_type = desc.get('type', str)
        value = getattr(limit, attr)
        if 'default' in desc:
            default = (desc['default']() if callable(desc['default']) else
                       desc['default'])
            if value == default:
                continue
        attr_node = etree.SubElement(limit_node, 'attr', name=attr)
        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)