Skip to content

Commit 9122458

Browse files
committed
Initial release: CloudEvents Python SDK 0.0.1a0
Signed-off-by: Denis Makogon <lildee1991@gmail.com>
1 parent 159332b commit 9122458

23 files changed

+830
-0
lines changed

.gitignore

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
6+
# C extensions
7+
*.so
8+
9+
# Distribution / packaging
10+
.Python
11+
env/
12+
build/
13+
develop-eggs/
14+
dist/
15+
downloads/
16+
eggs/
17+
.eggs/
18+
lib/
19+
lib64/
20+
parts/
21+
sdist/
22+
var/
23+
wheels/
24+
*.egg-info/
25+
.installed.cfg
26+
*.egg
27+
28+
# PyInstaller
29+
# Usually these files are written by a python script from a template
30+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
31+
*.manifest
32+
*.spec
33+
34+
# Installer logs
35+
pip-log.txt
36+
pip-delete-this-directory.txt
37+
38+
# Unit test / coverage reports
39+
htmlcov/
40+
.tox/
41+
.coverage
42+
.coverage.*
43+
.cache
44+
nosetests.xml
45+
coverage.xml
46+
*.cover
47+
.hypothesis/
48+
49+
# Translations
50+
*.mo
51+
*.pot
52+
53+
# Django stuff:
54+
*.log
55+
local_settings.py
56+
57+
# Flask stuff:
58+
instance/
59+
.webassets-cache
60+
61+
# Scrapy stuff:
62+
.scrapy
63+
64+
# Sphinx documentation
65+
docs/_build/
66+
67+
# PyBuilder
68+
target/
69+
70+
# Jupyter Notebook
71+
.ipynb_checkpoints
72+
73+
# pyenv
74+
.python-version
75+
76+
# celery beat schedule file
77+
celerybeat-schedule
78+
79+
# SageMath parsed files
80+
*.sage.py
81+
82+
# dotenv
83+
.env
84+
85+
# virtualenv
86+
.venv
87+
venv/
88+
ENV/
89+
90+
# Spyder project settings
91+
.spyderproject
92+
.spyproject
93+
94+
# Rope project settings
95+
.ropeproject
96+
97+
# mkdocs documentation
98+
/site
99+
100+
# mypy
101+
.mypy_cache/
102+
*.pyc
103+
.testrepository
104+
.tox/*
105+
dist/*
106+
build/*
107+
html/*
108+
*.egg*
109+
cover/*
110+
.coverage
111+
rdserver.txt
112+
python-troveclient.iml
113+
114+
# Files created by releasenotes build
115+
releasenotes/build
116+
.coverage.*
117+
*.json
118+
.cache
119+
*.log*
120+
*.csv
121+
venv
122+
.venv
123+
ChangeLog
124+
AUTHORS
125+
.pytest_cache/

cloudevents/__init__.py

Whitespace-only changes.

cloudevents/sdk/__init__.py

Whitespace-only changes.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
from cloudevents.sdk.converters import binary
16+
from cloudevents.sdk.converters import structured
17+
18+
TypeBinary = binary.BinaryHTTPCloudEventConverter.TYPE
19+
TypeStructured = structured.JSONHTTPCloudEventConverter.TYPE

cloudevents/sdk/converters/base.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
import typing
16+
17+
from cloudevents.sdk.event import base
18+
19+
20+
class Converter(object):
21+
22+
TYPE = None
23+
24+
def __init__(self,
25+
event_class: base.BaseEvent,
26+
supported_media_types: typing.Mapping[str, bool]):
27+
self.event = event_class()
28+
self.supported_media_types = supported_media_types
29+
30+
def can_read(self, media_type: str) -> bool:
31+
return media_type in self.supported_media_types
32+
33+
def can_write(self, media_type: str) -> bool:
34+
return media_type in self.supported_media_types
35+
36+
def read(self, headers: dict, body: typing.IO) -> base.BaseEvent:
37+
raise Exception("not implemented")
38+
39+
def write(self, event: base.BaseEvent,
40+
data_marshaller: typing.Callable) -> (dict, typing.IO):
41+
raise Exception("not implemented")

cloudevents/sdk/converters/binary.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
import typing
16+
17+
from cloudevents.sdk import exceptions
18+
from cloudevents.sdk.converters import base
19+
from cloudevents.sdk.event import base as event_base
20+
from cloudevents.sdk.event import v01
21+
22+
23+
class BinaryHTTPCloudEventConverter(base.Converter):
24+
25+
TYPE = "binary"
26+
27+
def __init__(self, event_class: event_base.BaseEvent,
28+
supported_media_types: typing.Mapping[str, bool]):
29+
if event_class == v01.Event:
30+
raise exceptions.UnsupportedEvent(event_class)
31+
32+
super().__init__(event_class, supported_media_types)
33+
34+
def read(self,
35+
headers: dict, body: typing.IO) -> event_base.BaseEvent:
36+
# we ignore headers, since the whole CE is in request body
37+
event = self.event
38+
event.UnmarshalBinary(headers, body)
39+
return event
40+
41+
def write(self, event: event_base.BaseEvent,
42+
data_marshaller: typing.Callable) -> (dict, typing.IO):
43+
hs, data = event.MarshalBinary()
44+
return hs, data_marshaller(data)
45+
46+
47+
def NewBinaryHTTPCloudEventConverter(
48+
event_class: event_base.BaseEvent) -> BinaryHTTPCloudEventConverter:
49+
media_types = {
50+
"application/json": True,
51+
"application/xml": True,
52+
"application/octet-stream": True,
53+
}
54+
return BinaryHTTPCloudEventConverter(event_class, media_types)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
import typing
16+
17+
from cloudevents.sdk.converters import base
18+
from cloudevents.sdk.event import base as event_base
19+
20+
21+
class JSONHTTPCloudEventConverter(base.Converter):
22+
23+
TYPE = "structured"
24+
25+
def __init__(self, event_class: event_base.BaseEvent,
26+
supported_media_types: typing.Mapping[str, bool]):
27+
super().__init__(event_class, supported_media_types)
28+
29+
def read(self, headers: dict,
30+
body: typing.IO) -> event_base.BaseEvent:
31+
# we ignore headers, since the whole CE is in request body
32+
event = self.event
33+
event.UnmarshalJSON(body)
34+
return event
35+
36+
def write(self,
37+
event: event_base.BaseEvent,
38+
data_marshaller: typing.Callable) -> (dict, typing.IO):
39+
return {}, event.MarshalJSON()
40+
41+
42+
def NewJSONHTTPCloudEventConverter(
43+
event_class: event_base.BaseEvent) -> JSONHTTPCloudEventConverter:
44+
media_types = {
45+
"application/cloudevents+json": True,
46+
}
47+
48+
return JSONHTTPCloudEventConverter(event_class, media_types)

cloudevents/sdk/event/__init__.py

Whitespace-only changes.

cloudevents/sdk/event/base.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
import io
16+
import ujson
17+
import typing
18+
19+
20+
class BaseEvent(object):
21+
22+
def Properties(self) -> dict:
23+
props = dict()
24+
for name, value in self.__dict__.items():
25+
if str(name).startswith("ce__"):
26+
props.update(
27+
{
28+
str(name).replace("ce__", ""): value.get()
29+
}
30+
)
31+
32+
return props
33+
34+
def Extensions(self) -> dict:
35+
props = self.Properties()
36+
return props.get("extensions")
37+
38+
def Get(self, key: str) -> (object, bool):
39+
formatted_key = "ce__{0}".format(key.lower())
40+
ok = hasattr(self, formatted_key)
41+
value = getattr(self, formatted_key, None)
42+
if not ok:
43+
exts = self.Extensions()
44+
return exts.get(key), key in exts
45+
46+
return value.get(), ok
47+
48+
def Set(self, key: str, value: object):
49+
formatted_key = "ce__{0}".format(key)
50+
key_exists = hasattr(self, formatted_key)
51+
if key_exists:
52+
attr = getattr(self, formatted_key)
53+
attr.set(value)
54+
setattr(self, formatted_key, attr)
55+
return
56+
57+
exts = self.Extensions()
58+
exts.update({key: value})
59+
self.Set("extensions", exts)
60+
61+
def MarshalJSON(self) -> typing.IO:
62+
return io.StringIO(ujson.dumps(self.Properties()))
63+
64+
def UnmarshalJSON(self, b: typing.IO):
65+
raw_ce = ujson.load(b)
66+
for name, value in raw_ce.items():
67+
self.Set(name, value)
68+
69+
def UnmarshalBinary(self, headers: dict, body: typing.IO):
70+
props = self.Properties()
71+
for key in props:
72+
self.Set(key, headers.get("ce-{0}".format(key)))
73+
74+
data = None
75+
if body:
76+
data = body.read()
77+
78+
self.Set("data", data)
79+
80+
def MarshalBinary(self) -> (dict, object):
81+
headers = {}
82+
props = self.Properties()
83+
for key, value in props.items():
84+
if key not in ["data", "extensions"]:
85+
headers["ce-{0}".format(key)] = value
86+
87+
exts = props.get("extensions")
88+
headers.update(**exts)
89+
data, _ = self.Get("data")
90+
return headers, data

0 commit comments

Comments
 (0)