GCP Pub/Sub DAO
The library provides DAO classes for GCP pubsub publisher/subscriber.
Installation
pip install gcp-pubsub-dao
Usage
- sync subscriber:
from gcp_pubsub_dao import PubSubSubscriberDAO, Message
dao = PubSubSubscriberDAO(project_id="prodect-dev", subscription_id="subscription")
messages: Message = dao.get_messages(messages_count=2)
for message in messages:
print(message.data)
dao.ack_messages(ack_ids=[message[0].ack_id])
dao.nack_messages(ack_ids=[message[1].ack_id])
dao.close() # to clean up connections
- sync publisher:
from gcp_pubsub_dao import PubSubPublisherDAO
dao = PubSubPublisherDAO(project_id="prodect-dev")
try:
dao.publish_message(topic_name="topic", payload=b"asdfsdf", attributes={"kitId": "AW12345678"})
except Exception as ex:
print(ex)
- async subscriber:
from gcp_pubsub_dao import AsyncPubSubSubscriberDAO, Message
dao = AsyncPubSubSubscriberDAO(project_id="prodect-dev", subscription_id="subscription")
messages: Message = await dao.get_messages(messages_count=2)
for message in messages:
print(message.data)
await dao.ack_messages(ack_ids=[message[0].ack_id])
await dao.nack_messages(ack_ids=[message[1].ack_id])
- async publisher:
from gcp_pubsub_dao import AsyncPubSubPublisherDAO
dao = AsyncPubSubPublisherDAO(project_id="prodect-dev")
try:
await dao.publish_message(topic_name="topic", payload=b"asdfsdf", attributes={"kitId": "AW12345678"})
except Exception as ex:
print(ex)
- async worker pool
import asyncio
import sys
sys.path.append("./")
from gcp_pubsub_dao import AsyncPubSubSubscriberDAO
from gcp_pubsub_dao.worker_pool import WorkerPool, WorkerTask, HandlerResult
from gcp_pubsub_dao.entities import Message
async def handler1(message: Message):
print(f"handler1: {message}")
await asyncio.sleep(2)
return HandlerResult(ack_id=message.ack_id, is_success=True)
async def handler2(message: Message):
print(f"handler2: {message}")
await asyncio.sleep(5)
return HandlerResult(ack_id=message.ack_id, is_success=True)
def heartbeat_func():
print("Heartbeat: Worker is alive")
async def main():
tasks = [
WorkerTask(
subscriber_dao=AsyncPubSubSubscriberDAO(project_id="ash-dev-273120", subscription_id="http-sender-sub"),
handler=handler1,
),
WorkerTask(
subscriber_dao=AsyncPubSubSubscriberDAO(project_id="ash-dev-273120", subscription_id="email-sender-sub"),
handler=handler2,
),
]
# Create worker pool with heartbeat function
wp = WorkerPool(heartbeat_func=heartbeat_func)
# Run in async mode (default) - all tasks run concurrently
await wp.run(tasks=tasks)
# Or run in sync mode - tasks run one by one in order
# await wp.run(tasks=tasks, mode="sync")
if __name__ == "__main__":
asyncio.run(main())
Worker Pool Features
The WorkerPool provides two execution modes:
Async Mode (default)
- All tasks run concurrently using
asyncio.TaskGroup - Tasks can execute in any order or simultaneously
- Best for independent tasks that don't need to be processed in sequence
Sync Mode
- Tasks run one by one in the order they are provided
- Each task completes before the next one starts
- Useful when tasks need to be processed in a specific sequence
- Note: Message processing within each task is still asynchronous
Heartbeat Function
- Optional callback function that gets called during worker execution
- Useful for monitoring worker health and activity
- Called before processing messages in each iteration
- Can be used for logging, metrics, or health checks
WorkerTask Configuration
subscriber_dao: The async subscriber DAO instancehandler: Async function that processes messages and returnsHandlerResultbatch_size: Number of messages to fetch per batch (default: 10)return_immediately: Whether to return immediately if no messages (default: False)
Event Dispatcher
EventDispatcher lets a consumer run one worker against a single (unfiltered) subscription and route
each message to a handler by event name, instead of running one worker per filtered subscription.
The event name is read from the event message attribute and looked up in the handler registry:
| Case | Behavior |
|---|---|
| Event has a registered handler | The handler is awaited and its HandlerResult is returned unchanged — ack on success, nack on failure, same as a plain WorkerTask handler. Exceptions propagate to the worker pool as before. |
| Event has no registered handler (or the attribute is missing) | The message is acked immediately and silently — no log, no retry, no dead-lettering. |
The dispatcher is itself a handler, so it plugs straight into WorkerTask:
from gcp_pubsub_dao import AsyncPubSubSubscriberDAO, EventDispatcher, WorkerPool, WorkerTask
dispatcher = EventDispatcher(
handlers={
"kit-accessioned": kit_accessioned_handler.handle,
"kit-issue": kit_issue_handler.handle,
},
)
task = WorkerTask(
subscriber_dao=AsyncPubSubSubscriberDAO(project_id="ash-dev-273120", subscription_id="order-task-my-service"),
handler=dispatcher,
)
await WorkerPool().run(tasks=[task])
Options
handlers: Mapping of event name → async handler (Callable[[Message], Awaitable[HandlerResult]]). The mapping is copied on init, so later changes to the original dict have no effect.event_attribute: Message attribute holding the event name (default:"event").
Shadow mode
ShadowModeEventHandler is a handler that does no real work: it logs the kit id, event name and message id
at INFO level and acks the message. Register it in the dispatcher in place of the real handlers to
validate a new subscription's routing against live traffic without processing anything:
from loguru import logger
from gcp_pubsub_dao import EventDispatcher, ShadowModeEventHandler
shadow_handler = ShadowModeEventHandler(logger)
dispatcher = EventDispatcher(handlers={event: shadow_handler for event in real_handlers})
Each message routed to it produces a single INFO record with the context passed as kwargs
(with loguru they end up in record["extra"]):
logger.info("Shadow dispatch", kit_id="AW12345678", event="kit-accessioned", message_id="1234567890")
Messages that have no entry in the registry never reach the shadow handler, so they are acked silently and not logged — exactly as they would be once real handlers are in place.
Options:
logger: Any object withinfo(msg, **kwargs)andwarning(msg, **kwargs)methods accepting arbitrary keyword context — e.g.loguru.logger. The library does not depend on loguru. Note that the stdliblogging.Loggerdoes not accept arbitrary kwargs, so wrap it in an adapter if you need to use it.event_attribute: Message attribute holding the event name (default:"event").kit_id_getter: Callable extracting the kit id from a message. By default it uses thekitIdattribute, then thekit_idfield of the JSON payload, otherwiseNone. If the getter raises, a warning is logged (witherrorandmessage_idkwargs),kit_id=Noneis reported and the message is still acked.
Metadata
Release files for gcp-pubsub-dao 0.6.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| gcp_pubsub_dao-0.6.2.tar.gz | 68.3 kB | Without provenance | Details for gcp_pubsub_dao-0.6.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| gcp_pubsub_dao-0.6.2-py3-none-any.whl | Python 3 | none | any | Details for gcp_pubsub_dao-0.6.2-py3-none-any.whl |
Total release size: 79.6 kB
Release files / gcp_pubsub_dao-0.6.2.tar.gz
| Download URL | gcp_pubsub_dao-0.6.2.tar.gz |
|---|---|
| Size | 68.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fb9e247f87c479d71938f82ee1edac94354d4497d541b6523a9c346d8e67ba46
|
|
BLAKE2b-256 checksum How to use checksums |
ab6000181bafb9174a353c632369cde66b8bf567e4b22c7e7ceeaa3641d3003d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / gcp_pubsub_dao-0.6.2-py3-none-any.whl
| Download URL | gcp_pubsub_dao-0.6.2-py3-none-any.whl |
|---|---|
| Size | 11.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
42d5f3b3d588930c0ff396de65816f1d76fdd786bc3e33b1dccccb718e37c3d7
|
|
BLAKE2b-256 checksum How to use checksums |
a0861221481e3ee499eabfac3d768a5168c9b341f570d883c57b8d36695d0e04
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for gcp-pubsub-dao 0.6.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.