diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 52e7c9a0..8f474e11 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,9 +7,9 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7.0.0 - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6.3.0 with: python-version: '3.12' cache: 'pip' @@ -17,6 +17,7 @@ jobs: - name: Install dev dependencies run: python -m pip install -r requirements/dev.txt - name: Run linting + continue-on-error: true run: python -m tox -e lint,mypy,mypy-samples-image,mypy-samples-json test: @@ -26,9 +27,9 @@ jobs: os: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7.0.0 - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6.3.0 with: python-version: ${{ matrix.python }} cache: 'pip' diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index eeebb883..fe681257 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -12,14 +12,14 @@ jobs: name: Build source distribution runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7.0.0 with: fetch-depth: 0 - name: Build SDist and wheel run: pipx run build - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7.0.1 with: name: artifact path: dist/* @@ -31,17 +31,17 @@ jobs: if: github.event_name == 'push' needs: [ build_dist ] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7.0.0 with: fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6.3.0 with: python-version: "3.12" cache: 'pip' - name: Install build dependencies run: pip install -U setuptools wheel build - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8.0.1 with: # unpacks default artifact into dist/ # if `name: artifact` is omitted, the action will create extra parent dir diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a8f3341..512a43f5 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] +## [1.13.0] + +### Fixed +- Kafka `to_structured` converter dropping `datacontentype` attribute + when setting a header. ([#296]) + ## [1.12.1] ### Changed @@ -310,3 +316,4 @@ CloudEvents v2 is a rewrite with ongoing development ([#271]) [#248]: https://github.com/cloudevents/sdk-python/pull/248 [#249]: https://github.com/cloudevents/sdk-python/pull/249 [#271]: https://github.com/cloudevents/sdk-python/pull/271 +[#296]: https://github.com/cloudevents/sdk-python/pull/296 diff --git a/cloudevents/__init__.py b/cloudevents/__init__.py index 63da30fc..6ecab33a 100644 --- a/cloudevents/__init__.py +++ b/cloudevents/__init__.py @@ -12,4 +12,4 @@ # License for the specific language governing permissions and limitations # under the License. -__version__ = "1.12.1" +__version__ = "1.13.0" diff --git a/cloudevents/kafka/conversion.py b/cloudevents/kafka/conversion.py index bdf2acab..8ecac6b8 100644 --- a/cloudevents/kafka/conversion.py +++ b/cloudevents/kafka/conversion.py @@ -213,8 +213,9 @@ def to_structured( attrs["data"] = data headers = {} - if "datacontenttype" in attrs: - headers["content-type"] = attrs.pop("datacontenttype").encode("utf-8") + datacontenttype = attrs.get("datacontenttype") + if datacontenttype is not None: + headers["content-type"] = datacontenttype.encode("utf-8") try: value = envelope_marshaller(attrs) diff --git a/cloudevents/tests/test_kafka_conversions.py b/cloudevents/tests/test_kafka_conversions.py index 584a05e4..5deab891 100644 --- a/cloudevents/tests/test_kafka_conversions.py +++ b/cloudevents/tests/test_kafka_conversions.py @@ -247,6 +247,7 @@ def test_sets_value_default_marshallers(self, source_event): "source": source_event["source"], "type": source_event["type"], "time": source_event["time"], + "datacontenttype": source_event["datacontenttype"], "partitionkey": source_event["partitionkey"], "data": self.expected_data, } @@ -263,6 +264,7 @@ def test_sets_value_custom_data_marshaller_default_envelope( "source": source_event["source"], "type": source_event["type"], "time": source_event["time"], + "datacontenttype": source_event["datacontenttype"], "partitionkey": source_event["partitionkey"], "data_base64": base64.b64encode( custom_marshaller(self.expected_data) @@ -281,6 +283,7 @@ def test_sets_value_custom_envelope_marshaller( "source": source_event["source"], "type": source_event["type"], "time": source_event["time"], + "datacontenttype": source_event["datacontenttype"], "partitionkey": source_event["partitionkey"], "data": self.expected_data, } @@ -299,6 +302,7 @@ def test_sets_value_custom_marshallers(self, source_event, custom_marshaller): "source": source_event["source"], "type": source_event["type"], "time": source_event["time"], + "datacontenttype": source_event["datacontenttype"], "partitionkey": source_event["partitionkey"], "data_base64": base64.b64encode( custom_marshaller(self.expected_data) @@ -335,6 +339,13 @@ def test_sets_headers(self, source_event): "utf-8" ) + def test_datacontenttype_attribute_present_after_setting_header(self, source_event): + result = to_structured(source_event) + datacontenttype = source_event.get("datacontenttype") + assert len(result.headers) == 1 + assert result.headers["content-type"] == datacontenttype.encode("utf-8") + assert datacontenttype in result.value.decode("utf-8") + def test_datamarshaller_exception(self, source_event): with pytest.raises(cloud_exceptions.DataMarshallerError): to_structured(source_event, data_marshaller=failing_func)