Skip to content

Introduce typings #207

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Jan 4, 2023
Merged
Show file tree
Hide file tree
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
16 changes: 13 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-toml
- repo: https://github.com/pycqa/isort
rev: 5.10.1
rev: 5.11.4
hooks:
- id: isort
args: [ "--profile", "black", "--filter-files" ]
- repo: https://github.com/psf/black
rev: 22.10.0
rev: 22.12.0
hooks:
- id: black
language_version: python3.10
- repo: https://github.com/pre-commit/mirrors-mypy
rev: "v0.991"
hooks:
- id: mypy
files: ^(cloudevents/)
exclude: ^(cloudevents/tests/)
types: [ python ]
args: [ ]
additional_dependencies:
- 'pydantic'
4 changes: 4 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
include README.md
include CHANGELOG.md
include LICENSE
include cloudevents/py.typed
2 changes: 1 addition & 1 deletion cloudevents/abstract/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@

from cloudevents.abstract.event import AnyCloudEvent, CloudEvent

__all__ = [AnyCloudEvent, CloudEvent]
__all__ = ["AnyCloudEvent", "CloudEvent"]
15 changes: 7 additions & 8 deletions cloudevents/abstract/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
from types import MappingProxyType
from typing import Mapping

AnyCloudEvent = typing.TypeVar("AnyCloudEvent", bound="CloudEvent")


class CloudEvent:
"""
Expand All @@ -29,10 +31,10 @@ class CloudEvent:

@classmethod
def create(
cls,
cls: typing.Type[AnyCloudEvent],
attributes: typing.Dict[str, typing.Any],
data: typing.Optional[typing.Any],
) -> "AnyCloudEvent":
) -> AnyCloudEvent:
"""
Creates a new instance of the CloudEvent using supplied `attributes`
and `data`.
Expand Down Expand Up @@ -70,7 +72,7 @@ def _get_attributes(self) -> typing.Dict[str, typing.Any]:
raise NotImplementedError()

@abstractmethod
def _get_data(self) -> typing.Optional[typing.Any]:
def get_data(self) -> typing.Optional[typing.Any]:
"""
Returns the data of the event.

Expand All @@ -85,7 +87,7 @@ def _get_data(self) -> typing.Optional[typing.Any]:

def __eq__(self, other: typing.Any) -> bool:
if isinstance(other, CloudEvent):
same_data = self._get_data() == other._get_data()
same_data = self.get_data() == other.get_data()
same_attributes = self._get_attributes() == other._get_attributes()
return same_data and same_attributes
return False
Expand Down Expand Up @@ -140,7 +142,4 @@ def __contains__(self, key: str) -> bool:
return key in self._get_attributes()

def __repr__(self) -> str:
return str({"attributes": self._get_attributes(), "data": self._get_data()})


AnyCloudEvent = typing.TypeVar("AnyCloudEvent", bound=CloudEvent)
return str({"attributes": self._get_attributes(), "data": self.get_data()})
59 changes: 26 additions & 33 deletions cloudevents/conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from cloudevents.sdk.event import v1, v03


def _best_effort_serialize_to_json(
def _best_effort_serialize_to_json( # type: ignore[no-untyped-def]
value: typing.Any, *args, **kwargs
) -> typing.Optional[typing.Union[bytes, str, typing.Any]]:
"""
Expand All @@ -43,18 +43,18 @@ def _best_effort_serialize_to_json(
return value


_default_marshaller_by_format = {
_default_marshaller_by_format: typing.Dict[str, types.MarshallerType] = {
converters.TypeStructured: lambda x: x,
converters.TypeBinary: _best_effort_serialize_to_json,
} # type: typing.Dict[str, types.MarshallerType]
}

_obj_by_version = {"1.0": v1.Event, "0.3": v03.Event}


def to_json(
event: AnyCloudEvent,
data_marshaller: types.MarshallerType = None,
) -> typing.Union[str, bytes]:
data_marshaller: typing.Optional[types.MarshallerType] = None,
) -> bytes:
"""
Converts given `event` to a JSON string.

Expand All @@ -69,7 +69,7 @@ def to_json(
def from_json(
event_type: typing.Type[AnyCloudEvent],
data: typing.Union[str, bytes],
data_unmarshaller: types.UnmarshallerType = None,
data_unmarshaller: typing.Optional[types.UnmarshallerType] = None,
) -> AnyCloudEvent:
"""
Parses JSON string `data` into a CloudEvent.
Expand All @@ -91,9 +91,9 @@ def from_json(

def from_http(
event_type: typing.Type[AnyCloudEvent],
headers: typing.Dict[str, str],
data: typing.Union[str, bytes, None],
data_unmarshaller: types.UnmarshallerType = None,
headers: typing.Mapping[str, str],
data: typing.Optional[typing.Union[str, bytes]],
data_unmarshaller: typing.Optional[types.UnmarshallerType] = None,
) -> AnyCloudEvent:
"""
Parses CloudEvent `data` and `headers` into an instance of a given `event_type`.
Expand Down Expand Up @@ -133,14 +133,14 @@ def from_http(
except json.decoder.JSONDecodeError:
raise cloud_exceptions.MissingRequiredFields(
"Failed to read specversion from both headers and data. "
f"The following can not be parsed as json: {data}"
"The following can not be parsed as json: {!r}".format(data)
)
if hasattr(raw_ce, "get"):
specversion = raw_ce.get("specversion", None)
else:
raise cloud_exceptions.MissingRequiredFields(
"Failed to read specversion from both headers and data. "
f"The following deserialized data has no 'get' method: {raw_ce}"
"The following deserialized data has no 'get' method: {}".format(raw_ce)
)

if specversion is None:
Expand All @@ -152,7 +152,7 @@ def from_http(

if event_handler is None:
raise cloud_exceptions.InvalidRequiredFields(
f"Found invalid specversion {specversion}"
"Found invalid specversion {}".format(specversion)
)

event = marshall.FromRequest(
Expand All @@ -163,20 +163,19 @@ def from_http(
attrs.pop("extensions", None)
attrs.update(**event.extensions)

result_data: typing.Optional[typing.Any] = event.data
if event.data == "" or event.data == b"":
# TODO: Check binary unmarshallers to debug why setting data to ""
# returns an event with data set to None, but structured will return ""
data = None
else:
data = event.data
return event_type.create(attrs, data)
# returns an event with data set to None, but structured will return ""
result_data = None
return event_type.create(attrs, result_data)


def _to_http(
event: AnyCloudEvent,
format: str = converters.TypeStructured,
data_marshaller: types.MarshallerType = None,
) -> typing.Tuple[dict, typing.Union[bytes, str]]:
data_marshaller: typing.Optional[types.MarshallerType] = None,
) -> typing.Tuple[typing.Dict[str, str], bytes]:
"""
Returns a tuple of HTTP headers/body dicts representing this Cloud Event.

Expand All @@ -196,7 +195,7 @@ def _to_http(
event_handler = _obj_by_version[event["specversion"]]()
for attribute_name in event:
event_handler.Set(attribute_name, event[attribute_name])
event_handler.data = event.data
event_handler.data = event.get_data()

return marshaller.NewDefaultHTTPMarshaller().ToRequest(
event_handler, format, data_marshaller=data_marshaller
Expand All @@ -205,8 +204,8 @@ def _to_http(

def to_structured(
event: AnyCloudEvent,
data_marshaller: types.MarshallerType = None,
) -> typing.Tuple[dict, typing.Union[bytes, str]]:
data_marshaller: typing.Optional[types.MarshallerType] = None,
) -> typing.Tuple[typing.Dict[str, str], bytes]:
"""
Returns a tuple of HTTP headers/body dicts representing this Cloud Event.

Expand All @@ -222,8 +221,8 @@ def to_structured(


def to_binary(
event: AnyCloudEvent, data_marshaller: types.MarshallerType = None
) -> typing.Tuple[dict, typing.Union[bytes, str]]:
event: AnyCloudEvent, data_marshaller: typing.Optional[types.MarshallerType] = None
) -> typing.Tuple[typing.Dict[str, str], bytes]:
"""
Returns a tuple of HTTP headers/body dicts representing this Cloud Event.

Expand Down Expand Up @@ -287,19 +286,13 @@ def to_dict(event: AnyCloudEvent) -> typing.Dict[str, typing.Any]:
:returns: The canonical dict representation of the event.
"""
result = {attribute_name: event.get(attribute_name) for attribute_name in event}
result["data"] = event.data
result["data"] = event.get_data()
return result


def _json_or_string(
content: typing.Optional[typing.AnyStr],
) -> typing.Optional[
typing.Union[
typing.Dict[typing.Any, typing.Any],
typing.List[typing.Any],
typing.AnyStr,
]
]:
content: typing.Optional[typing.Union[str, bytes]],
) -> typing.Any:
"""
Returns a JSON-decoded dictionary or a list of dictionaries if
a valid JSON string is provided.
Expand Down
22 changes: 11 additions & 11 deletions cloudevents/http/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@
from cloudevents.http.json_methods import to_json # deprecated

__all__ = [
to_binary,
to_structured,
from_json,
from_http,
from_dict,
CloudEvent,
is_binary,
is_structured,
to_binary_http,
to_structured_http,
to_json,
"to_binary",
"to_structured",
"from_json",
"from_http",
"from_dict",
"CloudEvent",
"is_binary",
"is_structured",
"to_binary_http",
"to_structured_http",
"to_json",
]
6 changes: 3 additions & 3 deletions cloudevents/http/conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

def from_json(
data: typing.Union[str, bytes],
data_unmarshaller: types.UnmarshallerType = None,
data_unmarshaller: typing.Optional[types.UnmarshallerType] = None,
) -> CloudEvent:
"""
Parses JSON string `data` into a CloudEvent.
Expand All @@ -38,8 +38,8 @@ def from_json(

def from_http(
headers: typing.Dict[str, str],
data: typing.Union[str, bytes, None],
data_unmarshaller: types.UnmarshallerType = None,
data: typing.Optional[typing.Union[str, bytes]],
data_unmarshaller: typing.Optional[types.UnmarshallerType] = None,
) -> CloudEvent:
"""
Parses CloudEvent `data` and `headers` into a CloudEvent`.
Expand Down
2 changes: 1 addition & 1 deletion cloudevents/http/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def __init__(self, attributes: typing.Dict[str, str], data: typing.Any = None):
def _get_attributes(self) -> typing.Dict[str, typing.Any]:
return self._attributes

def _get_data(self) -> typing.Optional[typing.Any]:
def get_data(self) -> typing.Optional[typing.Any]:
return self.data

def __setitem__(self, key: str, value: typing.Any) -> None:
Expand Down
20 changes: 10 additions & 10 deletions cloudevents/http/http_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
details="Use cloudevents.conversion.to_binary function instead",
)
def to_binary(
event: AnyCloudEvent, data_marshaller: types.MarshallerType = None
) -> typing.Tuple[dict, typing.Union[bytes, str]]:
event: AnyCloudEvent, data_marshaller: typing.Optional[types.MarshallerType] = None
) -> typing.Tuple[typing.Dict[str, str], bytes]:
return _moved_to_binary(event, data_marshaller)


Expand All @@ -42,8 +42,8 @@ def to_binary(
)
def to_structured(
event: AnyCloudEvent,
data_marshaller: types.MarshallerType = None,
) -> typing.Tuple[dict, typing.Union[bytes, str]]:
data_marshaller: typing.Optional[types.MarshallerType] = None,
) -> typing.Tuple[typing.Dict[str, str], bytes]:
return _moved_to_structured(event, data_marshaller)


Expand All @@ -53,21 +53,21 @@ def to_structured(
)
def from_http(
headers: typing.Dict[str, str],
data: typing.Union[str, bytes, None],
data_unmarshaller: types.UnmarshallerType = None,
data: typing.Optional[typing.AnyStr],
data_unmarshaller: typing.Optional[types.UnmarshallerType] = None,
) -> CloudEvent:
return _moved_from_http(headers, data, data_unmarshaller)


@deprecated(deprecated_in="1.0.2", details="Use to_binary function instead")
def to_binary_http(
event: CloudEvent, data_marshaller: types.MarshallerType = None
) -> typing.Tuple[dict, typing.Union[bytes, str]]:
event: CloudEvent, data_marshaller: typing.Optional[types.MarshallerType] = None
) -> typing.Tuple[typing.Dict[str, str], bytes]:
return _moved_to_binary(event, data_marshaller)


@deprecated(deprecated_in="1.0.2", details="Use to_structured function instead")
def to_structured_http(
event: CloudEvent, data_marshaller: types.MarshallerType = None
) -> typing.Tuple[dict, typing.Union[bytes, str]]:
event: CloudEvent, data_marshaller: typing.Optional[types.MarshallerType] = None
) -> typing.Tuple[typing.Dict[str, str], bytes]:
return _moved_to_structured(event, data_marshaller)
6 changes: 3 additions & 3 deletions cloudevents/http/json_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
)
def to_json(
event: AnyCloudEvent,
data_marshaller: types.MarshallerType = None,
) -> typing.Union[str, bytes]:
data_marshaller: typing.Optional[types.MarshallerType] = None,
) -> bytes:
return _moved_to_json(event, data_marshaller)


Expand All @@ -42,6 +42,6 @@ def to_json(
)
def from_json(
data: typing.Union[str, bytes],
data_unmarshaller: types.UnmarshallerType = None,
data_unmarshaller: typing.Optional[types.UnmarshallerType] = None,
) -> CloudEvent:
return _moved_from_json(data, data_unmarshaller)
6 changes: 5 additions & 1 deletion cloudevents/http/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import typing

from deprecation import deprecated

from cloudevents.conversion import (
Expand All @@ -24,5 +26,7 @@
deprecated_in="1.6.0",
details="You SHOULD NOT use the default marshaller",
)
def default_marshaller(content: any):
def default_marshaller(
content: typing.Any,
) -> typing.Optional[typing.Union[bytes, str, typing.Any]]:
return _moved_default_marshaller(content)
Loading