Skip to content

Keep every URL value of a multipart list field - #5676

Open
AbdullahRasheed45 wants to merge 1 commit into
bentoml:mainfrom
AbdullahRasheed45:keep-all-url-values-in-multipart-lists
Open

Keep every URL value of a multipart list field#5676
AbdullahRasheed45 wants to merge 1 commit into
bentoml:mainfrom
AbdullahRasheed45:keep-all-url-values-in-multipart-lists

Conversation

@AbdullahRasheed45

Copy link
Copy Markdown

Closes #5675

The problem

_build_multipart walks the values of a file field and splits them two ways: real uploads are appended to the files list, while values the file manager hands back as plain strings — HTTP URLs are passed through untouched — go into data.

The string branch assigned instead of accumulating:

for v in value:
    file = self._file_manager.get_file(v)
    if isinstance(file, str):
        data[name] = file        # each value overwrites the previous one
    else:
        files.append((name, file))

So a list[Path] field carrying several URLs reached the service with only the last one. Real uploads were never affected, because that branch already appended — which is why the issue's local-file control passed while the URL case lost values.

Confirmed against the encoded request body on main, with two URLs going in:

before:  ['https://ex.invalid/b.txt']
after:   ['https://ex.invalid/a.txt', 'https://ex.invalid/b.txt']

The change

Collect the string values in a list so each is sent as its own part.

The aiohttp client (proxy2) needed a second change: it feeds data entries to FormData.add_field one at a time, which takes a single value, so the loop now adds one field per value. Without that it would have passed a list straight to add_field.

Both clients carried the same bug, so both are fixed here rather than in separate PRs.

Nothing changes for a field carrying a single value: one entry in, one part out, as before.

Tests

Added tests/unit/_internal/client/test_multipart_list_fields.py, driving _build_multipart directly with a passthrough file manager so no network or running service is involved:

  • both clients keep every URL of a multi-value list field — these two fail on main
  • a single URL is sent exactly once, parametrized over the array and scalar field shapes, so the fix cannot start duplicating parts
  • URLs and a real upload mixed in one field still produce one form value and one file part, since that is the path the accumulation could most easily disturb

pytest tests/unit/_internal/client/ → 6 passed for the new file. test_session_manager.py::test_session_refresh_creates_new_connector fails in my environment, but it fails identically on an unmodified checkout (missing async plugin), so it is unrelated.

ruff check and ruff format are clean on all three files. ruff check reports 9 pre-existing errors in the two client modules on an unmodified checkout as well; I left those alone to keep the diff to the bug.

_build_multipart walks the values of a file field and splits them: real uploads
are appended to the files list, while values the file manager returns as plain
strings, such as HTTP URLs, went into data[name].

The string branch assigned rather than accumulated, so each value overwrote the
previous one and a list field carrying several URLs arrived at the service with
only the last. Real uploads were unaffected because that branch already appended.

Collect the string values in a list instead. The aiohttp client also needs its
FormData loop to add one field per value, since it feeds data entries to
add_field one at a time.

Both clients were affected and are fixed together.
@AbdullahRasheed45
AbdullahRasheed45 requested a review from a team as a code owner July 27, 2026 00:48
@AbdullahRasheed45
AbdullahRasheed45 requested review from jianshen92 and removed request for a team July 27, 2026 00:48
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.

Remote clients drop preceding URL values for multipart list file inputs

1 participant