Skip to content

Commit 702c64f

Browse files
committed
http: allow put_file()ing file-like objects
1 parent 53ebb3b commit 702c64f

File tree

3 files changed

+30
-4
lines changed

3 files changed

+30
-4
lines changed

fsspec/implementations/http.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import absolute_import, division, print_function
22

33
import asyncio
4+
import io
45
import logging
56
import re
67
import weakref
@@ -14,7 +15,7 @@
1415
from fsspec.callbacks import _DEFAULT_CALLBACK
1516
from fsspec.exceptions import FSTimeoutError
1617
from fsspec.spec import AbstractBufferedFile
17-
from fsspec.utils import DEFAULT_BLOCK_SIZE, tokenize
18+
from fsspec.utils import DEFAULT_BLOCK_SIZE, nullcontext, tokenize
1819

1920
from ..caching import AllBytes
2021

@@ -262,9 +263,19 @@ async def _put_file(
262263
**kwargs,
263264
):
264265
async def gen_chunks():
265-
with open(rpath, "rb") as f:
266-
callback.set_size(f.seek(0, 2))
267-
f.seek(0)
266+
# Support passing arbitrary file-like objects
267+
# and use them instead of streams.
268+
if isinstance(rpath, io.IOBase):
269+
context = nullcontext(rpath)
270+
use_seek = False # might not support seeking
271+
else:
272+
context = open(rpath, "rb")
273+
use_seek = True
274+
275+
with context as f:
276+
if use_seek:
277+
callback.set_size(f.seek(0, 2))
278+
f.seek(0)
268279

269280
chunk = f.read(64 * 1024)
270281
while chunk:

fsspec/implementations/tests/test_http.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,11 @@ def test_put_file(server, tmp_path, method, reset_files):
465465
fs.get_file(server + "/hey", dwl_file)
466466
assert dwl_file.read_bytes() == data
467467

468+
src_file.write_bytes(b"xxx")
469+
with open(src_file, "rb") as stream:
470+
fs.put_file(stream, server + "/hey_2", method=method)
471+
assert fs.cat(server + "/hey_2") == b"xxx"
472+
468473

469474
@pytest.mark.xfail(
470475
condition=sys.flags.optimize > 1, reason="no docstrings when optimised"

fsspec/utils.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import pathlib
55
import re
66
import sys
7+
from contextlib import contextmanager
78
from functools import partial
89
from hashlib import md5
910
from urllib.parse import urlsplit
@@ -466,3 +467,12 @@ def wrapper(cls):
466467
return cls
467468

468469
return wrapper
470+
471+
472+
try:
473+
from contextlib import nullcontext
474+
except ImportError:
475+
476+
@contextmanager
477+
def nullcontext(obj=None):
478+
yield obj

0 commit comments

Comments
 (0)