Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 53 additions & 2 deletions src/cloudevents/v1/abstract/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
"""
Expand Down Expand Up @@ -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]

Expand All @@ -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
Expand Down