Hello community,

here is the log from the commit of package python-fs for openSUSE:Factory 
checked in at 2019-03-05 12:26:51
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-fs (Old)
 and      /work/SRC/openSUSE:Factory/.python-fs.new.28833 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-fs"

Tue Mar  5 12:26:51 2019 rev:6 rq:681664 version:2.4.4

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-fs/python-fs.changes      2019-02-15 
09:55:49.655751866 +0100
+++ /work/SRC/openSUSE:Factory/.python-fs.new.28833/python-fs.changes   
2019-03-05 12:26:53.448835931 +0100
@@ -1,0 +2,25 @@
+Fri Mar  1 09:24:19 UTC 2019 - John Vandenberg <[email protected]>
+
+- Remove unnecessary bcond_without test
+- Simplifed build dependencies on scandir and typing
+- Fix compatibility Requires for older Python which were using incorrect
+  comparisons with python_version_nodots, and incorrectly used Recommends
+- Update to v2.4.4
+  * OSFS fail in nfs mounts
+- from 2.4.3
+  * Fixed broken "case_insensitive" check
+  * Fixed Windows test fails
+- from 2.4.2
+  * Fixed exception when Python runs with -OO
+- from 2.4.1
+  * Fixed hash method missing from WrapFS
+- from 2.4.0
+  * Added exclude and filter_dirs arguments to walk
+  * Micro-optimizations to walk
+- from 2.3.1
+  * Add encoding check in OSFS.validatepath
+- from 2.3.0
+  * IllegalBackReference had mangled error message
+  * Added FS.hash method
+
+-------------------------------------------------------------------

Old:
----
  fs-2.2.1.tar.gz

New:
----
  fs-2.4.4.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-fs.spec ++++++
--- /var/tmp/diff_new_pack.gES6hN/_old  2019-03-05 12:26:54.112836050 +0100
+++ /var/tmp/diff_new_pack.gES6hN/_new  2019-03-05 12:26:54.116836051 +0100
@@ -16,10 +16,10 @@
 # Please submit bugfixes or comments via https://bugs.opensuse.org/
 #
 
+
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
-%bcond_without  test
 Name:           python-fs
-Version:        2.2.1
+Version:        2.4.4
 Release:        0
 Summary:        Python's filesystem abstraction layer
 License:        MIT
@@ -28,32 +28,35 @@
 Source:         
https://files.pythonhosted.org/packages/source/f/fs/fs-%{version}.tar.gz
 # PATCH-FIX-UPSTREAM more-relaxed-requirements.patch [email protected] 
-- Weaken the version dependencies
 Patch0:         more-relaxed-requirements.patch
-BuildRequires:  %{python_module setuptools}
-BuildRequires:  fdupes
-BuildRequires:  python-rpm-macros
-%if %{with test}
-BuildRequires:  %{python_module appdirs >= 1.4}
+BuildRequires:  %{python_module appdirs >= 1.4.3}
 BuildRequires:  %{python_module mock}
 BuildRequires:  %{python_module nose}
-BuildRequires:  %{python_module pyftpdlib}
 BuildRequires:  %{python_module psutil}
+BuildRequires:  %{python_module pyftpdlib}
 BuildRequires:  %{python_module pysendfile}
 BuildRequires:  %{python_module pytz}
+BuildRequires:  %{python_module scandir >= 1.5}
+BuildRequires:  %{python_module setuptools}
 BuildRequires:  %{python_module six >= 1.10.0}
+BuildRequires:  %{python_module typing}
+BuildRequires:  fdupes
 BuildRequires:  python-backports.os
-BuildRequires:  python-typing
-%endif
-Requires:       python-appdirs
+BuildRequires:  python-rpm-macros
+Requires:       python-appdirs >= 1.4.3
 Requires:       python-psutil
 Requires:       python-pytz
 Requires:       python-setuptools
-Requires:       python-six
+Requires:       python-six >= 1.10.0
+Requires:       python-typing >= 3.6
+Recommends:     python-pyftpdlib
 %ifpython2
 Requires:       python-backports.os
 %endif
-%if %{python_version_nodots} < 35
-Requires:       python-typing
-Recommends:     python-enum34
+%if %{python_version_nodots} < 34
+Requires:       python-enum34 >= 1.1.6
+Recommends:     python-pysendfile
+%endif
+%if %{python3_version_nodots} < 35
 Recommends:     python-scandir >= 1.5
 %endif
 BuildArch:      noarch
@@ -78,11 +81,9 @@
 %python_install
 %python_expand %fdupes %{buildroot}%{$python_sitelib}
 
-%if %{with test}
 %check
 export LANG=en_US.UTF-8
 %python_exec  setup.py test
-%endif
 
 %files %{python_files}
 %doc README.md

++++++ fs-2.2.1.tar.gz -> fs-2.4.4.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fs-2.2.1/PKG-INFO new/fs-2.4.4/PKG-INFO
--- old/fs-2.2.1/PKG-INFO       2019-01-06 14:28:10.000000000 +0100
+++ new/fs-2.4.4/PKG-INFO       2019-02-23 11:15:03.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: fs
-Version: 2.2.1
+Version: 2.4.4
 Summary: Python's filesystem abstraction layer
 Home-page: https://github.com/PyFilesystem/pyfilesystem2
 Author: Will McGugan
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fs-2.2.1/fs/_version.py new/fs-2.4.4/fs/_version.py
--- old/fs-2.2.1/fs/_version.py 2019-01-06 14:28:03.000000000 +0100
+++ new/fs-2.4.4/fs/_version.py 2019-02-23 11:14:55.000000000 +0100
@@ -1,3 +1,3 @@
 """Version, used in module and setup.py.
 """
-__version__ = "2.2.1"
+__version__ = "2.4.4"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fs-2.2.1/fs/base.py new/fs-2.4.4/fs/base.py
--- old/fs-2.2.1/fs/base.py     2019-01-06 13:13:51.000000000 +0100
+++ new/fs-2.4.4/fs/base.py     2019-02-23 11:14:55.000000000 +0100
@@ -9,6 +9,7 @@
 from __future__ import absolute_import, print_function, unicode_literals
 
 import abc
+import hashlib
 import itertools
 import os
 import threading
@@ -65,6 +66,7 @@
 def _new_name(method, old_name):
     """Return a method with a deprecation warning."""
     # Looks suspiciously like a decorator, but isn't!
+
     @wraps(method)
     def _method(*args, **kwargs):
         warnings.warn(
@@ -75,6 +77,16 @@
         )
         return method(*args, **kwargs)
 
+    deprecated_msg = """
+        Note:
+            .. deprecated:: 2.2.0
+                Please use `~{}`
+""".format(
+        method.__name__
+    )
+    if getattr(_method, "__doc__"):
+        _method.__doc__ += deprecated_msg
+
     return _method
 
 
@@ -1472,7 +1484,7 @@
                 pass
             else:
                 if len(sys_path) > max_sys_path_length:
-                    _msg = "path too long " "(max {max_chars} characters in 
sys path)"
+                    _msg = "path too long (max {max_chars} characters in sys 
path)"
                     msg = _msg.format(max_chars=max_sys_path_length)
                     raise errors.InvalidPath(path, msg=msg)
         path = abspath(normpath(path))
@@ -1593,3 +1605,31 @@
         from .tree import render
 
         render(self, **kwargs)
+
+    def hash(self, path, name):
+        # type: (Text, Text) -> Text
+        """Get the hash of a file's contents.
+
+        Arguments:
+            path(str): A path on the filesystem.
+            name(str): One of the algorithms supported by the hashlib module, 
e.g. `"md5"`
+
+        Returns:
+            str: The hex digest of the hash.
+
+        Raises:
+            fs.errors.UnsupportedHash: If the requested hash is not supported.
+
+        """
+        _path = self.validatepath(path)
+        try:
+            hash_object = hashlib.new(name)
+        except ValueError:
+            raise errors.UnsupportedHash("hash '{}' is not 
supported".format(name))
+        with self.openbin(path) as binary_file:
+            while True:
+                chunk = binary_file.read(1024 * 1024)
+                if not chunk:
+                    break
+                hash_object.update(chunk)
+        return hash_object.hexdigest()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fs-2.2.1/fs/copy.py new/fs-2.4.4/fs/copy.py
--- old/fs-2.2.1/fs/copy.py     2019-01-06 13:13:51.000000000 +0100
+++ new/fs-2.4.4/fs/copy.py     2019-02-15 16:03:01.000000000 +0100
@@ -35,7 +35,7 @@
         walker (~fs.walk.Walker, optional): A walker object that will be
             used to scan for files in ``src_fs``. Set this if you only want
             to consider a sub-set of the resources in ``src_fs``.
-        on_copy (callable):A function callback called after a single file copy
+        on_copy (callable): A function callback called after a single file copy
             is executed. Expected signature is ``(src_fs, src_path, dst_fs,
             dst_path)``.
         workers (int): Use `worker` threads to copy data, or ``0`` (default) 
for
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fs-2.2.1/fs/errors.py new/fs-2.4.4/fs/errors.py
--- old/fs-2.2.1/fs/errors.py   2018-08-19 16:37:51.000000000 +0200
+++ new/fs-2.4.4/fs/errors.py   2019-02-10 14:13:50.000000000 +0100
@@ -364,3 +364,12 @@
 
     def __reduce__(self):
         return type(self), (self.path,)
+
+
+class UnsupportedHash(ValueError):
+    """The requested hash algorithm is not supported.
+
+    This exception will be thrown if a hash algorithm is requested that is
+    not supported by hashlib.
+
+    """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fs-2.2.1/fs/info.py new/fs-2.4.4/fs/info.py
--- old/fs-2.2.1/fs/info.py     2019-01-06 14:28:03.000000000 +0100
+++ new/fs-2.4.4/fs/info.py     2019-01-30 17:51:00.000000000 +0100
@@ -45,6 +45,8 @@
 
     """
 
+    __slots__ = ["raw", "_to_datetime", "namespaces"]
+
     def __init__(self, raw_info, to_datetime=epoch_to_datetime):
         # type: (RawInfo, ToDatetime) -> None
         """Create a resource info object from a raw info dict.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fs-2.2.1/fs/osfs.py new/fs-2.4.4/fs/osfs.py
--- old/fs-2.2.1/fs/osfs.py     2018-12-28 15:25:39.000000000 +0100
+++ new/fs-2.4.4/fs/osfs.py     2019-02-23 11:14:55.000000000 +0100
@@ -47,7 +47,7 @@
 from .permissions import Permissions
 from .error_tools import convert_os_errors
 from .mode import Mode, validate_open_mode
-from .errors import NoURL
+from .errors import FileExpected, NoURL
 
 if False:  # typing.TYPE_CHECKING
     from typing import (
@@ -134,7 +134,7 @@
                 raise errors.CreateFailed("root path does not exist")
 
         _meta = self._meta = {
-            "case_insensitive": os.path.normcase("Aa") != "aa",
+            "case_insensitive": os.path.normcase("Aa") == "aa",
             "network": False,
             "read_only": False,
             "supports_rename": True,
@@ -151,9 +151,13 @@
             _meta["invalid_path_chars"] = "\0"
 
             if "PC_PATH_MAX" in os.pathconf_names:
-                _meta["max_sys_path_length"] = os.pathconf(
-                    fsencode(_root_path), os.pathconf_names["PC_PATH_MAX"]
-                )
+                try:
+                    _meta["max_sys_path_length"] = os.pathconf(
+                        fsencode(_root_path), os.pathconf_names["PC_PATH_MAX"]
+                    )
+                except OSError:  # pragma: no cover
+                    # The above fails with nfs mounts on OSX. Go figure.
+                    pass
 
     def __repr__(self):
         # type: () -> str
@@ -245,12 +249,17 @@
 
     def _gettarget(self, sys_path):
         # type: (Text) -> Optional[Text]
-        try:
-            target = os.readlink(fsencode(sys_path))
-        except OSError:
-            return None
-        else:
-            return target
+        if hasattr(os, "readlink"):
+            try:
+                if _WINDOWS_PLATFORM:  # pragma: no cover
+                    target = os.readlink(sys_path)
+                else:
+                    target = os.readlink(fsencode(sys_path))
+            except OSError:
+                pass
+            else:
+                return target
+        return None
 
     def _make_link_info(self, sys_path):
         # type: (Text) -> Dict[Text, object]
@@ -328,6 +337,8 @@
         _mode.validate_bin()
         self.check()
         _path = self.validatepath(path)
+        if _path == "/":
+            raise errors.FileExpected(path)
         sys_path = self._to_sys_path(_path)
         with convert_os_errors("openbin", path):
             if six.PY2 and _mode.exclusive:
@@ -454,7 +465,12 @@
             self.check()
             namespaces = namespaces or ()
             _path = self.validatepath(path)
-            sys_path = self._to_sys_path(_path)
+            if _WINDOWS_PLATFORM:
+                sys_path = os.path.join(
+                    self._root_path, path.lstrip("/").replace("/", os.sep)
+                )
+            else:
+                sys_path = self._to_sys_path(_path)
             with convert_os_errors("scandir", path, directory=True):
                 for dir_entry in scandir(sys_path):
                     info = {
@@ -595,6 +611,8 @@
         validate_open_mode(mode)
         self.check()
         _path = self.validatepath(path)
+        if _path == "/":
+            raise FileExpected(path)
         sys_path = self._to_sys_path(_path)
         with convert_os_errors("open", path):
             if six.PY2 and _mode.exclusive:
@@ -627,3 +645,17 @@
                 if accessed is not None or modified is not None:
                     with convert_os_errors("setinfo", path):
                         os.utime(sys_path, (accessed, modified))
+
+    def validatepath(self, path):
+        # type: (Text) -> Text
+        """Check path may be encoded, in addition to usual checks."""
+        try:
+            fsencode(path)
+        except UnicodeEncodeError as error:
+            raise errors.InvalidCharsInPath(
+                path,
+                msg="path '{path}' could not be encoded for the filesystem 
(check LANG env var); {error}".format(
+                    path=path, error=error
+                ),
+            )
+        return super(OSFS, self).validatepath(path)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fs-2.2.1/fs/path.py new/fs-2.4.4/fs/path.py
--- old/fs-2.2.1/fs/path.py     2018-08-19 16:37:51.000000000 +0200
+++ new/fs-2.4.4/fs/path.py     2019-01-26 13:32:28.000000000 +0100
@@ -66,7 +66,7 @@
         >>> normpath("foo/../../bar")
         Traceback (most recent call last)
             ...
-        IllegalBackReference: Too many backrefs in 'foo/../../bar'
+        IllegalBackReference: path 'foo/../../bar' contains back-references 
outside of filesystem"
 
     """
     if path in "/":
@@ -86,7 +86,7 @@
             else:
                 components.append(component)
     except IndexError:
-        raise IllegalBackReference("Too many backrefs in '{}'".format(path))
+        raise IllegalBackReference(path)
     return prefix + "/".join(components)
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fs-2.2.1/fs/test.py new/fs-2.4.4/fs/test.py
--- old/fs-2.2.1/fs/test.py     2019-01-06 13:13:51.000000000 +0100
+++ new/fs-2.4.4/fs/test.py     2019-02-20 15:13:10.000000000 +0100
@@ -1834,3 +1834,16 @@
     def test_glob(self):
         self.assertIsInstance(self.fs.glob, glob.BoundGlobber)
 
+    def test_hash(self):
+        self.fs.makedir("foo").writebytes("hashme.txt", b"foobar" * 1024)
+        self.assertEqual(
+            self.fs.hash("foo/hashme.txt", "md5"), 
"9fff4bb103ab8ce4619064109c54cb9c"
+        )
+        with self.assertRaises(errors.UnsupportedHash):
+            self.fs.hash("foo/hashme.txt", "nohash")
+
+        with self.fs.opendir("foo") as foo_fs:
+            self.assertEqual(
+                foo_fs.hash("hashme.txt", "md5"), 
"9fff4bb103ab8ce4619064109c54cb9c"
+            )
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fs-2.2.1/fs/walk.py new/fs-2.4.4/fs/walk.py
--- old/fs-2.2.1/fs/walk.py     2018-08-19 16:37:51.000000000 +0200
+++ new/fs-2.4.4/fs/walk.py     2019-02-15 16:03:01.000000000 +0100
@@ -17,7 +17,7 @@
 from ._repr import make_repr
 from .errors import FSError
 from .path import abspath
-from .path import join
+from .path import combine
 from .path import normpath
 
 if False:  # typing.TYPE_CHECKING
@@ -68,6 +68,12 @@
             a list of filename patterns, e.g. ``['*.py']``. Files will
             only be returned if the final component matches one of the
             patterns.
+        exclude (list, optional): If supplied, this parameter should be
+            a list of filename patterns, e.g. ``['~*']``. Files matching
+            any of these patterns will be removed from the walk.
+        filter_dirs (list, optional): A list of patterns that will be used
+            to match directories paths. The walk will only open directories
+            that match at least one of these patterns.
         exclude_dirs (list, optional): A list of patterns that will be
             used to filter out directories from the walk. e.g.
             ``['*.svn', '*.git']``.
@@ -81,6 +87,8 @@
         on_error=None,  # type: Optional[OnError]
         search="breadth",  # type: Text
         filter=None,  # type: Optional[List[Text]]
+        exclude=None,  # type: Optional[List[Text]]
+        filter_dirs=None,  # type: Optional[List[Text]]
         exclude_dirs=None,  # type: Optional[List[Text]]
         max_depth=None,  # type: Optional[int]
     ):
@@ -99,6 +107,8 @@
         self.on_error = on_error
         self.search = search
         self.filter = filter
+        self.exclude = exclude
+        self.filter_dirs = filter_dirs
         self.exclude_dirs = exclude_dirs
         self.max_depth = max_depth
         super(Walker, self).__init__()
@@ -169,6 +179,8 @@
             on_error=(self.on_error, None),
             search=(self.search, "breadth"),
             filter=(self.filter, None),
+            exclude=(self.exclude, None),
+            filter_dirs=(self.filter_dirs, None),
             exclude_dirs=(self.exclude_dirs, None),
             max_depth=(self.max_depth, None),
         )
@@ -192,6 +204,8 @@
         """
         if self.exclude_dirs is not None and fs.match(self.exclude_dirs, 
info.name):
             return False
+        if self.filter_dirs is not None and not fs.match(self.filter_dirs, 
info.name):
+            return False
         return self.check_open_dir(fs, path, info)
 
     def check_open_dir(self, fs, path, info):
@@ -251,6 +265,9 @@
             bool: `True` if the file should be included.
 
         """
+
+        if self.exclude is not None and fs.match(self.exclude, info.name):
+            return False
         return fs.match(self.filter, info.name)
 
     def _scan(
@@ -341,9 +358,10 @@
             recursively within the given directory.
 
         """
+        _combine = combine
         for _path, info in self._iter_walk(fs, path=path):
             if info is not None and not info.is_dir:
-                yield join(_path, info.name)
+                yield _combine(_path, info.name)
 
     def dirs(self, fs, path="/"):
         # type: (FS, Text) -> Iterator[Text]
@@ -358,9 +376,10 @@
             recursively within the given directory.
 
         """
+        _combine = combine
         for _path, info in self._iter_walk(fs, path=path):
             if info is not None and info.is_dir:
-                yield join(_path, info.name)
+                yield _combine(_path, info.name)
 
     def info(
         self,
@@ -381,10 +400,11 @@
             (str, Info): a tuple of ``(<absolute path>, <resource info>)``.
 
         """
+        _combine = combine
         _walk = self._iter_walk(fs, path=path, namespaces=namespaces)
         for _path, info in _walk:
             if info is not None:
-                yield join(_path, info.name), info
+                yield _combine(_path, info.name), info
 
     def _walk_breadth(
         self,
@@ -398,19 +418,27 @@
         queue = deque([path])
         push = queue.appendleft
         pop = queue.pop
-        depth = self._calculate_depth(path)
+
+        _combine = combine
+        _scan = self._scan
+        _calculate_depth = self._calculate_depth
+        _check_open_dir = self._check_open_dir
+        _check_scan_dir = self._check_scan_dir
+        _check_file = self.check_file
+
+        depth = _calculate_depth(path)
 
         while queue:
             dir_path = pop()
-            for info in self._scan(fs, dir_path, namespaces=namespaces):
+            for info in _scan(fs, dir_path, namespaces=namespaces):
                 if info.is_dir:
-                    _depth = self._calculate_depth(dir_path) - depth + 1
-                    if self._check_open_dir(fs, dir_path, info):
+                    _depth = _calculate_depth(dir_path) - depth + 1
+                    if _check_open_dir(fs, dir_path, info):
                         yield dir_path, info  # Opened a directory
-                        if self._check_scan_dir(fs, dir_path, info, _depth):
-                            push(join(dir_path, info.name))
+                        if _check_scan_dir(fs, dir_path, info, _depth):
+                            push(_combine(dir_path, info.name))
                 else:
-                    if self.check_file(fs, info):
+                    if _check_file(fs, info):
                         yield dir_path, info  # Found a file
             yield dir_path, None  # End of directory
 
@@ -425,15 +453,18 @@
         """
         # No recursion!
 
-        def scan(path):
-            # type: (Text) -> Iterator[Info]
-            """Perform scan."""
-            return self._scan(fs, path, namespaces=namespaces)
+        _combine = combine
+        _scan = self._scan
+        _calculate_depth = self._calculate_depth
+        _check_open_dir = self._check_open_dir
+        _check_scan_dir = self._check_scan_dir
+        _check_file = self.check_file
+        depth = _calculate_depth(path)
 
         stack = [
-            (path, scan(path), None)
+            (path, _scan(fs, path, namespaces=namespaces), None)
         ]  # type: List[Tuple[Text, Iterator[Info], Optional[Tuple[Text, 
Info]]]]
-        depth = self._calculate_depth(path)
+
         push = stack.append
 
         while stack:
@@ -445,15 +476,21 @@
                 yield dir_path, None
                 del stack[-1]
             elif info.is_dir:
-                _depth = self._calculate_depth(dir_path) - depth + 1
-                if self._check_open_dir(fs, dir_path, info):
-                    if self._check_scan_dir(fs, dir_path, info, _depth):
-                        _path = join(dir_path, info.name)
-                        push((_path, scan(_path), (dir_path, info)))
+                _depth = _calculate_depth(dir_path) - depth + 1
+                if _check_open_dir(fs, dir_path, info):
+                    if _check_scan_dir(fs, dir_path, info, _depth):
+                        _path = _combine(dir_path, info.name)
+                        push(
+                            (
+                                _path,
+                                _scan(fs, _path, namespaces=namespaces),
+                                (dir_path, info),
+                            )
+                        )
                     else:
                         yield dir_path, info
             else:
-                if self.check_file(fs, info):
+                if _check_file(fs, info):
                     yield dir_path, info
 
 
@@ -525,6 +562,12 @@
                 of file name patterns, e.g. ``['*.py']``. Files will only be
                 returned if the final component matches one of the
                 patterns.
+            exclude (list, optional): If supplied, this parameter should be
+                a list of filename patterns, e.g. ``['~*', '.*']``. Files 
matching
+                any of these patterns will be removed from the walk.
+            filter_dirs (list, optional): A list of patterns that will be used
+                to match directories paths. The walk will only open directories
+                that match at least one of these patterns.
             exclude_dirs (list): A list of patterns that will be used
                 to filter out directories from the walk, e.g. ``['*.svn',
                 '*.git']``.
@@ -574,6 +617,12 @@
                 of file name patterns, e.g. ``['*.py']``. Files will only be
                 returned if the final component matches one of the
                 patterns.
+            exclude (list, optional): If supplied, this parameter should be
+                a list of filename patterns, e.g. ``['~*', '.*']``. Files 
matching
+                any of these patterns will be removed from the walk.
+            filter_dirs (list, optional): A list of patterns that will be used
+                to match directories paths. The walk will only open directories
+                that match at least one of these patterns.
             exclude_dirs (list): A list of patterns that will be used
                 to filter out directories from the walk, e.g. ``['*.svn',
                 '*.git']``.
@@ -606,6 +655,9 @@
                 `False` to re-raise it.
             search (str): If ``'breadth'`` then the directory will be
                 walked *top down*. Set to ``'depth'`` to walk *bottom up*.
+            filter_dirs (list, optional): A list of patterns that will be used
+                to match directories paths. The walk will only open directories
+                that match at least one of these patterns.
             exclude_dirs (list): A list of patterns that will be used
                 to filter out directories from the walk, e.g. ``['*.svn',
                 '*.git']``.
@@ -650,6 +702,12 @@
                 of file name patterns, e.g. ``['*.py']``. Files will only be
                 returned if the final component matches one of the
                 patterns.
+            exclude (list, optional): If supplied, this parameter should be
+                a list of filename patterns, e.g. ``['~*', '.*']``. Files 
matching
+                any of these patterns will be removed from the walk.
+            filter_dirs (list, optional): A list of patterns that will be used
+                to match directories paths. The walk will only open directories
+                that match at least one of these patterns.
             exclude_dirs (list): A list of patterns that will be used
                 to filter out directories from the walk, e.g. ``['*.svn',
                 '*.git']``.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fs-2.2.1/fs/wrapfs.py new/fs-2.4.4/fs/wrapfs.py
--- old/fs-2.2.1/fs/wrapfs.py   2019-01-06 13:13:51.000000000 +0100
+++ new/fs-2.4.4/fs/wrapfs.py   2019-02-20 15:13:10.000000000 +0100
@@ -491,6 +491,13 @@
         path = abspath(normpath(path))
         return path
 
+    def hash(self, path, name):
+        # type: (Text, Text) -> Text
+        self.check()
+        _fs, _path = self.delegate_path(path)
+        with unwrap_errors(path):
+            return _fs.hash(_path, name)
+
     @property
     def walk(self):
         # type: () -> BoundWalker
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fs-2.2.1/fs.egg-info/PKG-INFO 
new/fs-2.4.4/fs.egg-info/PKG-INFO
--- old/fs-2.2.1/fs.egg-info/PKG-INFO   2019-01-06 14:28:10.000000000 +0100
+++ new/fs-2.4.4/fs.egg-info/PKG-INFO   2019-02-23 11:15:03.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: fs
-Version: 2.2.1
+Version: 2.4.4
 Summary: Python's filesystem abstraction layer
 Home-page: https://github.com/PyFilesystem/pyfilesystem2
 Author: Will McGugan
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fs-2.2.1/tests/test_archives.py 
new/fs-2.4.4/tests/test_archives.py
--- old/fs-2.2.1/tests/test_archives.py 2019-01-06 13:13:51.000000000 +0100
+++ new/fs-2.4.4/tests/test_archives.py 2019-02-23 10:58:23.000000000 +0100
@@ -86,9 +86,10 @@
         except errors.NoSysPath:
             pass
         else:
-            self.assertEqual(
-                top.permissions.mode, 
stat.S_IMODE(os.stat(source_syspath).st_mode)
-            )
+            if top.has_namespace("access"):
+                self.assertEqual(
+                    top.permissions.mode, 
stat.S_IMODE(os.stat(source_syspath).st_mode)
+                )
 
         self.assertEqual(top.get("details", "type"), ResourceType.file)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fs-2.2.1/tests/test_ftpfs.py 
new/fs-2.4.4/tests/test_ftpfs.py
--- old/fs-2.2.1/tests/test_ftpfs.py    2019-01-06 13:13:51.000000000 +0100
+++ new/fs-2.4.4/tests/test_ftpfs.py    2019-02-23 10:58:23.000000000 +0100
@@ -25,6 +25,7 @@
 from fs import errors
 from fs.opener import open_fs
 from fs.ftpfs import FTPFS, ftp_errors
+from fs.path import join
 from fs.subfs import SubFS
 from fs.test import FSTestCases
 
@@ -200,7 +201,7 @@
 
     def test_create(self):
 
-        directory = os.path.join("home", self.user, "test", "directory")
+        directory = join("home", self.user, "test", "directory")
         base = "ftp://user:1234@{}:{}/foo".format(self.server.host, 
self.server.port)
         url = "{}/{}".format(base, directory)
 
@@ -215,7 +216,7 @@
         # Open the base filesystem and check the subdirectory exists
         with open_fs(base) as ftp_fs:
             self.assertTrue(ftp_fs.isdir(directory))
-            self.assertTrue(ftp_fs.isfile(os.path.join(directory, "foo")))
+            self.assertTrue(ftp_fs.isfile(join(directory, "foo")))
 
         # Open without `create` and check the file exists
         with open_fs(url) as ftp_fs:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fs-2.2.1/tests/test_osfs.py 
new/fs-2.4.4/tests/test_osfs.py
--- old/fs-2.2.1/tests/test_osfs.py     2018-10-03 11:58:58.000000000 +0200
+++ new/fs-2.4.4/tests/test_osfs.py     2019-02-23 10:58:23.000000000 +0100
@@ -1,3 +1,4 @@
+# coding: utf-8
 from __future__ import unicode_literals
 
 import errno
@@ -68,23 +69,23 @@
         with self.assertRaises(errors.CreateFailed):
             fs = osfs.OSFS("/does/not/exists/")
 
-    @unittest.skipIf(osfs.sendfile is None, 'sendfile not supported')
+    @unittest.skipIf(osfs.sendfile is None, "sendfile not supported")
     def test_copy_sendfile(self):
         # try copying using sendfile
-        with mock.patch.object(osfs, 'sendfile') as sendfile:
-            sendfile.side_effect = OSError(errno.ENOTSUP, 'sendfile not 
supported')
+        with mock.patch.object(osfs, "sendfile") as sendfile:
+            sendfile.side_effect = OSError(errno.ENOTSUP, "sendfile not 
supported")
             self.test_copy()
         # check other errors are transmitted
-        self.fs.touch('foo')
-        with mock.patch.object(osfs, 'sendfile') as sendfile:
+        self.fs.touch("foo")
+        with mock.patch.object(osfs, "sendfile") as sendfile:
             sendfile.side_effect = OSError(errno.EWOULDBLOCK)
             with self.assertRaises(OSError):
-                self.fs.copy('foo', 'foo_copy')
+                self.fs.copy("foo", "foo_copy")
         # check parent exist and is dir
         with self.assertRaises(errors.ResourceNotFound):
-            self.fs.copy('foo', 'spam/eggs')
+            self.fs.copy("foo", "spam/eggs")
         with self.assertRaises(errors.DirectoryExpected):
-            self.fs.copy('foo', 'foo_copy/foo')
+            self.fs.copy("foo", "foo_copy/foo")
 
     def test_create(self):
         """Test create=True"""
@@ -114,6 +115,7 @@
         finally:
             shutil.rmtree(dir_path)
 
+    @unittest.skipIf(not hasattr(os, "symlink"), "No symlink support")
     def test_symlinks(self):
         with open(self._get_real_path("foo"), "wb") as f:
             f.write(b"foobar")
@@ -131,3 +133,12 @@
         bar_info = self.fs.getinfo("bar", namespaces=["link", "lstat"])
         self.assertIn("link", bar_info.raw)
         self.assertIn("lstat", bar_info.raw)
+
+    def test_validatepath(self):
+        """Check validatepath detects bad encodings."""
+
+        with mock.patch("fs.osfs.fsencode") as fsencode:
+            fsencode.side_effect = lambda error: "–".encode("ascii")
+            with self.assertRaises(errors.InvalidCharsInPath):
+                with self.fs.open("13 – Marked Register.pdf", "wb") as fh:
+                    fh.write(b"foo")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fs-2.2.1/tests/test_tarfs.py 
new/fs-2.4.4/tests/test_tarfs.py
--- old/fs-2.2.1/tests/test_tarfs.py    2019-01-06 13:13:51.000000000 +0100
+++ new/fs-2.4.4/tests/test_tarfs.py    2019-02-23 10:58:23.000000000 +0100
@@ -26,6 +26,7 @@
 class TestWriteReadTarFS(unittest.TestCase):
     def setUp(self):
         fh, self._temp_path = tempfile.mkstemp()
+        os.close(fh)
 
     def tearDown(self):
         os.remove(self._temp_path)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fs-2.2.1/tests/test_walk.py 
new/fs-2.4.4/tests/test_walk.py
--- old/fs-2.2.1/tests/test_walk.py     2019-01-06 13:13:51.000000000 +0100
+++ new/fs-2.4.4/tests/test_walk.py     2019-02-15 16:03:01.000000000 +0100
@@ -63,6 +63,22 @@
         ]
         self.assertEqual(_walk, expected)
 
+    def test_walk_filter_dirs(self):
+        _walk = []
+        for step in self.fs.walk(filter_dirs=["foo*"]):
+            self.assertIsInstance(step, walk.Step)
+            path, dirs, files = step
+            _walk.append(
+                (path, [info.name for info in dirs], [info.name for info in 
files])
+            )
+        expected = [
+            ("/", ["foo1", "foo2", "foo3"], []),
+            ("/foo1", [], ["top1.txt", "top2.txt"]),
+            ("/foo2", [], ["top3.bin"]),
+            ("/foo3", [], []),
+        ]
+        self.assertEqual(_walk, expected)
+
     def test_walk_depth(self):
         _walk = []
         for step in self.fs.walk(search="depth"):
@@ -193,6 +209,19 @@
 
         self.assertEqual(files, [])
 
+    def test_walk_files_exclude(self):
+        # Test exclude argument works
+        files = list(self.fs.walk.files(exclude=["*.txt"]))
+        self.assertEqual(files, ["/foo2/top3.bin"])
+
+        # Test exclude doesn't break filter
+        files = list(self.fs.walk.files(filter=["*.bin"], exclude=["*.txt"]))
+        self.assertEqual(files, ["/foo2/top3.bin"])
+
+        # Test excluding everything
+        files = list(self.fs.walk.files(exclude=["*"]))
+        self.assertEqual(files, [])
+
     def test_walk_info(self):
         walk = []
         for path, info in self.fs.walk.info():
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fs-2.2.1/tests/test_zipfs.py 
new/fs-2.4.4/tests/test_zipfs.py
--- old/fs-2.2.1/tests/test_zipfs.py    2019-01-06 13:13:51.000000000 +0100
+++ new/fs-2.4.4/tests/test_zipfs.py    2019-02-23 10:58:23.000000000 +0100
@@ -22,6 +22,7 @@
 class TestWriteReadZipFS(unittest.TestCase):
     def setUp(self):
         fh, self._temp_path = tempfile.mkstemp()
+        os.close(fh)
 
     def tearDown(self):
         os.remove(self._temp_path)


Reply via email to