Skip to content

Commit ff808ce

Browse files
authored
Merge pull request #737 from martindurant/put_single
Put files under target directory if it exists
2 parents 113a498 + 576b1a1 commit ff808ce

File tree

5 files changed

+26
-4
lines changed

5 files changed

+26
-4
lines changed

fsspec/asyn.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,9 @@ async def _put(
378378
lpath = make_path_posix(lpath)
379379
fs = LocalFileSystem()
380380
lpaths = fs.expand_path(lpath, recursive=recursive)
381-
rpaths = other_paths(lpaths, rpath)
381+
rpaths = other_paths(
382+
lpaths, rpath, exists=isinstance(rpath, str) and await self._isdir(rpath)
383+
)
382384

383385
is_dir = {l: os.path.isdir(l) for l in lpaths}
384386
rdirs = [r for l, r in zip(lpaths, rpaths) if is_dir[l]]

fsspec/implementations/tests/test_memory.py

+13
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import os
2+
13
import pytest
24

35

@@ -19,6 +21,17 @@ def test_strip(m):
1921
assert m._strip_protocol("/b/c/") == "/b/c"
2022

2123

24+
def test_put_single(m, tmpdir):
25+
fn = os.path.join(str(tmpdir), "dir")
26+
os.mkdir(fn)
27+
open(os.path.join(fn, "abc"), "w").write("text")
28+
m.put(fn, "/test") # no-op, no files
29+
assert not m.exists("/test/abc")
30+
assert not m.exists("/test/dir")
31+
m.put(fn + "/", "/test", recursive=True)
32+
assert m.cat("/test/dir/abc") == b"text"
33+
34+
2235
def test_ls(m):
2336
m.mkdir("/dir")
2437
m.mkdir("/dir/dir1")

fsspec/spec.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -802,7 +802,9 @@ def put(self, lpath, rpath, recursive=False, callback=_DEFAULT_CALLBACK, **kwarg
802802
lpath = make_path_posix(lpath)
803803
fs = LocalFileSystem()
804804
lpaths = fs.expand_path(lpath, recursive=recursive)
805-
rpaths = other_paths(lpaths, rpath)
805+
rpaths = other_paths(
806+
lpaths, rpath, exists=isinstance(rpath, str) and self.isdir(rpath)
807+
)
806808

807809
callback.set_size(len(rpaths))
808810
for lpath, rpath in callback.wrap(zip(lpaths, rpaths)):

fsspec/tests/test_spec.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ def imitate_transfer(size, chunk, *, file=True):
462462
if file:
463463
# The reason that there is a relative_update(0) at the
464464
# end is that, we don't have an early exit on the
465-
# impleementations of get_file/put_file so it needs to
465+
# implementations of get_file/put_file so it needs to
466466
# go through the callback to get catch by the while's
467467
# condition and then it will stop the transfer.
468468
events.append(("relative_update", 0))

fsspec/utils.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ def common_prefix(paths):
335335
return "/".join(parts[0][:i])
336336

337337

338-
def other_paths(paths, path2, is_dir=None):
338+
def other_paths(paths, path2, is_dir=None, exists=False):
339339
"""In bulk file operations, construct a new file tree from a list of files
340340
341341
Parameters
@@ -349,6 +349,9 @@ def other_paths(paths, path2, is_dir=None):
349349
For the special case where the input in one element, whether to regard the value
350350
as the target path, or as a directory to put a file path within. If None, a
351351
directory is inferred if the path ends in '/'
352+
exists: bool (optional)
353+
For a str destination, it is already exists (and is a dir), files should
354+
end up inside.
352355
353356
Returns
354357
-------
@@ -359,6 +362,8 @@ def other_paths(paths, path2, is_dir=None):
359362
path2 = path2.rstrip("/")
360363
if len(paths) > 1:
361364
cp = common_prefix(paths)
365+
if exists:
366+
cp = cp.rsplit("/", 1)[0]
362367
path2 = [p.replace(cp, path2, 1) for p in paths]
363368
else:
364369
if is_dir:

0 commit comments

Comments
 (0)