Hello community,

here is the log from the commit of package python-pathspec for openSUSE:Factory 
checked in at 2020-10-29 09:46:48
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pathspec (Old)
 and      /work/SRC/openSUSE:Factory/.python-pathspec.new.3463 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-pathspec"

Thu Oct 29 09:46:48 2020 rev:6 rq:839941 version:0.8.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pathspec/python-pathspec.changes  
2020-03-11 18:56:50.835715147 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-pathspec.new.3463/python-pathspec.changes    
    2020-10-29 09:46:50.508085623 +0100
@@ -1,0 +2,12 @@
+Wed Oct  7 03:51:16 UTC 2020 - John Vandenberg <[email protected]>
+
+- Update to v0.8.0
+  * Expose what patterns matched paths. Added `util.detailed_match_files()`
+  * `match_tree()` doesn't return symlinks
+    - Add `PathSpec.match_tree_entries` and `util.iter_tree_entries()` to
+      support directories and symlinks
+    - API change: `match_tree()` has been renamed to `match_tree_files()`
+      The old name `match_tree()` is still available as an alias
+    - API change: `match_tree_files()` now returns symlinks
+
+-------------------------------------------------------------------

Old:
----
  pathspec-0.7.0.tar.gz

New:
----
  pathspec-0.8.0.tar.gz

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

Other differences:
------------------
++++++ python-pathspec.spec ++++++
--- /var/tmp/diff_new_pack.Z1l1eX/_old  2020-10-29 09:46:51.268086342 +0100
+++ /var/tmp/diff_new_pack.Z1l1eX/_new  2020-10-29 09:46:51.272086345 +0100
@@ -18,7 +18,7 @@
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-pathspec
-Version:        0.7.0
+Version:        0.8.0
 Release:        0
 Summary:        Utility library for gitignore style pattern matching of file 
paths
 License:        MPL-2.0

++++++ pathspec-0.7.0.tar.gz -> pathspec-0.8.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pathspec-0.7.0/CHANGES.rst 
new/pathspec-0.8.0/CHANGES.rst
--- old/pathspec-0.7.0/CHANGES.rst      2019-12-27 16:28:16.000000000 +0100
+++ new/pathspec-0.8.0/CHANGES.rst      2020-04-09 15:04:11.000000000 +0200
@@ -3,6 +3,19 @@
 ==============
 
 
+0.8.0 (2020-04-09)
+------------------
+
+- `Issue #30`_: Expose what patterns matched paths. Added 
`util.detailed_match_files()`.
+- `Issue #31`_: `match_tree()` doesn't return symlinks.
+- Add `PathSpec.match_tree_entries` and `util.iter_tree_entries()` to support 
directories and symlinks.
+- API change: `match_tree()` has been renamed to `match_tree_files()`. The old 
name `match_tree()` is still available as an alias.
+- API change: `match_tree_files()` now returns symlinks. This is a bug fix but 
it will change the returned results.
+
+.. _`Issue #30`: https://github.com/cpburnz/python-path-specification/issues/30
+.. _`Issue #31`: https://github.com/cpburnz/python-path-specification/issues/31
+
+
 0.7.0 (2019-12-27)
 ------------------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pathspec-0.7.0/PKG-INFO new/pathspec-0.8.0/PKG-INFO
--- old/pathspec-0.7.0/PKG-INFO 2019-12-27 16:31:53.000000000 +0100
+++ new/pathspec-0.8.0/PKG-INFO 2020-04-09 15:08:33.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.2
 Name: pathspec
-Version: 0.7.0
+Version: 0.8.0
 Summary: Utility library for gitignore style pattern matching of file paths.
 Home-page: https://github.com/cpburnz/python-path-specification
 Author: Caleb P. Burns
@@ -163,6 +163,19 @@
         ==============
         
         
+        0.8.0 (2020-04-09)
+        ------------------
+        
+        - `Issue #30`_: Expose what patterns matched paths. Added 
`util.detailed_match_files()`.
+        - `Issue #31`_: `match_tree()` doesn't return symlinks.
+        - Add `PathSpec.match_tree_entries` and `util.iter_tree_entries()` to 
support directories and symlinks.
+        - API change: `match_tree()` has been renamed to `match_tree_files()`. 
The old name `match_tree()` is still available as an alias.
+        - API change: `match_tree_files()` now returns symlinks. This is a bug 
fix but it will change the returned results.
+        
+        .. _`Issue #30`: 
https://github.com/cpburnz/python-path-specification/issues/30
+        .. _`Issue #31`: 
https://github.com/cpburnz/python-path-specification/issues/31
+        
+        
         0.7.0 (2019-12-27)
         ------------------
         
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pathspec-0.7.0/pathspec/__init__.py 
new/pathspec-0.8.0/pathspec/__init__.py
--- old/pathspec-0.7.0/pathspec/__init__.py     2019-12-27 16:28:25.000000000 
+0100
+++ new/pathspec-0.8.0/pathspec/__init__.py     2020-04-09 15:08:03.000000000 
+0200
@@ -24,7 +24,7 @@
 from __future__ import unicode_literals
 
 __author__ = "Caleb P. Burns"
-__copyright__ = "Copyright © 2013-2018 Caleb P. Burns"
+__copyright__ = "Copyright © 2013-2020 Caleb P. Burns"
 __created__ = "2013-10-12"
 __credits__ = [
        "dahlia <https://github.com/dahlia>",
@@ -46,13 +46,15 @@
        "mroutis <https://github.com/mroutis>",
        "jdufresne <https://github.com/jdufresne>",
        "groodt <https://github.com/groodt>",
+       "ftrofin <https://github.com/ftrofin>",
+       "pykong <https://github.com/pykong>",
 ]
 __email__ = "[email protected]"
 __license__ = "MPL 2.0"
 __project__ = "pathspec"
 __status__ = "Development"
-__updated__ = "2019-12-27"
-__version__ = "0.7.0"
+__updated__ = "2020-04-09"
+__version__ = "0.8.0"
 
 from .pathspec import PathSpec
 from .pattern import Pattern, RegexPattern
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pathspec-0.7.0/pathspec/compat.py 
new/pathspec-0.8.0/pathspec/compat.py
--- old/pathspec-0.7.0/pathspec/compat.py       2018-12-01 18:57:36.000000000 
+0100
+++ new/pathspec-0.8.0/pathspec/compat.py       2020-04-07 05:10:57.000000000 
+0200
@@ -13,6 +13,7 @@
        unicode = unicode
        string_types = (basestring,)
 
+       from collections import Iterable
        from itertools import izip_longest
 
        def iterkeys(mapping):
@@ -23,6 +24,7 @@
        unicode = str
        string_types = (unicode,)
 
+       from collections.abc import Iterable
        from itertools import zip_longest as izip_longest
 
        def iterkeys(mapping):
@@ -30,7 +32,7 @@
 
 try:
        # Python 3.6+.
-       from collections.abc import Collection as collection_type
+       from collections.abc import Collection
 except ImportError:
        # Python 2.7 - 3.5.
-       from collections import Container as collection_type
+       from collections import Container as Collection
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pathspec-0.7.0/pathspec/pathspec.py 
new/pathspec-0.8.0/pathspec/pathspec.py
--- old/pathspec-0.7.0/pathspec/pathspec.py     2018-12-01 18:57:36.000000000 
+0100
+++ new/pathspec-0.8.0/pathspec/pathspec.py     2020-04-07 05:39:17.000000000 
+0200
@@ -5,7 +5,7 @@
 """
 
 from . import util
-from .compat import collection_type, iterkeys, izip_longest, string_types, 
unicode
+from .compat import Collection, iterkeys, izip_longest, string_types, unicode
 
 
 class PathSpec(object):
@@ -22,7 +22,7 @@
                yields each compiled pattern (:class:`.Pattern`).
                """
 
-               self.patterns = patterns if isinstance(patterns, 
collection_type) else list(patterns)
+               self.patterns = patterns if isinstance(patterns, Collection) 
else list(patterns)
                """
                *patterns* (:class:`~collections.abc.Collection` of 
:class:`.Pattern`)
                contains the compiled patterns.
@@ -68,7 +68,7 @@
                if not callable(pattern_factory):
                        raise TypeError("pattern_factory:{!r} is not 
callable.".format(pattern_factory))
 
-               if isinstance(lines, (bytes, unicode)):
+               if not util._is_iterable(lines):
                        raise TypeError("lines:{!r} is not an 
iterable.".format(lines))
 
                lines = [pattern_factory(line) for line in lines if line]
@@ -78,8 +78,8 @@
                """
                Matches the file to this path-spec.
 
-               *file* (:class:`str`) is the file path to be matched against
-               :attr:`self.patterns <PathSpec.patterns>`.
+               *file* (:class:`str` or :class:`~pathlib.PurePath`) is the file 
path
+               to be matched against :attr:`self.patterns <PathSpec.patterns>`.
 
                *separators* (:class:`~collections.abc.Collection` of 
:class:`str`)
                optionally contains the path separators to normalize. See
@@ -90,13 +90,36 @@
                norm_file = util.normalize_file(file, separators=separators)
                return util.match_file(self.patterns, norm_file)
 
+       def match_entries(self, entries, separators=None):
+               """
+               Matches the entries to this path-spec.
+
+               *entries* (:class:`~collections.abc.Iterable` of 
:class:`~util.TreeEntry`)
+               contains the entries to be matched against :attr:`self.patterns 
<PathSpec.patterns>`.
+
+               *separators* (:class:`~collections.abc.Collection` of 
:class:`str`;
+               or :data:`None`) optionally contains the path separators to
+               normalize. See :func:`~pathspec.util.normalize_file` for more
+               information.
+
+               Returns the matched entries (:class:`~collections.abc.Iterable` 
of
+               :class:`~util.TreeEntry`).
+               """
+               if not util._is_iterable(entries):
+                       raise TypeError("entries:{!r} is not an 
iterable.".format(entries))
+
+               entry_map = util._normalize_entries(entries, 
separators=separators)
+               match_paths = util.match_files(self.patterns, 
iterkeys(entry_map))
+               for path in match_paths:
+                       yield entry_map[path]
+
        def match_files(self, files, separators=None):
                """
                Matches the files to this path-spec.
 
-               *files* (:class:`~collections.abc.Iterable` of :class:`str`) 
contains
-               the file paths to be matched against :attr:`self.patterns
-               <PathSpec.patterns>`.
+               *files* (:class:`~collections.abc.Iterable` of :class:`str; or
+               :class:`pathlib.PurePath`) contains the file paths to be matched
+               against :attr:`self.patterns <PathSpec.patterns>`.
 
                *separators* (:class:`~collections.abc.Collection` of 
:class:`str`;
                or :data:`None`) optionally contains the path separators to
@@ -106,7 +129,7 @@
                Returns the matched files (:class:`~collections.abc.Iterable` of
                :class:`str`).
                """
-               if isinstance(files, (bytes, unicode)):
+               if not util._is_iterable(files):
                        raise TypeError("files:{!r} is not an 
iterable.".format(files))
 
                file_map = util.normalize_files(files, separators=separators)
@@ -114,24 +137,49 @@
                for path in matched_files:
                        yield file_map[path]
 
-       def match_tree(self, root, on_error=None, follow_links=None):
+       def match_tree_entries(self, root, on_error=None, follow_links=None):
                """
                Walks the specified root path for all files and matches them to 
this
                path-spec.
 
-               *root* (:class:`str`) is the root directory to search for files.
+               *root* (:class:`str`; or :class:`pathlib.PurePath`) is the root
+               directory to search.
 
                *on_error* (:class:`~collections.abc.Callable` or :data:`None`)
                optionally is the error handler for file-system exceptions. See
-               :func:`~pathspec.util.iter_tree` for more information.
+               :func:`~pathspec.util.iter_tree_entries` for more information.
 
+               *follow_links* (:class:`bool` or :data:`None`) optionally is 
whether
+               to walk symbolic links that resolve to directories. See
+               :func:`~pathspec.util.iter_tree_files` for more information.
+
+               Returns the matched files (:class:`~collections.abc.Iterable` of
+               :class:`str`).
+               """
+               entries = util.iter_tree_entries(root, on_error=on_error, 
follow_links=follow_links)
+               return self.match_entries(entries)
+
+       def match_tree_files(self, root, on_error=None, follow_links=None):
+               """
+               Walks the specified root path for all files and matches them to 
this
+               path-spec.
+
+               *root* (:class:`str`; or :class:`pathlib.PurePath`) is the root
+               directory to search for files.
+
+               *on_error* (:class:`~collections.abc.Callable` or :data:`None`)
+               optionally is the error handler for file-system exceptions. See
+               :func:`~pathspec.util.iter_tree_files` for more information.
 
                *follow_links* (:class:`bool` or :data:`None`) optionally is 
whether
-               to walk symbolik links that resolve to directories. See
-               :func:`~pathspec.util.iter_tree` for more information.
+               to walk symbolic links that resolve to directories. See
+               :func:`~pathspec.util.iter_tree_files` for more information.
 
                Returns the matched files (:class:`~collections.abc.Iterable` of
                :class:`str`).
                """
-               files = util.iter_tree(root, on_error=on_error, 
follow_links=follow_links)
+               files = util.iter_tree_files(root, on_error=on_error, 
follow_links=follow_links)
                return self.match_files(files)
+
+       # Alias `match_tree_files()` as `match_tree()`.
+       match_tree = match_tree_files
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pathspec-0.7.0/pathspec/tests/test_util.py 
new/pathspec-0.8.0/pathspec/tests/test_util.py
--- old/pathspec-0.7.0/pathspec/tests/test_util.py      2018-12-01 
18:58:00.000000000 +0100
+++ new/pathspec-0.8.0/pathspec/tests/test_util.py      2020-04-07 
04:21:51.000000000 +0200
@@ -7,15 +7,16 @@
 import os
 import os.path
 import shutil
+import sys
 import tempfile
 import unittest
 
-from pathspec.util import iter_tree, RecursionError
+from pathspec.util import iter_tree_entries, iter_tree_files, RecursionError, 
normalize_file
 
 
 class IterTreeTest(unittest.TestCase):
        """
-       The ``IterTreeTest`` class tests `pathspec.util.iter_tree()`.
+       The ``IterTreeTest`` class tests `pathspec.util.iter_tree_files()`.
        """
 
        def make_dirs(self, dirs):
@@ -98,7 +99,7 @@
                        'Dir/Inner/e',
                        'Dir/Inner/f',
                ])
-               results = set(iter_tree(self.temp_dir))
+               results = set(iter_tree_files(self.temp_dir))
                self.assertEqual(results, set(map(self.ospath, [
                        'a',
                        'b',
@@ -176,7 +177,7 @@
                        ('Dir/dx', 'Dir/d'),
                        ('DirX', 'Dir'),
                ])
-               results = set(iter_tree(self.temp_dir))
+               results = set(iter_tree_files(self.temp_dir))
                self.assertEqual(results, set(map(self.ospath, [
                        'a',
                        'ax',
@@ -213,7 +214,7 @@
                        ('Dir/Ex', 'Dir/Target'),
                        ('Dir/Fx', 'Dir/Target'),
                ])
-               results = set(iter_tree(self.temp_dir))
+               results = set(iter_tree_files(self.temp_dir))
                self.assertEqual(results, set(map(self.ospath, [
                        'Ax/Ex/file',
                        'Ax/Fx/file',
@@ -244,7 +245,7 @@
                        ('Dir/Self', 'Dir'),
                ])
                with self.assertRaises(RecursionError) as context:
-                       set(iter_tree(self.temp_dir))
+                       set(iter_tree_files(self.temp_dir))
                self.assertEqual(context.exception.first_path, 'Dir')
                self.assertEqual(context.exception.second_path, 
self.ospath('Dir/Self'))
 
@@ -270,7 +271,7 @@
                        ('C/Ax', 'A'),
                ])
                with self.assertRaises(RecursionError) as context:
-                       set(iter_tree(self.temp_dir))
+                       set(iter_tree_files(self.temp_dir))
                self.assertIn(context.exception.first_path, ('A', 'B', 'C'))
                self.assertEqual(context.exception.second_path, {
                        'A': self.ospath('A/Bx/Cx/Ax'),
@@ -290,7 +291,7 @@
                        ('A', 'DOES_NOT_EXIST'),
                ])
                with self.assertRaises(OSError) as context:
-                       set(iter_tree(self.temp_dir, on_error=reraise))
+                       set(iter_tree_files(self.temp_dir, on_error=reraise))
                self.assertEqual(context.exception.errno, errno.ENOENT)
 
        def test_2_7_ignore_broken_links(self):
@@ -301,7 +302,7 @@
                self.make_links([
                        ('A', 'DOES_NOT_EXIST'),
                ])
-               results = set(iter_tree(self.temp_dir))
+               results = set(iter_tree_files(self.temp_dir))
                self.assertEqual(results, set())
 
        def test_2_8_no_follow_links(self):
@@ -325,7 +326,7 @@
                        ('Dir/Dx', 'Dir/D'),
                        ('DirX', 'Dir'),
                ])
-               results = set(iter_tree(self.temp_dir, follow_links=False))
+               results = set(iter_tree_files(self.temp_dir, 
follow_links=False))
                self.assertEqual(results, set(map(self.ospath, [
                        'A',
                        'Ax',
@@ -335,4 +336,45 @@
                        'Dir/Cx',
                        'Dir/D',
                        'Dir/Dx',
+                       'DirX',
                ])))
+
+       def test_3_entries(self):
+               """
+               Tests to make sure all files are found.
+               """
+               self.make_dirs([
+                       'Empty',
+                       'Dir',
+                       'Dir/Inner',
+               ])
+               self.make_files([
+                       'a',
+                       'b',
+                       'Dir/c',
+                       'Dir/d',
+                       'Dir/Inner/e',
+                       'Dir/Inner/f',
+               ])
+               results = {entry.path for entry in 
iter_tree_entries(self.temp_dir)}
+               self.assertEqual(results, set(map(self.ospath, [
+                       'a',
+                       'b',
+                       'Dir',
+                       'Dir/c',
+                       'Dir/d',
+                       'Dir/Inner',
+                       'Dir/Inner/e',
+                       'Dir/Inner/f',
+                       'Empty',
+               ])))
+
+       @unittest.skipIf(sys.version_info < (3, 4), "pathlib entered stdlib in 
Python 3.4")
+       def test_4_normalizing_pathlib_path(self):
+               """
+               Tests passing pathlib.Path as argument.
+               """
+               from pathlib import Path
+               first_spec = normalize_file(Path('a.txt'))
+               second_spec = normalize_file('a.txt')
+               self.assertEqual(first_spec, second_spec)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pathspec-0.7.0/pathspec/util.py 
new/pathspec-0.8.0/pathspec/util.py
--- old/pathspec-0.7.0/pathspec/util.py 2018-12-01 18:57:36.000000000 +0100
+++ new/pathspec-0.8.0/pathspec/util.py 2020-04-07 05:43:06.000000000 +0200
@@ -8,7 +8,7 @@
 import posixpath
 import stat
 
-from .compat import collection_type, string_types
+from .compat import Collection, Iterable, string_types, unicode
 
 NORMALIZE_PATH_SEPS = [sep for sep in [os.sep, os.altsep] if sep and sep != 
posixpath.sep]
 """
@@ -20,11 +20,96 @@
 
 _registered_patterns = {}
 """
-*_registered_patterns* (``dict``) maps a name (``str``) to the
-registered pattern factory (``callable``).
+*_registered_patterns* (:class:`dict`) maps a name (:class:`str`) to the
+registered pattern factory (:class:`~collections.abc.Callable`).
 """
 
-def iter_tree(root, on_error=None, follow_links=None):
+
+def detailed_match_files(patterns, files, all_matches=None):
+       """
+       Matches the files to the patterns, and returns which patterns matched
+       the files.
+
+       *patterns* (:class:`~collections.abc.Iterable` of 
:class:`~pathspec.pattern.Pattern`)
+       contains the patterns to use.
+
+       *files* (:class:`~collections.abc.Iterable` of :class:`str`) contains
+       the normalized file paths to be matched against *patterns*.
+
+       *all_matches* (:class:`boot` or :data:`None`) is whether to return all
+       matches patterns (:data:`True`), or only the last matched pattern
+       (:data:`False`). Default is :data:`None` for :data:`False`.
+
+       Returns the matched files (:class:`dict`) which maps each matched file
+       (:class:`str`) to the patterns that matched in order 
(:class:`.MatchDetail`).
+       """
+       all_files = files if isinstance(files, Collection) else list(files)
+       return_files = {}
+       for pattern in patterns:
+               if pattern.include is not None:
+                       result_files = pattern.match(all_files)
+                       if pattern.include:
+                               # Add files and record pattern.
+                               for result_file in result_files:
+                                       if result_file in return_files:
+                                               if all_matches:
+                                                       
return_files[result_file].patterns.append(pattern)
+                                               else:
+                                                       
return_files[result_file].patterns[0] = pattern
+                                       else:
+                                               return_files[result_file] = 
MatchDetail([pattern])
+
+                       else:
+                               # Remove files.
+                               for file in result_files:
+                                       del return_files[file]
+
+       return return_files
+
+
+def _is_iterable(value):
+       """
+       Check whether the value is an iterable (excludes strings).
+
+       *value* is the value to check,
+
+       Returns whether *value* is a iterable (:class:`bool`).
+       """
+       return isinstance(value, Iterable) and not isinstance(value, (unicode, 
bytes))
+
+
+def iter_tree_entries(root, on_error=None, follow_links=None):
+       """
+       Walks the specified directory for all files and directories.
+
+       *root* (:class:`str`) is the root directory to search.
+
+       *on_error* (:class:`~collections.abc.Callable` or :data:`None`)
+       optionally is the error handler for file-system exceptions. It will be
+       called with the exception (:exc:`OSError`). Reraise the exception to
+       abort the walk. Default is :data:`None` to ignore file-system
+       exceptions.
+
+       *follow_links* (:class:`bool` or :data:`None`) optionally is whether
+       to walk symbolic links that resolve to directories. Default is
+       :data:`None` for :data:`True`.
+
+       Raises :exc:`RecursionError` if recursion is detected.
+
+       Returns an :class:`~collections.abc.Iterable` yielding each file or
+       directory entry (:class:`.TreeEntry`) relative to *root*.
+       """
+       if on_error is not None and not callable(on_error):
+               raise TypeError("on_error:{!r} is not 
callable.".format(on_error))
+
+       if follow_links is None:
+               follow_links = True
+
+       for entry in _iter_tree_entries_next(os.path.abspath(root), '', {}, 
on_error, follow_links):
+               yield entry
+
+
+def iter_tree_files(root, on_error=None, follow_links=None):
        """
        Walks the specified directory for all files.
 
@@ -37,7 +122,7 @@
        exceptions.
 
        *follow_links* (:class:`bool` or :data:`None`) optionally is whether
-       to walk symbolik links that resolve to directories. Default is
+       to walk symbolic links that resolve to directories. Default is
        :data:`None` for :data:`True`.
 
        Raises :exc:`RecursionError` if recursion is detected.
@@ -51,10 +136,16 @@
        if follow_links is None:
                follow_links = True
 
-       for file_rel in _iter_tree_next(os.path.abspath(root), '', {}, 
on_error, follow_links):
-               yield file_rel
+       for entry in _iter_tree_entries_next(os.path.abspath(root), '', {}, 
on_error, follow_links):
+               if not entry.is_dir(follow_links):
+                       yield entry.path
 
-def _iter_tree_next(root_full, dir_rel, memo, on_error, follow_links):
+
+# Alias `iter_tree_files()` as `iter_tree()`.
+iter_tree = iter_tree_files
+
+
+def _iter_tree_entries_next(root_full, dir_rel, memo, on_error, follow_links):
        """
        Scan the directory for all descendant files.
 
@@ -64,14 +155,16 @@
        *root_full*.
 
        *memo* (:class:`dict`) keeps track of ancestor directories
-       encountered. Maps each ancestor real path (:class:`str``) to relative
+       encountered. Maps each ancestor real path (:class:`str`) to relative
        path (:class:`str`).
 
        *on_error* (:class:`~collections.abc.Callable` or :data:`None`)
        optionally is the error handler for file-system exceptions.
 
-       *follow_links* (:class:`bool`) is whether to walk symbolik links that
+       *follow_links* (:class:`bool`) is whether to walk symbolic links that
        resolve to directories.
+
+       Yields each entry (:class:`.TreeEntry`).
        """
        dir_full = os.path.join(root_full, dir_rel)
        dir_real = os.path.realpath(dir_full)
@@ -84,19 +177,19 @@
        else:
                raise RecursionError(real_path=dir_real, 
first_path=memo[dir_real], second_path=dir_rel)
 
-       for node in os.listdir(dir_full):
-               node_rel = os.path.join(dir_rel, node)
+       for node_name in os.listdir(dir_full):
+               node_rel = os.path.join(dir_rel, node_name)
                node_full = os.path.join(root_full, node_rel)
 
                # Inspect child node.
                try:
-                       node_stat = os.lstat(node_full)
+                       node_lstat = os.lstat(node_full)
                except OSError as e:
                        if on_error is not None:
                                on_error(e)
                        continue
 
-               if stat.S_ISLNK(node_stat.st_mode):
+               if stat.S_ISLNK(node_lstat.st_mode):
                        # Child node is a link, inspect the target node.
                        is_link = True
                        try:
@@ -107,24 +200,28 @@
                                continue
                else:
                        is_link = False
+                       node_stat = node_lstat
 
                if stat.S_ISDIR(node_stat.st_mode) and (follow_links or not 
is_link):
                        # Child node is a directory, recurse into it and yield 
its
-                       # decendant files.
-                       for file_rel in _iter_tree_next(root_full, node_rel, 
memo, on_error, follow_links):
-                               yield file_rel
-
-               elif stat.S_ISREG(node_stat.st_mode):
-                       # Child node is a file, yield it.
-                       yield node_rel
+                       # descendant files.
+                       yield TreeEntry(node_name, node_rel, node_lstat, 
node_stat)
+
+                       for entry in _iter_tree_entries_next(root_full, 
node_rel, memo, on_error, follow_links):
+                               yield entry
+
+               elif stat.S_ISREG(node_stat.st_mode) or is_link:
+                       # Child node is either a file or an unfollowed link, 
yield it.
+                       yield TreeEntry(node_name, node_rel, node_lstat, 
node_stat)
 
        # NOTE: Make sure to remove the canonical (real) path of the directory
        # from the ancestors memo once we are done with it. This allows the
        # same directory to appear multiple times. If this is not done, the
-       # second occurance of the directory will be incorrectly interpreted as
-       # a recursion. See 
<https://github.com/cpburnz/python-path-specification/pull/7>.
+       # second occurrence of the directory will be incorrectly interpreted
+       # as a recursion. See 
<https://github.com/cpburnz/python-path-specification/pull/7>.
        del memo[dir_real]
 
+
 def lookup_pattern(name):
        """
        Lookups a registered pattern factory by name.
@@ -136,6 +233,7 @@
        """
        return _registered_patterns[name]
 
+
 def match_file(patterns, file):
        """
        Matches the file to the patterns.
@@ -155,6 +253,7 @@
                                matched = pattern.include
        return matched
 
+
 def match_files(patterns, files):
        """
        Matches the files to the patterns.
@@ -167,7 +266,7 @@
 
        Returns the matched files (:class:`set` of :class:`str`).
        """
-       all_files = files if isinstance(files, collection_type) else list(files)
+       all_files = files if isinstance(files, Collection) else list(files)
        return_files = set()
        for pattern in patterns:
                if pattern.include is not None:
@@ -178,11 +277,32 @@
                                return_files.difference_update(result_files)
        return return_files
 
+
+def _normalize_entries(entries, separators=None):
+       """
+       Normalizes the entry paths to use the POSIX path separator.
+
+       *entries* (:class:`~collections.abc.Iterable` of :class:`.TreeEntry`)
+       contains the entries to be normalized.
+
+       *separators* (:class:`~collections.abc.Collection` of :class:`str`; or
+       :data:`None`) optionally contains the path separators to normalize.
+       See :func:`normalize_file` for more information.
+
+       Returns a :class:`dict` mapping the each normalized file path 
(:class:`str`)
+       to the entry (:class:`.TreeEntry`)
+       """
+       norm_files = {}
+       for entry in entries:
+               norm_files[normalize_file(entry.path, separators=separators)] = 
entry
+       return norm_files
+
+
 def normalize_file(file, separators=None):
        """
        Normalizes the file path to use the POSIX path separator (i.e., 
``'/'``).
 
-       *file* (:class:`str`) is the file path.
+       *file* (:class:`str` or :class:`pathlib.PurePath`) is the file path.
 
        *separators* (:class:`~collections.abc.Collection` of :class:`str`; or
        :data:`None`) optionally contains the path separators to normalize.
@@ -196,7 +316,10 @@
        # Normalize path separators.
        if separators is None:
                separators = NORMALIZE_PATH_SEPS
-       norm_file = file
+
+       # Convert path object to string.
+       norm_file = str(file)
+
        for sep in separators:
                norm_file = norm_file.replace(sep, posixpath.sep)
 
@@ -206,12 +329,13 @@
 
        return norm_file
 
+
 def normalize_files(files, separators=None):
        """
        Normalizes the file paths to use the POSIX path separator.
 
-       *files* (:class:`~collections.abc.Iterable` of :class:`str`) contains
-       the file paths to be normalized.
+       *files* (:class:`~collections.abc.Iterable` of :class:`str` or
+       :class:`pathlib.PurePath`) contains the file paths to be normalized.
 
        *separators* (:class:`~collections.abc.Collection` of :class:`str`; or
        :data:`None`) optionally contains the path separators to normalize.
@@ -225,6 +349,7 @@
                norm_files[normalize_file(path, separators=separators)] = path
        return norm_files
 
+
 def register_pattern(name, pattern_factory, override=None):
        """
        Registers the specified pattern factory.
@@ -348,3 +473,128 @@
                :attr:`self.real_path <RecursionError.real_path>`.
                """
                return self.args[2]
+
+
+class MatchDetail(object):
+       """
+       The :class:`.MatchDetail` class contains information about
+       """
+
+       #: Make the class dict-less.
+       __slots__ = ('patterns',)
+
+       def __init__(self, patterns):
+               """
+               Initialize the :class:`.MatchDetail` instance.
+
+               *patterns* (:class:`~collections.abc.Sequence` of 
:class:`~pathspec.pattern.Pattern`)
+               contains the patterns that matched the file in the order they 
were
+               encountered.
+               """
+
+               self.patterns = patterns
+               """
+               *patterns* (:class:`~collections.abc.Sequence` of 
:class:`~pathspec.pattern.Pattern`)
+               contains the patterns that matched the file in the order they 
were
+               encountered.
+               """
+
+
+class TreeEntry(object):
+       """
+       The :class:`.TreeEntry` class contains information about a file-system
+       entry.
+       """
+
+       #: Make the class dict-less.
+       __slots__ = ('_lstat', 'name', 'path', '_stat')
+
+       def __init__(self, name, path, lstat, stat):
+               """
+               Initialize the :class:`.TreeEntry` instance.
+
+               *name* (:class:`str`) is the base name of the entry.
+
+               *path* (:class:`str`) is the relative path of the entry.
+
+               *lstat* (:class:`~os.stat_result`) is the stat result of the 
direct
+               entry.
+
+               *stat* (:class:`~os.stat_result`) is the stat result of the 
entry,
+               potentially linked.
+               """
+
+               self._lstat = lstat
+               """
+               *_lstat* (:class:`~os.stat_result`) is the stat result of the 
direct
+               entry.
+               """
+
+               self.name = name
+               """
+               *name* (:class:`str`) is the base name of the entry.
+               """
+
+               self.path = path
+               """
+               *path* (:class:`str`) is the path of the entry.
+               """
+
+               self._stat = stat
+               """
+               *_stat* (:class:`~os.stat_result`) is the stat result of the 
linked
+               entry.
+               """
+
+       def is_dir(self, follow_links=None):
+               """
+               Get whether the entry is a directory.
+
+               *follow_links* (:class:`bool` or :data:`None`) is whether to 
follow
+               symbolic links. If this is :data:`True`, a symlink to a 
directory
+               will result in :data:`True`. Default is :data:`None` for 
:data:`True`.
+
+               Returns whether the entry is a directory (:class:`bool`).
+               """
+               if follow_links is None:
+                       follow_links = True
+
+               node_stat = self._stat if follow_links else self._lstat
+               return stat.S_ISDIR(node_stat.st_mode)
+
+       def is_file(self, follow_links=None):
+               """
+               Get whether the entry is a regular file.
+
+               *follow_links* (:class:`bool` or :data:`None`) is whether to 
follow
+               symbolic links. If this is :data:`True`, a symlink to a regular 
file
+               will result in :data:`True`. Default is :data:`None` for 
:data:`True`.
+
+               Returns whether the entry is a regular file (:class:`bool`).
+               """
+               if follow_links is None:
+                       follow_links = True
+
+               node_stat = self._stat if follow_links else self._lstat
+               return stat.S_ISREG(node_stat.st_mode)
+
+       def is_symlink(self):
+               """
+               Returns whether the entry is a symbolic link (:class:`bool`).
+               """
+               return stat.S_ISLNK(self._lstat.st_mode)
+
+       def stat(self, follow_links=None):
+               """
+               Get the cached stat result for the entry.
+
+               *follow_links* (:class:`bool` or :data:`None`) is whether to 
follow
+               symbolic links. If this is :data:`True`, the stat result of the
+               linked file will be returned. Default is :data:`None` for 
:data:`True`.
+
+               Returns that stat result (:class:`~os.stat_result`).
+               """
+               if follow_links is None:
+                       follow_links = True
+
+               return self._stat if follow_links else self._lstat
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pathspec-0.7.0/pathspec.egg-info/PKG-INFO 
new/pathspec-0.8.0/pathspec.egg-info/PKG-INFO
--- old/pathspec-0.7.0/pathspec.egg-info/PKG-INFO       2019-12-27 
16:31:53.000000000 +0100
+++ new/pathspec-0.8.0/pathspec.egg-info/PKG-INFO       2020-04-09 
15:08:33.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.2
 Name: pathspec
-Version: 0.7.0
+Version: 0.8.0
 Summary: Utility library for gitignore style pattern matching of file paths.
 Home-page: https://github.com/cpburnz/python-path-specification
 Author: Caleb P. Burns
@@ -163,6 +163,19 @@
         ==============
         
         
+        0.8.0 (2020-04-09)
+        ------------------
+        
+        - `Issue #30`_: Expose what patterns matched paths. Added 
`util.detailed_match_files()`.
+        - `Issue #31`_: `match_tree()` doesn't return symlinks.
+        - Add `PathSpec.match_tree_entries` and `util.iter_tree_entries()` to 
support directories and symlinks.
+        - API change: `match_tree()` has been renamed to `match_tree_files()`. 
The old name `match_tree()` is still available as an alias.
+        - API change: `match_tree_files()` now returns symlinks. This is a bug 
fix but it will change the returned results.
+        
+        .. _`Issue #30`: 
https://github.com/cpburnz/python-path-specification/issues/30
+        .. _`Issue #31`: 
https://github.com/cpburnz/python-path-specification/issues/31
+        
+        
         0.7.0 (2019-12-27)
         ------------------
         


Reply via email to