Skip to content

Commit aba326c

Browse files
authored
Merge pull request #15 from jeffrey82221/feature/add_cp
feature: add copy method Thank you @jeffrey82221
2 parents 2346325 + 501580c commit aba326c

File tree

3 files changed

+167
-5
lines changed

3 files changed

+167
-5
lines changed

dropboxdrivefs/core.py

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import logging
2-
import os.path
3-
42
import dropbox.files
53
import requests
64
from dropbox.exceptions import ApiError
@@ -176,7 +174,56 @@ def _refactor_metadata(self, metadata, detail=True):
176174
return {"name": metadata.path_display, "size": None, "type": None}
177175
else:
178176
return metadata.path_display
179-
177+
178+
def copy(self, path1, path2, recursive=True, on_error=None, **kwargs):
179+
""" Copy objects from path1 to path2
180+
Parameters:
181+
----------
182+
path1: a folder or file, or a list of folders or files
183+
should we add the metadate with the path
184+
path2: a folder or a file
185+
recursive: bool
186+
whether to copy files in a folder recursively.
187+
on_error : "raise", "ignore"
188+
If raise, any not-found exceptions will be raised; if ignore any
189+
not-found exceptions will cause the path to be skipped; defaults to
190+
raise unless recursive is true, where the default is ignore
191+
"""
192+
if on_error is None and recursive:
193+
on_error = "ignore"
194+
elif on_error is None:
195+
on_error = "raise"
196+
if isinstance(path1, list):
197+
for file_path in path1:
198+
if on_error == 'raise':
199+
assert not file_path.endswith('/'), 'multiple file copy should takes files as input'
200+
try:
201+
self.copy(file_path, path2)
202+
except BaseException as e:
203+
if on_error == 'raise':
204+
raise e
205+
else:
206+
if path1.endswith('/') and path2.endswith('/'):
207+
if on_error == 'raise':
208+
assert recursive, 'recursive should be True for folder copying'
209+
if not self.exists(path2[:-1]):
210+
path1 = path1[:-1]
211+
path2 = path2[:-1]
212+
else:
213+
try:
214+
self.rm(path2[:-1], recursive=True)
215+
except BaseException as e:
216+
if on_error == 'raise':
217+
raise e
218+
path1 = path1[:-1]
219+
path2 = path2[:-1]
220+
elif path2.endswith('/'):
221+
path2 = path2 + path1.split('/')[-1]
222+
try:
223+
self.dbx.files_copy(path1, path2)
224+
except BaseException as e:
225+
if on_error == 'raise':
226+
raise e
180227

181228
class DropboxDriveFile(AbstractBufferedFile):
182229
""" fetch_all, fetch_range, and read method are based from the http implementation

test/test.py

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,57 @@
11
import unittest
2-
from unittest.mock import patch
3-
2+
from unittest.mock import patch, call
43
import dropbox
54

65
import dropboxdrivefs
76
from dropboxdrivefs import DropboxDriveFileSystem
87

8+
import os
9+
10+
# Get the directory containing the current file
11+
dropboxdrivefs_dir = os.path.dirname(os.path.dirname( os.path.abspath(__file__)))
12+
13+
14+
@patch.object(dropbox.Dropbox, "files_copy")
15+
class TestDropboxCopy(unittest.TestCase):
16+
17+
def setUp(self) -> None:
18+
19+
self.fs = DropboxDriveFileSystem('123')
20+
21+
def test_copy_dir_to_dir_recursive(self, mock_files_copy):
22+
self.fs.cp(os.path.join(dropboxdrivefs_dir, "example_file/"), "/target/newdir/", recursive=True)
23+
self.assertEqual(mock_files_copy.call_count, 1)
24+
mock_files_copy.assert_has_calls([call(os.path.join(dropboxdrivefs_dir, "example_file"), "/target/newdir")])
25+
26+
27+
def test_copy_dir_to_dir_non_recursive(self, mock_files_copy):
28+
self.fs.cp(os.path.join(dropboxdrivefs_dir, "example_file/"), "/target/newdir/", recursive=True)
29+
with self.assertRaises(AssertionError) as context:
30+
self.fs.cp(os.path.join(dropboxdrivefs_dir, "example_file/"), "/target/newdir/", recursive=False)
31+
self.assertEqual(str(context.exception), "recursive should be True for folder copying")
32+
33+
def test_copy_file_to_dir(self, mock_files_copy): # 1a. File to existing directory
34+
self.fs.cp(os.path.join(dropboxdrivefs_dir, "example_file/test_db/test1.txt"), '/target/')
35+
self.assertEqual(mock_files_copy.call_count, 1)
36+
mock_files_copy.assert_has_calls([call(os.path.join(dropboxdrivefs_dir, "example_file/test_db/test1.txt"), "/target/test1.txt")])
37+
38+
39+
def test_copy_file_to_file(self, mock_files_copy):
40+
# 1d. File to File in new directory
41+
self.fs.cp(os.path.join(dropboxdrivefs_dir, "example_file/test_db/test1.txt"), "/target/newdir/newfile.txt")
42+
self.assertEqual(mock_files_copy.call_count, 1)
43+
mock_files_copy.assert_has_calls([call(os.path.join(dropboxdrivefs_dir, "example_file/test_db/test1.txt"), "/target/newdir/newfile.txt")])
44+
45+
46+
def test_copy_multiple_files_to_dir(self, mock_files_copy):
47+
48+
self.fs.cp([os.path.join(dropboxdrivefs_dir, "example_file/test_db/test1.txt"),
49+
os.path.join(dropboxdrivefs_dir, "example_file/test_db/test2.txt")], "/target/")
50+
self.assertEqual(mock_files_copy.call_count, 2)
51+
mock_files_copy.assert_has_calls([call(os.path.join(dropboxdrivefs_dir, "example_file/test_db/test1.txt"), "/target/test1.txt"),
52+
call(os.path.join(dropboxdrivefs_dir, "example_file/test_db/test2.txt"), "/target/test2.txt")],
53+
any_order=True)
54+
955

1056
@patch.object(dropbox.Dropbox, "files_list_folder")
1157
class TestDropboxLs(unittest.TestCase):

test/test_copy.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
"""
2+
Testing Copy Method of DropboxDriveFileSystem
3+
4+
Test case reference from https://filesystem-spec.readthedocs.io/en/latest/copying.html
5+
"""
6+
import pytest
7+
from dropboxdrivefs import DropboxDriveFileSystem
8+
9+
@pytest.fixture
10+
def dropbox_fs():
11+
fs = DropboxDriveFileSystem('123')
12+
if not fs.exists('/source'):
13+
fs.mkdir('/source')
14+
if not fs.exists('/source/subdir'):
15+
fs.mkdir('/source/subdir')
16+
if not fs.exists('/target'):
17+
fs.mkdir('/target')
18+
with fs.open('/source/subdir/subfile1', 'w') as f:
19+
f.write('hello')
20+
with fs.open('/source/file1', 'w') as f:
21+
f.write('hello')
22+
with fs.open('/source/file2', 'w') as f:
23+
f.write('hello')
24+
return fs
25+
26+
def test_db_cp(dropbox_fs):
27+
try:
28+
# 1f. Directory to new directory
29+
dropbox_fs.cp("/source/subdir/", "/target/newdir/", recursive=True)
30+
assert dropbox_fs.exists('/target/newdir/subfile1')
31+
dropbox_fs.rm('/target/newdir', recursive=True)
32+
# 1e. Directory to existing directory
33+
dropbox_fs.cp("/source/subdir/", "/target/", recursive=True)
34+
assert dropbox_fs.exists('/target/subfile1')
35+
dropbox_fs.rm('/target/subfile1')
36+
# 1a. File to existing directory
37+
dropbox_fs.cp('/source/subdir/subfile1', '/target/')
38+
assert dropbox_fs.exists('/target/subfile1')
39+
dropbox_fs.rm('/target/subfile1')
40+
# 1b. File to new directory
41+
dropbox_fs.cp('/source/subdir/subfile1', '/target/newdir/')
42+
assert dropbox_fs.exists('/target/newdir/subfile1')
43+
dropbox_fs.rm('/target/newdir', recursive=True)
44+
# 1c. File to File in existing directory
45+
dropbox_fs.cp("/source/subdir/subfile1", "/target/newfile")
46+
assert dropbox_fs.exists('/target/newfile')
47+
dropbox_fs.rm('/target/newfile')
48+
# 1d. File to File in new directory
49+
dropbox_fs.cp("/source/subdir/subfile1", "/target/newdir/newfile")
50+
assert dropbox_fs.exists('/target/newdir/newfile')
51+
dropbox_fs.rm('/target/newdir', recursive=True)
52+
# 2a. List of Files to existing directory
53+
dropbox_fs.cp(["/source/file1", "/source/file2", "/source/subdir/subfile1"], "/target/")
54+
assert dropbox_fs.exists('/target/file1')
55+
assert dropbox_fs.exists('/target/file2')
56+
assert dropbox_fs.exists('/target/subfile1')
57+
dropbox_fs.rm('/target/file1')
58+
dropbox_fs.rm('/target/file2')
59+
dropbox_fs.rm('/target/subfile1')
60+
# 2b. List of Files to new directory
61+
dropbox_fs.cp(["/source/file1", "/source/file2", "/source/subdir/subfile1"], "/target/newdir/")
62+
assert dropbox_fs.exists('/target/newdir/file1')
63+
assert dropbox_fs.exists('/target/newdir/file2')
64+
assert dropbox_fs.exists('/target/newdir/subfile1')
65+
dropbox_fs.rm('/target/newdir/', recursive=True)
66+
finally:
67+
# Final Delete
68+
dropbox_fs.rm('/source/', recursive=True)
69+
dropbox_fs.rm('/target/', recursive=True)

0 commit comments

Comments
 (0)