From 42b4fe1d3a57664d00770011c290ba0fe7dc9c1c Mon Sep 17 00:00:00 2001 From: Tudor Plugaru Date: Sat, 9 Nov 2024 21:48:03 +0200 Subject: [PATCH] chore: Add copyright and fix missing type info Signed-off-by: Tudor Plugaru --- src/cloudevents/core/v1/__init__.py | 14 +++++++ src/cloudevents/core/v1/event.py | 54 +++++++++++++++------------ tests/test_core/__init__.py | 13 +++++++ tests/test_core/test_v1/__init__.py | 13 +++++++ tests/test_core/test_v1/test_event.py | 21 +++++++++-- 5 files changed, 87 insertions(+), 28 deletions(-) diff --git a/src/cloudevents/core/v1/__init__.py b/src/cloudevents/core/v1/__init__.py index a9fb8fb..896dfe1 100644 --- a/src/cloudevents/core/v1/__init__.py +++ b/src/cloudevents/core/v1/__init__.py @@ -1,3 +1,17 @@ +# Copyright 2018-Present The CloudEvents Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + """ CloudEvent implementation for v1.0 """ diff --git a/src/cloudevents/core/v1/event.py b/src/cloudevents/core/v1/event.py index eea6010..ec9abe0 100644 --- a/src/cloudevents/core/v1/event.py +++ b/src/cloudevents/core/v1/event.py @@ -12,34 +12,48 @@ # License for the specific language governing permissions and limitations # under the License. -from typing import Any, Optional +from typing import Any, Optional, Final from datetime import datetime import re -REQUIRED_ATTRIBUTES = {"id", "source", "type", "specversion"} -OPTIONAL_ATTRIBUTES = {"datacontenttype", "dataschema", "subject", "time"} +REQUIRED_ATTRIBUTES: Final[set[str]] = {"id", "source", "type", "specversion"} +OPTIONAL_ATTRIBUTES: Final[set[str]] = { + "datacontenttype", + "dataschema", + "subject", + "time", +} class CloudEvent: - def __init__(self, attributes: dict, data: Optional[dict] = None) -> None: + """ + The CloudEvent Python wrapper contract exposing generically-available + properties and APIs. + + Implementations might handle fields and have other APIs exposed but are + obliged to follow this contract. + """ + + def __init__(self, attributes: dict[str, Any], data: Optional[dict] = None) -> None: """ Create a new CloudEvent instance. :param attributes: The attributes of the CloudEvent instance. - :type attributes: dict :param data: The payload of the CloudEvent instance. - :type data: Optional[dict] :raises ValueError: If any of the required attributes are missing or have invalid values. :raises TypeError: If any of the attributes have invalid types. """ self._validate_attribute(attributes) - self._attributes = attributes - self._data = data + self._attributes: dict = attributes + self._data: Optional[dict] = data - def _validate_attribute(self, attributes: dict) -> None: + @staticmethod + def _validate_attribute(attributes: dict) -> None: """ - Private method that validates the attributes of the CloudEvent as per the CloudEvents specification. + Validates the attributes of the CloudEvent as per the CloudEvents specification. + + See https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md#required-attributes """ missing_attributes = [ attr for attr in REQUIRED_ATTRIBUTES if attr not in attributes @@ -95,27 +109,22 @@ class CloudEvent: if not attributes["dataschema"]: raise ValueError("Attribute 'dataschema' must not be empty") - for custom_extension in ( + for extension_attributes in ( set(attributes.keys()) - REQUIRED_ATTRIBUTES - OPTIONAL_ATTRIBUTES ): - if custom_extension == "data": + if extension_attributes == "data": raise ValueError( "Extension attribute 'data' is reserved and must not be used" ) - if not custom_extension[0].isalpha(): + if not (1 <= len(extension_attributes) <= 20): raise ValueError( - f"Extension attribute '{custom_extension}' should start with a letter" + f"Extension attribute '{extension_attributes}' should be between 1 and 20 characters long" ) - if not (5 <= len(custom_extension) <= 20): + if not re.match(r"^[a-z0-9]+$", extension_attributes): raise ValueError( - f"Extension attribute '{custom_extension}' should be between 5 and 20 characters long" - ) - - if not re.match(r"^[a-z0-9]+$", custom_extension): - raise ValueError( - f"Extension attribute '{custom_extension}' should only contain lowercase letters and numbers" + f"Extension attribute '{extension_attributes}' should only contain lowercase letters and numbers" ) def get_attribute(self, attribute: str) -> Optional[Any]: @@ -123,10 +132,8 @@ class CloudEvent: Retrieve a value of an attribute of the event denoted by the given `attribute`. :param attribute: The name of the event attribute to retrieve the value for. - :type attribute: str :return: The event attribute value. - :rtype: Optional[Any] """ return self._attributes[attribute] @@ -135,6 +142,5 @@ class CloudEvent: Retrieve data of the event. :return: The data of the event. - :rtype: Optional[dict] """ return self._data diff --git a/tests/test_core/__init__.py b/tests/test_core/__init__.py index e69de29..8043675 100644 --- a/tests/test_core/__init__.py +++ b/tests/test_core/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2018-Present The CloudEvents Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. diff --git a/tests/test_core/test_v1/__init__.py b/tests/test_core/test_v1/__init__.py index e69de29..8043675 100644 --- a/tests/test_core/test_v1/__init__.py +++ b/tests/test_core/test_v1/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2018-Present The CloudEvents Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. diff --git a/tests/test_core/test_v1/test_event.py b/tests/test_core/test_v1/test_event.py index fcf541e..2732978 100644 --- a/tests/test_core/test_v1/test_event.py +++ b/tests/test_core/test_v1/test_event.py @@ -1,3 +1,17 @@ +# Copyright 2018-Present The CloudEvents Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + from cloudevents.core.v1.event import CloudEvent import pytest @@ -162,14 +176,13 @@ def test_dataschema_validation(dataschema: Any, error: str) -> None: @pytest.mark.parametrize( "extension_name,error", [ - ("123", "Extension attribute '123' should start with a letter"), ( - "shrt", - "Extension attribute 'shrt' should be between 5 and 20 characters long", + "", + "Extension attribute '' should be between 1 and 20 characters long", ), ( "thisisaverylongextension", - "Extension attribute 'thisisaverylongextension' should be between 5 and 20 characters long", + "Extension attribute 'thisisaverylongextension' should be between 1 and 20 characters long", ), ( "ThisIsNotValid",