CloudEvents SDK for Python with zero dependencies and complete protocol bindings
Project description
PyEventCloud
CloudEvents SDK for Python - A complete, type-safe implementation of the CloudEvents specification with zero dependencies.
Installation
pip install pyeventcloud
Quick Start
Creating CloudEvents
from pyeventcloud import CloudEvent, create_event
# Create with auto-generated ID
event = create_event(
source="https://example.com/source",
type="com.example.event",
data={"message": "Hello CloudEvents!"}
)
# Create with explicit ID
event = CloudEvent(
id="custom-id-123",
source="https://example.com/source",
type="com.example.event",
specversion="1.0",
datacontenttype="application/json",
data={"key": "value"}
)
# Access attributes directly
print(event.id) # custom-id-123
print(event.source) # https://example.com/source
print(event.data) # {'key': 'value'}
JSON Serialization
from pyeventcloud import to_json, from_json
# Serialize to JSON
json_str = to_json(event)
# Deserialize from JSON
event = from_json(json_str)
# Batch operations
from pyeventcloud import to_json_batch, from_json_batch
events = [event1, event2, event3]
json_str = to_json_batch(events)
events = from_json_batch(json_str)
HTTP Binding
from pyeventcloud import HTTPBinding, JSONFormatter
binding = HTTPBinding()
formatter = JSONFormatter()
# Structured content mode (event in body)
body, headers = binding.to_structured(event, formatter)
# body is bytes, headers is dict[str, str]
# Content-Type: application/cloudevents+json
# Binary content mode (attributes in headers)
body, headers = binding.to_binary(event)
# headers["ce-id"], headers["ce-source"], etc.
# body contains event data
# Parse from HTTP request
event = binding.from_structured(request.body, request.headers, formatter)
event = binding.from_binary(request.body, request.headers)
Kafka Binding
from pyeventcloud import KafkaBinding, JSONFormatter
binding = KafkaBinding()
formatter = JSONFormatter()
# Structured content mode
message = binding.to_structured(event, formatter)
# Returns: KafkaMessage with value, headers, key
# Binary content mode
message = binding.to_binary(event)
# message["value"]: bytes
# message["headers"]: list[tuple[str, bytes]]
# message["key"]: bytes | None
# 'partitionkey' extension maps to Kafka message key
event = CloudEvent(
id="123",
source="app",
type="order.created",
partitionkey="customer-456" # Maps to Kafka partition key
)
message = binding.to_binary(event)
# message["key"] == b"customer-456"
Extensions
from pyeventcloud import CloudEvent, register_extension
# Built-in partitionkey extension
event = CloudEvent(
id="123",
source="app",
type="event.type",
partitionkey="partition-1" # Built-in extension
)
# Custom extensions
event = CloudEvent(
id="123",
source="app",
type="event.type",
customextension="value" # Any custom extension
)
# Access extensions
print(event.extensions["customextension"]) # "value"
# Register custom extension
class MyExtension:
name = "myext"
attributes = {"myfield": str}
def validate(self, event):
value = event.extensions.get("myfield")
if value and not isinstance(value, str):
raise ValidationError("myfield must be a string")
def get_attributes(self, event):
return {"myfield": event.extensions.get("myfield")}
register_extension(MyExtension())
Factory Pattern
from pyeventcloud import JSONFormatter, from_dict
formatter = JSONFormatter()
# Custom factory for event creation
def custom_factory(data: dict) -> CloudEvent:
# Add metadata or transform data
data["id"] = f"prefix-{data['id']}"
return from_dict(data)
event = formatter.deserialize(json_str, event_factory=custom_factory)
API Reference
Core
CloudEvent- Main CloudEvent classcreate_event()- Factory function with auto-generated IDfrom_dict()- Create CloudEvent from dictionary
Formats
JSONFormatter- JSON serialization/deserializationto_json()- Serialize single event to JSONfrom_json()- Deserialize single event from JSONto_json_batch()- Serialize multiple eventsfrom_json_batch()- Deserialize multiple events
Bindings
HTTPBinding- HTTP protocol binding (structured & binary modes)KafkaBinding- Kafka protocol binding (structured & binary modes)EventSerializer- Protocol for custom serializers
Extensions
PartitioningExtension- Built-in partitionkey extensionregister_extension()- Register custom extensionget_extension()- Get registered extensionExtensionRegistry- Extension registry class
Exceptions
CloudEventError- Base exceptionValidationError- Validation failuresSerializationError- Serialization/deserialization errorsBindingError- Protocol binding errorsExtensionError- Extension-related errors
Design Philosophy
Fail-Fast Validation
PyEventCloud validates events immediately when created, raising clear errors:
# Invalid event raises ValidationError immediately
try:
event = CloudEvent(
id="", # Empty ID
source="https://example.com",
type="event.type"
)
except ValidationError as e:
print(e) # "id must be a non-empty string"
print(e.attribute) # "id"
Format Decoupling
Bindings are completely decoupled from formats via the EventSerializer protocol:
# Any serializer implementing the protocol works
binding = HTTPBinding()
body, headers = binding.to_structured(event, JSONFormatter())
body, headers = binding.to_structured(event, AvroFormatter()) # Future
body, headers = binding.to_structured(event, ProtobufFormatter()) # Future
Development
Setup
# Install uv if you haven't already
curl -LsSf https://astral.sh/uv/install.sh | sh
# Clone the repository
git clone https://github.com/plugarut/pyeventcloud.git
cd pyeventcloud
# Install dependencies
uv sync --dev
Running Tests
# Run all tests
uv run pytest
# Run with coverage
uv run pytest --cov=src/pyeventcloud --cov-report=term-missing
# Run type checking
uv run mypy src/
# Run specific test file
uv run pytest tests/unit/test_event.py -v
Building
# Build distribution packages
uv build
# Check build artifacts
ls -lh dist/
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass and mypy is happy
- Submit a pull request
License
Apache License 2.0 - See LICENSE for details.
Acknowledgments
- Inspired by the CloudEvents Python SDK
- Implements CloudEvents v1.0
- HTTP binding follows HTTP Protocol Binding
- Kafka binding follows Kafka Protocol Binding
Links
Project details
Release history Release notifications | RSS feed
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 pyeventcloud-0.1.0.tar.gz.
File metadata
- Download URL: pyeventcloud-0.1.0.tar.gz
- Upload date:
- Size: 20.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
97530fc8216a6a4779a62f270c37260896f1bc345d9966ff793ccfec2b4a48a2
|
|
| MD5 |
7c2d90819d8a0e932f430b1447fc76f7
|
|
| BLAKE2b-256 |
5c19f488b82fff88b10a89ff8dc0ec31822ce3dec08e8f80ec0c6cc417812451
|
Provenance
The following attestation bundles were made for pyeventcloud-0.1.0.tar.gz:
Publisher:
publish.yaml on PlugaruT/pyeventcloud
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyeventcloud-0.1.0.tar.gz -
Subject digest:
97530fc8216a6a4779a62f270c37260896f1bc345d9966ff793ccfec2b4a48a2 - Sigstore transparency entry: 598596485
- Sigstore integration time:
-
Permalink:
PlugaruT/pyeventcloud@2823f9263e34bbf072b19aa590c2b6246cf89397 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/PlugaruT
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@2823f9263e34bbf072b19aa590c2b6246cf89397 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pyeventcloud-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pyeventcloud-0.1.0-py3-none-any.whl
- Upload date:
- Size: 30.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6264d97f06e3764e16c76f7c4ed67d6f3c280a9d1eb301dade42fdfb1aeb6aa6
|
|
| MD5 |
122433e13870936e9e06e1eb3fc0ccfb
|
|
| BLAKE2b-256 |
e642696774490e15c965e971794edb515c366e8d6203bd94c6ce743c2eb0ffc3
|
Provenance
The following attestation bundles were made for pyeventcloud-0.1.0-py3-none-any.whl:
Publisher:
publish.yaml on PlugaruT/pyeventcloud
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyeventcloud-0.1.0-py3-none-any.whl -
Subject digest:
6264d97f06e3764e16c76f7c4ed67d6f3c280a9d1eb301dade42fdfb1aeb6aa6 - Sigstore transparency entry: 598596488
- Sigstore integration time:
-
Permalink:
PlugaruT/pyeventcloud@2823f9263e34bbf072b19aa590c2b6246cf89397 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/PlugaruT
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@2823f9263e34bbf072b19aa590c2b6246cf89397 -
Trigger Event:
push
-
Statement type: