fix: support PathLike content in upload tuples#3474
Open
VectorPeak wants to merge 1 commit into
Open
Conversation
Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@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.
Changes being requested
Fix multipart upload tuple handling so
PathLikecontent is normalized before the tuple is passed through to httpx.What Problem This Solves
The README documents upload parameters as accepting
bytes,PathLike, or a tuple such as(filename, contents, media type). In practice, the SDK already handled a directPathLikevalue correctly, for example:However, the tuple form did not get the same normalization when the tuple's content element was path-like:
That tuple shape is useful because it lets callers provide a custom upload filename while still asking the SDK to read the file contents from disk. It also supports the existing extended tuple forms for content type and headers:
Before this change, tuple inputs could be classified as generic file content before the tuple-specific branch had a chance to inspect and normalize the second element. As a result, callers following the documented upload shape could not reliably combine a custom filename, media type, or headers with
PathLikefile content.Conceptually, the bug was a normalization-order issue: the SDK handled "a path as the whole file value", but not "a path inside the file tuple". Those two forms should reach httpx in the same normalized shape: a filename plus bytes-like file content.
Change
This updates both the synchronous and asynchronous file normalization helpers so tuple values are handled before generic file-content detection.
The core code movement is intentionally small: tuple values now get normalized first, so the tuple's second element can still be read through the existing file-content helpers instead of being treated as final multipart content too early.
For tuple inputs, the SDK now preserves the tuple metadata while normalizing only the content element:
"custom.txt", remains the upload filename;read_file_content()orasync_read_file_content(), soPathLikevalues are converted into bytes;The direct
PathLikepath remains unchanged. PassingPath("input.jsonl")directly still uses the path's filename and reads the file contents as before. The change only makes the nested tuple case follow the same documented contract.Evidence
Focused tests now cover the previously missing tuple shapes for both sync and async normalization.
The sync tests verify that
to_httpx_files()handles:{"file": ("custom.txt", readme_path)} [("file", ("custom.txt", readme_path))] {"file": ("custom.txt", readme_path, "text/markdown")} {"file": ("custom.txt", readme_path, "text/markdown", {"X-Test": "1"})}The async tests mirror the same cases through
async_to_httpx_files()usinganyio.Path, covering:{"file": ("custom.txt", anyio.Path(readme_path))} [("file", ("custom.txt", anyio.Path(readme_path)))] {"file": ("custom.txt", anyio.Path(readme_path), "text/markdown")} {"file": ("custom.txt", anyio.Path(readme_path), "text/markdown", {"X-Test": "1"})}These tests check the important boundary behavior: after normalization, httpx receives tuple content as bytes, while the caller-provided filename, media type, and headers remain intact.
Validation run:
Possible call chain / impact
A typical public call can start with an upload parameter like:
That value is converted into request files, passed through
to_httpx_files()orasync_to_httpx_files(), and then handed off to httpx for multipart encoding.The intended boundary is:
This PR keeps the OpenAI SDK responsible for its documented
PathLikeextension before the value crosses into httpx's multipart layer. It does not broaden the accepted file input types; it only makes the already-documented tuple form behave consistently with directPathLikeuploads.The affected surface is limited to multipart file normalization. Existing direct
bytes, directPathLike, file-like objects, tuple filenames, tuple media types, and tuple headers continue to flow through the same helper functions and are covered by the added regression tests.Additional context & links
Fixes #3473
Validation:
mamba run -n prw-openai-python-py3.9 python -m pytest tests/test_files.py -q --tb=shortgit diff --check