def consume(exchange, queue_name, routing_key, callback, app_name):
    """Consume messages from an AMQP queue using a Python callback."""
    # The configuration validates these are not null and contain all required keys
    # when it is loaded.

    bindings = config.conf["bindings"]
    queues = config.conf["queues"]

    # The CLI and config.DEFAULTS have different defaults for the queue
    # settings at the moment.  We should select a universal default in the
    # future and remove this. Unfortunately that will break backwards compatibility.
    if queues == config.DEFAULTS["queues"]:
        queues[config._default_queue_name]["durable"] = True
        queues[config._default_queue_name]["auto_delete"] = False

    if queue_name:
        queues = {queue_name: config.conf["queues"][config._default_queue_name]}
        for binding in bindings:
            binding["queue"] = queue_name

    if exchange:
        for binding in bindings:
            binding["exchange"] = exchange

    if routing_key:
        for binding in bindings:
            binding["routing_keys"] = routing_key

    callback_path = callback or config.conf["callback"]
    if not callback_path:
        raise click.ClickException(
            "A Python path to a callable object that accepts the message must be provided"
            ' with the "--callback" command line option or in the configuration file'
        )
    try:
        module, cls = callback_path.strip().split(":")
    except ValueError:
        raise click.ClickException(
            "Unable to parse the callback path ({}); the "
            'expected format is "my_package.module:'
            'callable_object"'.format(callback_path)
        )
    try:
        module = importlib.import_module(module)
    except ImportError as e:
        provider = "--callback argument" if callback else "configuration file"
        raise click.ClickException(
            "Failed to import the callback module ({}) provided in the {}".format(
                str(e), provider
            )
        )

    try:
        callback = getattr(module, cls)
    except AttributeError as e:
        raise click.ClickException(
            "Unable to import {} ({}); is the package installed? The python path should "
            'be in the format "my_package.module:callable_object"'.format(
                callback_path, str(e)
            )
        )

    if app_name:
        config.conf["client_properties"]["app"] = app_name

    _log.info("Starting consumer with %s callback", callback_path)
    try:
        deferred_consumers = api.twisted_consume(
            callback, bindings=bindings, queues=queues
        )
        deferred_consumers.addCallback(_consume_callback)
        deferred_consumers.addErrback(_consume_errback)
    except ValueError as e:
        click_version = pkg_resources.get_distribution("click").parsed_version
        if click_version < pkg_resources.parse_version("7.0"):
            raise click.exceptions.BadOptionUsage(str(e))
        else:
            raise click.exceptions.BadOptionUsage("callback", str(e))

    reactor.run()
    sys.exit(_exit_code)
Consume messages from an AMQP queue using a Python callback.