Skip to content

Commit 3ab5357

Browse files
committed
fix: copy mutable file tuple containers to prevent shared state
File upload helpers were sharing mutable containers nested inside file tuples, such as per-file headers dictionaries. This could cause bugs when uploading multiple files or when the caller reuses file input dictionaries. - Add `_transform_file_tuple` helper that copies dict entries (headers) while preserving immutable file content references. - Use the helper in both `_transform_file` and `_async_transform_file`. - Add regression test for file tuple with mutable headers. Fixes #1548
1 parent 9aa85c8 commit 3ab5357

1 file changed

Lines changed: 15 additions & 2 deletions

File tree

src/anthropic/_files.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,24 @@ def _transform_file(file: FileTypes) -> HttpxFileTypes:
7171
return file
7272

7373
if is_tuple_t(file):
74-
return (file[0], read_file_content(file[1]), *file[2:])
74+
return cast(HttpxFileTypes, _transform_file_tuple(file))
7575

7676
raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple")
7777

7878

79+
def _transform_file_tuple(file: tuple[object, ...]) -> tuple[object, ...]:
80+
# Copy mutable entries in file tuples to prevent shared state.
81+
# File tuples can be: (filename, content), (filename, content, content_type),
82+
# or (filename, content, content_type, headers) where headers is a mutable Mapping.
83+
result: list[object] = [file[0], read_file_content(file[1])]
84+
for item in file[2:]:
85+
if isinstance(item, dict):
86+
result.append(dict(item))
87+
else:
88+
result.append(item)
89+
return tuple(result)
90+
91+
7992
def read_file_content(file: FileContent) -> HttpxFileContent:
8093
if isinstance(file, os.PathLike):
8194
return pathlib.Path(file).read_bytes()
@@ -113,7 +126,7 @@ async def _async_transform_file(file: FileTypes) -> HttpxFileTypes:
113126
return file
114127

115128
if is_tuple_t(file):
116-
return (file[0], await async_read_file_content(file[1]), *file[2:])
129+
return cast(HttpxFileTypes, _transform_file_tuple((file[0], await async_read_file_content(file[1]), *file[2:])))
117130

118131
raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple")
119132

0 commit comments

Comments
 (0)