2121 DATACONTENTTYPE_ATTR ,
2222 decode_header_value ,
2323 encode_header_value ,
24+ get_event_factory_for_version ,
2425)
2526from cloudevents .core .formats .base import Format
2627from cloudevents .core .formats .json import JSONFormat
27- from cloudevents .core .v1 . event import CloudEvent
28+ from cloudevents .core .spec import SPECVERSION_V1_0
2829
2930CE_PREFIX : Final [str ] = "ce-"
3031
@@ -94,11 +95,14 @@ def to_binary(event: BaseCloudEvent, event_format: Format) -> HTTPMessage:
9495def from_binary (
9596 message : HTTPMessage ,
9697 event_format : Format ,
97- event_factory : EventFactory ,
98+ event_factory : EventFactory | None = None ,
9899) -> BaseCloudEvent :
99100 """
100101 Parse an HTTP binary content mode message to a CloudEvent.
101102
103+ Auto-detects the CloudEvents version from the 'ce-specversion' header
104+ and uses the appropriate event factory if not explicitly provided.
105+
102106 Extracts CloudEvent attributes from ce-prefixed HTTP headers and treats the
103107 'Content-Type' header as the 'datacontenttype' attribute. The HTTP body is
104108 parsed as event data according to the content type.
@@ -116,7 +120,7 @@ def from_binary(
116120
117121 :param message: HTTPMessage to parse
118122 :param event_format: Format implementation for data deserialization
119- :param event_factory: Factory function to create CloudEvent instances
123+ :param event_factory: Factory function to create CloudEvent instances (auto-detected if None)
120124 :return: CloudEvent instance
121125 """
122126 attributes : dict [str , Any ] = {}
@@ -130,6 +134,11 @@ def from_binary(
130134 elif normalized_name == CONTENT_TYPE_HEADER :
131135 attributes [DATACONTENTTYPE_ATTR ] = header_value
132136
137+ # Auto-detect version if factory not provided
138+ if event_factory is None :
139+ specversion = attributes .get ("specversion" , SPECVERSION_V1_0 )
140+ event_factory = get_event_factory_for_version (specversion )
141+
133142 datacontenttype = attributes .get (DATACONTENTTYPE_ATTR )
134143 data = event_format .read_data (message .body , datacontenttype )
135144
@@ -172,40 +181,51 @@ def to_structured(event: BaseCloudEvent, event_format: Format) -> HTTPMessage:
172181def from_structured (
173182 message : HTTPMessage ,
174183 event_format : Format ,
175- event_factory : EventFactory ,
184+ event_factory : EventFactory | None = None ,
176185) -> BaseCloudEvent :
177186 """
178187 Parse an HTTP structured content mode message to a CloudEvent.
179188
180189 Deserializes the CloudEvent from the HTTP body using the specified format.
181190 Any ce-prefixed headers are ignored as the body contains all event metadata.
182191
192+ If event_factory is not provided, version detection is delegated to the format
193+ implementation, which will auto-detect based on the 'specversion' field.
194+
183195 Example:
184196 >>> from cloudevents.core.v1.event import CloudEvent
185197 >>> from cloudevents.core.formats.json import JSONFormat
186198 >>>
199+ >>> # Explicit factory (recommended for performance)
187200 >>> message = HTTPMessage(
188201 ... headers={"content-type": "application/cloudevents+json"},
189202 ... body=b'{"type": "com.example.test", "source": "/test", ...}'
190203 ... )
191204 >>> event = from_structured(message, JSONFormat(), CloudEvent)
205+ >>>
206+ >>> # Auto-detect version (convenient)
207+ >>> event = from_structured(message, JSONFormat())
192208
193209 :param message: HTTPMessage to parse
194210 :param event_format: Format implementation for deserialization
195- :param event_factory: Factory function to create CloudEvent instances
211+ :param event_factory: Factory function to create CloudEvent instances.
212+ If None, the format will auto-detect the version.
196213 :return: CloudEvent instance
197214 """
215+ # Delegate version detection to format layer
198216 return event_format .read (event_factory , message .body )
199217
200218
201219def from_http (
202220 message : HTTPMessage ,
203221 event_format : Format ,
204- event_factory : EventFactory ,
222+ event_factory : EventFactory | None = None ,
205223) -> BaseCloudEvent :
206224 """
207225 Parse an HTTP message to a CloudEvent with automatic mode detection.
208226
227+ Auto-detects CloudEvents version and uses appropriate event factory if not provided.
228+
209229 Automatically detects whether the message uses binary or structured content mode:
210230 - If any ce- prefixed headers are present → binary mode
211231 - Otherwise → structured mode
@@ -233,7 +253,7 @@ def from_http(
233253
234254 :param message: HTTPMessage to parse
235255 :param event_format: Format implementation for deserialization
236- :param event_factory: Factory function to create CloudEvent instances
256+ :param event_factory: Factory function to create CloudEvent instances (auto-detected if None)
237257 :return: CloudEvent instance
238258 """
239259 if any (key .lower ().startswith (CE_PREFIX ) for key in message .headers .keys ()):
@@ -271,21 +291,23 @@ def to_binary_event(
271291def from_binary_event (
272292 message : HTTPMessage ,
273293 event_format : Format | None = None ,
274- ) -> CloudEvent :
294+ ) -> BaseCloudEvent :
275295 """
276- Convenience wrapper for from_binary with JSON format and CloudEvent as defaults.
296+ Convenience wrapper for from_binary with JSON format and auto-detection.
297+
298+ Auto-detects CloudEvents version (v0.3 or v1.0) from headers.
277299
278300 Example:
279301 >>> from cloudevents.core.bindings import http
280302 >>> event = http.from_binary_event(message)
281303
282304 :param message: HTTPMessage to parse
283305 :param event_format: Format implementation (defaults to JSONFormat)
284- :return: CloudEvent instance
306+ :return: CloudEvent instance (v0.3 or v1.0 based on specversion)
285307 """
286308 if event_format is None :
287309 event_format = JSONFormat ()
288- return from_binary (message , event_format , CloudEvent )
310+ return from_binary (message , event_format , None )
289311
290312
291313def to_structured_event (
@@ -317,39 +339,41 @@ def to_structured_event(
317339def from_structured_event (
318340 message : HTTPMessage ,
319341 event_format : Format | None = None ,
320- ) -> CloudEvent :
342+ ) -> BaseCloudEvent :
321343 """
322- Convenience wrapper for from_structured with JSON format and CloudEvent as defaults.
344+ Convenience wrapper for from_structured with JSON format and auto-detection.
345+
346+ Auto-detects CloudEvents version (v0.3 or v1.0) from body.
323347
324348 Example:
325349 >>> from cloudevents.core.bindings import http
326350 >>> event = http.from_structured_event(message)
327351
328352 :param message: HTTPMessage to parse
329353 :param event_format: Format implementation (defaults to JSONFormat)
330- :return: CloudEvent instance
354+ :return: CloudEvent instance (v0.3 or v1.0 based on specversion)
331355 """
332356 if event_format is None :
333357 event_format = JSONFormat ()
334- return from_structured (message , event_format , CloudEvent )
358+ return from_structured (message , event_format , None )
335359
336360
337361def from_http_event (
338362 message : HTTPMessage ,
339363 event_format : Format | None = None ,
340- ) -> CloudEvent :
364+ ) -> BaseCloudEvent :
341365 """
342- Convenience wrapper for from_http with JSON format and CloudEvent as defaults .
343- Auto-detects binary or structured mode.
366+ Convenience wrapper for from_http with JSON format and auto-detection .
367+ Auto-detects binary or structured mode, and CloudEvents version .
344368
345369 Example:
346370 >>> from cloudevents.core.bindings import http
347371 >>> event = http.from_http_event(message)
348372
349373 :param message: HTTPMessage to parse
350374 :param event_format: Format implementation (defaults to JSONFormat)
351- :return: CloudEvent instance
375+ :return: CloudEvent instance (v0.3 or v1.0 based on specversion)
352376 """
353377 if event_format is None :
354378 event_format = JSONFormat ()
355- return from_http (message , event_format , CloudEvent )
379+ return from_http (message , event_format , None )
0 commit comments