Skip to content

[azure-core/corehttp] Add JSONL streaming support#48077

Draft
l0lawrence wants to merge 1 commit into
Azure:mainfrom
l0lawrence:l0lawrence-jsonl-stream-followup
Draft

[azure-core/corehttp] Add JSONL streaming support#48077
l0lawrence wants to merge 1 commit into
Azure:mainfrom
l0lawrence:l0lawrence-jsonl-stream-followup

Conversation

@l0lawrence

@l0lawrence l0lawrence commented Jul 15, 2026

Copy link
Copy Markdown
Member

Adds JSONL streaming support to both azure-core (azure.core.streaming) and corehttp (corehttp.streaming), kept in sync so the two implementations stay identical (only the rest-path docstrings differ).

Ports the design from the design gist and the original corehttp prototype in #39478 (closed unmerged), with the review gaps addressed. Part of #38806.

What's included (per package)

  • Stream / AsyncStream stream-agnostic iterators
  • JSONLDecoder / AsyncJSONLDecoder (incremental UTF-8 line decoding)
  • deserialization_callback receives (response, event) so generated code can support the cls custom-deserializer pattern
  • Sync + async tests, test-server JSONL routes, sample (samples/sample_stream.py)
  • CHANGELOG entry + README section (azure-core also bumps version to 1.42.0)

SSE support remains a follow-up, as noted in the original prototype.

Validation (both packages)

  • 26/26 stream tests pass (sync + async) each
  • black: clean · pylint: 10.00/10 · mypy: clean for the streaming module

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
7 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@l0lawrence
l0lawrence force-pushed the l0lawrence-jsonl-stream-followup branch from f10ef48 to 785e97e Compare July 15, 2026 22:47
@l0lawrence l0lawrence changed the title [corehttp] JSONL streaming (follow-up on #39478) [azure-core] Add JSONL streaming support Jul 15, 2026
Adds a stream-agnostic Stream/AsyncStream iterator plus
JSONLDecoder/AsyncJSONLDecoder to both azure-core (azure.core.streaming)
and corehttp (corehttp.streaming), kept in sync. The
deserialization_callback receives the response and each decoded event so
generated code can support the cls custom-deserializer pattern.

Each package includes sync/async tests, test-server JSONL routes, a
sample, CHANGELOG entry, and a README section (azure-core also bumps the
version to 1.42.0).

Based on the design in
https://gist.github.com/kristapratico/d330af39962ea05b10384b865e37b36f
and the original corehttp prototype in Azure#39478.
Part of Azure#38806.

Co-authored-by: Krista Pratico <krpratic@microsoft.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@l0lawrence
l0lawrence force-pushed the l0lawrence-jsonl-stream-followup branch from 785e97e to 8fe667c Compare July 15, 2026 22:59
@l0lawrence l0lawrence changed the title [azure-core] Add JSONL streaming support [azure-core/corehttp] Add JSONL streaming support Jul 15, 2026
for chunk in iter_bytes:
decoded += decoder.decode(chunk)
if decoded:
decoded_lines = decoded.splitlines()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think we can use splitlines as is since it breaks on more than just what the JSONL spec specifies https://docs.python.org/3.4/library/stdtypes.html#str.splitlines.

splitlines will treat \u2028 and \u2029 as split but the spec https://www.rfc-editor.org/info/rfc8259/ has it as

 certain characters such as U+2028 LINE SEPARATOR and U+2029
   PARAGRAPH SEPARATOR are legal in JSON but not JavaScript

So line termination will have to be simply \n and `\r\n'

'{"msg": "a\u2028b"}\n'.splitlines()
['{"msg": "a', 'b"}'  # one valid record -> two invalid fragments -> JSONDecodeError


'{"msg": "a\u2028b"}\n'.split("\n")
['{"msg": "a\u2028b"}', '']          # one record + trailing empty from the final \n
json.loads('{"msg": "a\u2028b"}')
{'msg': 'a\u2028b'}                   # U+2028 preserved inside the string value

async def __anext__(self) -> ReturnType_co:
return await self._iterator.__anext__()

async def __aiter__(self) -> AsyncIterator[ReturnType_co]: # pylint: disable=invalid-overridden-method

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think the iter funcs need to return Self

:keyword response: The response object.
:paramtype response: ~corehttp.rest.AsyncHttpResponse
:keyword decoder: A decoder to use for the stream.
:paramtype decoder: ~corehttp.streaming.decoders.AsyncStreamDecoder

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

should it be _decoders here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants