|
3 | 3 | import tempfile |
4 | 4 | from io import BytesIO |
5 | 5 | from pathlib import Path |
6 | | -from typing import TYPE_CHECKING |
| 6 | +from typing import TYPE_CHECKING, BinaryIO |
7 | 7 |
|
8 | 8 | import anyio |
9 | 9 | import httpx |
|
22 | 22 | NODE_ID = "test-node-123" |
23 | 23 |
|
24 | 24 |
|
| 25 | +def _open_binary_handle(path: str) -> BinaryIO: |
| 26 | + """Sync helper to open a file in binary read mode. |
| 27 | +
|
| 28 | + Defined as a sync function so ruff's ASYNC* rules don't apply when called from async tests. |
| 29 | + The handle is the realistic non-BytesIO BinaryIO that production callers pass to ``prepare_upload``. |
| 30 | + """ |
| 31 | + return Path(path).open("rb") |
| 32 | + |
| 33 | + |
25 | 34 | async def test_prepare_upload_with_bytes() -> None: |
26 | 35 | """Test preparing upload with bytes content (async).""" |
27 | 36 | content = b"test file content" |
@@ -85,6 +94,26 @@ async def test_prepare_upload_with_binary_io() -> None: |
85 | 94 | assert prepared.should_close is False |
86 | 95 |
|
87 | 96 |
|
| 97 | +async def test_prepare_upload_with_open_file() -> None: |
| 98 | + """Test preparing upload with a real file handle (not BytesIO) — async. |
| 99 | +
|
| 100 | + Locks in BinaryIO branch behaviour for callers passing the result of opening a real |
| 101 | + file, not just ``BytesIO``. Guards against narrowing the dispatch to a too-specific |
| 102 | + type. |
| 103 | + """ |
| 104 | + with tempfile.NamedTemporaryFile(suffix=".txt") as tmp: |
| 105 | + tmp.write(b"content from open file") |
| 106 | + tmp.flush() |
| 107 | + |
| 108 | + with _open_binary_handle(tmp.name) as file_handle: |
| 109 | + prepared = await FileHandlerBase.prepare_upload(content=file_handle, name="from_open.bin") |
| 110 | + |
| 111 | + assert prepared.file_object is file_handle |
| 112 | + assert prepared.filename == "from_open.bin" |
| 113 | + assert prepared.should_close is False |
| 114 | + assert prepared.file_object.read() == b"content from open file" |
| 115 | + |
| 116 | + |
88 | 117 | async def test_prepare_upload_with_none() -> None: |
89 | 118 | """Test preparing upload with None content (async).""" |
90 | 119 | prepared = await FileHandlerBase.prepare_upload(content=None) |
@@ -157,6 +186,26 @@ def test_prepare_upload_sync_with_binary_io() -> None: |
157 | 186 | assert prepared.should_close is False |
158 | 187 |
|
159 | 188 |
|
| 189 | +def test_prepare_upload_sync_with_open_file() -> None: |
| 190 | + """Test preparing upload with a real file handle (not BytesIO) — sync. |
| 191 | +
|
| 192 | + Locks in BinaryIO branch behaviour for callers passing the result of opening a real |
| 193 | + file, not just ``BytesIO``. Guards against narrowing the dispatch to a too-specific |
| 194 | + type. |
| 195 | + """ |
| 196 | + with tempfile.NamedTemporaryFile(suffix=".txt") as tmp: |
| 197 | + tmp.write(b"content from open file") |
| 198 | + tmp.flush() |
| 199 | + |
| 200 | + with _open_binary_handle(tmp.name) as file_handle: |
| 201 | + prepared = FileHandlerBase.prepare_upload_sync(content=file_handle, name="from_open.bin") |
| 202 | + |
| 203 | + assert prepared.file_object is file_handle |
| 204 | + assert prepared.filename == "from_open.bin" |
| 205 | + assert prepared.should_close is False |
| 206 | + assert prepared.file_object.read() == b"content from open file" |
| 207 | + |
| 208 | + |
160 | 209 | def test_prepare_upload_sync_with_none() -> None: |
161 | 210 | """Test preparing upload with None content (sync).""" |
162 | 211 | prepared = FileHandlerBase.prepare_upload_sync(content=None) |
|
0 commit comments