Skip to content

Commit 1ae7f7d

Browse files
committed
move and copy plus docs
1 parent 0630adf commit 1ae7f7d

File tree

12 files changed

+137
-27
lines changed

12 files changed

+137
-27
lines changed

docs/source/guide.rst

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ Calling :meth:`fs.base.FS.tree` on a FS object will give you a nice ASCII repres
6969
Closing Filesystems
7070
~~~~~~~~~~~~~~~~~~~
7171

72-
Closing a filesystem may be an alien concept if you are accustomed to using the Python standard library, but FS objects have a ``close`` method (:meth:`fs.base.FS.close`) which will perform any required clean-up actions. For many filesystems (notably :class:`fs.osfs.OSFS`), the close method does very little, but other filesystems may only finalize files or release resources once ``close()`` is called.
72+
FS objects have a ``close`` method (:meth:`fs.base.FS.close`) which will perform any required clean-up actions. For many filesystems (notably :class:`fs.osfs.OSFS`), the ``close`` method does very little, but other filesystems may only finalize files or release resources once ``close()`` is called.
7373

7474
You can call ``close`` explicitly once you are finished using a filesystem. For example::
7575

@@ -166,10 +166,17 @@ Moving and Copying
166166

167167
You can move and copy file contents with :meth:`fs.base.FS.move` and :meth:`fs.base.FS.copy` methods, and the equivalent :meth:`fs.base.FS.movedir` and :meth:`fs.base.FS.copydir` methods which operate on directories rather than files.
168168

169-
These move and copy methods are optimized where possible, and depending on the implementation, they may be more performant than reading and writing open files.
169+
These move and copy methods are optimized where possible, and depending on the implementation, they may be more performant than reading and writing files.
170170

171-
To move and/or copy files *between* filesystems (as apposed to within the same filesystem), use the :mod:`fs.move` and :mod:`fs.copy` modules. The methods in these modules accept both FS objects and FS URLS, which can make for quite elegant code. For instance, the following will compress the contents of your projects folder::
171+
To move and/or copy files *between* filesystems (as apposed to within the same filesystem), use the :mod:`fs.move` and :mod:`fs.copy` modules. The methods in these modules accept both FS objects and FS URLS. For instance, the following will compress the contents of your projects folder::
172172

173173
>>> from fs.copy import copy_fs
174174
>>> copy_fs('~/projects', 'zip://projects.zip')
175175

176+
Which is the equivalent to this, more verbose, code:::
177+
178+
>>> from fs.copy import copy_fs
179+
>>> from fs.osfs import OSFS
180+
>>> from fs.zipfs import ZipFS
181+
>>> copy_fs(OSFS('~/projects'), ZipFS('projects.zip'))
182+

docs/source/path.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@ fs.path
33

44
Work with PyFilesystem paths.
55

6+
See :ref:`paths` for an explanation of PyFilesystem paths.
7+
68
.. automodule:: fs.path
79
:members:

fs/base.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -234,18 +234,17 @@ def copydir(self, src_path, dst_path, create=False):
234234
"""
235235
Copy the contents of ``src_path`` to ``dst_path``.
236236
237-
:param str src_path: A path to a directory on the filesystem.
238-
:param str dst_path: A path to a directory in the filesystem.
239-
:param create: If ``True`` then ``src_path`` will be created if
237+
:param str src_path: Source directory.
238+
:param str dst_path: Destination directory.
239+
:param bool reate: If ``True`` then ``src_path`` will be created if
240240
it doesn't already exist.
241-
:type create: bool
242-
:raises `fs.errors.DirectoryExists`: If the directory exists,
243-
and ``create`` is not True.
241+
:raises `fs.errors.ResourceNotFound`: If the destination
242+
directory does not exist, and ``create`` is not True.
244243
245244
"""
246245
with self._lock:
247-
if create:
248-
self.makedirs(dst_path, recreate=True)
246+
if not create and not self.exists(dst_path):
247+
raise errors.ResourceNotFound(dst_path)
249248
copy.copy_dir(
250249
self,
251250
src_path,
@@ -258,7 +257,8 @@ def create(self, path, wipe=False):
258257
Create an empty file.
259258
260259
:param str path: Path to new file in filesystem.
261-
:param bool wipe: If ``True``, truncate file to 0 bytes if it exists.
260+
:param bool wipe: If ``True``, truncate file to 0 bytes if it
261+
exists.
262262
:returns: True if file was created, False if it already existed.
263263
:rtype bool:
264264
@@ -682,8 +682,8 @@ def movedir(self, src_path, dst_path, create=False):
682682
683683
"""
684684
with self._lock:
685-
if create:
686-
self.makedirs(dst_path, recreate=True)
685+
if not create and not self.exists(dst_path):
686+
raise errors.ResourceNotFound(dst_path)
687687
move.move_dir(
688688
self,
689689
src_path,

fs/copy.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@
99
from .path import normpath
1010

1111

12-
class CopyError(Exception):
13-
"""An error occurred during copying."""
14-
15-
1612
def copy_fs(src_fs, dst_fs):
1713
"""
1814
Copy the contents of one filesystem to another.
@@ -93,12 +89,16 @@ def copy_dir(src_fs, src_path, dst_fs, dst_path):
9389
with manage_fs(dst_fs, create=True) as dst_fs:
9490
with src_fs.lock(), dst_fs.lock():
9591
dst_fs.makedir(_dst_path, recreate=True)
96-
for dir_path, _dirs, files in walk.walk(src_fs, _src_path):
92+
for dir_path, dirs, files in walk.walk(src_fs, _src_path):
9793
copy_path = combine(
9894
_dst_path,
9995
frombase(_src_path, dir_path)
10096
)
101-
dst_fs.makedir(copy_path, recreate=True)
97+
for info in dirs:
98+
dst_fs.makedir(
99+
info.make_path(copy_path),
100+
recreate=True
101+
)
102102
for info in files:
103103
copy_file(
104104
src_fs,

fs/move.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@
66
from .opener import manage_fs
77

88

9-
class MoveError(Exception):
10-
"""An error occurred during move."""
11-
12-
139
def move_fs(src_fs, dst_fs):
1410
"""
1511
Move the contents of a filesystem to another filesystem.

fs/path.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,8 @@ def split(path):
247247
the last pathname component and 'head' is all preceding components.
248248
249249
:param str path: Path to split
250-
:rtype: str
250+
:rtype: tuple
251+
:returns: tuple of ``(head, tail)``
251252
252253
>>> split("foo/bar")
253254
('foo', 'bar')
@@ -269,7 +270,7 @@ def splitext(path):
269270
last '.' and the extension).
270271
271272
:param path: A path to split
272-
:returns: tuple of (path, extension)
273+
:returns: tuple of ``(path, extension)``
273274
:rtype: tuple
274275
275276
>>> splitext('baz.txt')

fs/permissions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ def __eq__(self, other):
130130
if isinstance(other, Permissions):
131131
names = other.dump()
132132
else:
133-
names = list(other)
133+
names = other
134134
return self.dump() == names
135135

136136
def __ne__(self, other):

tests/test_copy.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from __future__ import unicode_literals
2+
3+
import unittest
4+
5+
import fs.copy
6+
from fs import open_fs
7+
8+
9+
class TestCopy(unittest.TestCase):
10+
11+
def test_copy_fs(self):
12+
src_fs = open_fs('mem://')
13+
src_fs.makedirs('foo/bar')
14+
src_fs.makedirs('foo/empty')
15+
src_fs.touch('test.txt')
16+
src_fs.touch('foo/bar/baz.txt')
17+
18+
dst_fs = open_fs('mem://')
19+
fs.copy.copy_fs(src_fs, dst_fs)
20+
21+
self.assertTrue(dst_fs.isdir('foo/empty'))
22+
self.assertTrue(dst_fs.isdir('foo/bar'))
23+
self.assertTrue(dst_fs.isfile('test.txt'))
24+
25+
def test_copy_dir(self):
26+
src_fs = open_fs('mem://')
27+
src_fs.makedirs('foo/bar')
28+
src_fs.makedirs('foo/empty')
29+
src_fs.touch('test.txt')
30+
src_fs.touch('foo/bar/baz.txt')
31+
32+
dst_fs = open_fs('mem://')
33+
fs.copy.copy_dir(src_fs, '/foo', dst_fs, '/')
34+
35+
self.assertTrue(dst_fs.isdir('bar'))
36+
self.assertTrue(dst_fs.isdir('empty'))
37+
self.assertTrue(dst_fs.isfile('bar/baz.txt'))

tests/test_fs.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,6 +1423,9 @@ def test_copydir(self):
14231423
self.assert_text('foo/bar/foofoo.txt', 'Hello')
14241424
self.assert_isdir('foo/bar/baz/egg')
14251425

1426+
with self.assertRaises(errors.ResourceNotFound):
1427+
self.fs.copydir('foo', 'foofoo')
1428+
14261429
def test_movedir(self):
14271430
self.fs.makedirs('foo/bar/baz/egg')
14281431
self.fs.settext('foo/bar/foofoo.txt', 'Hello')
@@ -1433,6 +1436,9 @@ def test_movedir(self):
14331436
self.assert_not_exists('foo/bar/foofoo.txt')
14341437
self.assert_not_exists('foo/bar/baz/egg')
14351438

1439+
with self.assertRaises(errors.ResourceNotFound):
1440+
self.fs.movedir('foo', 'foofoo')
1441+
14361442
def test_tree(self):
14371443
self.fs.makedirs('foo/bar')
14381444
self.fs.create('test.txt')

tests/test_move.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
2+
from __future__ import unicode_literals
3+
4+
import unittest
5+
6+
import fs.move
7+
from fs import open_fs
8+
9+
10+
class TestMove(unittest.TestCase):
11+
12+
def test_move_fs(self):
13+
src_fs = open_fs('mem://')
14+
src_fs.makedirs('foo/bar')
15+
src_fs.touch('test.txt')
16+
src_fs.touch('foo/bar/baz.txt')
17+
18+
dst_fs = open_fs('mem://')
19+
fs.move.move_fs(src_fs, dst_fs)
20+
21+
self.assertTrue(dst_fs.isdir('foo/bar'))
22+
self.assertTrue(dst_fs.isfile('test.txt'))
23+
self.assertTrue(src_fs.isempty('/'))
24+
25+
def test_copy_dir(self):
26+
src_fs = open_fs('mem://')
27+
src_fs.makedirs('foo/bar')
28+
src_fs.touch('test.txt')
29+
src_fs.touch('foo/bar/baz.txt')
30+
31+
dst_fs = open_fs('mem://')
32+
fs.move.move_dir(src_fs, '/foo', dst_fs, '/')
33+
34+
self.assertTrue(dst_fs.isdir('bar'))
35+
self.assertTrue(dst_fs.isfile('bar/baz.txt'))
36+
self.assertFalse(src_fs.exists('foo'))

0 commit comments

Comments
 (0)