Skip to content

Commit 7efc362

Browse files
authored
* fixes * version bump
1 parent 33b55e2 commit 7efc362

File tree

8 files changed

+47
-8
lines changed

8 files changed

+47
-8
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/)
55
and this project adheres to [Semantic Versioning](http://semver.org/).
66

7+
## [2.0.26] - 2018-07-26
8+
9+
### Fixed
10+
11+
- fs.copy and fs.move disable workers if not thread-safe
12+
- fs.match detects case insensitivity
13+
- Open in exclusive mode is atomic (@squishy)
14+
- Exceptions can be pickleabe (@Spacerat)
15+
716
## [2.0.25] - 2018-07-20
817

918
### Added

fs/base.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1552,8 +1552,9 @@ def match(self, patterns, name):
15521552
return True
15531553
if isinstance(patterns, six.text_type):
15541554
raise TypeError('patterns must be a list or sequence')
1555-
case_sensitive = typing.cast(
1556-
bool, self.getmeta().get('case_sensitive', True))
1555+
case_sensitive = not typing.cast(
1556+
bool, self.getmeta().get('case_insensitive', False)
1557+
)
15571558
matcher = wildcard.get_matcher(patterns, case_sensitive)
15581559
return matcher(name)
15591560

fs/copy.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from .errors import FSError
99
from .opener import manage_fs
1010
from .path import abspath, combine, frombase, normpath
11+
from .tools import is_thread_safe
1112
from .walk import Walker
1213

1314
if False: # typing.TYPE_CHECKING
@@ -287,7 +288,8 @@ def dst():
287288

288289
with src() as _src_fs, dst() as _dst_fs:
289290
with _src_fs.lock(), _dst_fs.lock():
290-
with Copier(num_workers=workers) as copier:
291+
_thread_safe = is_thread_safe(_src_fs, _dst_fs)
292+
with Copier(num_workers=workers if _thread_safe else 0) as copier:
291293
_dst_fs.makedir(_dst_path, recreate=True)
292294
for dir_path, dirs, files in walker.walk(_src_fs, _src_path):
293295
copy_path = combine(_dst_path, frombase(_src_path, dir_path))
@@ -347,7 +349,8 @@ def dst():
347349

348350
with src() as _src_fs, dst() as _dst_fs:
349351
with _src_fs.lock(), _dst_fs.lock():
350-
with Copier(num_workers=workers) as copier:
352+
_thread_safe = is_thread_safe(_src_fs, _dst_fs)
353+
with Copier(num_workers=workers if _thread_safe else 0) as copier:
351354
_dst_fs.makedir(_dst_path, recreate=True)
352355
namespace = ("details", "modified")
353356
dst_state = {

fs/mirror.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@
2525
from ._bulk import Copier
2626
from .copy import copy_file_internal
2727
from .errors import ResourceNotFound
28-
from .walk import Walker
2928
from .opener import manage_fs
29+
from .tools import is_thread_safe
30+
from .walk import Walker
3031

3132
if False: # typing.TYPE_CHECKING
3233
from typing import Callable, Optional, Text, Union
@@ -83,7 +84,8 @@ def dst():
8384

8485
with src() as _src_fs, dst() as _dst_fs:
8586
with _src_fs.lock(), _dst_fs.lock():
86-
with Copier(num_workers=workers) as copier:
87+
_thread_safe = is_thread_safe(_src_fs, _dst_fs)
88+
with Copier(num_workers=workers if _thread_safe else 0) as copier:
8789
_mirror(
8890
_src_fs,
8991
_dst_fs,

fs/test.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1810,6 +1810,10 @@ def test_movedir(self):
18101810

18111811
def test_match(self):
18121812
self.assertTrue(self.fs.match(['*.py'], 'foo.py'))
1813+
self.assertEqual(
1814+
self.fs.match(['*.py'], 'FOO.PY'),
1815+
self.fs.getmeta().get('case_insensitive', False)
1816+
)
18131817

18141818
def test_tree(self):
18151819
self.fs.makedirs('foo/bar')

fs/tools.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,20 @@ def get_intermediate_dirs(fs, dir_path):
8585
break
8686
raise errors.DirectoryExpected(dir_path)
8787
return intermediates[::-1][:-1]
88+
89+
90+
def is_thread_safe(*filesystems):
91+
# type: (FS) -> bool
92+
"""Check if all filesystems are thread-safe.
93+
94+
Arguments:
95+
filesystems (FS): Filesystems instances to check.
96+
97+
Returns:
98+
bool: if all filesystems are thread safe.
99+
100+
"""
101+
return all(
102+
fs.getmeta().get('thread_safe', False)
103+
for fs in filesystems
104+
)

tests/test_errors.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,14 @@ def test_raise_in_multiprocessing(self):
3535
[errors.CreateFailed],
3636
[errors.NoSysPath, 'some_path'],
3737
[errors.NoURL, 'some_path', 'some_purpose'],
38-
[errors.Unsupported]
38+
[errors.Unsupported],
39+
[errors.IllegalBackReference, 'path']
3940
]
4041
try:
4142
pool = multiprocessing.Pool(1)
4243
for args in tests:
44+
exc = args[0](*args[1:])
45+
exc.__reduce__()
4346
with self.assertRaises(args[0]):
4447
pool.apply(_multiprocessing_test_task, args)
4548
finally:

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[tox]
2-
envlist = {py27,py34,py35,py36}{,-scandir},pypy
2+
envlist = {py27,py34,py35,py36,py37}{,-scandir},pypy
33
sitepackages = False
44
skip_missing_interpreters=True
55

0 commit comments

Comments
 (0)