A decorator-based library for handling Google Cloud Pub/Sub messages with FastAPI integration, inspired by Micronaut's @PubSubListener
Project description
GCP PubSub Events
A decorator-based Python library for handling Google Cloud Pub/Sub messages, inspired by Micronaut's @PubSubListener pattern.
๐ Features
- Decorator-based: Clean, annotation-style API similar to Micronaut
- Context Manager Support: Proper lifecycle management with
withandasync withsyntax - FastAPI Integration: Seamless integration with FastAPI using lifespan events
- Automatic registration: Classes marked with
@pubsub_listenerare automatically registered - Type-safe event handling: Support for Pydantic models with automatic validation
- Async support: Handlers can be async or sync
- Error handling: Proper ack/nack based on handler success/failure
- Thread management: Automatic background thread handling with graceful shutdown
- Modular design: Well-organized package structure for maintainability
๐ฆ Installation
pip install gcp-pubsub-events
Or install from source:
git clone <repository-url>
cd gcp-pubsub-events
pip install -e .
๐๏ธ Project Structure
gcp_pubsub_events/
โโโ __init__.py # Main package exports
โโโ core/ # Core functionality
โ โโโ __init__.py
โ โโโ acknowledgement.py # Message acknowledgment handling
โ โโโ client.py # Main PubSub client
โ โโโ registry.py # Listener registry management
โโโ decorators/ # Decorator implementations
โ โโโ __init__.py
โ โโโ listener.py # @pubsub_listener decorator
โ โโโ subscription.py # @subscription decorator
โโโ utils/ # Utility functions
โ โโโ __init__.py
โ โโโ serialization.py # Event serialization utilities
โโโ exceptions/ # Custom exceptions
โโโ __init__.py
โโโ base.py
โโโ serialization.py
โโโ subscription.py
๐ Quick Start
1. Define Event Classes
from pydantic import BaseModel, Field, field_validator
class RegistrationEvent(BaseModel):
email: str = Field(..., description="User's email address")
user_id: str = Field(..., description="Unique user identifier")
timestamp: datetime = Field(default_factory=datetime.now)
@field_validator('email')
@classmethod
def validate_email(cls, v):
if '@' not in v:
raise ValueError('Invalid email format')
return v.lower()
2. Create a Listener Service
from gcp_pubsub_events import pubsub_listener, subscription, Acknowledgement
@pubsub_listener
class UserEventService:
def __init__(self, user_service):
self.user_service = user_service
@subscription("user.registered", RegistrationEvent)
async def on_user_registered(self, event: RegistrationEvent, acknowledgement: Acknowledgement):
try:
await self.user_service.create_profile(event.email, event.user_id)
acknowledgement.ack()
print(f"User registered: {event.email}")
except Exception as error:
acknowledgement.nack()
print(f"Error: {error}")
3. Start Listening
Option A: Context Manager (Recommended)
from gcp_pubsub_events import pubsub_manager
# Initialize your services
user_service = UserService()
user_event_service = UserEventService(user_service)
# Use context manager for automatic cleanup
with pubsub_manager("your-gcp-project-id") as manager:
print("PubSub listener started. Press Ctrl+C to stop.")
# Your application runs here
# Cleanup happens automatically when exiting the context
Option B: FastAPI Integration
from contextlib import asynccontextmanager
from fastapi import FastAPI
from gcp_pubsub_events import async_pubsub_manager
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup: Initialize PubSub
async with async_pubsub_manager("your-gcp-project-id") as manager:
app.state.pubsub = manager
yield
# Shutdown: Automatic cleanup
app = FastAPI(lifespan=lifespan)
@app.get("/")
def read_root():
return {"status": "running"}
Option C: Manual Management
from gcp_pubsub_events import create_pubsub_app
# Initialize your services
user_service = UserService()
user_event_service = UserEventService(user_service)
# Manual management (not recommended for production)
client = create_pubsub_app("your-gcp-project-id")
try:
client.start_listening()
except KeyboardInterrupt:
client.stop_listening()
๐ API Reference
Core Components
@pubsub_listener
Class decorator that marks a class as a PubSub listener. Instances are automatically registered.
@subscription(subscription_name, event_type=None)
Method decorator that marks a method as a subscription handler.
Parameters:
subscription_name: The GCP Pub/Sub subscription nameevent_type: Optional event class for automatic deserialization
Acknowledgement
Handles message acknowledgement.
Methods:
ack(): Acknowledge successful processingnack(): Negative acknowledge (mark as failed)acknowledged(property): Check if message was acknowledged
PubSubClient
Main client for managing subscriptions.
Methods:
start_listening(timeout=None): Start listening to all registered subscriptionsstop_listening(): Stop listening
PubSubManager (Recommended)
Enhanced manager with context manager support for proper lifecycle management.
Methods:
start(): Start the PubSub listener in a background threadstop(timeout=10.0): Stop listening with optional timeoutis_running(property): Check if manager is currently running
Context Manager Support:
# Sync context manager
with PubSubManager("project-id") as manager:
# Your code here
pass
# Async context manager
async with PubSubManager("project-id") as manager:
# Your async code here
pass
Factory Functions
create_pubsub_app(project_id, max_workers=10, max_messages=100)
Create and configure a PubSub application (legacy approach).
pubsub_manager(project_id, max_workers=5, max_messages=100, **flow_control_settings)
Context manager for PubSub operations.
async_pubsub_manager(project_id, max_workers=5, max_messages=100, **flow_control_settings)
Async context manager for PubSub operations (ideal for FastAPI).
๐ง Advanced Usage
Custom Event Validation
from pydantic import BaseModel, Field, field_validator
class PaymentEvent(BaseModel):
amount: float = Field(..., gt=0)
currency: str = Field(..., pattern="^[A-Z]{3}$")
user_id: str
@field_validator('amount')
@classmethod
def validate_amount(cls, v):
return round(v, 2) # Round to 2 decimal places
Error Handling
The library automatically handles acknowledgements based on handler success:
- If handler completes without exception:
ack()is called - If handler raises exception:
nack()is called - Manual acknowledgement is also supported
Sync and Async Handlers
@pubsub_listener
class EventService:
@subscription("sync.topic")
def sync_handler(self, event, acknowledgement):
# Synchronous processing
pass
@subscription("async.topic")
async def async_handler(self, event, acknowledgement):
# Asynchronous processing
await some_async_operation()
๐งช Testing
The library includes comprehensive testing support with the PubSub emulator:
# Install development dependencies
pip install -e .[dev]
# Start the emulator
gcloud beta emulators pubsub start --host-port=localhost:8085
# Set environment variable
export PUBSUB_EMULATOR_HOST=localhost:8085
# Run tests
pytest tests/ -v
# Run tests with coverage
pytest tests/ --cov=gcp_pubsub_events --cov-report=html
Test Coverage
We maintain high test coverage with comprehensive unit, integration, and end-to-end tests:
- Unit tests: Fast, isolated component tests
- Integration tests: Real PubSub emulator integration
- E2E tests: Complete workflow scenarios
- Performance tests: Throughput and latency benchmarks
Coverage reports are automatically generated and uploaded to Codecov.
๐ Examples
- Basic Example:
examples/basic_example.py- Simple usage with Pydantic models - Advanced Example:
examples/advanced_example.py- Complex validation and multiple event types
๐ ๏ธ Development
Setup Development Environment
git clone <repository-url>
cd gcp-pubsub-events
python -m venv venv
source venv/bin/activate # or `venv\Scripts\activate` on Windows
pip install -e ".[dev]"
Running Tests
# Install test dependencies
pip install pytest pytest-asyncio
# Run tests
pytest
๐ Requirements
- Python 3.7+
- google-cloud-pubsub>=2.0.0
- pydantic>=2.0.0
๐ค Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Submit a pull request
๐ License
MIT License - see LICENSE file for details.
๐ Links
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gcp_pubsub_events-1.0.1.tar.gz.
File metadata
- Download URL: gcp_pubsub_events-1.0.1.tar.gz
- Upload date:
- Size: 23.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.10.17
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8feb40b88703910bba7e298d9425cc465a4b7198c1ddedfa0aa70e21f7ba87d0
|
|
| MD5 |
2e490433647b520e4d655f73c4572809
|
|
| BLAKE2b-256 |
5623193cb6a7451c28b8bf3962302584423887874b4e4954f3a574e88cbfa45e
|
File details
Details for the file gcp_pubsub_events-1.0.1-py3-none-any.whl.
File metadata
- Download URL: gcp_pubsub_events-1.0.1-py3-none-any.whl
- Upload date:
- Size: 27.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.10.17
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
36f3f01cf53d266d93b7af3854995e8e5ef37e0595cb685d7cd58c6053c75c61
|
|
| MD5 |
834b962f687956fcfc9aaccea6503df3
|
|
| BLAKE2b-256 |
17ebfed3b176705607381e3828c8282b3f37c61e94d988308625207e6f5cd806
|