Creates a plugin builder function suitable to extract some specific
    objects (either compulsory or optional) and other simpler data

    >>> def dummy_needed_funct1(foo, bar):
    ...     pass
    >>> class dummy_needed_class1:
    ...     def __init__(self, baz):
    ...         pass
    >>> def dummy_optional_funct2(boo):
    ...     pass
    >>> create_enhanced_plugin_builder(
    ...    compulsory_objects={
    ...         'needed_funct1' : dummy_needed_funct1,
    ...         'needed_class1' : dummy_needed_class1
    ...     },
    ...     optional_objects={
    ...         'optional_funct2' : dummy_optional_funct2,
    ...     },
    ...     other_data={
    ...         'CFG_SOME_DATA' : (str, ''),
    ...         'CFG_SOME_INT' : (int, 0),
    ...     })
    <function plugin_builder at 0xb7812064>

    :param compulsory_objects: map of name of an object to look for inside
        the `plugin` and a *signature* for a class or callable. Every
        name specified in this map **must exists** in the plugin, otherwise
        the plugin will fail to load.
    :type compulsory_objects: dict
    :param optional_objects: map of name of an object to look for inside
        the C{plugin} and a I{signature} for a class or callable. Every
        name specified in this map must B{can exists} in the plugin.
    :type optional_objects: dict
    :param other_data: map of other simple data that can be loaded from
        the plugin. The map has the same format of the C{content}
        parameter of L{invenio_ext.legacy.handler.wash_urlargd}.
    :type other_data: dict
    :return: a I{plugin_builder} function that can be used with the
        map function. Such function will build the plugin
        in the form of a map, where every key is one of the keys inside
        the three maps provided as parameters and the corresponding value
        is the expected class or callable or simple data.

def create_enhanced_plugin_builder(compulsory_objects=None,
                                   optional_objects=None,
                                   other_data=None):
    """
    Creates a plugin builder function suitable to extract some specific
    objects (either compulsory or optional) and other simpler data

    >>> def dummy_needed_funct1(foo, bar):
    ...     pass
    >>> class dummy_needed_class1:
    ...     def __init__(self, baz):
    ...         pass
    >>> def dummy_optional_funct2(boo):
    ...     pass
    >>> create_enhanced_plugin_builder(
    ...    compulsory_objects={
    ...         'needed_funct1' : dummy_needed_funct1,
    ...         'needed_class1' : dummy_needed_class1
    ...     },
    ...     optional_objects={
    ...         'optional_funct2' : dummy_optional_funct2,
    ...     },
    ...     other_data={
    ...         'CFG_SOME_DATA' : (str, ''),
    ...         'CFG_SOME_INT' : (int, 0),
    ...     })
    <function plugin_builder at 0xb7812064>

    :param compulsory_objects: map of name of an object to look for inside
        the `plugin` and a *signature* for a class or callable. Every
        name specified in this map **must exists** in the plugin, otherwise
        the plugin will fail to load.
    :type compulsory_objects: dict
    :param optional_objects: map of name of an object to look for inside
        the C{plugin} and a I{signature} for a class or callable. Every
        name specified in this map must B{can exists} in the plugin.
    :type optional_objects: dict
    :param other_data: map of other simple data that can be loaded from
        the plugin. The map has the same format of the C{content}
        parameter of L{invenio_ext.legacy.handler.wash_urlargd}.
    :type other_data: dict
    :return: a I{plugin_builder} function that can be used with the
        map function. Such function will build the plugin
        in the form of a map, where every key is one of the keys inside
        the three maps provided as parameters and the corresponding value
        is the expected class or callable or simple data.
    """
    from invenio_utils.washers import wash_urlargd

    def plugin_builder(the_plugin):
        """
        Enhanced plugin_builder created by L{create_enhanced_plugin_builder}.

        :param plugin: the code of the module as just read from package.
        :return: the plugin in the form of a map.
        """
        plugin_name = the_plugin.__name__
        plugin = {}

        if compulsory_objects:
            for object_name, object_signature in \
                    iteritems(compulsory_objects):
                the_object = getattr(the_plugin, object_name, None)
                if the_object is None:
                    raise AutodiscoveryError(
                        'Plugin "%s" does not '
                        'contain compulsory object "%s"' %
                        (plugin_name, object_name))
                try:
                    check_signature(object_name, the_object, object_signature)
                except AutodiscoveryError as err:
                    raise AutodiscoveryError(
                        'Plugin "%s" contains '
                        'object "%s" with a wrong signature: %s' %
                        (plugin_name, object_name, err))
                plugin[object_name] = the_object

        if optional_objects:
            for object_name, object_signature in iteritems(optional_objects):
                the_object = getattr(the_plugin, object_name, None)
                if the_object is not None:
                    try:
                        check_signature(
                            object_name,
                            the_object,
                            object_signature)
                    except AutodiscoveryError as err:
                        raise AutodiscoveryError(
                            'Plugin "%s" '
                            'contains object "%s" with a wrong signature: %s' %
                            (plugin_name, object_name, err))
                    plugin[object_name] = the_object

        if other_data:
            the_other_data = {}
            for data_name, (dummy, data_default) in iteritems(other_data):
                the_other_data[data_name] = getattr(the_plugin, data_name,
                                                    data_default)

            try:
                the_other_data = wash_urlargd(the_other_data, other_data)
            except Exception as err:
                raise AutodiscoveryError(
                    'Plugin "%s" contains other '
                    'data with problems: %s' %
                    (plugin_name, err))

            plugin.update(the_other_data)
        return plugin

    return plugin_builder