Skip to content

Commit

Permalink
fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
willmcgugan committed Jul 24, 2018
1 parent 997ee16 commit 4b73b3c
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 7 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [2.0.26] - Unreleased

### Fixed

- fs.copy and fs.move disable workers if not thread-safe
- fs.match detects case insensitivity
- Open in exclusive mode is atomic (@squishy)
- Exceptions can be pickleabe (@Spacerat)

## [2.0.25] - 2018-07-20

### Added
Expand Down
5 changes: 3 additions & 2 deletions fs/base.py
Expand Up @@ -1552,8 +1552,9 @@ def match(self, patterns, name):
return True
if isinstance(patterns, six.text_type):
raise TypeError('patterns must be a list or sequence')
case_sensitive = typing.cast(
bool, self.getmeta().get('case_sensitive', True))
case_sensitive = not typing.cast(
bool, self.getmeta().get('case_insensitive', False)
)
matcher = wildcard.get_matcher(patterns, case_sensitive)
return matcher(name)

Expand Down
7 changes: 5 additions & 2 deletions fs/copy.py
Expand Up @@ -8,6 +8,7 @@
from .errors import FSError
from .opener import manage_fs
from .path import abspath, combine, frombase, normpath
from .tools import is_thread_safe
from .walk import Walker

if False: # typing.TYPE_CHECKING
Expand Down Expand Up @@ -287,7 +288,8 @@ def dst():

with src() as _src_fs, dst() as _dst_fs:
with _src_fs.lock(), _dst_fs.lock():
with Copier(num_workers=workers) as copier:
_thread_safe = is_thread_safe(_src_fs, _dst_fs)
with Copier(num_workers=workers if _thread_safe else 0) as copier:
_dst_fs.makedir(_dst_path, recreate=True)
for dir_path, dirs, files in walker.walk(_src_fs, _src_path):
copy_path = combine(_dst_path, frombase(_src_path, dir_path))
Expand Down Expand Up @@ -347,7 +349,8 @@ def dst():

with src() as _src_fs, dst() as _dst_fs:
with _src_fs.lock(), _dst_fs.lock():
with Copier(num_workers=workers) as copier:
_thread_safe = is_thread_safe(_src_fs, _dst_fs)
with Copier(num_workers=workers if _thread_safe else 0) as copier:
_dst_fs.makedir(_dst_path, recreate=True)
namespace = ("details", "modified")
dst_state = {
Expand Down
6 changes: 4 additions & 2 deletions fs/mirror.py
Expand Up @@ -25,8 +25,9 @@
from ._bulk import Copier
from .copy import copy_file_internal
from .errors import ResourceNotFound
from .walk import Walker
from .opener import manage_fs
from .tools import is_thread_safe
from .walk import Walker

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

with src() as _src_fs, dst() as _dst_fs:
with _src_fs.lock(), _dst_fs.lock():
with Copier(num_workers=workers) as copier:
_thread_safe = is_thread_safe(_src_fs, _dst_fs)
with Copier(num_workers=workers if _thread_safe else 0) as copier:
_mirror(
_src_fs,
_dst_fs,
Expand Down
4 changes: 4 additions & 0 deletions fs/test.py
Expand Up @@ -1810,6 +1810,10 @@ def test_movedir(self):

def test_match(self):
self.assertTrue(self.fs.match(['*.py'], 'foo.py'))
self.assertEqual(
self.fs.match(['*.py'], 'FOO.PY'),
self.fs.getmeta().get('case_insensitive', False)
)

def test_tree(self):
self.fs.makedirs('foo/bar')
Expand Down
17 changes: 17 additions & 0 deletions fs/tools.py
Expand Up @@ -85,3 +85,20 @@ def get_intermediate_dirs(fs, dir_path):
break
raise errors.DirectoryExpected(dir_path)
return intermediates[::-1][:-1]


def is_thread_safe(*filesystems):
# type: (FS) -> bool
"""Check if all filesystems are thread-safe.
Arguments:
filesystems (FS): Filesystems instances to check.
Returns:
bool: if all filesystems are thread safe.
"""
return all(
fs.getmeta().get('thread_safe', False)
for fs in filesystems
)
5 changes: 4 additions & 1 deletion tests/test_errors.py
Expand Up @@ -35,11 +35,14 @@ def test_raise_in_multiprocessing(self):
[errors.CreateFailed],
[errors.NoSysPath, 'some_path'],
[errors.NoURL, 'some_path', 'some_purpose'],
[errors.Unsupported]
[errors.Unsupported],
[errors.IllegalBackReference, 'path']
]
try:
pool = multiprocessing.Pool(1)
for args in tests:
exc = args[0](*args[1:])
exc.__reduce__()
with self.assertRaises(args[0]):
pool.apply(_multiprocessing_test_task, args)
finally:
Expand Down

0 comments on commit 4b73b3c

Please sign in to comment.