diff --git a/.isort.cfg b/.isort.cfg deleted file mode 100644 index 22880d4..0000000 --- a/.isort.cfg +++ /dev/null @@ -1,4 +0,0 @@ -[settings] -line_length = 80 -multi_line_output = 3 -include_trailing_comma = True diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3deea52..5b4630f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,10 +1,17 @@ repos: -- repo: https://github.com/timothycrosley/isort/ - rev: 5.0.4 + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 hooks: - - id: isort -- repo: https://github.com/psf/black - rev: 19.10b0 + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-toml + - repo: https://github.com/pycqa/isort + rev: 5.10.1 hooks: - - id: black - language_version: python3.9 + - id: isort + args: [ "--profile", "black", "--filter-files" ] + - repo: https://github.com/psf/black + rev: 22.6.0 + hooks: + - id: black + language_version: python3.10 diff --git a/CHANGELOG.md b/CHANGELOG.md index a9a2a38..85ec853 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- Added `.get` accessor for even properties ([#165]) + +### Changed +- Code quality and styling tooling is unified and configs compatibility is ensured ([#167]) + ## [1.3.0] — 2022-09-07 ### Added - Python 3.9 support ([#144]) @@ -17,11 +23,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed typings issues ([#149]) - The package redistributive ability by inlining required `pypi-packaging.py` functions ([#151]) -## [1.2.0] +## [1.2.0] — 2020-08-20 ### Added - Added GenericException, DataMarshallingError and DataUnmarshallingError ([#120]) -## [1.1.0] +## [1.1.0] — 2020-08-18 ### Changed - Changed from_http to now expect headers argument before data ([#110]) - Renamed exception names ([#111]) @@ -32,12 +38,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Deprecated - Renamed to_binary_http and to_structured_http. ([#108]) -## [1.0.1] +## [1.0.1] — 2020-08-14 ### Added - CloudEvent exceptions and event type checking in http module ([#96]) - CloudEvent equality override ([#98]) -## [1.0.0] +## [1.0.0] — 2020-08-11 ### Added - Update types and handle data_base64 structured ([#34]) - Added a user friendly CloudEvent class with data validation ([#36]) @@ -51,7 +57,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed - Removed support for Cloudevents V0.2 and V0.1 ([#43]) -## [0.3.0] +## [0.3.0] — 2020-07-11 ### Added - Added Cloudevents V0.3 and V1 implementations ([#22]) - Add helpful text to README ([#23]) @@ -92,7 +98,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Initial release -[0.3.0]: https://github.com/cloudevents/sdk-python/compare/0.2.4...HEAD +[1.3.0]: https://github.com/cloudevents/sdk-python/compare/1.2.0...1.3.0 +[1.2.0]: https://github.com/cloudevents/sdk-python/compare/1.1.0...1.2.0 +[1.1.0]: https://github.com/cloudevents/sdk-python/compare/1.0.1...1.1.0 +[1.0.1]: https://github.com/cloudevents/sdk-python/compare/1.0.0...1.0.1 +[1.0.0]: https://github.com/cloudevents/sdk-python/compare/0.3.0...1.0.0 +[0.3.0]: https://github.com/cloudevents/sdk-python/compare/0.2.4...0.3.0 [0.2.4]: https://github.com/cloudevents/sdk-python/compare/0.2.3...0.2.4 [0.2.3]: https://github.com/cloudevents/sdk-python/compare/0.2.2...0.2.3 [0.2.2]: https://github.com/cloudevents/sdk-python/compare/0.2.1...0.2.2 @@ -126,4 +137,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#110]: https://github.com/cloudevents/sdk-python/pull/110 [#111]: https://github.com/cloudevents/sdk-python/pull/111 [#119]: https://github.com/cloudevents/sdk-python/pull/119 -[#120]: https://github.com/cloudevents/sdk-python/pull/120 \ No newline at end of file +[#120]: https://github.com/cloudevents/sdk-python/pull/120 +[#144]: https://github.com/cloudevents/sdk-python/pull/144 +[#149]: https://github.com/cloudevents/sdk-python/pull/149 +[#150]: https://github.com/cloudevents/sdk-python/pull/150 +[#151]: https://github.com/cloudevents/sdk-python/pull/151 +[#158]: https://github.com/cloudevents/sdk-python/pull/158 +[#159]: https://github.com/cloudevents/sdk-python/pull/159 +[#160]: https://github.com/cloudevents/sdk-python/pull/160 +[#165]: https://github.com/cloudevents/sdk-python/pull/165 +[#167]: https://github.com/cloudevents/sdk-python/pull/167 diff --git a/Makefile b/Makefile index 762c390..317caf2 100644 --- a/Makefile +++ b/Makefile @@ -16,4 +16,4 @@ help: # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/OWNERS b/OWNERS index 07a0f50..6d9a2c4 100644 --- a/OWNERS +++ b/OWNERS @@ -2,3 +2,5 @@ admins: - grant - denismakogon - cumason123 + - Klaudioz + - xSAVIKx diff --git a/README.md b/README.md index fe152f1..6efd4ea 100644 --- a/README.md +++ b/README.md @@ -133,19 +133,19 @@ the same API. It will use semantic versioning with following rules: Each SDK may have its own unique processes, tooling and guidelines, common governance related material can be found in the -[CloudEvents `community`](https://github.com/cloudevents/spec/tree/master/community) +[CloudEvents `docs`](https://github.com/cloudevents/spec/tree/main/docs) directory. In particular, in there you will find information concerning how SDK projects are -[managed](https://github.com/cloudevents/spec/blob/master/community/SDK-GOVERNANCE.md), -[guidelines](https://github.com/cloudevents/spec/blob/master/community/SDK-maintainer-guidelines.md) +[managed](https://github.com/cloudevents/spec/blob/main/docs/GOVERNANCE.md), +[guidelines](https://github.com/cloudevents/spec/blob/main/docs/SDK-maintainer-guidelines.md) for how PR reviews and approval, and our -[Code of Conduct](https://github.com/cloudevents/spec/blob/master/community/GOVERNANCE.md#additional-information) +[Code of Conduct](https://github.com/cloudevents/spec/blob/main/docs/GOVERNANCE.md#additional-information) information. ## Maintenance -We use black and isort for autoformatting. We setup a tox environment to reformat -the codebase. +We use [black][black] and [isort][isort] for autoformatting. We set up a [tox][tox] environment +to reformat the codebase. e.g. @@ -155,3 +155,7 @@ tox -e reformat ``` For information on releasing version bumps see [RELEASING.md](RELEASING.md) + +[black]: https://black.readthedocs.io/ +[isort]: https://pycqa.github.io/isort/ +[tox]: https://tox.wiki/ diff --git a/cloudevents/http/event.py b/cloudevents/http/event.py index f39124f..83adf39 100644 --- a/cloudevents/http/event.py +++ b/cloudevents/http/event.py @@ -26,9 +26,7 @@ class CloudEvent: Supports both binary and structured mode CloudEvents """ - def __init__( - self, attributes: typing.Dict[str, str], data: typing.Any = None - ): + def __init__(self, attributes: typing.Dict[str, str], data: typing.Any = None): """ Event Constructor :param attributes: a dict with cloudevent attributes. Minimally diff --git a/cloudevents/http/event_type.py b/cloudevents/http/event_type.py index b74a0ec..0df43f4 100644 --- a/cloudevents/http/event_type.py +++ b/cloudevents/http/event_type.py @@ -26,6 +26,4 @@ def is_structured(headers: typing.Dict[str, str]) -> bool: headers = {key.lower(): value for key, value in headers.items()} content_type = headers.get("content-type", "") structured_parser = structured.JSONHTTPCloudEventConverter() - return structured_parser.can_read( - content_type=content_type, headers=headers - ) + return structured_parser.can_read(content_type=content_type, headers=headers) diff --git a/cloudevents/sdk/converters/structured.py b/cloudevents/sdk/converters/structured.py index d807236..d63c60a 100644 --- a/cloudevents/sdk/converters/structured.py +++ b/cloudevents/sdk/converters/structured.py @@ -26,9 +26,7 @@ class JSONHTTPCloudEventConverter(base.Converter): TYPE = "structured" MIME_TYPE = "application/cloudevents+json" - def can_read( - self, content_type: str, headers: typing.Dict[str, str] = {} - ) -> bool: + def can_read(self, content_type: str, headers: typing.Dict[str, str] = {}) -> bool: return ( isinstance(content_type, str) and content_type.startswith(self.MIME_TYPE) diff --git a/cloudevents/sdk/event/base.py b/cloudevents/sdk/event/base.py index a14439b..6d84330 100644 --- a/cloudevents/sdk/event/base.py +++ b/cloudevents/sdk/event/base.py @@ -206,8 +206,7 @@ class BaseEvent(EventGetterSetter): data = data_marshaller(data) except Exception as e: raise cloud_exceptions.DataMarshallerError( - "Failed to marshall data with error: " - f"{type(e).__name__}('{e}')" + f"Failed to marshall data with error: {type(e).__name__}('{e}')" ) if isinstance(data, (bytes, bytes, memoryview)): props["data_base64"] = base64.b64encode(data).decode("ascii") @@ -256,9 +255,7 @@ class BaseEvent(EventGetterSetter): body: typing.Union[bytes, str], data_unmarshaller: types.UnmarshallerType, ): - required_binary_fields = { - f"ce-{field}" for field in self._ce_required_fields - } + required_binary_fields = {f"ce-{field}" for field in self._ce_required_fields} missing_fields = required_binary_fields - headers.keys() if len(missing_fields) > 0: @@ -277,8 +274,7 @@ class BaseEvent(EventGetterSetter): raw_ce = data_unmarshaller(body) except Exception as e: raise cloud_exceptions.DataUnmarshallerError( - "Failed to unmarshall data with error: " - f"{type(e).__name__}('{e}')" + f"Failed to unmarshall data with error: {type(e).__name__}('{e}')" ) self.Set("data", raw_ce) @@ -304,8 +300,7 @@ class BaseEvent(EventGetterSetter): data = data_marshaller(data) except Exception as e: raise cloud_exceptions.DataMarshallerError( - "Failed to marshall data with error: " - f"{type(e).__name__}('{e}')" + f"Failed to marshall data with error: {type(e).__name__}('{e}')" ) if isinstance(data, str): # Convenience method for json.dumps data = data.encode("utf-8") diff --git a/cloudevents/sdk/event/v03.py b/cloudevents/sdk/event/v03.py index 6070506..8e56d56 100644 --- a/cloudevents/sdk/event/v03.py +++ b/cloudevents/sdk/event/v03.py @@ -33,9 +33,7 @@ class Event(base.BaseEvent): self.ce__type = opt.Option("type", None, True) self.ce__datacontenttype = opt.Option("datacontenttype", None, False) - self.ce__datacontentencoding = opt.Option( - "datacontentencoding", None, False - ) + self.ce__datacontentencoding = opt.Option("datacontentencoding", None, False) self.ce__subject = opt.Option("subject", None, False) self.ce__time = opt.Option("time", None, False) self.ce__schemaurl = opt.Option("schemaurl", None, False) diff --git a/cloudevents/tests/test_event_from_request_converter.py b/cloudevents/tests/test_event_from_request_converter.py index e981790..26ccc21 100644 --- a/cloudevents/tests/test_event_from_request_converter.py +++ b/cloudevents/tests/test_event_from_request_converter.py @@ -24,12 +24,8 @@ from cloudevents.tests import data @pytest.mark.parametrize("event_class", [v03.Event, v1.Event]) def test_binary_converter_upstream(event_class): - m = marshaller.NewHTTPMarshaller( - [binary.NewBinaryHTTPCloudEventConverter()] - ) - event = m.FromRequest( - event_class(), data.headers[event_class], None, lambda x: x - ) + m = marshaller.NewHTTPMarshaller([binary.NewBinaryHTTPCloudEventConverter()]) + event = m.FromRequest(event_class(), data.headers[event_class], None, lambda x: x) assert event is not None assert event.EventType() == data.ce_type assert event.EventID() == data.ce_id @@ -38,9 +34,7 @@ def test_binary_converter_upstream(event_class): @pytest.mark.parametrize("event_class", [v03.Event, v1.Event]) def test_structured_converter_upstream(event_class): - m = marshaller.NewHTTPMarshaller( - [structured.NewJSONHTTPCloudEventConverter()] - ) + m = marshaller.NewHTTPMarshaller([structured.NewJSONHTTPCloudEventConverter()]) event = m.FromRequest( event_class(), {"Content-Type": "application/cloudevents+json"}, diff --git a/cloudevents/tests/test_event_pipeline.py b/cloudevents/tests/test_event_pipeline.py index 2f6dd10..a452c7f 100644 --- a/cloudevents/tests/test_event_pipeline.py +++ b/cloudevents/tests/test_event_pipeline.py @@ -59,14 +59,8 @@ def test_extensions_are_set_upstream(): def test_binary_event_v1(): - event = ( - v1.Event() - .SetContentType("application/octet-stream") - .SetData(b"\x00\x01") - ) - m = marshaller.NewHTTPMarshaller( - [structured.NewJSONHTTPCloudEventConverter()] - ) + event = v1.Event().SetContentType("application/octet-stream").SetData(b"\x00\x01") + m = marshaller.NewHTTPMarshaller([structured.NewJSONHTTPCloudEventConverter()]) _, body = m.ToRequest(event, converters.TypeStructured, lambda x: x) assert isinstance(body, bytes) @@ -76,9 +70,7 @@ def test_binary_event_v1(): def test_object_event_v1(): - event = ( - v1.Event().SetContentType("application/json").SetData({"name": "john"}) - ) + event = v1.Event().SetContentType("application/json").SetData({"name": "john"}) m = marshaller.NewDefaultHTTPMarshaller() diff --git a/cloudevents/tests/test_http_cloudevent.py b/cloudevents/tests/test_http_cloudevent.py index 52ffdfb..19bbbb5 100644 --- a/cloudevents/tests/test_http_cloudevent.py +++ b/cloudevents/tests/test_http_cloudevent.py @@ -33,9 +33,7 @@ def your_dummy_data(): return '{"name":"paul"}' -def test_http_cloudevent_equality( - dummy_attributes, my_dummy_data, your_dummy_data -): +def test_http_cloudevent_equality(dummy_attributes, my_dummy_data, your_dummy_data): data = my_dummy_data event1 = CloudEvent(dummy_attributes, data) event2 = CloudEvent(dummy_attributes, data) @@ -165,15 +163,11 @@ def test_get_operation_on_non_existing_attribute_must_return_default_value_if_gi dummy_event, non_exiting_attribute_name ): dummy_value = "Hello World" - assert ( - dummy_event.get(non_exiting_attribute_name, dummy_value) == dummy_value - ) + assert dummy_event.get(non_exiting_attribute_name, dummy_value) == dummy_value def test_get_operation_on_non_existing_attribute_should_not_copy_default_value( dummy_event, non_exiting_attribute_name ): dummy_value = object() - assert ( - dummy_event.get(non_exiting_attribute_name, dummy_value) is dummy_value - ) + assert dummy_event.get(non_exiting_attribute_name, dummy_value) is dummy_value diff --git a/cloudevents/tests/test_http_events.py b/cloudevents/tests/test_http_events.py index d61fff6..bc9f0bc 100644 --- a/cloudevents/tests/test_http_events.py +++ b/cloudevents/tests/test_http_events.py @@ -74,9 +74,7 @@ async def echo(request): decoder = None if "binary-payload" in request.headers: decoder = lambda x: x - event = from_http( - dict(request.headers), request.body, data_unmarshaller=decoder - ) + event = from_http(dict(request.headers), request.body, data_unmarshaller=decoder) data = ( event.data if isinstance(event.data, (bytes, bytearray, memoryview)) @@ -143,9 +141,7 @@ def test_emit_structured_event(specversion): "specversion": specversion, "data": test_data, } - _, r = app.test_client.post( - "/event", headers=headers, data=json.dumps(body) - ) + _, r = app.test_client.post("/event", headers=headers, data=json.dumps(body)) # Convert byte array to dict # e.g. r.body = b'{"payload-content": "Hello World!"}' @@ -463,9 +459,7 @@ def test_empty_json_structured(): data = "" with pytest.raises(cloud_exceptions.MissingRequiredFields) as e: from_http(headers, data) - assert "Failed to read specversion from both headers and data" in str( - e.value - ) + assert "Failed to read specversion from both headers and data" in str(e.value) def test_uppercase_headers_with_none_data_binary(): @@ -520,7 +514,5 @@ def test_non_dict_data_no_headers_bug(): data = "123" with pytest.raises(cloud_exceptions.MissingRequiredFields) as e: from_http(headers, data) - assert "Failed to read specversion from both headers and data" in str( - e.value - ) + assert "Failed to read specversion from both headers and data" in str(e.value) assert "The following deserialized data has no 'get' method" in str(e.value) diff --git a/cloudevents/tests/test_marshaller.py b/cloudevents/tests/test_marshaller.py index d2e5b4e..8922b7b 100644 --- a/cloudevents/tests/test_marshaller.py +++ b/cloudevents/tests/test_marshaller.py @@ -60,23 +60,17 @@ def test_to_request_wrong_marshaller(): def test_from_request_cannot_read(binary_headers): with pytest.raises(exceptions.UnsupportedEventConverter): - m = marshaller.HTTPMarshaller( - [binary.NewBinaryHTTPCloudEventConverter()] - ) + m = marshaller.HTTPMarshaller([binary.NewBinaryHTTPCloudEventConverter()]) m.FromRequest(v1.Event(), {}, "") with pytest.raises(exceptions.UnsupportedEventConverter): - m = marshaller.HTTPMarshaller( - [structured.NewJSONHTTPCloudEventConverter()] - ) + m = marshaller.HTTPMarshaller([structured.NewJSONHTTPCloudEventConverter()]) m.FromRequest(v1.Event(), binary_headers, "") def test_to_request_invalid_converter(): with pytest.raises(exceptions.NoSuchConverter): - m = marshaller.HTTPMarshaller( - [structured.NewJSONHTTPCloudEventConverter()] - ) + m = marshaller.HTTPMarshaller([structured.NewJSONHTTPCloudEventConverter()]) m.ToRequest(v1.Event(), "") diff --git a/cloudevents/tests/test_with_sanic.py b/cloudevents/tests/test_with_sanic.py index 56f03e0..7e3487d 100644 --- a/cloudevents/tests/test_with_sanic.py +++ b/cloudevents/tests/test_with_sanic.py @@ -30,9 +30,7 @@ async def is_ok(request): @app.route("/echo", ["POST"]) async def echo(request): - event = m.FromRequest( - v1.Event(), dict(request.headers), request.body, lambda x: x - ) + event = m.FromRequest(v1.Event(), dict(request.headers), request.body, lambda x: x) hs, body = m.ToRequest(event, converters.TypeBinary, lambda x: x) return response.text(body.decode("utf-8"), headers=hs) diff --git a/pypi_packaging.py b/pypi_packaging.py index 2b33489..bf02797 100644 --- a/pypi_packaging.py +++ b/pypi_packaging.py @@ -1,4 +1,5 @@ import os + import pkg_resources from setup import pypi_config diff --git a/pyproject.toml b/pyproject.toml index 672bf5c..8727d44 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [tool.black] -line-length = 80 +line-length = 88 include = '\.pyi?$' exclude = ''' /( @@ -14,3 +14,6 @@ exclude = ''' | dist )/ ''' + +[tool.isort] +profile = "black" diff --git a/requirements/dev.txt b/requirements/dev.txt new file mode 100644 index 0000000..264984a --- /dev/null +++ b/requirements/dev.txt @@ -0,0 +1,9 @@ +black +isort +flake8 +pep8-naming +flake8-import-order +flake8-print +flake8-strict +tox +pre-commit diff --git a/samples/http-image-cloudevents/client.py b/samples/http-image-cloudevents/client.py index d2f6433..48cca62 100644 --- a/samples/http-image-cloudevents/client.py +++ b/samples/http-image-cloudevents/client.py @@ -63,9 +63,7 @@ def send_structured_cloud_event(url: str): if __name__ == "__main__": # Run client.py via: 'python3 client.py http://localhost:3000/' if len(sys.argv) < 2: - sys.exit( - "Usage: python with_requests.py " "" - ) + sys.exit("Usage: python with_requests.py ") url = sys.argv[1] send_binary_cloud_event(url) diff --git a/samples/http-json-cloudevents/client.py b/samples/http-json-cloudevents/client.py index a3c08eb..e36467d 100644 --- a/samples/http-json-cloudevents/client.py +++ b/samples/http-json-cloudevents/client.py @@ -54,9 +54,7 @@ if __name__ == "__main__": # expects a url from command line. # e.g. python3 client.py http://localhost:3000/ if len(sys.argv) < 2: - sys.exit( - "Usage: python with_requests.py " "" - ) + sys.exit("Usage: python with_requests.py ") url = sys.argv[1] send_binary_cloud_event(url) diff --git a/setup.py b/setup.py index 347bb06..02c5654 100644 --- a/setup.py +++ b/setup.py @@ -11,12 +11,12 @@ # 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 setuptools import setup, find_packages - -import os import codecs +import os import pathlib +from setuptools import find_packages, setup + def read(rel_path): here = os.path.abspath(os.path.dirname(__file__)) @@ -54,7 +54,9 @@ if __name__ == "__main__": classifiers=[ "Intended Audience :: Information Technology", "Intended Audience :: System Administrators", + "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", + "Development Status :: 5 - Production/Stable", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.6", diff --git a/tox.ini b/tox.ini index 73d8784..7ae5f78 100644 --- a/tox.ini +++ b/tox.ini @@ -13,7 +13,7 @@ setenv = commands = pytest {env:PYTESTARGS} {posargs} [testenv:reformat] -basepython=python3.10 +basepython = python3.10 deps = black isort @@ -30,4 +30,4 @@ deps = commands = black --check . isort -c cloudevents samples - flake8 cloudevents samples --ignore W503,E731 --max-line-length 88 + flake8 cloudevents samples --ignore W503,E731 --extend-ignore E203 --max-line-length 88