fix: address inconsistent serialization for binding objects - #356
Draft
hallvictoria wants to merge 9 commits into
Draft
fix: address inconsistent serialization for binding objects#356hallvictoria wants to merge 9 commits into
hallvictoria wants to merge 9 commits into
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces a uniform serialization contract (to_dict() / to_json()) across Azure Functions Python binding objects so that bindings can be reliably logged/serialized without per-type adapters.
Changes:
- Adds a new
Serializablemixin on binding ABCs and implementsto_dict()across concrete binding types. - Introduces
_serialize_value()to normalize common non-JSON types (bytes/datetime/timedelta). - Adds a new test suite validating serialization behavior across multiple bindings.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_serialization.py | Adds contract-level tests for to_dict() / to_json() across bindings and _serialize_value(). |
| azure/functions/_abc.py | Introduces Serializable mixin and applies it to binding ABCs. |
| azure/functions/_utils.py | Adds _serialize_value() helper for JSON-safety normalization. |
| azure/functions/blob.py | Implements InputStream.to_dict() including metadata fields. |
| azure/functions/_blob.py | Adds a minimal InputStream.to_dict() for the internal InputStream type. |
| azure/functions/_queue.py | Implements QueueMessage.to_dict() using _serialize_value() for body/datetime fields. |
| azure/functions/_servicebus.py | Implements ServiceBusMessage.to_dict() including envelope fields and body serialization. |
| azure/functions/_eventhub.py | Implements EventHubEvent.to_dict() including body and key metadata. |
| azure/functions/_eventgrid.py | Implements to_dict() for EventGridEvent, EventGridOutputEvent, and CloudEvent. |
| azure/functions/kafka.py | Implements KafkaEvent.to_dict() including body/key/headers. |
| azure/functions/_kafka.py | Updates AbstractKafkaEvent to participate in the serialization contract. |
| azure/functions/timer.py | Implements TimerRequest.to_dict() including schedule fields. |
| azure/functions/_timer.py | Adds a base TimerRequest.to_dict() implementation. |
| azure/functions/_cosmosdb.py | Adds DocumentList.to_dict() returning list of document dicts. |
| azure/functions/_sql.py | Adds to_dict() and list serialization helpers for SQL row types. |
| azure/functions/_mysql.py | Adds to_dict() and list serialization helpers for MySQL row types. |
Comments suppressed due to low confidence (3)
azure/functions/_eventgrid.py:142
- EventGridOutputEvent.to_dict() returns the event payload from get_json() without normalization. If
datacontains bytes/datetime/timedelta values, the dict is not JSON-safe and to_json()/json.dumps may fail. Normalize the payload via _serialize_value.
'id': self.id,
'subject': self.subject,
'event_type': self.event_type,
'event_time': _serialize_value(self.event_time),
'data_version': self.data_version,
'data': self.get_json(),
}
azure/functions/_sql.py:84
- SqlRowList.to_json() switches to the standard-library json module, while SqlRow.to_json() uses the package's _jsonutils adapter (which can be backed by orjson). This makes list vs row serialization inconsistent and may regress performance/behavior when orjson is available. Use the existing
jsonadapter already imported at the top of the file.
def to_json(self) -> str:
"""Return the JSON representation of the SqlRowList."""
import json as _stdlib_json
return _stdlib_json.dumps(self.to_dict())
azure/functions/_mysql.py:84
- MySqlRowList.to_json() switches to the standard-library json module, while MySqlRow.to_json() uses the package's _jsonutils adapter (which can be backed by orjson). This makes list vs row serialization inconsistent and may regress performance/behavior when orjson is available. Use the existing
jsonadapter already imported at the top of the file.
def to_json(self) -> str:
"""Return the JSON representation of the MySqlRowList."""
import json as _stdlib_json
return _stdlib_json.dumps(self.to_dict())
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (3)
azure/functions/_eventgrid.py:141
EventGridOutputEvent.to_dict()returnsdatawithout normalization. Sinceto_json()will serialize this dict directly, passing non-JSON-native values (e.g., bytes/datetime nested indata) can raiseTypeErrorand violates the “JSON-safe” contract used elsewhere in this PR.
'event_type': self.event_type,
'event_time': _serialize_value(self.event_time),
'data_version': self.data_version,
'data': self.get_json(),
}
azure/functions/_sql.py:59
to_dict()now normalizes values via_serialize_value, butto_json()still serializesdict(self)directly. That meansto_json()can still fail on bytes/datetime values even thoughto_dict()would succeed. Consider delegatingto_json()toto_dict()so both serialization entry points behave consistently.
def to_json(self) -> str:
"""Return the JSON representation of the SqlRow"""
return json.dumps(dict(self))
def to_dict(self) -> dict:
azure/functions/_mysql.py:59
to_dict()now normalizes values via_serialize_value, butto_json()still serializesdict(self)directly. That meansto_json()can still fail on bytes/datetime values even thoughto_dict()would succeed. Consider delegatingto_json()toto_dict()so both serialization entry points behave consistently.
def to_json(self) -> str:
"""Return the JSON representation of the MySqlRow"""
return json.dumps(dict(self))
def to_dict(self) -> dict:
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fixes: #351 - add uniform
to_dict()/to_json()serialization contract to all binding typesBinding objects had four inconsistent conventions for serialization —
to_dict()(CosmosDB only),to_json()returningstr(SQL/MySQL rows),get_json()returning payload only (EventGrid, Queue),get_body()returning raw bytes (ServiceBus, EventHub, Kafka), or nothing at all (Blob, Timer).str(obj)fell back to an unusable Python repr that silently dropped the payload.Changes
Serializablemixin in_abc.py- abstractto_dict() -> Anywith a concreteto_json() -> strthat delegates to it. All binding ABCs now inherit from it._serialize_valuehelper in_utils.py- normalizes field values for JSON safety:bytes- decoded as UTF-8 when valid, else{"__encoding": "base64", "value": "..."}datetime/date- ISO-8601 stringtimedelta-str()to_dict()added to all concrete binding types:InputStreamname,uri,length,blob_properties,metadata(no body read)QueueMessagebodyServiceBusMessagebodyEventGridEvent/EventGridOutputEvent/CloudEventdataEventHubEventbody,partition_key,sequence_number,offset,enqueued_time,iothub_metadataKafkaEventbody,key,offset,partition,topic,timestamp,headersDocumentListdoc.to_dict()SqlRow/MySqlRowdict(self)(additive -to_json()was already present)SqlRowList/MySqlRowListrow.to_dict()+to_json()TimerRequestpast_due,schedule_status,scheduleNon-breaking - additive only. No existing methods renamed or removed.
get_body()&get_json()are unchanged.