From 702915968dbf84fa8f78020842bf3090ff6ffb62 Mon Sep 17 00:00:00 2001 From: "Jean Pierre Mandujano G." Date: Sun, 19 Jul 2026 14:49:15 -0500 Subject: [PATCH] docs: clarify abstract.CloudEvent docstring Explain the attributes-vs-data split, list the required and optional CloudEvents v1.0 attributes with a spec reference, and add a usage example. Document why the mapping interface (__getitem__/get) exposes context attributes only and why the payload must be read via .data. Fixes #247 Signed-off-by: Jean Pierre Mandujano G. --- src/cloudevents/v1/abstract/event.py | 55 +++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/src/cloudevents/v1/abstract/event.py b/src/cloudevents/v1/abstract/event.py index 18c6df19..7207aa68 100644 --- a/src/cloudevents/v1/abstract/event.py +++ b/src/cloudevents/v1/abstract/event.py @@ -25,6 +25,48 @@ class CloudEvent: The CloudEvent Python wrapper contract exposing generically-available properties and APIs. + A CloudEvent describes event data in a common, vendor-neutral way, as + defined by the CloudEvents specification + (https://github.com/cloudevents/spec). Every event is made of two + distinct parts: + + - Attributes (also called context attributes): metadata describing the + event. The CloudEvents v1.0 specification defines the required + attributes `id`, `source`, `type` and `specversion`, together with the + optional attributes `datacontenttype`, `dataschema`, `subject` and + `time`. Concrete implementations may also carry user-defined extension + attributes. + - Data: the optional event payload. The payload is not an attribute and is + kept separate from the attribute mapping. + + This class only defines the read-only contract shared by every concrete + implementation (for example `cloudevents.v1.http.CloudEvent` or the + Pydantic variants). It is abstract and cannot be instantiated directly; + use a concrete subclass or its `create` factory instead. + + Attributes can be read through a read-only, mapping-like interface + (`__getitem__`, `get`, `__iter__`, `__len__` and `__contains__`). That + interface exposes the attributes only: the event `data` is never part of + it and must be read through the `.data` accessor (or `get_data`). This is + why `event["data"]` raises a `KeyError` and `event.get("data")` returns the + default instead of the payload. + + Example: + >>> from cloudevents.v1.http import CloudEvent + >>> event = CloudEvent( + ... { + ... "type": "com.example.sampletype1", + ... "source": "https://example.com/event-producer", + ... }, + ... {"message": "Hello World!"}, + ... ) + >>> event["type"] + 'com.example.sampletype1' + >>> event.data + {'message': 'Hello World!'} + >>> "data" in event # `data` is the payload, not an attribute + False + Implementations might handle fields and have other APIs exposed but are obliged to follow this contract. """ @@ -96,11 +138,15 @@ def __getitem__(self, key: str) -> typing.Any: """ Returns a value of an attribute of the event denoted by the given `key`. - The `data` of the event should be accessed by the `.data` accessor rather - than this mapping. + This mapping-style accessor exposes the event's context attributes + only. The event `data` (its payload) is not an attribute and is + deliberately not reachable this way; it must be read through the + `.data` accessor (or `get_data`). Requesting `event["data"]` therefore + raises a `KeyError` rather than returning the payload. :param key: The name of the event attribute to retrieve the value for. :returns: The event attribute value. + :raises KeyError: If no attribute with the given `key` exists. """ return self._get_attributes()[key] @@ -110,6 +156,11 @@ def get( """ Retrieves an event attribute value for the given `key`. + Like `__getitem__`, this operates over the event's context attributes + only and never over the event `data`; `event.get("data")` returns the + `default` rather than the payload, which must be read through the + `.data` accessor (or `get_data`). + Returns the `default` value if the attribute for the given key does not exist. The implementation MUST NOT throw an error when the key does not exist, but