* chore: improve typing of functions returning AnyCloudEvent
kafka.conversion.from_binary() and from_structured() return
AnyCloudEvent type var according to their event_type argument, but when
event_type is None, type checkers cannot infer the return type. We now
use an overload to declare that the return type is http.CloudEvent when
event_type is None.
Previously users had to explicitly annotate this type when calling
without event_type. This happens quite a lot in this repo's
test_kafka_conversions.py — this fixes quite a few type errors like:
> error: Need type annotation for "result" [var-annotated]
Signed-off-by: Hal Blackburn <hwtb2@cam.ac.uk>
* chore: type v1.Event chainable Set*() methods
The v1.Event self-returning Set*() methods like SetData() were returning
BaseEvent, which doesn't declare the same Set* methods. As a result,
chaining more than one Set* method would make the return type unknown.
This was causing type errors in test_event_pipeline.py.
The Set*() methods now return the Self type.
Signed-off-by: Hal Blackburn <hwtb2@cam.ac.uk>
* chore: fix type errors in tests
mypy was failing with lots of type errors in test modules. I've not
annotated all fixtures, mostly fixed existing type errors.
Signed-off-by: Hal Blackburn <hwtb2@cam.ac.uk>
* chore: allow non-dict headers types in from_http()
from_http() conversion function was requiring its headers argument to
be a typing.Dict, which makes it incompatible with headers types of http
libraries, which support features like multiple values per key.
typing.Mapping and even _typeshed.SupportsItems do not cover these
types. For example,
samples/http-image-cloudevents/image_sample_server.py was failing to
type check where it calls `from_http(request.headers, ...)`.
To support these kind of headers types in from_http(), we now define our
own SupportsDuplicateItems protocol, which is broader than
_typeshed.SupportsItems.
I've only applied this to from_http(), as typing.Mapping is OK for most
other methods that accept dict-like objects, and using this more lenient
interface everywhere would impose restrictions on our implementation,
even though it might be more flexible for users.
Signed-off-by: Hal Blackburn <hwtb2@cam.ac.uk>
* build: run mypy via tox
Tox now runs mypy on cloudevents itself, and the samples.
Signed-off-by: Hal Blackburn <hwtb2@cam.ac.uk>
* build(ci): run mypy in CI alongside linting
Signed-off-by: Hal Blackburn <hwtb2@cam.ac.uk>
* chore: fix minor mypy type complaint in samples
Signed-off-by: Hal Blackburn <hwtb2@cam.ac.uk>
* feat: use Mapping, not Dict for input arguments
Mapping imposes less restrictions on callers, because it's read-only and
allows non-dict types to be passed without copying them as dict(), or
passing dict-like values and ignoring the resulting type error.
Signed-off-by: Hal Blackburn <hwtb2@cam.ac.uk>
* chore: fix tests on py3.8
Tests were failing because the sanic dependency dropped support for
py3.8 in its current release. sanic is now pinned to the last compatible
version for py3.8 only.
Signed-off-by: Hal Blackburn <hwtb2@cam.ac.uk>
* feat: support new model_validate_json() kwargs
Pydantic added by_alias and by_name keyword arguments to
BaseModel.model_validate_json in 2.11.1:
|
||
---|---|---|
.github | ||
cloudevents | ||
requirements | ||
samples | ||
.clomonitor.yml | ||
.coveragerc | ||
.gitignore | ||
.pre-commit-config.yaml | ||
CHANGELOG.md | ||
CONTRIBUTING.md | ||
LICENSE | ||
MAINTAINERS.md | ||
MANIFEST.in | ||
README.md | ||
RELEASING.md | ||
mypy.ini | ||
pypi_packaging.py | ||
pyproject.toml | ||
setup.py | ||
tox.ini |
README.md
Python SDK for CloudEvents
Status
This SDK is still considered a work in progress, therefore things might (and will) break with every update.
This SDK current supports the following versions of CloudEvents:
- v1.0
- v0.3
Python SDK
Package cloudevents provides primitives to work with CloudEvents specification: https://github.com/cloudevents/spec.
Installing
The CloudEvents SDK can be installed with pip:
pip install cloudevents
Sending CloudEvents
Below we will provide samples on how to send cloudevents using the popular
requests
library.
Binary HTTP CloudEvent
from cloudevents.http import CloudEvent
from cloudevents.conversion import to_binary
import requests
# Create a CloudEvent
# - The CloudEvent "id" is generated if omitted. "specversion" defaults to "1.0".
attributes = {
"type": "com.example.sampletype1",
"source": "https://example.com/event-producer",
}
data = {"message": "Hello World!"}
event = CloudEvent(attributes, data)
# Creates the HTTP request representation of the CloudEvent in binary content mode
headers, body = to_binary(event)
# POST
requests.post("<some-url>", data=body, headers=headers)
Structured HTTP CloudEvent
from cloudevents.conversion import to_structured
from cloudevents.http import CloudEvent
import requests
# Create a CloudEvent
# - The CloudEvent "id" is generated if omitted. "specversion" defaults to "1.0".
attributes = {
"type": "com.example.sampletype2",
"source": "https://example.com/event-producer",
}
data = {"message": "Hello World!"}
event = CloudEvent(attributes, data)
# Creates the HTTP request representation of the CloudEvent in structured content mode
headers, body = to_structured(event)
# POST
requests.post("<some-url>", data=body, headers=headers)
You can find a complete example of turning a CloudEvent into a HTTP request in the samples' directory.
Receiving CloudEvents
The code below shows how to consume a cloudevent using the popular python web framework flask:
from flask import Flask, request
from cloudevents.http import from_http
app = Flask(__name__)
# create an endpoint at http://localhost:/3000/
@app.route("/", methods=["POST"])
def home():
# create a CloudEvent
event = from_http(request.headers, request.get_data())
# you can access cloudevent fields as seen below
print(
f"Found {event['id']} from {event['source']} with type "
f"{event['type']} and specversion {event['specversion']}"
)
return "", 204
if __name__ == "__main__":
app.run(port=3000)
You can find a complete example of turning a CloudEvent into a HTTP request in the samples' directory.
SDK versioning
The goal of this package is to provide support for all released versions of CloudEvents, ideally while maintaining the same API. It will use semantic versioning with following rules:
- MAJOR version increments when backwards incompatible changes is introduced.
- MINOR version increments when backwards compatible feature is introduced INCLUDING support for new CloudEvents version.
- PATCH version increments when a backwards compatible bug fix is introduced.
Community
- There are bi-weekly calls immediately following the Serverless/CloudEvents call at 9am PT (US Pacific). Which means they will typically start at 10am PT, but if the other call ends early then the SDK call will start early as well. See the CloudEvents meeting minutes to determine which week will have the call.
- Slack: #cloudeventssdk channel under CNCF's Slack workspace.
- Email: https://lists.cncf.io/g/cncf-cloudevents-sdk
- Contact for additional information: Denis Makogon (
@denysmakogon
on slack).
Each SDK may have its own unique processes, tooling and guidelines, common
governance related material can be found in the
CloudEvents docs
directory. In particular, in there you will find information concerning
how SDK projects are
managed,
guidelines
for how PR reviews and approval, and our
Code of Conduct
information.
If there is a security concern with one of the CloudEvents specifications, or with one of the project's SDKs, please send an email to cncf-cloudevents-security@lists.cncf.io.
Additional SDK Resources
- List of current active maintainers
- How to contribute to the project
- SDK's License
- SDK's Release process
Maintenance
We use black and isort for autoformatting. We set up a tox environment to reformat the codebase.
e.g.
pip install tox
tox -e reformat
For information on releasing version bumps see RELEASING.md