Skip to main content
ReadTheDocs CI Latest Version https://img.shields.io/pypi/pyversions/pikabus.svg https://img.shields.io/pypi/l/pikabus.svg

The PikaBus library is an asyncio message bus built on aio-pika, making it easy to implement the messages, events and command pattern, as described in detail here:

Features

  • Secure messaging with amqp enabled by default, which includes:
    • Durable queues and persistent messages, meaning no messages are lost after a node restart.

    • Delivery confirms with RabbitMq publisher confirms.

    • Mandatory delivery turned on by default, so an unroutable message raises rather than vanishing.

  • Object oriented API with short and easy-to-use interface.

  • Fault-tolerant, with automatic reconnection and consumer recovery handled by aio-pika’s robust connection, plus a retry policy with real exponential backoff and jitter.

  • Genuinely asynchronous: no threads, one connection multiplexed over a channel per consumer.

  • Message handlers may be async def or plain def.

  • Graceful shutdown on SIGINT/SIGTERM, letting in-flight messages finish and acknowledge.

Installation

pip install PikaBus

or with uv:

uv add PikaBus

Requires Python 3.11 or newer.

Example

import asyncio
import datetime
from PikaBus.abstractions.AbstractPikaBus import AbstractPikaBus
from PikaBus.PikaBusSetup import PikaBusSetup


async def MessageHandlerMethod(**kwargs):
    """
    A message handler method may simply be a method with some **kwargs.
    The **kwargs will be given all incoming pipeline data, the bus and the incoming payload.

    Make it `async def` if it publishes anything - the bus methods are coroutines.
    """
    data: dict = kwargs['data']
    bus: AbstractPikaBus = kwargs['bus']
    payload: dict = kwargs['payload']
    print(payload)
    if payload['reply']:
        payload['reply'] = False
        await bus.Reply(payload=payload)


async def Main():
    # Connection details are an amqp url. Alternatively pass host/port/login/password kwargs.
    async with PikaBusSetup('amqp://amqp:amqp@localhost:5672/',
                            defaultListenerQueue='myQueue',
                            defaultSubscriptions='myTopic') as pikaBusSetup:
        pikaBusSetup.AddMessageHandler(MessageHandlerMethod)

        # Start consuming messages from the queue.
        # Returns once the consumers are actually consuming, so the sends below cannot race it.
        await pikaBusSetup.StartConsumers()

        # Create a temporary bus to subscribe on topics and send, defer or publish messages.
        async with pikaBusSetup.CreateBus() as bus:
            await bus.Subscribe('myTopic')
            payload = {'hello': 'world!', 'reply': True}

            # To send a message means sending a message explicitly to one receiver.
            await bus.Send(payload=payload, queue='myQueue')

            # To defer a message means sending a message explicitly to one receiver with some
            # delay before it is processed.
            await bus.Defer(payload=payload, delay=datetime.timedelta(seconds=1), queue='myQueue')

            # To publish a message means publishing a message on a topic received by any
            # subscribers of the topic.
            await bus.Publish(payload=payload, topic='myTopic')

        await asyncio.to_thread(input, 'Hit enter to stop all consuming channels \n\n')
        await pikaBusSetup.StopConsumers()


if __name__ == '__main__':
    asyncio.run(Main())

Quick Start

Clone PikaBus repo:

git clone https://github.com/hansehe/PikaBus.git

Start local RabbitMq instance with Docker:

docker run -d --name rabbit -e RABBITMQ_DEFAULT_USER=amqp -e RABBITMQ_DEFAULT_PASS=amqp -p 5672:5672 -p 15672:15672 rabbitmq:4-management

Open RabbitMq admin (user=amqp, password=amqp) at:

http://localhost:15672/

Then, run the example:

pip install PikaBus
python ./Examples/basic_example.py

Try restarting RabbitMq to notice how PikaBus tolerates downtime:

docker stop rabbit
docker start rabbit

Send or publish more messages to the running PikaBus consumer with:

python ./Examples/send_example.py
python ./Examples/publish_example.py

Migrating from 1.x

Python 3.11+ is required. aio-pika 10.x sets that floor. If you are on Python 3.6 - 3.10, stay on PikaBus 1.x.

Connection parameters became a url. pika is no longer a dependency, so pika.ConnectionParameters is gone:

# 1.x
credentials = pika.PlainCredentials('amqp', 'amqp')
connParams = pika.ConnectionParameters(host='localhost', port=5672,
                                       virtual_host='/', credentials=credentials)
pikaBusSetup = PikaBusSetup(connParams, defaultListenerQueue='myQueue')

# 2.0
pikaBusSetup = PikaBusSetup('amqp://amqp:amqp@localhost:5672/', defaultListenerQueue='myQueue')
# or
pikaBusSetup = PikaBusSetup(host='localhost', port=5672, virtualHost='/',
                            login='amqp', password='amqp', defaultListenerQueue='myQueue')

Await everything that touches the broker, and swap with for async with:

# 1.x                              # 2.0
pikaBusSetup.Init()                 await pikaBusSetup.Init()
pikaBusSetup.StartConsumers()       await pikaBusSetup.StartConsumers()
pikaBusSetup.StopConsumers()        await pikaBusSetup.StopConsumers()
pikaBusSetup.LoopForever()          await pikaBusSetup.WaitUntilStopped()
with pikaBusSetup.CreateBus() as b: async with pikaBusSetup.CreateBus() as b:
bus.Send(...)                       await bus.Send(...)

Using with on a bus raises a TypeError telling you to use async with.

Handlers that publish must be async def. A plain def handler still works, but the bus methods are coroutines, so calling one without awaiting it silently does nothing. A synchronous handler also runs on the event loop and must not block.

Read incoming headers from the new key. aio-pika has no frame objects:

# 1.x
headers = data[PikaConstants.DATA_KEY_INCOMING_MESSAGE][PikaConstants.DATA_KEY_HEADER_FRAME].headers
# 2.0
headers = data[PikaConstants.DATA_KEY_INCOMING_MESSAGE][PikaConstants.DATA_KEY_HEADERS]
# the raw aio-pika message is also available
message = data[PikaConstants.DATA_KEY_INCOMING_MESSAGE][PikaConstants.DATA_KEY_MESSAGE]

DATA_KEY_HEADER_FRAME still resolves to a shim exposing .headers and .delivery_tag, but it emits a DeprecationWarning and is removed in 2.1.

Behaviour changes

  • Deferred messages now really wait. 1.x republished a not-yet-due message and acknowledged it immediately, with no delay - so a ten second Defer cost thousands of broker round trips, and every error-handler retry backoff did the same. 2.0 holds the message unacknowledged instead, in hops of at most maxDeferredSleep (5 minutes by default). Nothing is lost if the process dies mid-wait, since the message was never acknowledged. Note a waiting message occupies one prefetch slot.

  • ``defaultPrefetchCount`` is 10, not 0. In 1.x, 0 meant unlimited: one consumer would pull an entire backlog into memory. Raise it for throughput.

  • ``retryParams`` are honoured. 1.x read only tries and reconnected in a tight loop with no delay. delay, max_delay, backoff and jitter now work.

  • A poison message that also breaks the error handler is requeued once, then rejected, rather than requeued forever. It is discarded unless the queue declares x-dead-letter-exchange.

  • Sending no longer binds the destination on every message. Queues are bound to the direct exchange when they are declared, and other destinations are bound once per channel. A Send to a queue PikaBus never initialised now raises aio_pika.exceptions.DeliveryError instead of the old Queue X does not exist!. Inside a transaction the failure surfaces at CommitTransaction() rather than at Send().

  • ``HealthCheck()`` can now return ``False``. In 1.x it returned True even with no consumers running at all. It now verifies the consumer is registered and its task is alive. Treat it as a readiness probe - it is briefly False during a reconnect - or pass allowReconnecting=True for liveness.

  • ``StopConsumers()`` no longer poisons the instance. In 1.x it shut down the shared thread pool, so StartConsumers() could never work again.

  • ``ha-mode: all`` was dropped from the default queue arguments, because it never did anything. Classic queue mirroring was configured with policies, not queue arguments, and was removed entirely in RabbitMq 4.0. If you believed those queues were mirrored, they were not. For real redundancy use defaultListenerQueueSettings={'arguments': {'x-queue-type': 'quorum'}} - but only on a queue that does not exist yet, since x-queue-type is checked when redeclaring.

  • The published AMQP ``timestamp`` property is now correct. 1.x ran a UTC time through time.mktime(), which reads it as local time, so the timestamp was off by the machine’s UTC offset and by a different amount either side of a DST change.

  • Header timestamps are ISO 8601. PikaBus.TimeSent and PikaBus.DeferredTime are now written as e.g. 2026-08-07T16:32:19.123456+00:00 instead of 1.x’s 08/07/2026 16:32:19 - an ambiguous US-style format with no timezone and only second resolution. StringToDatetime returns a timezone-aware datetime, and Defer() now accepts sub-second delays. Reading accepts both formats, so this is safe for a rolling upgrade - see below. Pass PikaProperties(timeFormat='%m/%d/%Y %H:%M:%S') if you need the old strings written on the wire.

  • ``messsageTypeHeaderKey`` is spelled ``messageTypeHeaderKey``. The old name still works with a DeprecationWarning. The wire header was never misspelled.

  • Concurrency is opt-in. Each consumer processes messages serially by default, as in 1.x. Set defaultConcurrency above 1 only if your handlers are safe to run concurrently; doing so gives up per-queue ordering.

Upgrading a running deployment

The timestamp format changed, but StringToDatetime parses both the ISO 8601 and the 1.x format regardless of which one is configured for writing. So a 2.0 consumer reads messages a 1.x publisher is still producing, and messages already sitting in a queue are consumed normally. No drain and no staged rollout are required - deploy in any order.

The first time a fallback parse happens, PikaBus logs one warning per format:

Parsed an incoming timestamp as '%m/%d/%Y %H:%M:%S' after ISO 8601 failed.
Another PikaBus version is still publishing to this queue.

That is your signal that some publisher has not been upgraded yet. It is informational, not an error - the message is handled normally. It appears once per process, not once per message.

The one direction that cannot work is a 1.x consumer reading a 2.0 message, since 1.x has no fallback and its released code cannot be changed. If you must run 1.x consumers alongside 2.0 publishers for a while, pin the 2.0 publishers to the old format with PikaProperties(timeFormat='%m/%d/%Y %H:%M:%S') and drop that argument once the 1.x consumers are gone. Otherwise, upgrade consumers before publishers.

Contribute

License

The project is licensed under the MIT license.

Versioning

This software follows Semantic Versioning

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pikabus-2.0.0.tar.gz (44.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pikabus-2.0.0-py3-none-any.whl (46.3 kB view details)

Uploaded Python 3

File details

Details for the file pikabus-2.0.0.tar.gz.

File metadata

  • Download URL: pikabus-2.0.0.tar.gz
  • Upload date:
  • Size: 44.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for pikabus-2.0.0.tar.gz
Algorithm Hash digest
SHA256 38d84e7b86b67001353795271cfb69307799cff36747dd2c76762f7c275912ae
MD5 fbcbaee3d96596d3036651caa2451c29
BLAKE2b-256 5f366c8db65006374b5f0d12d99233a19dafca2ab475814a28ccddcdc682f596

See more details on using hashes here.

File details

Details for the file pikabus-2.0.0-py3-none-any.whl.

File metadata

  • Download URL: pikabus-2.0.0-py3-none-any.whl
  • Upload date:
  • Size: 46.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for pikabus-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c9984813212f45597cd30d0edcf62a2bf5b599afc54b1a2b84f1db23d4fe4750
MD5 1c7cedb1004a49cfe9955d32ea2136bc
BLAKE2b-256 9820377123331c8476ef1215316849cfec033ee413edd0b683d7884541b8eb28

See more details on using hashes here.

Release history Release notifications | RSS feed

2.0.1

2 files

This release

2.0.0 This release

2 files

1.3.7

1 file

1.3.6

1 file

1.3.5

1 file

1.3.4

1 file

1.3.3

1 file

1.3.2

1 file

1.3.1

1 file

1.3.0

1 file

1.2.32

1 file

1.2.31

1 file

1.2.30

1 file

1.2.29

1 file

1.2.28

1 file

1.2.27

1 file

1.2.26

1 file

1.2.25

1 file

1.2.24

1 file

1.2.23

1 file

1.2.22

1 file

1.2.21

1 file

1.2.20

1 file

1.2.19

1 file

1.2.18

1 file

1.2.17

1 file

1.2.16

1 file

1.2.15

1 file

1.2.14

1 file

1.2.13

1 file

1.2.12

1 file

1.2.11

1 file

1.2.10

1 file

1.2.9

1 file

1.2.8

1 file

1.2.7

1 file

1.2.6

1 file

1.2.5

1 file

1.2.4

1 file

1.2.3

1 file

1.2.2

1 file

1.2.1

1 file

1.2.0

1 file

1.1.1

1 file

1.1.0

1 file

1.0.17

1 file

1.0.16

1 file

1.0.15

1 file

1.0.14

1 file

1.0.13

1 file

1.0.12

1 file

1.0.11

1 file

1.0.10

1 file

1.0.9

1 file

1.0.8

1 file

1.0.7

1 file

1.0.6

1 file

1.0.5

1 file

1.0.4

1 file

1.0.3

1 file

1.0.2

1 file

1.0.1

1 file

1.0.0

1 file

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page