Skip to content

fix: address inconsistent serialization for binding objects - #356

Draft
hallvictoria wants to merge 9 commits into
devfrom
hallvictoria/consistent-serialization
Draft

fix: address inconsistent serialization for binding objects#356
hallvictoria wants to merge 9 commits into
devfrom
hallvictoria/consistent-serialization

Conversation

@hallvictoria

@hallvictoria hallvictoria commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

fixes: #351 - add uniform to_dict() / to_json() serialization contract to all binding types

Binding objects had four inconsistent conventions for serialization — to_dict() (CosmosDB only), to_json() returning str (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

  • New Serializable mixin in _abc.py - abstract to_dict() -> Any with a concrete to_json() -> str that delegates to it. All binding ABCs now inherit from it.
  • New _serialize_value helper in _utils.py - normalizes field values for JSON safety:
    • bytes - decoded as UTF-8 when valid, else {"__encoding": "base64", "value": "..."}
    • datetime / date - ISO-8601 string
    • timedelta - str()
    • everything else passes through unchanged
  • to_dict() added to all concrete binding types:
Type Fields included
InputStream name, uri, length, blob_properties, metadata (no body read)
QueueMessage all envelope fields + body
ServiceBusMessage all ~25 envelope fields + body
EventGridEvent / EventGridOutputEvent / CloudEvent all envelope fields + data
EventHubEvent body, partition_key, sequence_number, offset, enqueued_time, iothub_metadata
KafkaEvent body, key, offset, partition, topic, timestamp, headers
DocumentList list of doc.to_dict()
SqlRow / MySqlRow dict(self) (additive - to_json() was already present)
SqlRowList / MySqlRowList list of row.to_dict() + to_json()
TimerRequest past_due, schedule_status, schedule

Non-breaking - additive only. No existing methods renamed or removed. get_body() & get_json() are unchanged.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 Serializable mixin on binding ABCs and implements to_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 data contains 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 json adapter 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 json adapter 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.

Comment thread azure/functions/_utils.py
Comment thread azure/functions/blob.py
Comment thread azure/functions/_eventgrid.py
Comment thread azure/functions/_eventgrid.py
Comment thread azure/functions/kafka.py
Comment thread azure/functions/_servicebus.py
Comment thread azure/functions/_sql.py Outdated
Comment thread azure/functions/_mysql.py Outdated
Comment thread azure/functions/_abc.py Outdated
hallvictoria and others added 2 commits July 27, 2026 14:21
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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() returns data without normalization. Since to_json() will serialize this dict directly, passing non-JSON-native values (e.g., bytes/datetime nested in data) can raise TypeError and 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, but to_json() still serializes dict(self) directly. That means to_json() can still fail on bytes/datetime values even though to_dict() would succeed. Consider delegating to_json() to to_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, but to_json() still serializes dict(self) directly. That means to_json() can still fail on bytes/datetime values even though to_dict() would succeed. Consider delegating to_json() to to_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:

Comment thread azure/functions/_abc.py
Comment thread azure/functions/_sql.py Outdated
Comment thread azure/functions/_mysql.py Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.

Comment thread azure/functions/_eventgrid.py Outdated
Comment thread azure/functions/_sql.py Outdated
Comment thread azure/functions/_mysql.py Outdated
hallvictoria and others added 2 commits July 27, 2026 15:06
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comment thread azure/functions/timer.py
hallvictoria and others added 2 commits July 27, 2026 15:29
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Comment thread azure/functions/timer.py Outdated
Comment thread azure/functions/_utils.py
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Provide a uniform serialization contract (to_dict/to_json) across trigger binding types

2 participants